Files
flxn-app/src/features/tournaments/components/started-tournament/index.tsx
2026-03-01 20:20:24 -06:00

109 lines
3.5 KiB
TypeScript

import { useMemo } from "react";
import { Tournament } from "../../types";
import { useAuth } from "@/contexts/auth-context";
import { Box, Divider, Stack, Text, Card, Center } from "@mantine/core";
import { Carousel } from "@mantine/carousel";
import ListLink from "@/components/list-link";
import { TreeStructureIcon, UsersIcon, ClockIcon, ListDashes } from "@phosphor-icons/react";
import TeamListButton from "../upcoming-tournament/team-list-button";
import RulesListButton from "../upcoming-tournament/rules-list-button";
import MatchCard from "@/features/matches/components/match-card";
import Header from "./header";
import { Podium } from "../podium";
const StartedTournament: React.FC<{ tournament: Tournament }> = ({
tournament,
}) => {
const { roles } = useAuth();
const isAdmin = useMemo(() => roles.includes("Admin"), [roles]);
const startedMatches = useMemo(() =>
tournament.matches?.filter(match => match.status === "started") || [],
[tournament.matches]
);
const isTournamentOver = useMemo(() => {
const matches = tournament.matches || [];
if (matches.length === 0) return false;
const nonByeMatches = matches.filter((match) => !(match.status === 'tbd' && match.bye === true));
if (nonByeMatches.length === 0) return false;
const finalsMatch = nonByeMatches.reduce((highest, current) =>
(!highest || current.lid > highest.lid) ? current : highest
);
return finalsMatch?.status === 'ended';
}, [tournament.matches]);
const hasGroupStage = useMemo(() => {
return tournament.matches?.some((match) => match.round === -1) || false;
}, [tournament.matches]);
return (
<Stack gap="lg">
<Header tournament={tournament} />
{startedMatches.length > 0 ? (
<Box>
<Carousel
slideSize="95%"
slideGap="xs"
withControls={false}
>
{startedMatches.map((match, index) => (
<Carousel.Slide key={match.id}>
<Box pl={index === 0 ? "md" : undefined } pr={index === startedMatches.length - 1 ? "md" : undefined}>
<MatchCard match={match} />
</Box>
</Carousel.Slide>
))}
</Carousel>
</Box>
) : isTournamentOver ? (
<Box px="lg" w="100%">
<Podium tournament={tournament} />
</Box>
) : (
<Card withBorder radius="lg" p="xl" mx="md">
<Center>
<Stack align="center" gap="md">
<ClockIcon size={48} color="var(--mantine-color-dimmed)" />
<Text size="lg" fw={500} c="dimmed">
No active matches
</Text>
</Stack>
</Center>
</Card>
)}
<Box>
<Divider />
{isAdmin && (
<ListLink
label={`Manage ${tournament.name}`}
to={`/admin/tournaments/${tournament.id}`}
Icon={UsersIcon}
/>
)}
{hasGroupStage && (
<ListLink
label={`View Groups`}
to={`/tournaments/${tournament.id}/groups`}
Icon={ListDashes}
/>
)}
<ListLink
label={`View Bracket`}
to={`/tournaments/${tournament.id}/bracket`}
Icon={TreeStructureIcon}
/>
<TeamListButton teams={tournament.teams || []} isRegional={tournament.regional} />
<RulesListButton tournamentId={tournament.id} />
</Box>
</Stack>
);
};
export default StartedTournament;