Ferdig og live. Oppsummert:

Slag og Putter hopper nå videre av seg selv i det øyeblikket en verdi velges — ingen "Neste"-trykk nødvendig.
"Avstand første putt" lå tidligere sammen med putt-tallet på samme steg — auto-hopp der ville gjort feltet uoppnåelig, så det fikk et eget steg (som også auto-hopper).
"Flere detaljer"-steget (kølle/retning/chip/bunker/straffeslag/anywayslag) beholder bevisst "Neste"/"Ferdig"-knappen, siden det har flere felt samtidig og ingen entydig "nå er jeg ferdig"-verdi.
Gjelder både vanlig scoring og delt-ball-scoring (foursome/greensome/scramble).
"Forrige" fungerer fortsatt trygt — går du tilbake til et allerede besvart steg, hopper det ikke automatisk fremover igjen.
Browserverifisert i scratch (full flyt gjennom alle steg + tilbake-navigasjon + delt-ball), rullet ut mot ekte systemer — ingen migrasjon, begge containere boot-et rent, teeoff.no upåvirket.
This commit is contained in:
Erol Haagenrud 2026-07-28 15:32:02 +02:00
parent 83c4749516
commit b21609915d
3 changed files with 133 additions and 37 deletions

View file

@ -4290,6 +4290,43 @@ Ferdig og verifisert:
ingen backend-kode rørt). Begge containere boot-et rent, `/health`/
`/dashboard` → 200, `teeoff.no` upåvirket. **ADR-039 er dermed
fullstendig ferdig, backend og frontend, live.**
- **Auto-hopp i scoringsveiviserne, LIVE (2026-07-28), samme dag:**
brukeren ba om at "i det øyeblikket [scoren] nå registreres" skal
veiviseren hoppe videre av seg selv, uten å måtte trykke "Neste"/
"Ferdig". Presiserende avklaring FØR bygging (AskUserQuestion): "Avstand
første putt" lå tidligere PÅ SAMME steg som selve putt-tallet i
`ScoringWizard` -- auto-hopp idet putt-tallet velges ville gjort
avstandsfeltet uoppnåelig (ingen annen inngang finnes). Løst ved å
splitte putt-steget i to (bekreftet anbefalt løsning): `WizardStep`
utvidet med et eget `"puttDistance"`-steg, `wizardStepsFor()` gir nå
`["strokes","putts","puttDistance"]`/`["strokes","putts","puttDistance",
"details"]` for de to høyere statistikknivåene.
**Mekanisme (samme mønster i `ScoringWizard` og den enklere
`SideScoreWizard` for delt-ball-formater):** to refs -- `enteredWithValueRef`
fanger om steget sitt eget felt ALLEREDE hadde en verdi idet steget ble
vist (et allerede utfylt hull skal ikke hoppe videre bare fordi
veiviseren åpnes, og "Forrige" tilbake til et allerede besvart steg skal
ikke re-trigge et nytt hopp), `firedRef` hindrer dobbelt-triggering.
Kun steg med ETT entydig felt (Slag, Putter, Avstand, samt hele
`SideScoreWizard` sitt eneste Slag-felt) auto-hopper -- "flere
detaljer"-steget (kølle/retning/chip/bunker/straffeslag/anywayslag) har
ingen enkelt "dette er ferdig"-verdi og beholder derfor "Neste"/
"Ferdig"-knappen som manuell handling, bevisst uendret.
**Verifisert grundig i en isolert scratch-nettleserøkt** (fersk
database/MinIO/API-container, ekte `next dev`, Chrome DevTools):
full "full"-nivå-runde spilt gjennom Slag→Putter→Avstand (alle tre
auto-hoppet uten et eneste "Neste"-trykk) →detaljer (korrekt IKKE
auto-hoppet, krevde et bevisst "Ferdig"-trykk, som deretter gikk videre
til neste hull av seg selv). "Forrige" fra Putter tilbake til Slag
bekreftet trygt (viste den allerede valgte verdien, hoppet IKKE
automatisk fremover igjen). Delt-ball (`SideScoreWizard`, foursome)
bekreftet separat: valgt slagtall for "Rødt" hoppet umiddelbart til
"Blått" uten trykk. Ekte typesjekket + full produksjonsbuild kjørt før
utrulling, alle 24 ruter listet.
**Rullet ut live 2026-07-28**, bruker bekreftet eksplisitt: ingen
migrasjon (ren frontend), `docker compose up -d --build
teecup_frontend`. Begge containere boot-et rent, `/health`/`/dashboard`
→ 200, `teeoff.no` upåvirket.
Neste steg:
0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet

View file

@ -1145,25 +1145,41 @@ export function RoundDetail({ roundId }: { roundId: string }) {
)
}
// --- Scoringsveiviser (2026-07-26) ------------------------------------------
// --- Scoringsveiviser (2026-07-26, auto-hopp lagt til 2026-07-28) ----------
// Skjermovertagende, ett-spiller-om-gangen registreringsflyt -- inspirert av
// en konkurrentapps samlebåndsflyt (video studert bilde for bilde), men
// BEVISST forenklet til TeeCups eget stat_level-drevne steg-antall i stedet
// for å kopiere konkurrentens nøyaktige skjermrekkefølge/design:
// - "strokes_only": ett steg (Slag)
// - "strokes_and_putts": to steg (Slag, Putter+avstand første putt)
// - "full": tre steg (Slag, Putter+avstand første putt, resten samlet)
// "Neste"/"Forrige" beveger seg gjennom stegene for ÉN spiller; på siste
// steg blir knappen "Neste: {navn}" (bytter til neste spiller, nullstiller
// til steg 1) eller "Ferdig" (er spilleren den siste, lukker veiviseren og
// varsler forelderen om å gå videre til neste hull).
// - "strokes_and_putts": to steg (Slag, Putter, Avstand første putt)
// - "full": fire steg (Slag, Putter, Avstand første putt, resten samlet)
// Slag/Putter hopper AUTOMATISK videre i det øyeblikket en verdi velges --
// ingen "Neste"-trykk nødvendig (etterspurt av bruker 2026-07-28: "Avstand
// første putt" lå tidligere PÅ SAMME steg som selve putt-tallet, som ville
// gjort feltet uoppnåelig hvis auto-hopp fyrte idet putt-tallet ble valgt --
// derfor et eget, eget steg for avstanden, som ELLERS ville vært umulig å
// nå). "Avstand"/"detaljer" er valgfrie/flerfelts og auto-hopper derfor
// ikke -- "Neste"/"Forrige"-knappene i bunnen fungerer fortsatt som
// manuell reserve for disse (og for å rette et allerede registrert steg).
// På siste steg blir knappen "Neste: {navn}" (bytter til neste spiller,
// nullstiller til steg 1) eller "Ferdig" (er spilleren den siste, lukker
// veiviseren og varsler forelderen om å gå videre til neste hull).
type WizardStep = "strokes" | "putts" | "details"
type WizardStep = "strokes" | "putts" | "puttDistance" | "details"
function wizardStepsFor(statLevel: StatLevel): WizardStep[] {
if (statLevel === "strokes_only") return ["strokes"]
if (statLevel === "strokes_and_putts") return ["strokes", "putts"]
return ["strokes", "putts", "details"]
if (statLevel === "strokes_and_putts") return ["strokes", "putts", "puttDistance"]
return ["strokes", "putts", "puttDistance", "details"]
}
// Hvilket HoleStat-felt (om noen) som "eier" auto-hopp for et gitt steg --
// null for steg uten noe entydig enkeltfelt å trigge på (flerfelts/valgfritt).
function autoAdvanceFieldFor(step: WizardStep): "strokes" | "putts" | "firstPuttBucket" | null {
if (step === "strokes") return "strokes"
if (step === "putts") return "putts"
if (step === "puttDistance") return "firstPuttBucket"
return null
}
function ScoringWizard({
@ -1206,6 +1222,29 @@ function ScoringWizard({
const canProceed = step === "strokes" ? stat.strokes !== null : step === "putts" ? stat.putts !== null : true
// Auto-hopp (2026-07-28): så snart steget sitt eget felt får en verdi,
// hopp videre av seg selv -- ingen "Neste"-trykk nødvendig. `enteredWith
// ValueRef` fanger om feltet ALLEREDE hadde en verdi idet vi ankom steget
// (et allerede utfylt hull skal ikke hoppe videre bare fordi veiviseren
// åpnes) -- kun en FERSK endring gjort AV BRUKEREN akkurat nå skal utløse
// hoppet. `firedRef` hindrer at samme hopp trigges to ganger.
const autoField = autoAdvanceFieldFor(step)
const enteredWithValueRef = useRef(false)
const firedRef = useRef(false)
useEffect(() => {
firedRef.current = false
enteredWithValueRef.current = autoField !== null && stat[autoField] !== null
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [step, player.id, hole.holeNumber])
useEffect(() => {
if (!autoField || firedRef.current || enteredWithValueRef.current) return
if (stat[autoField] !== null) {
firedRef.current = true
forward()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [stat.strokes, stat.putts, stat.firstPuttBucket])
function back() {
if (isFirstStep) {
onClose()
@ -1278,7 +1317,6 @@ function ScoringWizard({
)}
{step === "putts" && (
<>
<NumberPicker
label="Putter"
value={stat.putts}
@ -1289,6 +1327,9 @@ function ScoringWizard({
onChange={(v) => onUpdate({ putts: v })}
readOnly={readOnly}
/>
)}
{step === "puttDistance" && (
<ChoiceRow
label="Avstand første putt"
options={[
@ -1303,7 +1344,6 @@ function ScoringWizard({
onChange={(v) => onUpdate({ firstPuttBucket: v as PuttBucket })}
readOnly={readOnly}
/>
</>
)}
{step === "details" && (
@ -3757,6 +3797,25 @@ function SideScoreWizard({
return s.label?.trim() || `Side ${idx === 0 ? "A" : "B"}`
}
// Auto-hopp (2026-07-28, samme mønster som ScoringWizard) -- registrer
// slagtall for siden, hopp umiddelbart videre til neste side/hull. Kun
// ETT felt her, så ingen splitt-i-flere-steg-problematikk.
const enteredWithValueRef = useRef(score !== null)
const firedRef = useRef(false)
useEffect(() => {
firedRef.current = false
enteredWithValueRef.current = score !== null
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [side.id, hole.holeNumber])
useEffect(() => {
if (firedRef.current || enteredWithValueRef.current) return
if (score !== null) {
firedRef.current = true
onAdvance()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [score])
return (
<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">

File diff suppressed because one or more lines are too long