diff --git a/next-frontend/src/components/competitions/LiveMenu.tsx b/next-frontend/src/components/competitions/LiveMenu.tsx index 12f1474582..52b933afc9 100644 --- a/next-frontend/src/components/competitions/LiveMenu.tsx +++ b/next-frontend/src/components/competitions/LiveMenu.tsx @@ -23,7 +23,7 @@ export default async function LiveMenu({ const tabs = duringCompetitionTabs(competitionInfo, data.rounds); return ( - + {children} ); diff --git a/next-frontend/src/components/competitions/TabMenu.tsx b/next-frontend/src/components/competitions/TabMenu.tsx index 1ec0b414f6..006cd97f3d 100644 --- a/next-frontend/src/components/competitions/TabMenu.tsx +++ b/next-frontend/src/components/competitions/TabMenu.tsx @@ -8,6 +8,7 @@ import { Collapsible, Drawer, IconButton, + Separator, Spacer, Tabs, Text, @@ -24,25 +25,40 @@ import { useState } from "react"; import { TFunction } from "i18next"; import { LuAlignJustify } from "react-icons/lu"; import { iconMap } from "@/components/icons/iconMap"; +import { route } from "nextjs-routes"; +import { parseActivityCode } from "@/lib/wca/wcif/rounds"; + +function parseActivityCodeOrNull(path: string) { + try { + const { eventId } = parseActivityCode(path); + return eventId; + } catch { + return null; + } +} export default function TabMenu({ competitionInfo, children, tabs, + isLiveMenu = false, }: { children: React.ReactNode; competitionInfo: components["schemas"]["CompetitionInfo"]; tabs: CompetitionNavTab[]; + isLiveMenu?: boolean; }) { - const [openGroup, setOpenGroup] = useState(null); - const [drawerOpen, setDrawerOpen] = useState(false); - const pathName = usePathname(); const { t } = useT(); const path = _.last(pathName.split("/")); const currentPath = path === competitionInfo.id ? "general" : path; + const eventId = parseActivityCodeOrNull(currentPath!); + + const [openGroup, setOpenGroup] = useState(eventId); + const [drawerOpen, setDrawerOpen] = useState(false); + return ( setOpenGroup((prev) => (prev === tab.menuKey ? null : tab.menuKey)) } + isLiveMenu={isLiveMenu} + competitionInfo={competitionInfo} /> @@ -117,6 +135,7 @@ export default function TabMenu({ prev === tab.menuKey ? null : tab.menuKey, ) } + competitionInfo={competitionInfo} /> @@ -136,28 +155,68 @@ function TabList({ t, onToggle, openGroup, + isLiveMenu, + competitionInfo, }: { tabs: CompetitionNavTab[]; t: TFunction; openGroup: string | null; onToggle: (tab: CompetitionNavTab) => void; + isLiveMenu?: boolean; + competitionInfo: components["schemas"]["CompetitionInfo"]; }) { - return tabs.map((tab) => - "href" in tab ? ( - - - {t(tab.i18nKey)} - - - ) : ( - onToggle(tab)} - /> - ), + return ( + <> + {tabs.map((tab) => + "href" in tab ? ( + + + {t(tab.i18nKey)} + + + ) : ( + onToggle(tab)} + /> + ), + )} + {!isLiveMenu && ( + <> + + {competitionInfo.tab_names.map((tabName) => ( + + + + {tabName} + + + + ))} + + )} + ); } @@ -185,6 +244,7 @@ function CollapsibleTabGroup({ py="2" borderRadius="md" _hover={{ bg: "bg.subtle" }} + cursor="pointer" > {t(i18nKey)} diff --git a/next-frontend/src/lib/wca/competitions/tabs.ts b/next-frontend/src/lib/wca/competitions/tabs.ts index e1532b9898..3fe771a2ae 100644 --- a/next-frontend/src/lib/wca/competitions/tabs.ts +++ b/next-frontend/src/lib/wca/competitions/tabs.ts @@ -127,10 +127,15 @@ export const duringCompetitionTabs = ( rounds.length, Boolean(round.cutoff), ); + + const roundDone = + round.state === "locked" || + (round.state === "open" && + round.competitors_live_results_entered === round.total_competitors); return { i18nKey: `rounds.${roundTypeId}.name`, menuKey: round.id, - badge: round.state === "locked" ? "Done" : "live", + badge: roundDone ? "Done" : "live", disabled: round.state === "pending" || round.state === "ready", href: route({ pathname: "/competitions/[competitionId]/live/rounds/[roundId]",