'use client' import { useQuery, gql } from '@/lib/graphql/hooks' import { useEffect } from 'react' import Link from 'next/link' import { TeamFlag } from '@/components/team-flag' import { LiveBadge } from '@/components/live-badge' import { MatchCard } from '@/components/match-card' const HOME_QUERY = gql` query Home { tournamentStats { totalTournaments totalMatches totalGoals avgGoalsPerGame } liveMatches { id year round group date time isLive scoreFt scoreEt scoreP isQualiPlayoff team1 { name iso2 slug } team2 { name iso2 slug } } recentMatches(limit: 9) { id year round group date time isLive isQualiPlayoff scoreFt scoreEt scoreP team1 { name iso2 slug } team2 { name iso2 slug } } upcomingMatches(limit: 9) { id year round group date time isLive isQualiPlayoff scoreFt team1 { name iso2 slug } team2 { name iso2 slug } } topScorers(year: 2026, limit: 8) { playerName goals penalties ownGoals team { name iso2 } } tournament(year: 2026) { year totalGoals matchesCount avgGoalsPerGame } } ` function SectionHeader({ label }: { label: string }) { return (
{label}
) } function StatPill({ label, value }: { label: string; value: string | number }) { return (
{label}
{value ?? '–'}
) } interface UpcomingMatch { id: number; year: number; time?: string | null; date?: string | null team1: { name: string; iso2?: string | null } team2: { name: string; iso2?: string | null } } function formatKickoff(date: string | null | undefined, time: string | null | undefined): string { if (!date) return '' const today = new Date() const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1) if (time) { const m = time.match(/^(\d{2}):(\d{2})(?:\s+UTC([+-]\d+(?:\.\d+)?))?/) if (m) { const [y, mo, d] = date.split('-').map(Number) const h = parseInt(m[1]), min = parseInt(m[2]) const offsetH = m[3] ? parseFloat(m[3]) : 0 // Compute UTC kickoff, then let the browser render in its local timezone const local = new Date(Date.UTC(y, mo - 1, d, h - offsetH, min)) const isToday = local.toDateString() === today.toDateString() const isTomorrow = local.toDateString() === tomorrow.toDateString() const dayLabel = isToday ? 'Today' : isTomorrow ? 'Tomorrow' : local.toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' }) const localTime = local.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }) return `${dayLabel} · ${localTime}` } } // No time — fall back to venue date label only const matchDate = new Date(date + 'T00:00:00') const isToday = matchDate.toDateString() === today.toDateString() const isTomorrow = matchDate.toDateString() === tomorrow.toDateString() return isToday ? 'Today' : isTomorrow ? 'Tomorrow' : matchDate.toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' }) } function UpcomingFixture({ match }: { match: UpcomingMatch }) { const label = formatKickoff(match.date, match.time) return (
{match.team1.name} vs {match.team2.name}
{label &&
{label}
}
) } interface ScorerEntry { playerName: string; goals: number; penalties: number team?: { name: string; iso2?: string | null } | null } interface MatchData { id: number; year: number; round: string; group?: string | null date?: string | null; time?: string | null; isLive: boolean; isQualiPlayoff: boolean scoreFt?: number[] | null; scoreEt?: number[] | null; scoreP?: number[] | null team1: { name: string; iso2?: string | null; slug?: string | null } team2: { name: string; iso2?: string | null; slug?: string | null } } export default function HomePage() { const { data, loading } = useQuery(HOME_QUERY, { pollInterval: 60_000 }) useEffect(() => { document.title = 'World Cup' }, []) const stats = data?.tournamentStats const live: MatchData[] = data?.liveMatches ?? [] const recent: MatchData[] = data?.recentMatches ?? [] const upcoming: UpcomingMatch[] = data?.upcomingMatches ?? [] const scorers: ScorerEntry[] = data?.topScorers ?? [] const wc2026 = data?.tournament const maxGoals = Math.max(...scorers.map(s => s.goals), 1) return (
{/* ── Hero ── */}
{live.length > 0 ? :
World Cup 2026 · In Progress
}

World Cup 2026

USA · Canada · Mexico  ·  11 June – 19 July 2026 · 48 Teams

{stats ? <> {wc2026 && <> } : [1,2,3,4].map(i => (
))}
{/* Live matches */} {live.length > 0 && (
{live.map(m => )}
)} {/* Latest result */} {recent.length > 0 && (
)} {/* Recent grid */} {recent.length > 1 && (
{recent.slice(1).map(m => )}
)} {/* Upcoming */} {upcoming.length > 0 && (
{upcoming.map(m => )}
)} {/* Golden Boot 2026 */} {scorers.length > 0 && (
{scorers.map((s, i) => (
{i + 1} {s.team && }
{s.playerName}
{s.team?.name}{s.penalties > 0 ? ` · ${s.penalties}P` : ''}
{s.goals}
))}

View all-time top scorers →

)} {loading && !data && (
Loading live World Cup data…
)}
) }