Gammel components/new-round.tsx (3364 linjer) slettet, erstattet av
components/ny-runde/ + lib/ny-runde/ -- V0-eksporten (full teknisk spec,
null designføring) ported mot de faktiske backend-kontraktene i
app/routers/rounds.py: Gender-oversettelse, forenklet Tee-form (aldri
CR/Slope til klienten), CourseMeta-discriminated-union for teeoff- vs.
egen-bane, ekte POST /rounds -> sides -> participants-sekvens, ekte
kontosøk/gjeste-oppslag, ekte banesøk inkl. geolokasjon.
Fire bugs funnet og rettet under scratch-verifisering: manglende
forwardRef på TextInput (i selve V0-eksporten), manglende
tee-synk til eierens spillerkort etter banevalg (egen portingsfeil),
manglende Ingen/Alle-hurtigknapp i steg 5 (samme mangel rettet i appen
2026-08-06), og en produksjonsbug oppdaget EKTE brukere etter utrulling:
fetchFacilityCourses() leste responsen fra offisielt banesøk som en rå
array i stedet for {slug,name,courses}-objektet backend faktisk
returnerer -- ga en uendelig "Laster baner..."-hang for enhver runde
startet på en offisiell (teeoff-)bane. Rettet + lagt til reell
feilhåndtering i OfficialCourses (manglet helt, feilen ble tidligere
en stille, ufanget promise-rejection).
Se CHANGELOG.md punkt 41 og 43 for full verifiseringsdetalj.
67 lines
2.4 KiB
TypeScript
67 lines
2.4 KiB
TypeScript
"use client"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
import { Check } from "lucide-react"
|
|
import { useWizard } from "./wizard-context"
|
|
|
|
const STEP_LABELS: Record<number, string> = {
|
|
1: "Bane & tid",
|
|
2: "Spilleform",
|
|
3: "Spillere",
|
|
4: "Lag og rekkefølge",
|
|
5: "Deling",
|
|
}
|
|
|
|
export function ProgressSteps() {
|
|
const { state, visibleSteps } = useWizard()
|
|
const currentIdx = visibleSteps.indexOf(state.step)
|
|
|
|
return (
|
|
<nav aria-label="Fremdrift">
|
|
<ol className="flex items-center gap-1.5">
|
|
{visibleSteps.map((stepNo, i) => {
|
|
const isCurrent = i === currentIdx
|
|
const isDone = i < currentIdx
|
|
return (
|
|
<li key={stepNo} className="flex min-w-0 flex-1 items-center gap-1.5">
|
|
<div className="flex min-w-0 items-center gap-2">
|
|
<span
|
|
className={cn(
|
|
"flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-[11px] font-semibold tabular-nums transition-colors",
|
|
isCurrent && "bg-[var(--nr-accent)] text-[var(--nr-accent-ink)]",
|
|
isDone && "bg-[var(--nr-accent-soft)] text-[var(--nr-accent)]",
|
|
!isCurrent && !isDone && "bg-[var(--nr-surface-2)] text-[var(--nr-faint)] ring-1 ring-inset ring-[var(--nr-border)]",
|
|
)}
|
|
aria-hidden="true"
|
|
>
|
|
{isDone ? <Check className="h-3.5 w-3.5" strokeWidth={3} /> : i + 1}
|
|
</span>
|
|
<span
|
|
className={cn(
|
|
"hidden truncate text-xs font-medium sm:inline",
|
|
isCurrent ? "text-[var(--nr-ink)]" : "text-[var(--nr-muted)]",
|
|
)}
|
|
aria-current={isCurrent ? "step" : undefined}
|
|
>
|
|
{STEP_LABELS[stepNo]}
|
|
</span>
|
|
</div>
|
|
{i < visibleSteps.length - 1 ? (
|
|
<span
|
|
aria-hidden="true"
|
|
className={cn(
|
|
"h-px flex-1 transition-colors",
|
|
isDone ? "bg-[var(--nr-accent-ring)]" : "bg-[var(--nr-border)]",
|
|
)}
|
|
/>
|
|
) : null}
|
|
</li>
|
|
)
|
|
})}
|
|
</ol>
|
|
<p className="sr-only" aria-live="polite">
|
|
Steg {currentIdx + 1} av {visibleSteps.length}: {STEP_LABELS[state.step]}
|
|
</p>
|
|
</nav>
|
|
)
|
|
}
|