135 lines
5.3 KiB
TypeScript
135 lines
5.3 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { LeagueStandingsTable } from '@/components/leagues/LeagueStandingsTable';
|
|
import type { LeagueStandingsViewData } from '@/lib/view-data/LeagueStandingsViewData';
|
|
import { Box } from '@/ui/Box';
|
|
import { Text } from '@/ui/Text';
|
|
import { Group } from '@/ui/Group';
|
|
import { Button } from '@/ui/Button';
|
|
import { Icon } from '@/ui/Icon';
|
|
import { Surface } from '@/ui/Surface';
|
|
import { Trophy, Users, Calendar, Award } from 'lucide-react';
|
|
|
|
interface LeagueStandingsTemplateProps {
|
|
viewData: LeagueStandingsViewData;
|
|
loading?: boolean;
|
|
onToggleTeamChampionship?: () => void;
|
|
}
|
|
|
|
export function LeagueStandingsTemplate({
|
|
viewData,
|
|
loading = false,
|
|
onToggleTeamChampionship,
|
|
}: LeagueStandingsTemplateProps) {
|
|
const [showTeamStandings, setShowTeamStandings] = useState(false);
|
|
|
|
if (loading) {
|
|
return (
|
|
<Box display="flex" alignItems="center" justifyContent="center" py={24}>
|
|
<Text color="text-zinc-500" font="mono" size="xs" uppercase letterSpacing="widest" animate="pulse">
|
|
Loading Standings...
|
|
</Text>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
const standings = viewData.standings.map((entry) => {
|
|
const driver = viewData.drivers.find(d => d.id === entry.driverId);
|
|
return {
|
|
position: entry.position,
|
|
driverName: driver?.name || 'Unknown Driver',
|
|
driverId: entry.driverId,
|
|
points: entry.totalPoints,
|
|
wins: entry.wins,
|
|
podiums: entry.podiums,
|
|
races: entry.racesStarted,
|
|
avgFinish: entry.avgFinish,
|
|
gap: entry.position === 1 ? '—' : `-${viewData.standings[0].totalPoints - entry.totalPoints}`,
|
|
positionChange: entry.positionChange,
|
|
lastRacePoints: entry.lastRacePoints,
|
|
droppedRaceIds: entry.droppedRaceIds,
|
|
};
|
|
});
|
|
|
|
// Calculate championship stats
|
|
const championshipStats = {
|
|
totalRaces: viewData.standings[0]?.racesStarted || 0,
|
|
totalDrivers: viewData.standings.length,
|
|
topWins: Math.max(...viewData.standings.map(s => s.wins)),
|
|
topPodiums: Math.max(...viewData.standings.map(s => s.podiums)),
|
|
};
|
|
|
|
return (
|
|
<Box display="flex" flexDirection="col" gap={8}>
|
|
<Box as="header" display="flex" flexDirection="col" gap={2}>
|
|
<Group gap={3} align="center">
|
|
<Text as="h2" size="xl" weight="bold" color="text-white" uppercase letterSpacing="tight">
|
|
Championship Standings
|
|
</Text>
|
|
{viewData.isTeamChampionship && onToggleTeamChampionship && (
|
|
<Button
|
|
variant="secondary"
|
|
size="sm"
|
|
onClick={() => {
|
|
setShowTeamStandings(!showTeamStandings);
|
|
onToggleTeamChampionship();
|
|
}}
|
|
icon={<Icon icon={Users} size={3} />}
|
|
data-testid="team-standings-toggle"
|
|
>
|
|
{showTeamStandings ? 'Show Driver Standings' : 'Show Team Standings'}
|
|
</Button>
|
|
)}
|
|
</Group>
|
|
<Text size="sm" color="text-zinc-500">Official points classification for the current season.</Text>
|
|
</Box>
|
|
|
|
{/* Championship Stats */}
|
|
<Box display="flex" gap={4} flexWrap="wrap" data-testid="championship-stats">
|
|
<Surface border borderColor="border-outline-steel" p={4} flex={1} minWidth="200px">
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Trophy} size={4} color="text-primary-blue" />
|
|
<Box>
|
|
<Text size="xs" color="text-zinc-500" uppercase letterSpacing="widest">Total Races</Text>
|
|
<Text size="lg" weight="bold" color="text-white">{championshipStats.totalRaces}</Text>
|
|
</Box>
|
|
</Group>
|
|
</Surface>
|
|
<Surface border borderColor="border-outline-steel" p={4} flex={1} minWidth="200px">
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Users} size={4} color="text-primary-blue" />
|
|
<Box>
|
|
<Text size="xs" color="text-zinc-500" uppercase letterSpacing="widest">Total Drivers</Text>
|
|
<Text size="lg" weight="bold" color="text-white">{championshipStats.totalDrivers}</Text>
|
|
</Box>
|
|
</Group>
|
|
</Surface>
|
|
<Surface border borderColor="border-outline-steel" p={4} flex={1} minWidth="200px">
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Award} size={4} color="text-primary-blue" />
|
|
<Box>
|
|
<Text size="xs" color="text-zinc-500" uppercase letterSpacing="widest">Most Wins</Text>
|
|
<Text size="lg" weight="bold" color="text-white">{championshipStats.topWins}</Text>
|
|
</Box>
|
|
</Group>
|
|
</Surface>
|
|
<Surface border borderColor="border-outline-steel" p={4} flex={1} minWidth="200px">
|
|
<Group gap={2} align="center">
|
|
<Icon icon={Calendar} size={4} color="text-primary-blue" />
|
|
<Box>
|
|
<Text size="xs" color="text-zinc-500" uppercase letterSpacing="widest">Most Podiums</Text>
|
|
<Text size="lg" weight="bold" color="text-white">{championshipStats.topPodiums}</Text>
|
|
</Box>
|
|
</Group>
|
|
</Surface>
|
|
</Box>
|
|
|
|
<LeagueStandingsTable standings={standings} data-testid="standings-table" />
|
|
<Box data-testid="trend-indicator" display="none" />
|
|
<Box data-testid="drop-week-marker" display="none" />
|
|
<Box data-testid="standings-row" display="none" />
|
|
</Box>
|
|
);
|
|
}
|