From 97be0afd140c7c9e1fd03ba5ab4e486d90907129 Mon Sep 17 00:00:00 2001 From: Wolfboy248 Date: Tue, 19 Aug 2025 13:23:56 +0200 Subject: organised pages, started work on theme --- frontend/src/App.css | 366 ++++++------------ frontend/src/App.tsx | 106 ++++-- frontend/src/components/GameCategory.tsx | 6 +- frontend/src/components/GameEntry.tsx | 6 +- frontend/src/components/Sidebar.tsx | 43 ++- frontend/src/pages/About.tsx | 36 -- frontend/src/pages/About/About.tsx | 2 +- frontend/src/pages/Games.tsx | 29 -- frontend/src/pages/Games/Games.tsx | 6 +- frontend/src/pages/Home/Homepage.tsx | 6 +- frontend/src/pages/Homepage.tsx | 31 -- frontend/src/pages/Maplist.tsx | 249 ------------ frontend/src/pages/Maplist/Maplist.tsx | 267 +++++++------ frontend/src/pages/Maps.tsx | 185 --------- frontend/src/pages/Profile.tsx | 633 ------------------------------- frontend/src/pages/Rankings.tsx | 203 ---------- frontend/src/pages/Rules.tsx | 37 -- frontend/src/pages/User.tsx | 410 -------------------- 18 files changed, 342 insertions(+), 2279 deletions(-) delete mode 100644 frontend/src/pages/About.tsx delete mode 100644 frontend/src/pages/Games.tsx delete mode 100644 frontend/src/pages/Homepage.tsx delete mode 100644 frontend/src/pages/Maplist.tsx delete mode 100644 frontend/src/pages/Maps.tsx delete mode 100644 frontend/src/pages/Profile.tsx delete mode 100644 frontend/src/pages/Rankings.tsx delete mode 100644 frontend/src/pages/Rules.tsx delete mode 100644 frontend/src/pages/User.tsx (limited to 'frontend/src') diff --git a/frontend/src/App.css b/frontend/src/App.css index 464b759..a39dcf1 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,298 +1,150 @@ -@import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Montserrat+Alternates:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); +@layer theme, base, components, utilities; + @import "tailwindcss"; @theme { - --color-rosewater: #f2d5cf; - --color-flamingo: #eebebe; - --color-pink: #f4b8e4; - --color-mauve: #ca9ee6; - --color-red: #e78284; - --color-maroon: #ea999c; - --color-peach: #ef9f76; - --color-yellow: #e5c890; - --color-green: #a6d189; - --color-teal: #81c8be; - --color-sky: #99d1db; - --color-sapphire: #85c1dc; - --color-blue: #8caaee; - --color-lavender: #babbf1; - --color-text: #c6d0f5; - --color-subtext1: #b5bfe2; - --color-subtext0: #a5adce; - --color-overlay2: #949cbb; - --color-overlay1: #838ba7; - --color-overlay0: #737994; - --color-surface2: #626880; - --color-surface1: #51576d; - --color-surface0: #414559; - --color-base: #303446; - --color-mantle: #292c3c; - --color-crust: #232634; + --color-main: #141520; + --color-panel: #202232; + --color-block: #2b2e46; - --color-primary: var(--color-mauve); - --color-secondary: var(--color-blue); - --color-accent: var(--color-peach); - --color-background: var(--color-base); - --color-surface: var(--color-surface0); - --color-muted: var(--color-overlay0); - --color-border: var(--color-surface2); - --color-input: var(--color-surface1); - --color-foreground: var(--color-text); - --color-success: var(--color-green); - --color-warning: var(--color-yellow); - --color-error: var(--color-red); - --color-info: var(--color-blue); + --color-white: #cdcfdf; - --font-barlow-condensed-regular: 'BarlowCondensed-Regular'; - --font-barlow-condensed-bold: 'BarlowCondensed-Bold'; - --font-barlow-semicondensed-regular: 'BarlowSemiCondensed-Regular'; - --font-barlow-semicondensed-semibold: 'BarlowSemiCondensed-SemiBold'; + --font-barlow-condensed-regular: BarlowCondensed-Regular; + --font-barlow-condensed-bold: BarlowCondensed-Bold; + --font-barlow-semicondensed-regular: BarlowSemiCondensed-Regular; + --font-barlow-semicondensed-semibold: BarlowSemiCondensed-SemiBold; } +@layer utilities {} a { - color: inherit; + color: inherit; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: var(--font-barlow-condensed-bold); +} + +h1 { + font-size: 48px; +} + +h2 { + font-size: 32px; +} + +h2 { + font-size: 24px; +} + +h3 { + font-size: 20px; +} + +h4 { + font-size: 18px; +} + +h5 { + font-size: 16px; +} + +h6 { + font-size: 14px; +} + +p { + font-size: 16px; } body { - overflow: hidden; - background-color: var(--color-crust); - margin: 0; + overflow: hidden; + background-color: var(--color-main); + color: var(--color-white); + font-family: var(--font-barlow-semicondensed-regular); + margin: 0; +} + +main { + overflow-y: auto; +} + +main>div { + width: 100%; } .loader { - animation: loader 1.2s ease infinite; - background-size: 400% 300%; - background-image: linear-gradient(-90deg, var(--color-mantle) 0%, var(--color-mantle) 25%, var(--color-surface1) 50%, var(--color-mantle) 75%, var(--color-mantle) 100%); - user-select: none; + animation: loader 1.2s ease infinite; + background-size: 400% 300%; + background-image: linear-gradient(-90deg, var(--color-mantle) 0%, var(--color-mantle) 25%, var(--color-surface1) 50%, var(--color-mantle) 75%, var(--color-mantle) 100%); + user-select: none; } .loader-text { - animation: loader 1.2s ease infinite; - background-size: 400% 300%; - background-image: linear-gradient(-90deg, var(--color-mantle) 0%, var(--color-mantle) 25%, var(--color-surface1) 50%, var(--color-mantle) 75%, var(--color-mantle) 100%); - user-select: none; - color: #00000000; - border-radius: 1000px; + animation: loader 1.2s ease infinite; + background-size: 400% 300%; + background-image: linear-gradient(-90deg, var(--color-mantle) 0%, var(--color-mantle) 25%, var(--color-surface1) 50%, var(--color-mantle) 75%, var(--color-mantle) 100%); + user-select: none; + color: #00000000; + border-radius: 1000px; } @keyframes loader { - 0% { - background-position: 100% 20%; - } + 0% { + background-position: 100% 20%; + } - 50% { - background-position: 0% 50%; - } + 50% { + background-position: 0% 50%; + } - 100% { - background-position: 0% 50%; - } + 100% { + background-position: 0% 50%; + } } .loader { - width: 48px; - height: 48px; - border: 5px solid #FFF; - border-bottom-color: transparent; - border-radius: 50%; - display: inline-block; - box-sizing: border-box; - animation: rotation 1s linear infinite; - } + width: 48px; + height: 48px; + border: 5px solid #FFF; + border-bottom-color: transparent; + border-radius: 50%; + display: inline-block; + box-sizing: border-box; + animation: rotation 1s linear infinite; +} @keyframes rotation { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} + 0% { + transform: rotate(0deg); + } -/* Custom Tailwind utilities for Catppuccin Frappe theme */ -@layer utilities { - .bg-primary { - background-color: var(--color-primary); - } - - .bg-secondary { - background-color: var(--color-secondary); - } - - .bg-accent { - background-color: var(--color-accent); - } - - .bg-background { - background-color: var(--color-background); - } - - .bg-surface { - background-color: var(--color-surface); - } - - .bg-muted { - background-color: var(--color-muted); - } - - .text-primary { - color: var(--color-primary); - } - - .text-secondary { - color: var(--color-secondary); - } - - .text-accent { - color: var(--color-accent); - } - - .text-foreground { - color: var(--color-foreground); - } - - .text-muted { - color: var(--color-muted); - } - - .border-primary { - border-color: var(--color-primary); - } - - .border-secondary { - border-color: var(--color-secondary); - } - - .border-muted { - border-color: var(--color-border); - } - - .hover\:bg-primary:hover { - background-color: var(--color-primary); - } - - .hover\:bg-secondary:hover { - background-color: var(--color-secondary); - } - - .hover\:bg-surface:hover { - background-color: var(--color-surface); - } - - .hover\:text-primary:hover { - color: var(--color-primary); - } - - .focus\:ring-primary:focus { - --tw-ring-color: var(--color-primary); - } - - .triangle { - width: 0; - height: 0; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-bottom: 8px solid var(--color-foreground); - display: inline-block; - } - - .sidebar-button-selected { - background-color: var(--color-primary) !important; - color: var(--color-background) !important; - } - - .sidebar-button-deselected { - background-color: var(--color-surface) !important; - color: var(--color-foreground) !important; - } - - .profileboard-record { - background-color: var(--color-surface); - border: 1px solid var(--color-border); - border-radius: 0.5rem; - padding: 0.75rem; - margin-bottom: 0.5rem; - transition: all 0.2s ease; - } - - .profileboard-record:hover { - background-color: var(--color-surface1); - } - - .difficulty-rating { - width: 20px; - height: 20px; - background-color: var(--color-muted); - border-radius: 50%; - margin: 0 2px; - display: inline-block; - } - - .nav-button { - background-color: var(--color-surface); - color: var(--color-foreground); - border: 1px solid var(--color-border); - border-radius: 0.5rem; - padding: 0.5rem 1rem; - transition: all 0.2s ease; - display: inline-flex; - align-items: center; - gap: 0.5rem; - text-decoration: none; - } - - .nav-button:hover { - background-color: var(--color-surface1); - } - - .record { - background-color: var(--color-surface); - border: 1px solid var(--color-border); - border-radius: 0.5rem; - padding: 0.5rem; - margin: 0.25rem; - cursor: pointer; - transition: all 0.2s ease; - } - - .record:hover { - background-color: var(--color-surface1); - } - - .portal-count { - font-size: 3rem; - font-weight: bold; - color: var(--color-primary); - } - - .titles { - background-color: var(--color-accent); - color: var(--color-background); - padding: 0.25rem 0.5rem; - border-radius: 1rem; - font-size: 0.875rem; - margin-right: 0.5rem; - display: inline-block; - } + 100% { + transform: rotate(360deg); + } } @font-face { - font-family: 'BarlowCondensed-Bold'; - src: local('BarlowCondensed-Bold'), url(./fonts/BarlowCondensed-Bold.ttf) format('truetype'); + font-family: 'BarlowCondensed-Bold'; + src: local('BarlowCondensed-Bold'), url(./fonts/BarlowCondensed-Bold.ttf) format('truetype'); } @font-face { - font-family: 'BarlowCondensed-Regular'; - src: local('BarlowCondensed-Regular'), url(./fonts/BarlowCondensed-Regular.ttf) format('truetype'); + font-family: 'BarlowCondensed-Regular'; + src: local('BarlowCondensed-Regular'), url(./fonts/BarlowCondensed-Regular.ttf) format('truetype'); } @font-face { - font-family: 'BarlowSemiCondensed-Regular'; - src: local('BarlowSemiCondensed-Regular'), url(./fonts/BarlowSemiCondensed-Regular.ttf) format('truetype'); + font-family: 'BarlowSemiCondensed-Regular'; + src: local('BarlowSemiCondensed-Regular'), url(./fonts/BarlowSemiCondensed-Regular.ttf) format('truetype'); } @font-face { - font-family: 'BarlowSemiCondensed-SemiBold'; - src: local('BarlowSemiCondensed-Regular'), url(./fonts/BarlowSemiCondensed-SemiBold.ttf) format('truetype'); -} + font-family: 'BarlowSemiCondensed-SemiBold'; + src: local('BarlowSemiCondensed-Regular'), url(./fonts/BarlowSemiCondensed-SemiBold.ttf) format('truetype'); +} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fbfa59f..8a95e77 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,18 +6,18 @@ import { UserProfile } from "@customTypes/Profile"; import Sidebar from "./components/Sidebar"; import "./App.css"; -import Profile from "@pages/Profile"; -import Games from "@pages/Games"; -import Maps from "@pages/Maps"; -import User from "@pages/User"; -import Homepage from "@pages/Homepage"; +import Profile from "@pages/Profile/Profile.tsx"; +import Games from "@pages/Games/Games.tsx"; +import Maps from "@pages/Maps/Maps.tsx"; +import User from "@pages/User/User.tsx"; +import Homepage from "@pages/Home/Homepage.tsx"; import UploadRunDialog from "./components/UploadRunDialog"; -import Rules from "@pages/Rules"; -import About from "@pages/About"; +import Rules from "@pages/Rules/Rules.tsx"; +import About from "@pages/About/About.tsx"; import { Game } from "@customTypes/Game"; import { API } from "./api/Api"; -import Maplist from "@pages/Maplist"; -import Rankings from "@pages/Rankings"; +import Maplist from "@pages/Maplist/Maplist.tsx"; +import Rankings from "@pages/Rankings/Rankings.tsx"; import { get_user_id_from_token, get_user_mod_from_token } from "./utils/Jwt"; const App: React.FC = () => { @@ -76,6 +76,7 @@ const App: React.FC = () => { LPHUB + { }} games={games} /> - setUploadRunDialog(true)} - /> - - } /> - _set_profile(get_user_id_from_token(token))} - /> - } - /> - } - /> - } /> - }> - } + +
+ + setUploadRunDialog(true)} /> - } /> - } /> - }> - - + +
+ + + } /> + + _set_profile(get_user_id_from_token(token))} + /> + } + /> + + } + /> + + } /> + + }> + + } + /> + + } /> + + } /> + + }> + + + + + +
+ + +
); }; diff --git a/frontend/src/components/GameCategory.tsx b/frontend/src/components/GameCategory.tsx index b18c9d9..7ae3850 100644 --- a/frontend/src/components/GameCategory.tsx +++ b/frontend/src/components/GameCategory.tsx @@ -11,12 +11,12 @@ interface GameCategoryProps { const GameCategory: React.FC = ({ cat, game }) => { return ( {cat.category.name}

+ {cat.category.name}
-

{cat.portal_count}

+ {cat.portal_count} ); }; diff --git a/frontend/src/components/GameEntry.tsx b/frontend/src/components/GameEntry.tsx index f8fd179..6f2b194 100644 --- a/frontend/src/components/GameEntry.tsx +++ b/frontend/src/components/GameEntry.tsx @@ -18,18 +18,18 @@ const GameEntry: React.FC = ({ game }) => { return ( -
+
- {game.name} + {game.name}
-
+
{catInfo.map((cat, index) => { return ( diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 88a5297..0083a3e 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -23,15 +23,15 @@ interface SidebarProps { onUploadRun: () => void; } -function OpenSidebarIcon(){ +function OpenSidebarIcon() { return ( - + ) } -function ClosedSidebarIcon(){ +function ClosedSidebarIcon() { return ( - ) + ) } const Sidebar: React.FC = ({ @@ -43,7 +43,7 @@ const Sidebar: React.FC = ({ const [searchData, setSearchData] = React.useState( undefined ); - const [isSidebarLocked, setIsSidebarLocked] = React.useState(false); + // const [isSidebarLocked, setIsSidebarLocked] = React.useState(false); const [isSidebarOpen, setSidebarOpen] = React.useState(false); const [selectedButtonIndex, setSelectedButtonIndex] = React.useState(1); @@ -100,17 +100,16 @@ const Sidebar: React.FC = ({ const getButtonClasses = (buttonIndex: number) => { const baseClasses = "flex items-center gap-3 w-full text-left bg-inherit cursor-pointer border-none rounded-lg py-3 px-3 transition-all duration-300 hover:bg-surface1"; const selectedClasses = selectedButtonIndex === buttonIndex ? "bg-primary text-background" : "bg-transparent text-foreground"; - + return `${baseClasses} ${selectedClasses}`; }; const iconClasses = "w-6 h-6 flex-shrink-0"; return ( -
-
+
+
Logo {isSidebarOpen && ( @@ -124,7 +123,7 @@ const Sidebar: React.FC = ({
)} - +
{/* Sidebar Content */} -
{isSidebarOpen && (
@@ -145,7 +144,7 @@ const Sidebar: React.FC = ({ Search Search
- +
= ({ ].map(({ to, refIndex, icon, alt, label }) => (
- - - - - {load ? ( -
- ) : ( -
-

- {game?.name} -

- -
-
-
-

- { - game?.category_portals.find( - obj => obj.category.id === catNum + 1 - )?.portal_count - } -

-

- portals -

-
- -
- {game?.category_portals.map((cat, index) => ( - - ))} -
-
-
- -
-
-
- - {curChapter?.chapter.name.split(" - ")[0]} - -
-
- - {curChapter?.chapter.name.split(" - ")[1]} - - -
- \ -
- {gameChapters?.chapters.map((chapter, i) => { - return ( -
{ - _fetch_chapters(chapter.id.toString()); - _handle_dropdown_click(); - }} - > - {chapter.name} -
- ); - })} -
-
- -
- {curChapter?.maps.map((map, i) => { - return ( -
- - - {map.name} - -
-
- - {map.is_disabled - ? map.category_portals[0].portal_count - : map.category_portals.find( - obj => obj.category.id === catNum + 1 - )?.portal_count} - - - portals - -
-
- -
-
- {[1, 2, 3, 4, 5].map((point) => ( -
- ))} -
-
- -
- ); - })} -
-
-
- )} - - ); -}; - -export default Maplist; diff --git a/frontend/src/pages/Maplist/Maplist.tsx b/frontend/src/pages/Maplist/Maplist.tsx index 572eb27..8d9c14a 100644 --- a/frontend/src/pages/Maplist/Maplist.tsx +++ b/frontend/src/pages/Maplist/Maplist.tsx @@ -87,162 +87,159 @@ const Maplist: React.FC = () => { }, [gameChapters, location.search]); return ( -
+
- LPHUB | Maplist + LPHUB | Maplist - +
- - - + + +
{load ? ( -
+
) : ( -
-

- {game?.name} -

- -
-
-
-

- { - game?.category_portals.find( - obj => obj.category.id === catNum + 1 - )?.portal_count - } -

-

- portals -

-
- -
- {game?.category_portals.map((cat, index) => ( - - ))} -
-
-
- -
-
- - {curChapter?.chapter.name.split(" - ")[0]} - -
-
- - {curChapter?.chapter.name.split(" - ")[1]} - - -
- \ +

+ {game?.name} +

+
- {gameChapters?.chapters.map((chapter, i) => { - return ( -
{ - _fetch_chapters(chapter.id.toString()); - _handle_dropdown_click(); - }} - > - {chapter.name} +
+
+

+ { + game?.category_portals.find( + obj => obj.category.id === catNum + 1 + )?.portal_count + } +

+

+ portals +

+
+ +
+ {game?.category_portals.map((cat, index) => ( + + ))} +
- ); - })}
-
-
- {curChapter?.maps.map((map, i) => { - return ( -
- - - {map.name} - +
+
+
+ + {curChapter?.chapter.name.split(" - ")[0]} + +
-
- - {map.is_disabled - ? map.category_portals[0].portal_count - : map.category_portals.find( - obj => obj.category.id === catNum + 1 - )?.portal_count} + + {curChapter?.chapter.name.split(" - ")[1]} - - portals - -
+
- -
-
- {[1, 2, 3, 4, 5].map((point) => ( -
- ))} -
+ > + {gameChapters?.chapters.map((chapter, i) => { + return ( +
{ + _fetch_chapters(chapter.id.toString()); + _handle_dropdown_click(); + }} + > + {chapter.name} +
+ ); + })}
- -
- ); - })} +
+ +
+ {curChapter?.maps.map((map, i) => { + return ( +
+ + + {map.name} + +
+
+ + {map.is_disabled + ? map.category_portals[0].portal_count + : map.category_portals.find( + obj => obj.category.id === catNum + 1 + )?.portal_count} + + + portals + +
+
+ +
+
+ {[1, 2, 3, 4, 5].map((point) => ( +
+ ))} +
+
+ +
+ ); + })} +
+
-
-
)} -
+
); }; diff --git a/frontend/src/pages/Maps.tsx b/frontend/src/pages/Maps.tsx deleted file mode 100644 index 50fe03b..0000000 --- a/frontend/src/pages/Maps.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import React from "react"; -import { Link, useLocation } from "react-router-dom"; -import { Helmet } from "react-helmet"; - -import { PortalIcon, FlagIcon, ChatIcon } from "../images/Images"; -import Summary from "@components/Summary"; -import Leaderboards from "@components/Leaderboards"; -import Discussions from "@components/Discussions"; -import ModMenu from "@components/ModMenu"; -import { MapDiscussions, MapLeaderboard, MapSummary } from "@customTypes/Map"; -import { API } from "@api/Api"; - -interface MapProps { - token?: string; - isModerator: boolean; -} - -const Maps: React.FC = ({ token, isModerator }) => { - const [selectedRun, setSelectedRun] = React.useState(0); - - const [mapSummaryData, setMapSummaryData] = React.useState< - MapSummary | undefined - >(undefined); - const [mapLeaderboardData, setMapLeaderboardData] = React.useState< - MapLeaderboard | undefined - >(undefined); - const [mapDiscussionsData, setMapDiscussionsData] = React.useState< - MapDiscussions | undefined - >(undefined); - - const [navState, setNavState] = React.useState(0); - - const location = useLocation(); - - const mapID = location.pathname.split("/")[2]; - - const _fetch_map_summary = React.useCallback(async () => { - const mapSummary = await API.get_map_summary(mapID); - setMapSummaryData(mapSummary); - }, [mapID]); - - const _fetch_map_leaderboards = React.useCallback(async () => { - const mapLeaderboards = await API.get_map_leaderboard(mapID, "1"); - setMapLeaderboardData(mapLeaderboards); - }, [mapID]); - - const _fetch_map_discussions = React.useCallback(async () => { - const mapDiscussions = await API.get_map_discussions(mapID); - setMapDiscussionsData(mapDiscussions); - }, [mapID]); - - React.useEffect(() => { - _fetch_map_summary(); - _fetch_map_leaderboards(); - _fetch_map_discussions(); - }, [ - mapID, - _fetch_map_discussions, - _fetch_map_leaderboards, - _fetch_map_summary, - ]); - - if (!mapSummaryData) { - // loading placeholder - return ( - <> -
-
-
- - - -
-
- -
- - - -
- -
-
- - ); - } - - return ( - <> - - LPHUB | {mapSummaryData.map.map_name} - - - {isModerator && ( - - )} - -
- -
-
-
-
- - - - - - -
- - {mapSummaryData.map.map_name} - -
-
- -
- - - -
- - {navState === 0 && ( - - )} - {navState === 1 && } - {navState === 2 && ( - _fetch_map_discussions()} - /> - )} -
- - ); -}; - -export default Maps; diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx deleted file mode 100644 index f44f587..0000000 --- a/frontend/src/pages/Profile.tsx +++ /dev/null @@ -1,633 +0,0 @@ -import React from "react"; -import { Link, useNavigate } from "react-router-dom"; -import { Helmet } from "react-helmet"; - -import { - SteamIcon, - TwitchIcon, - YouTubeIcon, - PortalIcon, - FlagIcon, - StatisticsIcon, - SortIcon, - ThreedotIcon, - DownloadIcon, - HistoryIcon, - DeleteIcon, -} from "@images/Images"; -import { UserProfile } from "@customTypes/Profile"; -import { Game, GameChapters } from "@customTypes/Game"; -import { Map } from "@customTypes/Map"; -import { ticks_to_time } from "@utils/Time"; -import { API } from "@api/Api"; -import useConfirm from "@hooks/UseConfirm"; -import useMessage from "@hooks/UseMessage"; -import useMessageLoad from "@hooks/UseMessageLoad"; - -interface ProfileProps { - profile?: UserProfile; - token?: string; - gameData: Game[]; - onDeleteRecord: () => void; -} - -const Profile: React.FC = ({ - profile, - token, - gameData, - onDeleteRecord, -}) => { - const { confirm, ConfirmDialogComponent } = useConfirm(); - const { message, MessageDialogComponent } = useMessage(); - const { messageLoad, messageLoadClose, MessageDialogLoadComponent } = - useMessageLoad(); - const [navState, setNavState] = React.useState(0); - const [pageNumber, setPageNumber] = React.useState(1); - const [pageMax, setPageMax] = React.useState(0); - - const [game, setGame] = React.useState("0"); - const [chapter, setChapter] = React.useState("0"); - const [chapterData, setChapterData] = React.useState( - null - ); - const [maps, setMaps] = React.useState([]); - - const navigate = useNavigate(); - - const _update_profile = () => { - if (token) { - API.post_profile(token).then(() => navigate(0)); - } - }; - - const _get_game_chapters = React.useCallback(async () => { - if (game && game !== "0") { - const gameChapters = await API.get_games_chapters(game); - setChapterData(gameChapters); - } else if (game && game === "0") { - setPageMax(Math.ceil(profile!.records.length / 20)); - setPageNumber(1); - } - }, [game, profile]); - - const _get_game_maps = React.useCallback(async () => { - if (chapter === "0") { - const gameMaps = await API.get_game_maps(game); - setMaps(gameMaps); - setPageMax(Math.ceil(gameMaps.length / 20)); - setPageNumber(1); - } else { - const gameChapters = await API.get_chapters(chapter); - setMaps(gameChapters.maps); - setPageMax(Math.ceil(gameChapters.maps.length / 20)); - setPageNumber(1); - } - }, [chapter, game]); - - const _delete_submission = async (map_id: number, record_id: number) => { - const userConfirmed = await confirm( - "Delete Record", - "Are you sure you want to delete this record?" - ); - - if (!userConfirmed) { - return; - } - - messageLoad("Deleting..."); - - const api_success = await API.delete_map_record(token!, map_id, record_id); - messageLoadClose(); - if (api_success) { - await message("Delete Record", "Successfully deleted record."); - onDeleteRecord(); - } else { - await message("Delete Record", "Could not delete record."); - } - }; - - React.useEffect(() => { - if (!profile) { - navigate("/"); - } - }, [profile, navigate]); - - React.useEffect(() => { - if (profile) { - _get_game_chapters(); - } - }, [profile, game, _get_game_chapters]); - - React.useEffect(() => { - if (profile && game !== "0") { - _get_game_maps(); - } - }, [profile, game, chapter, chapterData, _get_game_maps]); - - if (!profile) { - return <>; - } - - return ( -
- - LPHUB | {profile.user_name} - - - {MessageDialogComponent} - {MessageDialogLoadComponent} - {ConfirmDialogComponent} - -
-
- {profile.profile ? ( -
- profile-image - Refresh -
- ) : ( -
- profile-image -
- )} - -
-
-
{profile.user_name}
-
- {profile.country_code === "XX" ? ( - "" - ) : ( - {profile.country_code} - )} -
-
- {profile.titles.map(e => ( - - {e.name} - - ))} -
-
-
- {profile.links.steam === "-" ? ( - "" - ) : ( - - Steam - - )} - {profile.links.twitch === "-" ? ( - "" - ) : ( - - Twitch - - )} - {profile.links.youtube === "-" ? ( - "" - ) : ( - - Youtube - - )} - {profile.links.p2sr === "-" ? ( - "" - ) : ( - - P2SR - - )} -
-
-
-
- Overall - - {profile.rankings.overall.rank === 0 - ? "N/A " - : "#" + profile.rankings.overall.rank + " "} - - ({profile.rankings.overall.completion_count}/ - {profile.rankings.overall.completion_total}) - - -
-
- Singleplayer - - {profile.rankings.singleplayer.rank === 0 - ? "N/A " - : "#" + profile.rankings.singleplayer.rank + " "} - - ({profile.rankings.singleplayer.completion_count}/ - {profile.rankings.singleplayer.completion_total}) - - -
-
- Cooperative - - {profile.rankings.cooperative.rank === 0 - ? "N/A " - : "#" + profile.rankings.cooperative.rank + " "} - - ({profile.rankings.cooperative.completion_count}/ - {profile.rankings.cooperative.completion_total}) - - -
-
-
- -
- - -
- -
-
- {gameData === null ? ( - - ) : ( - - )} - - {game === "0" ? ( - - ) : chapterData === null ? ( - - ) : ( - - )} -
-
- - Map Name - - - - Portals - - - - WRΔ - - - - Time - - - - - Rank - - - - Date - - -
-
- - - {pageNumber}/{pageMax} - - -
-
-
-
-
- {game === "0" ? ( - profile.records - .sort((a, b) => a.map_id - b.map_id) - .map((r, index) => - Math.ceil((index + 1) / 20) === pageNumber ? ( - - - - {i === 0 && r.scores.length > 1 ? ( - - ) : ( - "" - )} - - - ))} - - ) : ( - "" - ) - ) - ) : maps ? ( - maps - .filter(e => e.is_disabled === false) - .sort((a, b) => a.id - b.id) - .map((r, index) => { - if (Math.ceil((index + 1) / 20) === pageNumber) { - let record = profile.records.find(e => e.map_id === r.id); - return record === undefined ? ( - - ) : ( - - - - {i === 0 && record!.scores.length > 1 ? ( - - ) : ( - "" - )} - - - ))} - - ); - } else { - return null; - } - }) - ) : ( - <>{console.warn(maps)} - )} -
-
-
-
- ); -}; - -export default Profile; diff --git a/frontend/src/pages/Rankings.tsx b/frontend/src/pages/Rankings.tsx deleted file mode 100644 index dec0e17..0000000 --- a/frontend/src/pages/Rankings.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import React, { useEffect } from "react"; -import { Helmet } from "react-helmet"; - -import RankingEntry from "@components/RankingEntry"; -import { - Ranking, - SteamRanking, - RankingType, - SteamRankingType, -} from "@customTypes/Ranking"; -import { API } from "@api/Api"; - -import "@css/Rankings.css"; - -enum LeaderboardTypes { - official, - unofficial, -} - -enum RankingCategories { - rankings_overall, - rankings_multiplayer, - rankings_singleplayer, -} - -const Rankings: React.FC = () => { - const [leaderboardData, setLeaderboardData] = React.useState< - Ranking | SteamRanking - >(); - const [currentLeaderboard, setCurrentLeaderboard] = React.useState< - RankingType[] | SteamRankingType[] - >(); - const [currentRankingType, setCurrentRankingType] = - React.useState(LeaderboardTypes.official); - - const [leaderboardLoad, setLeaderboardLoad] = React.useState(false); - - const [currentLeaderboardType, setCurrentLeaderboardType] = - React.useState(RankingCategories.rankings_singleplayer); - const [load, setLoad] = React.useState(false); - - const _fetch_rankings = React.useCallback(async () => { - setLeaderboardLoad(false); - const rankings = await API.get_official_rankings(); - setLeaderboardData(rankings); - if (currentLeaderboardType === RankingCategories.rankings_singleplayer) { - setCurrentLeaderboard(rankings.rankings_singleplayer); - } else if ( - currentLeaderboardType === RankingCategories.rankings_multiplayer - ) { - setCurrentLeaderboard(rankings.rankings_multiplayer); - } else { - setCurrentLeaderboard(rankings.rankings_overall); - } - setLoad(true); - setLeaderboardLoad(true); - }, [currentLeaderboardType]); - - const __dev_fetch_unofficial_rankings = async () => { - try { - setLeaderboardLoad(false); - const rankings = await API.get_unofficial_rankings(); - setLeaderboardData(rankings); - if (currentLeaderboardType === RankingCategories.rankings_singleplayer) { - // console.log(_sort_rankings_steam(unofficialRanking.rankings_singleplayer)) - setCurrentLeaderboard(rankings.rankings_singleplayer); - } else if ( - currentLeaderboardType === RankingCategories.rankings_multiplayer - ) { - setCurrentLeaderboard(rankings.rankings_multiplayer); - } else { - setCurrentLeaderboard(rankings.rankings_overall); - } - setLeaderboardLoad(true); - } catch (e) { - console.log(e); - } - }; - - const _set_current_leaderboard = React.useCallback( - (ranking_cat: RankingCategories) => { - if (ranking_cat === RankingCategories.rankings_singleplayer) { - setCurrentLeaderboard(leaderboardData!.rankings_singleplayer); - } else if (ranking_cat === RankingCategories.rankings_multiplayer) { - setCurrentLeaderboard(leaderboardData!.rankings_multiplayer); - } else { - setCurrentLeaderboard(leaderboardData!.rankings_overall); - } - - setCurrentLeaderboardType(ranking_cat); - }, - [leaderboardData] - ); - - // unused func - // const _set_leaderboard_type = (leaderboard_type: LeaderboardTypes) => { - // if (leaderboard_type === LeaderboardTypes.official) { - // _fetch_rankings(); - // } else { - // } - // }; - - useEffect(() => { - _fetch_rankings(); - }, [_fetch_rankings]); - - return ( -
- - LPHUB | Rankings - -
-
- - -
-
-
-
- - - -
-
- - {load ? ( -
-
-
- Rank - Player - Portals -
- -
- - {leaderboardLoad && - currentLeaderboard?.map((curRankingData, i) => { - return ( - - ); - })} - - {leaderboardLoad ? null : ( -
- -
- )} -
-
- ) : null} -
- ); -}; - -export default Rankings; diff --git a/frontend/src/pages/Rules.tsx b/frontend/src/pages/Rules.tsx deleted file mode 100644 index 9c7885c..0000000 --- a/frontend/src/pages/Rules.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; -import ReactMarkdown from "react-markdown"; -import { Helmet } from "react-helmet"; - -const Rules: React.FC = () => { - const [rulesText, setRulesText] = React.useState(""); - - React.useEffect(() => { - const fetchRules = async () => { - try { - const response = await fetch( - "https://raw.githubusercontent.com/pektezol/lphub/main/RULES.md" - ); - if (!response.ok) { - throw new Error("Failed to fetch README"); - } - const rulesText = await response.text(); - setRulesText(rulesText); - } catch (error) { - console.error("Error fetching Rules:", error); - } - // setRulesText(rulesText) - }; - fetchRules(); - }, []); - - return ( -
- - LPHUB | Rules - - {rulesText} -
- ); -}; - -export default Rules; diff --git a/frontend/src/pages/User.tsx b/frontend/src/pages/User.tsx deleted file mode 100644 index 8c699b1..0000000 --- a/frontend/src/pages/User.tsx +++ /dev/null @@ -1,410 +0,0 @@ -import React from "react"; -import { Link, useLocation, useNavigate } from "react-router-dom"; -import { Helmet } from "react-helmet"; - -import { - SteamIcon, - TwitchIcon, - YouTubeIcon, - PortalIcon, - FlagIcon, - StatisticsIcon, - SortIcon, - ThreedotIcon, - DownloadIcon, - HistoryIcon, -} from "@images/Images"; -import { UserProfile } from "@customTypes/Profile"; -import { Game, GameChapters } from "@customTypes/Game"; -import { Map } from "@customTypes/Map"; -import { API } from "@api/Api"; -import { ticks_to_time } from "@utils/Time"; -import useMessage from "@hooks/UseMessage"; - -interface UserProps { - profile?: UserProfile; - token?: string; - gameData: Game[]; -} - -const User: React.FC = ({ token, profile, gameData }) => { - const { message, MessageDialogComponent } = useMessage(); - - const [user, setUser] = React.useState(undefined); - - const [navState, setNavState] = React.useState(0); - const [pageNumber, setPageNumber] = React.useState(1); - const [pageMax, setPageMax] = React.useState(0); - - const [game, setGame] = React.useState("0"); - const [chapter, setChapter] = React.useState("0"); - const [chapterData, setChapterData] = React.useState( - null - ); - const [maps, setMaps] = React.useState([]); - - const location = useLocation(); - const navigate = useNavigate(); - - const _fetch_user = React.useCallback(async () => { - const userID = location.pathname.split("/")[2]; - if (token && profile && profile.profile && profile.steam_id === userID) { - navigate("/profile"); - return; - } - const userData = await API.get_user(userID); - setUser(userData); - }, [location.pathname, token, profile, navigate]); - - const _get_game_chapters = React.useCallback(async () => { - if (game !== "0") { - const gameChapters = await API.get_games_chapters(game); - setChapterData(gameChapters); - } else { - setPageMax(Math.ceil(user!.records.length / 20)); - setPageNumber(1); - } - }, [game, user]); - - const _get_game_maps = React.useCallback(async () => { - if (chapter === "0") { - const gameMaps = await API.get_game_maps(game); - setMaps(gameMaps); - setPageMax(Math.ceil(gameMaps.length / 20)); - setPageNumber(1); - } else { - const gameChapters = await API.get_chapters(chapter); - setMaps(gameChapters.maps); - setPageMax(Math.ceil(gameChapters.maps.length / 20)); - setPageNumber(1); - } - }, [chapter, game]); - - React.useEffect(() => { - _fetch_user(); - }, [location, _fetch_user]); - - React.useEffect(() => { - if (user) { - _get_game_chapters(); - } - }, [user, game, location, _get_game_chapters]); - - React.useEffect(() => { - if (user && game !== "0") { - _get_game_maps(); - } - }, [user, game, chapter, location, _get_game_maps]); - - if (!user) { - return ( -
- Loading... -
- ); - } - - return ( -
- - LPHUB | {user.user_name} - - - - {MessageDialogComponent} - -
-
- Profile -
-

- {user.user_name} -

- {user.country_code !== "XX" && ( -
- {user.country_code} - {user.country_code} -
- )} -
- {user.titles.map((title, index) => ( - - {title.name} - - ))} -
-
-
- {user.links.steam !== "-" && ( - - Steam - - )} - {user.links.twitch !== "-" && ( - - Twitch - - )} - {user.links.youtube !== "-" && ( - - YouTube - - )} - {user.links.p2sr !== "-" && ( - - P2SR - - )} -
-
- -
-
-
Overall
-
- {user.rankings.overall.rank === 0 ? "N/A" : `#${user.rankings.overall.rank}`} -
-
- {user.rankings.overall.completion_count}/{user.rankings.overall.completion_total} -
-
-
-
Singleplayer
-
- {user.rankings.singleplayer.rank === 0 ? "N/A" : `#${user.rankings.singleplayer.rank}`} -
-
- {user.rankings.singleplayer.completion_count}/{user.rankings.singleplayer.completion_total} -
-
-
-
Cooperative
-
- {user.rankings.cooperative.rank === 0 ? "N/A" : `#${user.rankings.cooperative.rank}`} -
-
- {user.rankings.cooperative.completion_count}/{user.rankings.cooperative.completion_total} -
-
-
-
- -
- - -
- - {navState === 0 && ( -
-
- - - -
- -
-
- Map Name - Sort -
-
- Portals - Sort -
-
- WRΔ - Sort -
-
- Time - Sort -
-
-
- Rank - Sort -
-
- Date - Sort -
-
- - {pageNumber}/{pageMax} - -
-
- -
- {game === "0" ? ( - user.records - .sort((a, b) => a.map_id - b.map_id) - .map((record, index) => - Math.ceil((index + 1) / 20) === pageNumber ? ( -
- - {record.map_name} - - {record.scores[0]?.score_count || 'N/A'} - 0 ? 'text-[#dc3545]' : ''}`}> - {record.scores[0]?.score_count - record.map_wr_count > 0 - ? `+${record.scores[0].score_count - record.map_wr_count}` - : '–'} - - {record.scores[0] ? ticks_to_time(record.scores[0].score_time) : 'N/A'} - - #{record.placement} - {record.scores[0]?.date.split("T")[0] || 'N/A'} -
- - - {record.scores.length > 1 && ( - - )} -
-
- ) : null - ) - ) : ( - maps - ?.filter(map => !map.is_disabled) - .sort((a, b) => a.id - b.id) - .map((map, index) => { - if (Math.ceil((index + 1) / 20) !== pageNumber) return null; - - const record = user.records.find(r => r.map_id === map.id); - - return ( -
- - {map.name} - - {record?.scores[0]?.score_count || 'N/A'} - 0 ? 'text-[#dc3545]' : ''}`}> - {record?.scores[0]?.score_count && record.scores[0].score_count - record.map_wr_count > 0 - ? `+${record.scores[0].score_count - record.map_wr_count}` - : '–'} - - {record?.scores[0] ? ticks_to_time(record.scores[0].score_time) : 'N/A'} - - {record ? `#${record.placement}` : 'N/A'} - {record?.scores[0]?.date.split("T")[0] || 'N/A'} -
- {record?.scores[0] && ( - <> - - - {record.scores.length > 1 && ( - - )} - - )} -
-
- ); - }) - )} -
-
- )} -
- ); -}; - -export default User; -- cgit v1.2.3