'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 GROUPS_QUERY = gql` query Groups { groupStandings(year: 2026) { groupName pos played won drawn lost goalsFor goalsAgainst goalDiff pts team { id name iso2 slug } } matches(year: 2026, isQuali: false) { id group date time isLive scoreFt team1 { name iso2 slug } team2 { name iso2 slug } } } ` interface Standing { groupName: string; pos?: number | null played: number; won: number; drawn: number; lost: number goalsFor: number; goalsAgainst: number; goalDiff: number; pts: number team: { id: number; name: string; iso2?: string | null; slug: string } } interface MatchRow { id: number; group?: string | null; date?: string | null; time?: string | null isLive: boolean; scoreFt?: number[] | null team1: { name: string; iso2?: string | null; slug: string } team2: { name: string; iso2?: string | null; slug: string } } function utcKickoff(date: string, time: string): number { const m = time.match(/^(\d{2}):(\d{2})(?:\s+UTC([+-]\d+(?:\.\d+)?))?/) if (!m) return new Date(date).getTime() const [y, mo, d] = date.split('-').map(Number) const offsetH = m[3] ? parseFloat(m[3]) : 0 return Date.UTC(y, mo - 1, d, parseInt(m[1]) - offsetH, parseInt(m[2])) } function formatKickoff(date: string, time: string | null | undefined): string { if (!time) return new Date(date + 'T00:00:00').toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' }) const ms = utcKickoff(date, time) const local = new Date(ms) const today = new Date() const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1) const isToday = local.toDateString() === today.toDateString() const isTomorrow = local.toDateString() === tomorrow.toDateString() const day = isToday ? 'Today' : isTomorrow ? 'Tomorrow' : local.toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' }) return `${day} · ${local.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })}` } export default function GroupsPage() { const { data, loading } = useQuery(GROUPS_QUERY, { pollInterval: 60_000 }) useEffect(() => { document.title = 'Group Stage · World Cup' }, []) const standings: Standing[] = data?.groupStandings ?? [] const allMatches: MatchRow[] = data?.matches ?? [] const byGroup = standings.reduce>((acc, s) => { acc[s.groupName] = [...(acc[s.groupName] ?? []), s] return acc }, {}) const matchesByGroup = allMatches .filter(m => m.group) .reduce>((acc, m) => { acc[m.group!] = [...(acc[m.group!] ?? []), m] return acc }, {}) const groups = Object.entries(byGroup).sort(([a], [b]) => a.localeCompare(b)) return (

2026 Groups

48 teams · 12 groups · Top 2 + 8 best 3rd-place advance

{loading && !data && (
{Array.from({ length: 12 }).map((_, i) => (
))}
)}
{groups.map(([groupName, rows]) => { const sorted = [...rows].sort((a, b) => { if (b.pts !== a.pts) return b.pts - a.pts if (b.goalDiff !== a.goalDiff) return b.goalDiff - a.goalDiff return b.goalsFor - a.goalsFor }) const letter = groupName.replace('Group ', '') const groupMatches = (matchesByGroup[groupName] ?? []) .sort((a, b) => { if (!a.date) return 1 if (!b.date) return -1 const ta = a.time ? utcKickoff(a.date, a.time) : new Date(a.date).getTime() const tb = b.time ? utcKickoff(b.date!, b.time) : new Date(b.date!).getTime() return ta - tb }) const played = groupMatches.filter(m => m.scoreFt) const upcoming = groupMatches.filter(m => !m.scoreFt && !m.isLive) const live = groupMatches.filter(m => m.isLive) return (
{/* Header */}
GROUP {letter}
{/* Standings */}
Team PW DL GDPts
{sorted.map((t, idx) => (
{t.team.name}
{[t.played, t.won, t.drawn, t.lost].map((v, i) => ( {v} ))} {t.goalDiff > 0 ? `+${t.goalDiff}` : t.goalDiff} {t.pts}
))} {/* Live matches */} {live.length > 0 && (
{live.map(m => (
LIVE {m.team1.name} vs {m.team2.name}
))}
)} {/* Results */} {played.length > 0 && (
{played.map(m => (
{m.team1.name} {m.scoreFt![0]}–{m.scoreFt![1]} {m.team2.name}
))}
)} {/* Upcoming */} {upcoming.length > 0 && (
{upcoming.map(m => (
{m.team1.name} {m.date ? formatKickoff(m.date, m.time) : '–'} {m.team2.name}
))}
)}
) })}
) }