import type React from "react" import Link from "next/link" import { ChevronRight, Gauge, MapPin, Medal, Trophy } from "lucide-react" import { cn } from "@/lib/utils" export type RoundStatus = "active" | "completed" export type Round = { id: string name?: string | null courseName: string status: RoundStatus teeName: string holes: 9 | 18 date: string playerCount: number // Progress for rounds still in play. holesPlayed?: number // Owner's score, present once at least one hole is recorded. totalScore?: number toPar?: number // Netto til-par (2026-07-29) -- kun slagspill, kun når HCP faktisk // spores for spilleren (samme betingelse som differensial-chipen). netToPar?: number // To-sidede formater (match/fourball/foursome/greensome/scramble, // 2026-07-29): VIEWER-relativ løpende status ("1 UP"/"AS"/"Vunnet 2&1"), // aldri backend sin bokstavelige side A/B. matchLead sitt fortegn (>0 = // spilleren leder) styrer fargen -- unngår å måtte parse teksten. playFormat?: string matchStatus?: string | null matchLead?: number | null // Handicap differential, only when a completed round counted (one decimal). differential?: number | null // Lowest score-to-par among the viewer's own completed rounds (computed // by the caller across the full rounds list, not by the card itself). isPersonalBest?: boolean } const STATUS_CONFIG: Record = { active: { label: "Pågår" }, completed: { label: "Fullført" }, } // Egen lokal kopi per prosjektets konvensjon (liten duplisering fremfor // cross-file-import, se samme mønster i round-detail.tsx/round-scorecard.tsx). const FORMAT_LABELS: Record = { stroke: "Slagspill", stableford: "Stableford", match: "Match", skins: "Skins", fourball: "Fourball", foursome: "Foursome", greensome: "Greensome", scramble_2: "Scramble (2)", scramble_4: "Scramble (4)", chapman: "Chapman", copenhagen: "Københavner", bbb: "Bingo Bango Bongo", flag: "Flaggturnering", shamble: "Shamble", money_ball: "Money Ball", high_low_high: "High-low-high", scramble_solo: "Scramble mot enkeltspiller", scramble_solo_match: "Scramble mot enkeltspiller (matchspill)", } const dateFormatter = new Intl.DateTimeFormat("no-NO", { day: "numeric", month: "short", year: "numeric", }) function formatDate(value: string) { const parsed = new Date(value) if (Number.isNaN(parsed.getTime())) return value return dateFormatter.format(parsed) } // "+10", "-2" or "E" for level par. The sign carries the meaning (never color alone). function formatToPar(toPar: number) { if (toPar === 0) return "E" return toPar > 0 ? `+${toPar}` : `${toPar}` } function toParDescription(toPar: number) { if (toPar === 0) return "på par" return toPar > 0 ? `${toPar} over par` : `${Math.abs(toPar)} under par` } // Status vises nå som fet, farget tekst (samme "form+farge, aldri farge // alene"-prinsipp -- teksten SIER "Pågår"/"Fullført", fargen er kun en // forsterkning) i stedet for en pille-badge, for en tettere, // nøkkel/verdi-orientert korttype. const STATUS_TEXT_CLASS: Record = { active: "text-primary", completed: "text-brand-orange", } function RoundProgressBar({ played, holes }: { played: number; holes: number }) { const pct = holes > 0 ? Math.min(100, Math.round((played / holes) * 100)) : 0 return (
) } // Kompakt nøkkel/verdi-celle -- selve rammen for grid-strukturen under // tittelen (label liten/dempet over, verdi stor/fet under). function InfoCell({ label, value, valueClassName, }: { label: string value: React.ReactNode valueClassName?: string }) { return (
{label} {value}
) } export function RoundCard({ round }: { round: Round }) { const playerLabel = round.playerCount === 1 ? "spiller" : "spillere" const hasScore = typeof round.totalScore === "number" && typeof round.toPar === "number" const showDifferential = round.status === "completed" && typeof round.differential === "number" && round.differential !== null const differentialText = showDifferential && round.differential != null ? round.differential.toFixed(1).replace(".", ",") : null const showPersonalBest = round.status === "completed" && round.isPersonalBest === true // Skjules for "stroke" (samme "kun avvik fra standardformatet er verdt en // pille"-konvensjon som round-scorecard.tsx sin sammendrags-stripe). const formatLabel = round.playFormat && round.playFormat !== "stroke" ? FORMAT_LABELS[round.playFormat] ?? round.playFormat : null // To-sidede formater (match/fourball/foursome/greensome/scramble, // 2026-07-29): vis matchStatus i stedet for rå slagtall -- brutto // slagtotal alene sier lite i matchspill. Alltid VIEWER-relativ tekst // (aldri backend sin bokstavelige side A/B), samme kilde uansett om // runden pågår eller er fullført (bruker bekreftet begge). const hasMatchStatus = typeof round.matchStatus === "string" && round.matchStatus.length > 0 const matchLead = round.matchLead ?? 0 const matchStatusClassName = matchLead > 0 ? "text-primary" : matchLead < 0 ? "text-brand-orange" : "" // Concise summary so the compact score tile reads clearly for screen readers. const scoreSummary = hasMatchStatus ? `Resultat ${round.matchStatus}.` : hasScore ? `Resultat ${round.totalScore} slag, ${toParDescription(round.toPar as number)}.` : `${round.holesPlayed ?? 0} av ${round.holes} hull spilt.` const displayTitle = round.name?.trim() || round.courseName const ariaLabel = `${displayTitle}, ${STATUS_CONFIG[round.status].label}. ${scoreSummary}` const played = round.holesPlayed ?? 0 // Resultat vises nå BÅDE mens runden pågår og etter fullføring (2026-07-29, // bekreftet av bruker) -- ikke lenger gatet til kun fullførte runder. const showResultRow = !hasMatchStatus && hasScore const toPar = round.toPar as number | undefined const toParClassName = toPar === undefined ? "" : toPar > 0 ? "text-brand-orange" : toPar < 0 ? "text-primary" : "" const netToPar = round.netToPar const netToParClassName = netToPar === undefined ? "" : netToPar > 0 ? "text-brand-orange" : netToPar < 0 ? "text-primary" : "" return ( // Egen ytre wrapper (2026-07-26) -- kortet var tidligere HELE selve // lenken til rundesiden; en leaderboard-lenke kan derfor ikke ligge // NESTET inni den (ugyldig HTML, samme klasse feil som tidligere // nestede-form-/knapp-feller i prosjektet). Hovedinnholdet er fortsatt // én stor lenke til rundesiden, leaderboard-lenken en egen, separat // rad under.

{round.name?.trim() && ( {round.courseName} )}
{STATUS_CONFIG[round.status].label} {formatLabel && ( {formatLabel} )} {/* Vis "spilt av planlagt" når det faktisk AVVIKER fra planen -- ikke bare mens runden pågår. En runde markert Fullført etter f.eks. 9 av 18 planlagte hull skal fortsatt vise det avviket her, ikke bare stille bytte til status-teksten (2026-08-11, reelt funn: "Hull"-cellen under viser uansett kun PLANLAGT antall, aldri hvor mange som faktisk ble spilt). */} {played < round.holes && ( {played}/{round.holes} hull spilt )} {showPersonalBest && ( )}
{round.status === "active" && }
{hasMatchStatus && ( )} {showResultRow && ( <> {netToPar !== undefined && ( )} )}
{differentialText && ( )} {/* Leaderboard-lenke (2026-07-26) -- kun meningsfullt med flere enn én deltaker. Vises for BÅDE pågående og fullførte runder (en fullført rundes sluttstilling er fortsatt nyttig å se). */} {round.playerCount > 1 && (
) }