'use client' import { useQuery, gql } from '@/lib/graphql/hooks' import { useEffect } from 'react' import Link from 'next/link' import { TeamFlag } from '@/components/team-flag' const STATS_QUERY = gql` query Stats { tournaments { year host totalGoals matchesCount avgGoalsPerGame winner } topScorers(limit: 20) { playerName goals penalties ownGoals tournaments team { name iso2 slug } } teams { id name iso2 slug stats { appearances titles wins draws losses goalsFor goalsAgainst goalDiff winPct } } goalsByMinute { bucket count } confederationStats { confederation appearances titles totalGoals } hatTricks { playerName year round goals team { name iso2 } opponent { name iso2 } } biggestWins(limit: 10) { id year round date margin totalGoals scoreFt team1 { name iso2 } team2 { name iso2 } } highestScoringMatches(limit: 10) { id year round date totalGoals scoreFt team1 { name iso2 } team2 { name iso2 } } extraTimeStats { totalKnockoutMatches wentToExtraTime wentToPenalties extraTimePct penaltiesPct } } ` function SectionTitle({ children }: { children: React.ReactNode }) { return

{children}

} function Card({ children, className = '' }: { children: React.ReactNode; className?: string }) { return (
{children}
) } interface Tournament { year: number; host: string; totalGoals?: number | null; matchesCount?: number | null; avgGoalsPerGame?: string | number | null; winner?: string | null } interface Scorer { playerName: string; goals: number; penalties: number; ownGoals: number; tournaments: number; team?: { name: string; iso2?: string | null; slug: string } | null } interface TeamRow { id: number; name: string; iso2?: string | null; slug: string; stats?: { appearances: number; titles: number; wins: number; draws: number; losses: number; goalsFor: number; goalsAgainst: number; winPct: number } | null } interface MinuteBucket { bucket: string; count: number } interface ConfStat { confederation: string; appearances: number; titles: number; totalGoals: number } interface HatTrick { playerName: string; year: number; round: string; goals: number; team?: { name: string; iso2?: string | null } | null; opponent?: { name: string; iso2?: string | null } | null } interface MatchRow { id: number; year: number; round: string; date?: string | null; margin?: number | null; totalGoals?: number | null; scoreFt?: number[] | null; team1: { name: string; iso2?: string | null }; team2: { name: string; iso2?: string | null } } interface ETStats { totalKnockoutMatches: number; wentToExtraTime: number; wentToPenalties: number; extraTimePct: number; penaltiesPct: number } export default function StatsPage() { useEffect(() => { document.title = 'Statistics · World Cup' }, []) const { data, loading } = useQuery(STATS_QUERY) const tournaments: Tournament[] = (data?.tournaments ?? []).filter((t: Tournament) => t.totalGoals != null).sort((a: Tournament, b: Tournament) => a.year - b.year) const scorers: Scorer[] = data?.topScorers ?? [] const teams: TeamRow[] = (data?.teams ?? []).filter((t: TeamRow) => t.stats && t.stats.appearances > 0).sort((a: TeamRow, b: TeamRow) => (b.stats?.appearances ?? 0) - (a.stats?.appearances ?? 0)) const minuteBuckets: MinuteBucket[] = data?.goalsByMinute ?? [] const confStats: ConfStat[] = data?.confederationStats ?? [] const hatTricks: HatTrick[] = data?.hatTricks ?? [] const biggestWins: MatchRow[] = data?.biggestWins ?? [] const highScoring: MatchRow[] = data?.highestScoringMatches ?? [] const etStats: ETStats | null = data?.extraTimeStats ?? null const titlesByNation = teams .filter(t => (t.stats?.titles ?? 0) > 0) .sort((a, b) => (b.stats?.titles ?? 0) - (a.stats?.titles ?? 0)) .slice(0, 10) const maxGoals = Math.max(...tournaments.map(t => t.totalGoals ?? 0), 1) const maxScorer = Math.max(...scorers.map(s => s.goals), 1) const maxMinute = Math.max(...minuteBuckets.map(b => b.count), 1) return (

Historical Statistics

{loading && !data && (
Loading statistics…
)} {/* ── Goals per tournament bar chart ── */} {tournaments.length > 0 && (
⚽ Goals Scored per Tournament
{tournaments.map(t => { const h = Math.max(4, Math.round(((t.totalGoals ?? 0) / maxGoals) * 140)) const avg = t.avgGoalsPerGame ? Number(t.avgGoalsPerGame).toFixed(1) : null return (
{t.totalGoals}
) })}
{tournaments.map(t => (
{t.year}
))}
)}
{/* ── All-time top scorers ── */}
🏅 All-Time Top Scorers {scorers.map((s, i) => (
{i + 1} {s.team && }
{s.playerName}
{s.team?.name} · {s.tournaments} WC{s.tournaments !== 1 ? 's' : ''}{s.penalties > 0 ? ` · ${s.penalties}P` : ''}
{s.goals}
))}
{/* ── World Cup titles ── */}
🏆 World Cup Titles by Nation {titlesByNation.map((t, i) => (
{i + 1}
{t.name}
{Array.from({ length: t.stats?.titles ?? 0 }).map((_, j) => ( 🏆 ))}
{t.stats?.titles}
))}
{/* ── Goals by minute heatmap ── */} {minuteBuckets.length > 0 && (
⏱ Goals by Minute (All-Time)
{minuteBuckets.map(b => { const h = Math.max(8, Math.round((b.count / maxMinute) * 80)) return (
{b.count}
{b.bucket}
) })}
)}
{/* ── Biggest wins ── */}
💥 Biggest Victories {biggestWins.map(m => (
{m.team1.name} vs {m.team2.name}
{m.year} · {m.round}
{m.scoreFt?.[0]}–{m.scoreFt?.[1]} +{m.margin}
))}
{/* ── Highest scoring matches ── */}
🔥 Highest Scoring Matches {highScoring.map(m => (
{m.team1.name} vs {m.team2.name}
{m.year} · {m.round}
{m.scoreFt?.[0]}–{m.scoreFt?.[1]} {m.totalGoals} goals
))}
{/* ── Hat-tricks ── */} {hatTricks.length > 0 && (
🎩 Hat-Tricks
{hatTricks.map((h, i) => (
{h.team && }
{h.playerName}
{h.team?.name}
{h.goals}
{h.year} · {h.round} {h.opponent && vs {h.opponent.name}}
))}
)} {/* ── ET & Penalty stats ── */} {etStats && (
⚡ Extra Time & Penalty Shootouts
{[ { label: 'Knockout Matches', value: etStats.totalKnockoutMatches }, { label: 'Went to AET', value: `${etStats.wentToExtraTime} (${etStats.extraTimePct}%)` }, { label: 'Decided by PSO', value: `${etStats.wentToPenalties} (${etStats.penaltiesPct}%)` }, { label: 'Decided in 90min', value: etStats.totalKnockoutMatches - etStats.wentToExtraTime }, ].map(s => (
{s.label}
{s.value}
))}
)} {/* ── Confederation stats ── */} {confStats.length > 0 && (
🌍 Performance by Confederation {confStats.map(c => ( ))}
Confederation Appearances Titles Goals
{c.confederation} {c.appearances} {c.titles} {c.totalGoals}
)} {/* ── All-time team table ── */} {teams.length > 0 && (
📊 All-Time Team Table
{['#', 'Team', 'WC', 'W', 'D', 'L', 'GF', 'GA', 'GD', 'Win%'].map((h, i) => ( ))} {teams.slice(0, 40).map((t, i) => ( ))}
{h}
{i + 1} {t.name} {t.stats?.appearances} {t.stats?.wins} {t.stats?.draws} {t.stats?.losses} {t.stats?.goalsFor} {t.stats?.goalsAgainst} {(t.stats?.goalsFor ?? 0) - (t.stats?.goalsAgainst ?? 0) >= 0 ? `+${(t.stats?.goalsFor ?? 0) - (t.stats?.goalsAgainst ?? 0)}` : (t.stats?.goalsFor ?? 0) - (t.stats?.goalsAgainst ?? 0)} {t.stats?.winPct}%
)}
) }