Chip/Bunker/Straffeslag/Anywayslag som egen skjerm (ADR-078)

Splittet ScoringWizard sitt "details"-steg i "direction"/"holeDetails",
og bygget om HoleStatsSheet (org-turneringer) fra én lang skjerm til
tilsvarende steg-flyt -- samme opplevelse i begge scoringsflyter, uten å
røre HoleStatsSheet sin egen (bevisst annerledes) lagringsmodell.

Visuell polering via V0: de fire tellerne fikk et 2x2-rutenett av
flis-kort i stedet for en 1-kolonne-stabel. Stepper-komponenten er delt
mellom begge flyter, så endringen gjelder automatisk begge steder.

tsc + 45/45 vitest, scratch-verifisert i begge flyter (lys+mørk). Rullet ut.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Erol Haagenrud 2026-08-16 09:53:16 +02:00
parent 2ea4c26990
commit b292401fe4
5 changed files with 304 additions and 103 deletions

View file

@ -7663,6 +7663,73 @@ Containeren startet rent.
--- ---
## ADR-078: Chip/Bunker/Straffeslag/Anywayslag som egen skjerm ("Steg 2" av tre forbedringer i score-registreringen) (2026-08-16)
Foranledning: bruker observerte at Chip/Bunker/Straffeslag/Anywayslag ble
vist klemt inn under retningsvalget for utslag/innspill i begge
scoringsflytene -- ikke godt nok, burde være en egen skjerm. Bekreftet
(AskUserQuestion) at dette skal gjelde BEGGE flyter -- frittstående runder
OG org-turneringer -- "opplevelsen for brukeren skal være lik uansett".
**Strukturell splitt, begge flyter:**
- Frittstående runder (`ScoringWizard`, `round-detail.tsx`): `WizardStep`
utvidet fra `"strokes" | "putts" | "puttDistance" | "details"` til
`"strokes" | "putts" | "puttDistance" | "direction" | "holeDetails"` --
"details" splittet i to egne steg. GIR-auto-inferensen (2026-07-30)
flyttet fra å trigge på `step !== "details"` til `step !== "direction"`.
- Org-turneringer (`HoleStatsSheet`, `individual-tournament-detail.tsx`):
bygget om fra ÉN lang scrollbar skjerm til en LITEN intern steg-flyt
(samme stegrekkefølge som over), med samme auto-hopp og
GIR-auto-inferens som runde-siden. **Bevisst avgrensning**: lagringen
er UENDRET -- fortsatt ÉN batched `save()`-kall ved siste steg (nå
trigget fra "Lagre"-knappen i stedet for en alltid-synlig knapp), IKKE
konvertert til runde-sidens PATCH-per-felt-modell. De to flytene får nå
samme STRUKTUR/interaksjon, ikke delt kode eller delt lagrings-
mekanikk -- unngår å risikere en fungerende, annerledes arkitektur som
ikke var en del av forespørselen.
**Visuell polering (V0-designet)**: siden det gjenstående "Detaljer per
hull"-steget nå har en hel skjerm for seg selv, ble den tidligere
1-kolonne-stabelen (mye tomrom under fire små tellere) erstattet med et
FAST 2x2-rutenett av teller-FLISER -- egne kort med etikett + stort
sentrert tall + -/+ nederst. Interaksjonskontrakten er uendret (minus
disabled på 0, pluss disabled på `max`, alle trykkflater >=44px), kun det
visuelle uttrykket er nytt. `Stepper`-komponenten (delt av begge flyter
via `hole-stat-inputs.tsx`) oppdatert i selve komponenten -- automatisk
identisk i begge flyter uten duplisert arbeid. `grid-cols-2` (ALDRI
`grid-cols-3`) fordi seksjonen er fast `max-w-sm` (384px): to fliser gir
~184px hver, nok til flisens to 44px-knapper side om side.
V0-prompt skrevet av Claude (se `feedback_frontend_via_v0`), kjørt av
bruker, eksport (`tee-cup (12).zip`) integrert -- KUN den relevante
`Stepper`-omdesignet + grid-endringen ble portert inn (eksporten var en
full prosjekt-snapshot fra en delt V0-arbeidsflate, inkludert en eldre,
FØR-splitt kopi av hele `round-detail.tsx` -- ingen fullstendig
filerstatning, kun det faktisk endrede uttrykket for Stepper/rutenett
overført til den ekte, allerede steg-splittede filen). Zip slettet fra
`Temp-uploads/` etter integrering.
**Verifisert:** `tsc --noEmit` rent + 45/45 vitest (begge ganger --
strukturell splitt, og etter V0-integrering). Egen scratch-database +
scratch `teecup_api` + lokal `next dev`, to runder (port 18105/13105 for
strukturell verifisering, port 18106/13106 for V0-integrering): begge
flyter testet ende-til-ende gjennom ALLE steg (strokes → putts →
puttDistance → direction → holeDetails), GIR-auto-inferens bekreftet
fortsatt virker (trigget riktig på "direction"-steget), lagring bekreftet
i begge flyter (round-siden PATCH-per-felt, turnering-siden batched
"Lagre"), gjenåpning av et allerede utfylt hull viste korrekt lagrede
verdier. 2x2-rutenettet med teller-flisene bekreftet identisk i begge
flyter, interaksjon (øke/redusere, disabled ved 0/max) bekreftet
fungerende. Lys+mørk bekreftet i begge runder. Scratch-stackene
fullstendig revet ned -- ekte `teecup_db`/`teecup_api`/`teecup_frontend`
urørt.
**Ingen migrasjon** -- ren frontend-endring.
**Rullet ut**: ikke ennå -- venter på bekreftelse fra bruker.
---
## Utviklingsplan (rekkefølge) ## Utviklingsplan (rekkefølge)
1. ✅ Land tenant-modell → **Organisasjon** (ADR-001/002/003) 1. ✅ Land tenant-modell → **Organisasjon** (ADR-001/002/003)

View file

@ -11690,3 +11690,29 @@ Neste steg:
**Rullet ut 2026-08-16** -- ingen migrasjon, `docker compose build **Rullet ut 2026-08-16** -- ingen migrasjon, `docker compose build
teecup_frontend && up -d` etter eksplisitt bekreftelse fra bruker. teecup_frontend && up -d` etter eksplisitt bekreftelse fra bruker.
Containeren startet rent. Containeren startet rent.
94. **Chip/Bunker/Straffeslag/Anywayslag som egen skjerm (ADR-078) —
2026-08-16.** Andre av tre forbedringer i score-registreringen. Disse
fire tellerne var klemt inn under retningsvalget for utslag/innspill i
begge scoringsflytene (frittstående runder OG org-turneringer, etter
eksplisitt "opplevelsen skal være lik uansett").
`ScoringWizard`s "details"-steg splittet i "direction"/"holeDetails".
`HoleStatsSheet` (org-turneringer) bygget om fra ÉN lang skjerm til en
tilsvarende liten intern steg-flyt -- lagringen selv er BEVISST
uendret (fortsatt én batched "Lagre" der, ikke konvertert til runde-
sidens PATCH-per-felt). Visuell polering via V0 (Claude-skrevet
prompt): de fire tellerne fikk et 2x2-rutenett av flis-kort i stedet
for en 1-kolonne-stabel med mye tomrom -- `Stepper`-komponenten (delt
av begge flyter) oppdatert ett sted, automatisk identisk begge steder.
**Verifisert:** `tsc --noEmit` rent + 45/45 vitest. To scratch-runder
(strukturell splitt, så V0-integrering): begge flyter testet
ende-til-ende gjennom alle steg, GIR-auto-inferens fortsatt virker,
lagring bekreftet i begge flyter, gjenåpning viste korrekt lagrede
verdier, 2x2-rutenettet identisk og fungerende i begge flyter.
Lys+mørk bekreftet. Scratch-stackene fullstendig revet ned.
**Ingen migrasjon.**
**Rullet ut**: ikke ennå.

View file

@ -316,26 +316,42 @@ export function Stepper({
max?: number max?: number
}) { }) {
const atMax = max !== undefined && value >= max const atMax = max !== undefined && value >= max
const atMin = value <= 0
// Teller-FLIS (ADR-078, 2026-08-16, V0-designet) -- selvstendig kort med
// etikett + stort tall + -/+ på bunnen, ment for et 2x2-rutenett (se
// holeDetails-steget i round-detail.tsx/individual-tournament-detail.tsx).
// Interaksjonskontrakten er uendret: -/tall/+, minus disabled på 0, pluss
// disabled på max, alle trykkflater >=44px. Kun app-tokens.
return ( return (
<div className="flex flex-col gap-2"> <div className="flex flex-col items-center gap-3 rounded-2xl border border-border bg-card p-4">
<span className="text-base font-semibold text-foreground">{label}</span> <span className="text-center text-sm font-bold leading-tight text-foreground">{label}</span>
<div className="flex items-center justify-between gap-2 rounded-2xl border border-border bg-card p-1.5"> {/* Tallet bærer selve informasjonen (aldri farge alene); nullverdi
dempes kun visuelt via muted-foreground, som fortsatt er AA
kort. */}
<span
className={cn(
"text-4xl font-extrabold leading-none tabular-nums",
atMin ? "text-muted-foreground" : "text-foreground",
)}
>
{value}
</span>
<div className="flex w-full items-center gap-2">
<button <button
type="button" type="button"
disabled={readOnly || value <= 0} disabled={readOnly || atMin}
onClick={() => onChange(Math.max(0, value - 1))} onClick={() => onChange(Math.max(0, value - 1))}
aria-label={`Færre ${label}`} aria-label={`Færre ${label}`}
className="flex size-11 shrink-0 items-center justify-center rounded-xl border border-border bg-background text-foreground transition-colors hover:bg-accent/50 disabled:opacity-40" className="flex h-11 flex-1 items-center justify-center rounded-xl border border-border bg-background text-foreground transition-colors hover:bg-accent/50 disabled:opacity-40"
> >
<Minus aria-hidden="true" className="size-5" /> <Minus aria-hidden="true" className="size-5" />
</button> </button>
<span className="min-w-8 text-center text-xl font-extrabold tabular-nums text-foreground">{value}</span>
<button <button
type="button" type="button"
disabled={readOnly || atMax} disabled={readOnly || atMax}
onClick={() => onChange(value + 1)} onClick={() => onChange(value + 1)}
aria-label={`Flere ${label}`} aria-label={`Flere ${label}`}
className="flex size-11 shrink-0 items-center justify-center rounded-xl border border-border bg-background text-foreground transition-colors hover:bg-accent/50 disabled:opacity-40" className="flex h-11 flex-1 items-center justify-center rounded-xl border border-border bg-background text-foreground transition-colors hover:bg-accent/50 disabled:opacity-40"
> >
<Plus aria-hidden="true" className="size-5" /> <Plus aria-hidden="true" className="size-5" />
</button> </button>

View file

@ -3236,6 +3236,23 @@ type HolePatch = {
expected_version?: number | null expected_version?: number | null
} }
// ADR-078 (2026-08-16): HoleStatsSheet bygget om fra ÉN lang scrollbar
// skjerm til en liten intern steg-flyt -- samme visuelle språk (fulskjerm,
// header/footer) og samme stegrekkefølge som ScoringWizard (frittstående
// runder), etter eksplisitt ønske om at "opplevelsen for brukeren skal
// være lik uansett". IKKE en gjenbruk av ScoringWizard-koden -- lagringen
// er bevisst UENDRET (ÉN batched save() ved siste steg, ikke PATCH per
// felt slik runde-siden gjør) for å unngå å røre en fungerende, annerledes
// arkitektur mekanikk som ikke var en del av forespørselen. Se
// wizardStepsFor()/WizardStep i round-detail.tsx for det speilede
// mønsteret på runde-siden.
type SheetStep = "strokes" | "putts" | "puttDistance" | "direction" | "holeDetails"
function sheetStepsFor(statLevel: "strokes_and_putts" | "full"): SheetStep[] {
if (statLevel === "full") return ["strokes", "putts", "puttDistance", "direction", "holeDetails"]
return ["strokes", "putts", "puttDistance"]
}
function HoleStatsSheet({ function HoleStatsSheet({
hole, hole,
playerName, playerName,
@ -3268,6 +3285,43 @@ function HoleStatsSheet({
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const steps = sheetStepsFor(statLevel)
const [stepIndex, setStepIndex] = useState(0)
const step = steps[Math.min(stepIndex, steps.length - 1)]
const isFirstStep = stepIndex === 0
const isLastStep = stepIndex === steps.length - 1
const canProceed = step === "strokes" ? strokes !== null : true
// Auto-hopp, samme mønster som ScoringWizard (round-detail.tsx) --
// hopper videre av seg selv når steget sitt eget felt får en verdi.
const autoField = step === "strokes" ? "strokes" : step === "putts" ? "putts" : step === "puttDistance" ? "puttBucket" : null
const enteredWithValueRef = useRef(false)
const firedRef = useRef(false)
useEffect(() => {
firedRef.current = false
enteredWithValueRef.current =
autoField === "strokes" ? strokes !== null : autoField === "putts" ? putts !== null : autoField === "puttBucket" ? puttBucket !== null : false
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [step])
useEffect(() => {
if (!autoField || firedRef.current || enteredWithValueRef.current) return
const value = autoField === "strokes" ? strokes : autoField === "putts" ? putts : puttBucket
if (value !== null) {
firedRef.current = true
forward()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [strokes, putts, puttBucket])
// GIR-auto-inferens, samme formel/begrunnelse som ScoringWizard.
useEffect(() => {
if (step !== "direction") return
if (approach !== null) return
if (strokes === null || putts === null) return
if (strokes - putts <= hole.par - 2) setApproach("hit")
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [step, strokes, putts, approach, hole.par])
async function save() { async function save() {
if (strokes === null) { if (strokes === null) {
setError("Registrer antall slag først.") setError("Registrer antall slag først.")
@ -3296,6 +3350,21 @@ function HoleStatsSheet({
} }
} }
function back() {
if (isFirstStep) {
onClose()
return
}
setStepIndex((i) => i - 1)
}
function forward() {
if (!isLastStep) {
setStepIndex((i) => i + 1)
return
}
void save()
}
return ( return (
<div className="fixed inset-0 z-50 flex flex-col bg-background"> <div className="fixed inset-0 z-50 flex flex-col bg-background">
<header className="flex min-h-14 shrink-0 items-center gap-2 border-b border-border px-3"> <header className="flex min-h-14 shrink-0 items-center gap-2 border-b border-border px-3">
@ -3318,6 +3387,8 @@ function HoleStatsSheet({
<main className="flex min-h-0 flex-1 flex-col overflow-y-auto p-5 sm:p-6"> <main className="flex min-h-0 flex-1 flex-col overflow-y-auto p-5 sm:p-6">
<div className="mx-auto flex w-full max-w-sm flex-col gap-6"> <div className="mx-auto flex w-full max-w-sm flex-col gap-6">
{step === "strokes" && (
<>
<NumberPicker <NumberPicker
label="Slag" label="Slag"
value={strokes} value={strokes}
@ -3331,11 +3402,15 @@ function HoleStatsSheet({
readOnly={false} readOnly={false}
/> />
{/* Spillerens per-hull-historikk (ADR-072) -- skjuler seg selv når {/* Spillerens per-hull-historikk (ADR-072) -- skjuler seg
banen er custom/håndlaget eller spilleren ikke har historikk selv når banen er custom/håndlaget eller spilleren ikke
ennå (se HoleHistoryPanel/app/hole_history.py). */} har historikk ennå (se HoleHistoryPanel/app/hole_
history.py). */}
<HoleHistoryPanel url={historyUrl} /> <HoleHistoryPanel url={historyUrl} />
</>
)}
{step === "putts" && (
<NumberPicker <NumberPicker
label="Putter" label="Putter"
value={putts} value={putts}
@ -3347,7 +3422,9 @@ function HoleStatsSheet({
onChange={setPutts} onChange={setPutts}
readOnly={false} readOnly={false}
/> />
)}
{step === "puttDistance" && (
<ChoiceRow <ChoiceRow
label="Avstand første putt" label="Avstand første putt"
options={[ options={[
@ -3362,8 +3439,9 @@ function HoleStatsSheet({
onChange={setPuttBucket} onChange={setPuttBucket}
readOnly={false} readOnly={false}
/> />
)}
{statLevel === "full" && ( {step === "direction" && (
<> <>
<ClubPicker <ClubPicker
label="Kølle brukt ved utslaget" label="Kølle brukt ved utslaget"
@ -3393,30 +3471,36 @@ function HoleStatsSheet({
readOnly={false} readOnly={false}
/> />
</WizardSection> </WizardSection>
</>
)}
<WizardSection title="Detaljer per hull"> {/* Teller-flis-rutenett (V0-designet, ADR-078, 2026-08-16) --
<div className="grid grid-cols-1 gap-3"> samme 2x2-mønster som round-detail.tsx sitt speilede
holeDetails-steg, se kommentaren der for begrunnelsen. */}
{step === "holeDetails" && (
<div className="grid grid-cols-2 gap-3">
<Stepper label="Chip" value={chip} onChange={setChip} readOnly={false} max={strokes ?? undefined} /> <Stepper label="Chip" value={chip} onChange={setChip} readOnly={false} max={strokes ?? undefined} />
<Stepper label="Bunker" value={bunker} onChange={setBunker} readOnly={false} max={strokes ?? undefined} /> <Stepper label="Bunker" value={bunker} onChange={setBunker} readOnly={false} max={strokes ?? undefined} />
<Stepper label="Straffeslag" value={penalty} onChange={setPenalty} readOnly={false} max={strokes ?? undefined} /> <Stepper label="Straffeslag" value={penalty} onChange={setPenalty} readOnly={false} max={strokes ?? undefined} />
<Stepper label="Anywayslag" value={anywayStrokes} onChange={setAnywayStrokes} readOnly={false} max={strokes ?? undefined} /> <Stepper label="Anywayslag" value={anywayStrokes} onChange={setAnywayStrokes} readOnly={false} max={strokes ?? undefined} />
</div> </div>
</WizardSection>
</>
)} )}
{error && <p className="text-sm font-medium text-destructive">{error}</p>} {error && <p className="text-sm font-medium text-destructive">{error}</p>}
</div> </div>
</main> </main>
<footer className="shrink-0 border-t border-border p-4"> <footer className="grid shrink-0 grid-cols-2 gap-3 border-t border-border p-4">
<Button type="button" variant="outline" onClick={back} className="h-14 rounded-2xl text-base font-bold">
{isFirstStep ? "Avbryt" : "Forrige"}
</Button>
<Button <Button
type="button" type="button"
disabled={saving || strokes === null} disabled={!canProceed || saving}
onClick={save} onClick={forward}
className="h-14 w-full rounded-2xl text-base font-bold disabled:opacity-40" className="h-14 rounded-2xl text-base font-bold disabled:opacity-40"
> >
{saving ? "Lagrer…" : "Lagre"} {isLastStep ? (saving ? "Lagrer…" : "Lagre") : "Neste"}
</Button> </Button>
</footer> </footer>
</div> </div>

View file

@ -1921,12 +1921,17 @@ export function RoundDetail({ roundId }: { roundId: string }) {
// nullstiller til steg 1) eller "Ferdig" (er spilleren den siste, lukker // nullstiller til steg 1) eller "Ferdig" (er spilleren den siste, lukker
// veiviseren og varsler forelderen om å gå videre til neste hull). // veiviseren og varsler forelderen om å gå videre til neste hull).
type WizardStep = "strokes" | "putts" | "puttDistance" | "details" type WizardStep = "strokes" | "putts" | "puttDistance" | "direction" | "holeDetails"
function wizardStepsFor(statLevel: StatLevel): WizardStep[] { function wizardStepsFor(statLevel: StatLevel): WizardStep[] {
if (statLevel === "strokes_only") return ["strokes"] if (statLevel === "strokes_only") return ["strokes"]
if (statLevel === "strokes_and_putts") return ["strokes", "putts", "puttDistance"] if (statLevel === "strokes_and_putts") return ["strokes", "putts", "puttDistance"]
return ["strokes", "putts", "puttDistance", "details"] // ADR-078 (2026-08-16): "details" splittet i to egne skjermer -- retning
// (utslag/innspill, + kølle/slagmåling som hører til utslaget) og
// Chip/Bunker/Straffeslag/Anywayslag fikk hver sin egen skjerm i stedet
// for å dele én, etter tilbakemelding om at detaljene druknet under
// retningsvalget.
return ["strokes", "putts", "puttDistance", "direction", "holeDetails"]
} }
// Hvilket HoleStat-felt (om noen) som "eier" auto-hopp for et gitt steg -- // Hvilket HoleStat-felt (om noen) som "eier" auto-hopp for et gitt steg --
@ -2032,10 +2037,11 @@ function ScoringWizard({
// ALDRI et allerede satt (manuelt ELLER tidligere auto-satt) approach- // ALDRI et allerede satt (manuelt ELLER tidligere auto-satt) approach-
// valg, og gjelder aldri motsatt vei (GIR usann sier ingenting om // valg, og gjelder aldri motsatt vei (GIR usann sier ingenting om
// retning -- en scrambling-birdie gir identisk slag/putt-mønster som en // retning -- en scrambling-birdie gir identisk slag/putt-mønster som en
// ekte GIR). Trigges når "details"-steget nås, siden slag+putt begge må // ekte GIR). Trigges når "direction"-steget nås (ADR-078: flyttet fra det
// være kjent (kommer tidligere i wizard-rekkefølgen). // tidligere "details"-steget, som ble splittet i to), siden slag+putt
// begge må være kjent (kommer tidligere i wizard-rekkefølgen).
useEffect(() => { useEffect(() => {
if (step !== "details") return if (step !== "direction") return
if (stat.approach !== null) return if (stat.approach !== null) return
if (stat.strokes === null || stat.putts === null) return if (stat.strokes === null || stat.putts === null) return
if (stat.strokes - stat.putts <= hole.par - 2) { if (stat.strokes - stat.putts <= hole.par - 2) {
@ -2206,7 +2212,12 @@ function ScoringWizard({
/> />
)} )}
{step === "details" && ( {/* ADR-078 (2026-08-16): "Retning" er nå EGEN skjerm -- inkl.
kølle/slagmåling, som begge hører til utslaget. Splittet fra
det tidligere "details"-steget etter tilbakemelding om at
Chip/Bunker/Straffeslag/Anywayslag druknet under retnings-
valget. */}
{step === "direction" && (
<> <>
<ClubPicker <ClubPicker
label="Kølle brukt ved utslaget" label="Kølle brukt ved utslaget"
@ -2225,11 +2236,6 @@ function ScoringWizard({
variant="inline" variant="inline"
/> />
{/* Seksjon 1 -- retning (2026-08-11): retningskorsene utgjør nå
en tydelig egen gruppe med overskrift, adskilt fra tellerne
under, det er visuelt klart at skjermen består av to
ulike typer inndata (brukertilbakemelding: umulig å vite at
man måtte skrolle for å finne resten). */}
<WizardSection title="Retning"> <WizardSection title="Retning">
{hole.par !== 3 && ( {hole.par !== 3 && (
<DirectionCross <DirectionCross
@ -2251,23 +2257,27 @@ function ScoringWizard({
readOnly={readOnly} readOnly={readOnly}
/> />
</WizardSection> </WizardSection>
</>
)}
{/* Seksjon 2 -- detaljer per hull (2026-08-11): egen overskrift {/* ADR-078 (2026-08-16): Chip/Bunker/Straffeslag/Anywayslag har nå
+ divider fra "Retning" over. Alle FIRE tellerne (Chip/ EGEN skjerm, splittet fra "Retning" (se over). Alle FIRE
Bunker/Straffeslag/Anywayslag) er konseptuelt samme type tellerne er konseptuelt samme type data (antall småhendelser
data (antall småhendelser per hull, typisk 0-3) og bruker per hull, typisk 0-3) og bruker ETT konsistent
ETT konsistent +/--stepper-mønster -- Anywayslag var +/--teller-flis-mønster (Stepper, hole-stat-inputs.tsx).
tidligere det eneste tall-rutenettet, som brøt mønsteret
uten grunn. Alltid stablet, ALDRI et sm:grid-cols-3-sprang: Layout (V0-designet, 2026-08-16): et FAST 2x2-rutenett av
denne wizard-seksjonen er fast begrenset til max-w-sm teller-fliser i stedet for en høy stabel som etterlot mye
(384px, se containeren over) uansett hvor bred selve tomrom under som feltene har fått en egen skjerm. Fire
VIEWPORTET er -- et viewport-basert grid-cols-3-brudd ved likeverdige felt leses da som ett samlet sett og fyller
640px trigges derfor skjermer godt over 640px bredde mens kolonnen bevisst. grid-cols-2 er trygt -- og ALDRI
innholdskolonnen fortsatt kun er 384px, som får stepperne grid-cols-3 -- fordi seksjonen er fast max-w-sm (384px, se
(hver ~190px minimum pga. faste 44px-knapper) til å containeren over): to fliser gir ~184px hver, rikelig til
overlappe hverandre i stedet for å faktisk ha plass. */} flisens to 44px-knapper side om side, mens tre ville presset
<WizardSection title="Detaljer per hull"> hver flis under ~120px og fått knappene til å overlappe.
<div className="grid grid-cols-1 gap-3"> Viewport-uavhengig, det holder alle skjermbredder. */}
{step === "holeDetails" && (
<div className="grid grid-cols-2 gap-3">
<Stepper label="Chip" value={stat.chip} onChange={(v) => onUpdate({ chip: v })} readOnly={readOnly} max={stat.strokes ?? undefined} /> <Stepper label="Chip" value={stat.chip} onChange={(v) => onUpdate({ chip: v })} readOnly={readOnly} max={stat.strokes ?? undefined} />
<Stepper label="Bunker" value={stat.bunker} onChange={(v) => onUpdate({ bunker: v })} readOnly={readOnly} max={stat.strokes ?? undefined} /> <Stepper label="Bunker" value={stat.bunker} onChange={(v) => onUpdate({ bunker: v })} readOnly={readOnly} max={stat.strokes ?? undefined} />
<Stepper label="Straffeslag" value={stat.penalty} onChange={(v) => onUpdate({ penalty: v })} readOnly={readOnly} max={stat.strokes ?? undefined} /> <Stepper label="Straffeslag" value={stat.penalty} onChange={(v) => onUpdate({ penalty: v })} readOnly={readOnly} max={stat.strokes ?? undefined} />
@ -2279,8 +2289,6 @@ function ScoringWizard({
max={stat.strokes ?? undefined} max={stat.strokes ?? undefined}
/> />
</div> </div>
</WizardSection>
</>
)} )}
</div> </div>
</ScrollFade> </ScrollFade>