teecup/frontend/components/ny-runde/progress.tsx
Erol Haagenrud 11d10f7661 Erstatt /my-rounds/new med ny-runde-veiviser fra egen V0-prompt
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.
2026-08-08 12:34:25 +02:00

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>
)
}