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 (
);
}
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
)}
);
}