import { useMemo } from 'react'; import { Link } from '@tanstack/react-router'; import { PageHeader } from '@/components/ui/PageHeader'; import { useTournamentStore } from '@/stores/tournamentStore'; import { teamFlag } from '@/lib/teams'; import { kickoffDay, kickoffTime } from '@/lib/format'; import { cn } from '@/lib/cn'; import type { Fixture, MatchProbs, Stage, TeamSlot } from '@/lib/types'; const COLUMNS: { stage: Stage; label: string }[] = [ { stage: 'r32', label: 'Round of 32' }, { stage: 'r16', label: 'Round of 16' }, { stage: 'qf', label: 'Quarter-finals' }, { stage: 'sf', label: 'Semi-finals' }, { stage: 'final', label: 'Final' }, ]; function Side({ slot, score, winner }: { slot: TeamSlot; score: number | null; winner: boolean }) { return (
{slot.team ? ( <> {teamFlag(slot.team)} {slot.team} ) : ( {slot.label} )} {score !== null && {score}}
); } function AdvanceBar({ probs }: { probs: MatchProbs }) { // Advance probability = regulation win + half of draws (shootout ≈ coin flip). const home = Math.round((probs.home + probs.draw / 2) * 100); return (
{home}%
{100 - home}%
); } function BracketMatch({ f, probs }: { f: Fixture; probs?: MatchProbs | undefined }) { const decided = f.status === 'finished' && f.homeScore !== null && f.awayScore !== null; const showProbs = !!probs && !!f.home.team && !!f.away.team && f.status !== 'finished'; return ( f.awayScore!} />
f.homeScore!} /> {showProbs && }
M{f.num} · {f.status === 'finished' ? 'FT' : `${kickoffDay(f.kickoff)} ${kickoffTime(f.kickoff)}`}
); } export function BracketPage() { const snapshot = useTournamentStore((s) => s.snapshot); const model = useTournamentStore((s) => s.model); const probsByNum = useMemo( () => new Map((model?.matches ?? []).map((m) => [m.num, m.probs])), [model], ); const { byStage, third } = useMemo(() => { const fixtures = snapshot?.fixtures ?? []; const byStage: Record = { group: [], r32: [], r16: [], qf: [], sf: [], third: [], final: [], }; for (const f of fixtures) if (f.stage !== 'group') byStage[f.stage].push(f); for (const s of Object.keys(byStage) as Stage[]) byStage[s].sort((a, b) => a.num - b.num); return { byStage, third: byStage.third[0] }; }, [snapshot]); return (
{COLUMNS.map((col) => (

{col.label}

{byStage[col.stage].length ? byStage[col.stage].map((f) => ) :
}
))}
{third && (

Third-place play-off

)}
); }