teecup/frontend/components/round-card.tsx
Erol Haagenrud 6e688c6440 Update Todos
Diagnostisere de to feilmeldingene fra skjermbildene

Flytte frontend-sidene fra /rounds/* til /my-rounds/* (kollisjon med API-proxyen)

Oppdatere alle interne lenker/router-kall til nye stier

Oppdatere next.config.mjs-kommentaren med denne lærdommen

Typesjekket produksjonsbuild

Rulle ut live etter bekreftelse

Oppdatere statusdokumentasjon
Fant og fikset rot-årsaken til begge feilmeldingene — en reell, alvorlig bug, ikke bare kosmetikk:

/rounds var samtidig navnet på både frontend-listesiden og backend-APIets ressursprefiks. Next.js sin rewrite-presedens ("afterFiles") gjorde at:

GET/POST /rounds fra klienten aldri nådde backend — den statiske siden vant, så fetch() fikk Next sin egen HTML tilbake i stedet for JSON. Det ga akkurat de to feilmeldingene i skjermbildene.
/rounds/[id] (rundedetaljsiden) hadde det motsatte problemet — der vant rewriten, så selve siden var fullstendig uoppnåelig (ville vist rå backend-JSON i stedet for UI-et om du hadde kommet så langt).
Dette er nøyaktig samme klasse feil som medlemsside-hendelsen under ADR-016, bare i begge retninger samtidig siden det denne gangen var en eksakt navnekollisjon, ikke bare nøsting.

Fikset: flyttet alle tre frontend-sidene til /my-rounds/* (list, new, [id]) — API-et beholder /rounds uendret. Oppdatert alle interne lenker (round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx, dashboard.tsx), lagt inn en tydelig advarsel i next.config.mjs for å hindre at dette gjentar seg. Bekreftet: «55/50/44/32» i det andre skjermbildet er ikke en bug — det er Tjøme Golfklubb sine faktiske utslagsnavn i teeoff (lengde i hundremeter).

Typesjekket produksjonsbuild kompilerer rent med de nye rutene.
2026-07-23 12:07:40 +02:00

100 lines
3.3 KiB
TypeScript

import Link from "next/link"
import { CalendarDays, ChevronRight, Flag, MapPin, Users } from "lucide-react"
import { cn } from "@/lib/utils"
export type RoundStatus = "active" | "completed"
export type Round = {
id: string
courseName: string
status: RoundStatus
teeName: string
holes: 9 | 18
date: string
playerCount: number
}
const STATUS_CONFIG: Record<RoundStatus, { label: string; dot: string; badge: string }> = {
active: {
label: "Pågår",
dot: "bg-primary",
badge: "bg-primary/15 text-foreground",
},
completed: {
label: "Fullført",
dot: "bg-brand-orange",
badge: "bg-brand-orange/12 text-foreground",
},
}
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)
}
function RoundStatusBadge({ status }: { status: RoundStatus }) {
const config = STATUS_CONFIG[status]
return (
<span
className={cn(
"inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-sm font-semibold",
config.badge,
)}
>
<span className={cn("size-2 rounded-full", config.dot)} aria-hidden="true" />
{config.label}
</span>
)
}
export function RoundCard({ round }: { round: Round }) {
const playerLabel = round.playerCount === 1 ? "spiller" : "spillere"
return (
<Link
href={`/my-rounds/${round.id}`}
className="group flex min-h-[88px] w-full items-center gap-4 rounded-2xl border border-border bg-card p-5 text-left shadow-sm shadow-black/5 transition-colors hover:border-primary/60 hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
>
<div className="flex min-w-0 flex-1 flex-col gap-3">
<div className="flex flex-wrap items-center gap-x-3 gap-y-2">
<h3 className="flex min-w-0 items-center gap-2 text-lg font-bold text-foreground">
<MapPin aria-hidden="true" className="size-5 shrink-0 text-primary" />
<span className="truncate">{round.courseName}</span>
</h3>
<RoundStatusBadge status={round.status} />
</div>
<div className="flex flex-wrap items-center gap-x-5 gap-y-1.5 text-base text-muted-foreground">
<span className="flex items-center gap-1.5">
<Flag aria-hidden="true" className="size-4 shrink-0" />
<span>
{round.teeName} · {round.holes} hull
</span>
</span>
<span className="flex items-center gap-1.5">
<CalendarDays aria-hidden="true" className="size-4 shrink-0" />
<span className="tabular-nums">{formatDate(round.date)}</span>
</span>
<span className="flex items-center gap-1.5">
<Users aria-hidden="true" className="size-4 shrink-0" />
<span>
{round.playerCount} {playerLabel}
</span>
</span>
</div>
</div>
<ChevronRight
aria-hidden="true"
className="size-6 shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5 group-hover:text-foreground"
/>
</Link>
)
}