"use client" import { useEffect, useState } from "react" import Link from "next/link" import { ArrowLeft, Check, Circle, Trophy } from "lucide-react" import { cn } from "@/lib/utils" // --- Types (matcher API-kontrakten i app/routers/tournaments.py sin leaderboard-endepunkt) type ApiTeamStanding = { team_id: string team_name: string color: string | null points: number } type ApiSessionStanding = { session_id: string sequence: number name: string | null points_by_team: Record matches_total: number matches_decided: number } type ApiLeaderboard = { teams: [ApiTeamStanding, ApiTeamStanding] matches_total: number matches_decided: number sessions: ApiSessionStanding[] } // --- Formatting helpers ---------------------------------------------------- function formatPoints(value: number): string { return value.toLocaleString("nb-NO", { maximumFractionDigits: 1 }) } function sessionTitle(s: ApiSessionStanding): string { return s.name?.trim() || `Økt ${s.sequence}` } // --- Component ------------------------------------------------------------- export function TournamentLeaderboard({ organizationId, tournamentId, tournamentName, }: { organizationId: string tournamentId: string tournamentName: string }) { const [board, setBoard] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { let cancelled = false async function load() { try { const res = await fetch(`/orgs/${organizationId}/tournaments/${tournamentId}/leaderboard`, { credentials: "include", }) if (!res.ok) throw new Error(`load failed: ${res.status}`) const data: ApiLeaderboard = await res.json() if (cancelled) return setBoard({ ...data, sessions: [...data.sessions].sort((a, b) => a.sequence - b.sequence) }) } catch { if (!cancelled) setError("Klarte ikke å laste leaderboardet. Prøv å laste siden på nytt.") } finally { if (!cancelled) setLoading(false) } } void load() return () => { cancelled = true } }, [organizationId, tournamentId]) const detailHref = `/tournaments/${tournamentId}?org=${organizationId}&name=${encodeURIComponent(tournamentName)}` const programHref = `/tournaments/${tournamentId}/program?org=${organizationId}&name=${encodeURIComponent(tournamentName)}` return (

Stilling

Totalt antall poeng summert på tvers av alle økter, oppdatert etter hvert som matcher avgjøres.

{error && (

{error}

)} {loading || !board ? (
) : ( <> {(() => { const [teamA, teamB] = board.teams const leaderId = teamA.points === teamB.points ? null : teamA.points > teamB.points ? teamA.team_id : teamB.team_id return ( <>

{board.matches_decided} av {board.matches_total} matcher avgjort

Poeng per økt

{board.sessions.length === 0 ? (

Ingen økter opprettet ennå.

) : ( board.sessions.map((s) => ( )) )}
) })()} )}
) } // --- Scoreboard ------------------------------------------------------------ function Scoreboard({ teamA, teamB, leaderId, }: { teamA: ApiTeamStanding teamB: ApiTeamStanding leaderId: string | null }) { return (
vs
) } function TeamScore({ team, isLeader, align, }: { team: ApiTeamStanding isLeader: boolean align: "left" | "right" }) { const color = team.color ?? "#64748b" return (
{isLeader && ( )} {formatPoints(team.points)} poeng
) } // --- Session row ----------------------------------------------------------- function SessionRow({ session, teamA, teamB, }: { session: ApiSessionStanding teamA: ApiTeamStanding teamB: ApiTeamStanding }) { const notStarted = session.matches_decided === 0 const complete = session.matches_decided === session.matches_total const aPts = session.points_by_team[teamA.team_id] ?? 0 const bPts = session.points_by_team[teamB.team_id] ?? 0 const colorA = teamA.color ?? "#64748b" const colorB = teamB.color ?? "#64748b" return (

{sessionTitle(session)}

{complete ? ( ) : notStarted ? ( ) : ( {session.matches_decided} av {session.matches_total} matcher avgjort )}
{formatPoints(aPts)} {formatPoints(bPts)}
{teamB.team_name}
) }