improvements
This commit is contained in:
112
src/features/tournaments/components/podium.tsx
Normal file
112
src/features/tournaments/components/podium.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { Stack, Group, Text, ThemeIcon, Box, Center } from "@mantine/core";
|
||||
import { CrownIcon, MedalIcon } from "@phosphor-icons/react";
|
||||
import { Tournament } from "../types";
|
||||
|
||||
interface PodiumProps {
|
||||
tournament: Tournament;
|
||||
}
|
||||
|
||||
export const Podium = ({ tournament }: PodiumProps) => {
|
||||
if (!tournament.first_place) {
|
||||
return (
|
||||
<Box p="md">
|
||||
<Center>
|
||||
<Text c="dimmed" size="sm">
|
||||
Podium will appear here when the tournament is over
|
||||
</Text>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap="xs" px="md">
|
||||
{tournament.first_place && (
|
||||
<Group
|
||||
gap="md"
|
||||
p="md"
|
||||
style={{
|
||||
backgroundColor: 'var(--mantine-color-yellow-light)',
|
||||
borderRadius: 'var(--mantine-radius-md)',
|
||||
border: '3px solid var(--mantine-color-yellow-outline)',
|
||||
boxShadow: 'var(--mantine-shadow-md)',
|
||||
}}
|
||||
>
|
||||
<ThemeIcon size="xl" color="yellow" variant="light" radius="xl">
|
||||
<CrownIcon size={24} />
|
||||
</ThemeIcon>
|
||||
<Stack gap={4} style={{ flex: 1 }}>
|
||||
<Text size="md" fw={600}>
|
||||
{tournament.first_place.name}
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
{tournament.first_place.players?.map((player) => (
|
||||
<Text key={player.id} size="sm" c="dimmed">
|
||||
{player.first_name} {player.last_name}
|
||||
</Text>
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
{tournament.second_place && (
|
||||
<Group
|
||||
gap="md"
|
||||
p="xs"
|
||||
style={{
|
||||
backgroundColor: 'var(--mantine-color-default)',
|
||||
borderRadius: 'var(--mantine-radius-md)',
|
||||
border: '2px solid var(--mantine-color-default-border)',
|
||||
boxShadow: 'var(--mantine-shadow-sm)',
|
||||
}}
|
||||
>
|
||||
<ThemeIcon size="lg" color="gray" variant="light" radius="xl">
|
||||
<MedalIcon size={20} />
|
||||
</ThemeIcon>
|
||||
<Stack gap={4} style={{ flex: 1 }}>
|
||||
<Text size="sm" fw={600}>
|
||||
{tournament.second_place.name}
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
{tournament.second_place.players?.map((player) => (
|
||||
<Text key={player.id} size="xs" c="dimmed">
|
||||
{player.first_name} {player.last_name}
|
||||
</Text>
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
{tournament.third_place && (
|
||||
<Group
|
||||
gap="md"
|
||||
p="xs"
|
||||
style={{
|
||||
backgroundColor: 'var(--mantine-color-orange-light)',
|
||||
borderRadius: 'var(--mantine-radius-md)',
|
||||
border: '2px solid var(--mantine-color-orange-outline)',
|
||||
boxShadow: 'var(--mantine-shadow-sm)',
|
||||
}}
|
||||
>
|
||||
<ThemeIcon size="lg" color="orange" variant="light" radius="xl">
|
||||
<MedalIcon size={18} />
|
||||
</ThemeIcon>
|
||||
<Stack gap={4} style={{ flex: 1 }}>
|
||||
<Text size="sm" fw={600}>
|
||||
{tournament.third_place.name}
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
{tournament.third_place.players?.map((player) => (
|
||||
<Text key={player.id} size="xs" c="dimmed">
|
||||
{player.first_name} {player.last_name}
|
||||
</Text>
|
||||
))}
|
||||
</Group>
|
||||
</Stack>
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -4,11 +4,12 @@ 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 } from "@phosphor-icons/react";
|
||||
import { TreeStructureIcon, UsersIcon, ClockIcon, TrophyIcon } 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,
|
||||
@@ -22,6 +23,20 @@ const StartedTournament: React.FC<{ tournament: Tournament }> = ({
|
||||
[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]);
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
<Header tournament={tournament} />
|
||||
@@ -42,6 +57,10 @@ const StartedTournament: React.FC<{ tournament: Tournament }> = ({
|
||||
))}
|
||||
</Carousel>
|
||||
</Box>
|
||||
) : isTournamentOver ? (
|
||||
<Box px="lg" w="100%">
|
||||
<Podium tournament={tournament} />
|
||||
</Box>
|
||||
) : (
|
||||
<Card withBorder radius="lg" p="xl" mx="md">
|
||||
<Center>
|
||||
|
||||
@@ -146,12 +146,11 @@ export const TournamentStats = memo(({ tournament }: TournamentStatsProps) => {
|
||||
};
|
||||
|
||||
const teamStatsWithCalculations = useMemo(() => {
|
||||
return sortedTeamStats.map((stat, index) => ({
|
||||
return sortedTeamStats.map((stat) => ({
|
||||
...stat,
|
||||
index,
|
||||
winPercentage: stat.matches > 0 ? (stat.wins / stat.matches) * 100 : 0,
|
||||
avgCupsPerMatch: stat.matches > 0 ? stat.total_cups_made / stat.matches : 0,
|
||||
}));
|
||||
})).sort((a, b) => b.winPercentage - a.winPercentage);;
|
||||
}, [sortedTeamStats]);
|
||||
|
||||
const renderTeamStatsTable = () => {
|
||||
@@ -170,23 +169,14 @@ export const TournamentStats = memo(({ tournament }: TournamentStatsProps) => {
|
||||
return (
|
||||
<Stack gap={0}>
|
||||
<Text px="md" size="lg" fw={600}>Results</Text>
|
||||
{teamStatsWithCalculations.map((stat) => {
|
||||
<Text px="md" c="dimmed" size="xs" fw={500}>Sorted by win percentage</Text>
|
||||
{teamStatsWithCalculations.map((stat, index) => {
|
||||
return (
|
||||
<Box key={stat.id}>
|
||||
<UnstyledButton
|
||||
w="100%"
|
||||
p="md"
|
||||
style={{
|
||||
borderRadius: 0,
|
||||
transition: "background-color 0.15s ease",
|
||||
}}
|
||||
styles={{
|
||||
root: {
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--mantine-color-gray-0)',
|
||||
},
|
||||
},
|
||||
}}
|
||||
style={{ borderRadius: 0 }}
|
||||
>
|
||||
<Group justify="space-between" align="center" w="100%">
|
||||
<Group gap="sm" align="center">
|
||||
@@ -194,12 +184,12 @@ export const TournamentStats = memo(({ tournament }: TournamentStatsProps) => {
|
||||
<Stack gap={2}>
|
||||
<Group gap='xs'>
|
||||
<Text size="xs" c="dimmed">
|
||||
#{stat.index + 1}
|
||||
#{index + 1}
|
||||
</Text>
|
||||
<Text size="sm" fw={600}>
|
||||
{stat.team_name}
|
||||
</Text>
|
||||
{stat.index === 0 && isComplete && (
|
||||
{index === 0 && isComplete && (
|
||||
<ThemeIcon size="xs" color="yellow" variant="light" radius="xl">
|
||||
<CrownIcon size={12} />
|
||||
</ThemeIcon>
|
||||
@@ -259,7 +249,7 @@ export const TournamentStats = memo(({ tournament }: TournamentStatsProps) => {
|
||||
</Group>
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
{stat.index < teamStatsWithCalculations.length - 1 && <Divider />}
|
||||
{index < teamStatsWithCalculations.length - 1 && <Divider />}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user