diff options
| author | Pinapelz <yukais@pinapelz.com> | 2024-11-03 23:09:39 -0800 |
|---|---|---|
| committer | Pinapelz <yukais@pinapelz.com> | 2024-11-03 23:09:39 -0800 |
| commit | 221cb6fba838ac287860c660a4c3f94a7fe47e77 (patch) | |
| tree | 68232d8978acc3ddeab92d19cba2b83ae9ca7283 | |
| parent | 21d496c324b025d907574076e4115919ba2e7f64 (diff) | |
add sidebar navigation
| -rw-r--r-- | src/app/page.tsx | 74 | ||||
| -rw-r--r-- | src/components/SubscriberTable/SubscriberTable.tsx | 3 | ||||
| -rw-r--r-- | src/components/TitleBar/TitleBar.tsx | 245 |
3 files changed, 245 insertions, 77 deletions
diff --git a/src/app/page.tsx b/src/app/page.tsx index 9c243db..1723043 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,46 +1,50 @@ import SubscriberTable, { - type SubscriberDataTableProp, + type SubscriberDataTableProp, } from "../components/SubscriberTable/SubscriberTable"; import TitleBar from "../components/TitleBar/TitleBar"; async function Home() { - const graphURL = process.env.NEXT_PUBLIC_GRAPH_URL; - const data: SubscriberDataTableProp = await getData(); - return ( - <> - <TitleBar title="PhaseTracker" backgroundColor="black" /> - <div - className="sm:block hidden mt-4" - style={{ overflow: "hidden", height: "105vh", position: "relative" }} - > - <iframe - title="Phase Connect Subscriber Count Graph" - src={graphURL} - style={{ position: "absolute", top: 0, left: 0 }} - width="100%" - height="100%" - /> - </div> - <SubscriberTable {...data} /> - </> - ); + const graphURL = process.env.NEXT_PUBLIC_GRAPH_URL; + const data: SubscriberDataTableProp = await getData(); + return ( + <> + <TitleBar title="PhaseTracker" backgroundColor="black" /> + <div + className="sm:block hidden mt-4" + style={{ + overflow: "hidden", + height: "105vh", + position: "relative", + }} + > + <iframe + title="Phase Connect Subscriber Count Graph" + src={graphURL} + style={{ position: "absolute", top: 0, left: 0 }} + width="100%" + height="100%" + /> + </div> + <SubscriberTable {...data} /> + </> + ); } async function getData() { - const apiUrl = process.env.NEXT_PUBLIC_API_URL_TESTING; - const endpoint = "/api/subscribers"; - const headers = { - "Cache-Control": "no-cache", - }; - const cacheOption = "no-cache"; + const apiUrl = process.env.NEXT_PUBLIC_API_URL_TESTING; + const endpoint = "/api/subscribers"; + const headers = { + "Cache-Control": "no-cache", + }; + const cacheOption = "no-cache"; - const response = await fetch(`${apiUrl}${endpoint}`, { - headers: headers, - cache: cacheOption, - }); - if (!response.ok) { - console.log(response.statusText); - } - return response.json(); + const response = await fetch(`${apiUrl}${endpoint}`, { + headers: headers, + cache: cacheOption, + }); + if (!response.ok) { + console.log(response.statusText); + } + return response.json(); } export default Home; diff --git a/src/components/SubscriberTable/SubscriberTable.tsx b/src/components/SubscriberTable/SubscriberTable.tsx index 43c8c16..766cd3d 100644 --- a/src/components/SubscriberTable/SubscriberTable.tsx +++ b/src/components/SubscriberTable/SubscriberTable.tsx @@ -30,9 +30,6 @@ const DataTable = ({ channel_data, timestamp }: SubscriberDataTableProp) => { <h1 className="text-2xl font-bold text-gray-800"> Subscriber Count </h1> - <h2 className="text-xl text-gray-800"> - Click on a row to view more details! - </h2> <p className="text-gray-500 text-sm"> Updated Hourly. Retrieved at: {timestamp} </p> diff --git a/src/components/TitleBar/TitleBar.tsx b/src/components/TitleBar/TitleBar.tsx index 127534e..c4a65c4 100644 --- a/src/components/TitleBar/TitleBar.tsx +++ b/src/components/TitleBar/TitleBar.tsx @@ -1,51 +1,218 @@ +"use client"; import type React from "react"; import "../TitleBar/TitleBarStyle.css"; -import { faHouse } from "@fortawesome/free-solid-svg-icons"; +import { + faHouse, + faBars, + faTimes, + faChevronDown, + faChevronUp, + faSpinner, +} from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useState, useEffect } from "react"; +import Link from "next/link"; interface TitleBarProps { - title: string; - redirectUrl?: string; - showHomeButton?: boolean; - backgroundColor?: string; + title: string; + redirectUrl?: string; + showHomeButton?: boolean; + backgroundColor?: string; } const TitleBar: React.FC<TitleBarProps> = ({ - title, - redirectUrl, - showHomeButton, - backgroundColor, + title, + redirectUrl, + showHomeButton, + backgroundColor, }) => { - return ( - <> - <div - className="title-bar p-5 shadow-md" - style={{ backgroundColor: backgroundColor || "#2D4B71" }} - > - <div - style={{ - display: "flex", - justifyContent: "space-between", - alignItems: "center", - }} - > - <a href={redirectUrl}> - <span - className="text-white text-4xl font-bold" - style={{ fontFamily: "Quantico, sans-serif" }} - > - {title} - </span> - </a> - {showHomeButton && ( - <a href="/" className="text-white text-3xl"> - <FontAwesomeIcon icon={faHouse} /> - </a> - )} - </div> - </div> - </> - ); + const hideFromSidebar = ["Fuura Yuri"]; // List of names to hide (e.g., due to graduation) + const [isSidebarOpen, setIsSidebarOpen] = useState(false); + const [isMounted, setIsMounted] = useState(false); + const [groupingData, setPhaseData] = useState<{ + [key: string]: string[]; + } | null>(null); + const [collapsedSections, setCollapsedSections] = useState<{ + [key: string]: boolean; + }>({}); + const [loadingMember, setLoadingMember] = useState<string | null>(null); + + useEffect(() => { + setIsMounted(true); + const fetchPhaseData = async () => { + const apiUrl = process.env.NEXT_PUBLIC_API_URL_TESTING; + try { + const response = await fetch(apiUrl + "/api/groups"); + const data = await response.json(); + setPhaseData(data); + const initialCollapsedState = Object.keys(data).reduce( + (acc, phase) => { + acc[phase] = true; + return acc; + }, + {} as { [key: string]: boolean }, + ); + setCollapsedSections(initialCollapsedState); + } catch (error) { + console.error("Error fetching phase data:", error); + } + }; + + fetchPhaseData(); + }, []); + + const toggleSidebar = () => { + setIsSidebarOpen(!isSidebarOpen); + }; + + const toggleSection = (phase: string) => { + setCollapsedSections((prevState) => ({ + ...prevState, + [phase]: !prevState[phase], + })); + }; + + const handleMemberClick = (member: string) => { + setLoadingMember(member); + }; + + if (!isMounted) { + return null; + } + + return ( + <> + <div + className="title-bar p-5 shadow-md" + style={{ backgroundColor: backgroundColor || "#2D4B71" }} + > + <div + style={{ + display: "flex", + justifyContent: "space-between", + alignItems: "center", + }} + > + <button + onClick={toggleSidebar} + className="text-white text-3xl mr-4 focus:outline-none" + > + <FontAwesomeIcon + icon={isSidebarOpen ? faTimes : faBars} + /> + </button> + <a href={redirectUrl}> + <span + className="text-white text-4xl font-bold" + style={{ fontFamily: "Quantico, sans-serif" }} + > + {title} + </span> + </a> + {showHomeButton && ( + <a href="/" className="ml-2 text-white text-3xl"> + <FontAwesomeIcon icon={faHouse} /> + </a> + )} + </div> + </div> + + {/* Sidebar */} + <div + className={`fixed top-0 left-0 h-screen bg-black text-white shadow-lg transition-transform transform ${ + isSidebarOpen ? "translate-x-0" : "-translate-x-full" + } duration-500 ease-in-out z-50`} + style={{ width: "16rem", fontFamily: "Quantico, sans-serif" }} + > + <div className="p-4 text-3xl font-bold border-b border-gray-700"> + PhaseTracker + </div> + <ul className="text-xl border-b border-gray-700"> + <Link href="/"> + <li className="p-4 hover:bg-gray-700 transition-colors duration-300"> + Home + </li> + </Link> + <Link href="/about"> + <li className="p-4 hover:bg-gray-700 transition-colors duration-300"> + About + </li> + </Link> + </ul> + + <ul className="mt-4 text-xl"> + {groupingData ? ( + Object.entries(groupingData).map(([group, members]) => ( + <li key={group} className="p-4"> + <div + className="flex justify-between items-center cursor-pointer select-none" + onClick={() => toggleSection(group)} + > + <span className="font-bold text-lg"> + {group} + </span> + <FontAwesomeIcon + icon={ + collapsedSections[group] + ? faChevronDown + : faChevronUp + } + /> + </div> + {!collapsedSections[group] && ( + <ul className="ml-4 mt-2"> + {members + .filter( + (member) => + !hideFromSidebar.includes( + member, + ), + ) + .map((member) => ( + <a + href={`/stats/${member}`} + key={member} + > + <li + className="p-1 hover:bg-gray-700 transition-colors duration-300 flex items-center" + onClick={() => + handleMemberClick( + member, + ) + } + > + {member} + {loadingMember === + member && ( + <FontAwesomeIcon + icon={faSpinner} + spin + className="ml-2" + /> + )} + </li> + </a> + ))} + </ul> + )} + </li> + )) + ) : ( + <li className="p-4">Loading...</li> + )} + </ul> + </div> + + {/* Content overlay when sidebar is open */} + {isSidebarOpen && ( + <div + className="fixed top-0 left-0 w-full h-full bg-black opacity-50 transition-opacity duration-500" + style={{ zIndex: 40 }} + onClick={toggleSidebar} + ></div> + )} + </> + ); }; export default TitleBar; |
