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:
parent
83c4749516
commit
b21609915d
3 changed files with 133 additions and 37 deletions
37
CLAUDE.md
37
CLAUDE.md
|
|
@ -4290,6 +4290,43 @@ Ferdig og verifisert:
|
||||||
ingen backend-kode rørt). Begge containere boot-et rent, `/health`/
|
ingen backend-kode rørt). Begge containere boot-et rent, `/health`/
|
||||||
`/dashboard` → 200, `teeoff.no` upåvirket. **ADR-039 er dermed
|
`/dashboard` → 200, `teeoff.no` upåvirket. **ADR-039 er dermed
|
||||||
fullstendig ferdig, backend og frontend, live.**
|
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:
|
Neste steg:
|
||||||
0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet
|
0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet
|
||||||
|
|
|
||||||
|
|
@ -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
|
// Skjermovertagende, ett-spiller-om-gangen registreringsflyt -- inspirert av
|
||||||
// en konkurrentapps samlebåndsflyt (video studert bilde for bilde), men
|
// en konkurrentapps samlebåndsflyt (video studert bilde for bilde), men
|
||||||
// BEVISST forenklet til TeeCups eget stat_level-drevne steg-antall i stedet
|
// BEVISST forenklet til TeeCups eget stat_level-drevne steg-antall i stedet
|
||||||
// for å kopiere konkurrentens nøyaktige skjermrekkefølge/design:
|
// for å kopiere konkurrentens nøyaktige skjermrekkefølge/design:
|
||||||
// - "strokes_only": ett steg (Slag)
|
// - "strokes_only": ett steg (Slag)
|
||||||
// - "strokes_and_putts": to steg (Slag, Putter+avstand første putt)
|
// - "strokes_and_putts": to steg (Slag, Putter, Avstand første putt)
|
||||||
// - "full": tre steg (Slag, Putter+avstand første putt, resten samlet)
|
// - "full": fire steg (Slag, Putter, Avstand første putt, resten samlet)
|
||||||
// "Neste"/"Forrige" beveger seg gjennom stegene for ÉN spiller; på siste
|
// Slag/Putter hopper AUTOMATISK videre i det øyeblikket en verdi velges --
|
||||||
// steg blir knappen "Neste: {navn}" (bytter til neste spiller, nullstiller
|
// ingen "Neste"-trykk nødvendig (etterspurt av bruker 2026-07-28: "Avstand
|
||||||
// til steg 1) eller "Ferdig" (er spilleren den siste, lukker veiviseren og
|
// første putt" lå tidligere PÅ SAMME steg som selve putt-tallet, som ville
|
||||||
// varsler forelderen om å gå videre til neste hull).
|
// 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[] {
|
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"]
|
if (statLevel === "strokes_and_putts") return ["strokes", "putts", "puttDistance"]
|
||||||
return ["strokes", "putts", "details"]
|
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({
|
function ScoringWizard({
|
||||||
|
|
@ -1206,6 +1222,29 @@ function ScoringWizard({
|
||||||
|
|
||||||
const canProceed = step === "strokes" ? stat.strokes !== null : step === "putts" ? stat.putts !== null : true
|
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() {
|
function back() {
|
||||||
if (isFirstStep) {
|
if (isFirstStep) {
|
||||||
onClose()
|
onClose()
|
||||||
|
|
@ -1278,7 +1317,6 @@ function ScoringWizard({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{step === "putts" && (
|
{step === "putts" && (
|
||||||
<>
|
|
||||||
<NumberPicker
|
<NumberPicker
|
||||||
label="Putter"
|
label="Putter"
|
||||||
value={stat.putts}
|
value={stat.putts}
|
||||||
|
|
@ -1289,6 +1327,9 @@ function ScoringWizard({
|
||||||
onChange={(v) => onUpdate({ putts: v })}
|
onChange={(v) => onUpdate({ putts: v })}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{step === "puttDistance" && (
|
||||||
<ChoiceRow
|
<ChoiceRow
|
||||||
label="Avstand første putt"
|
label="Avstand første putt"
|
||||||
options={[
|
options={[
|
||||||
|
|
@ -1303,7 +1344,6 @@ function ScoringWizard({
|
||||||
onChange={(v) => onUpdate({ firstPuttBucket: v as PuttBucket })}
|
onChange={(v) => onUpdate({ firstPuttBucket: v as PuttBucket })}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{step === "details" && (
|
{step === "details" && (
|
||||||
|
|
@ -3757,6 +3797,25 @@ function SideScoreWizard({
|
||||||
return s.label?.trim() || `Side ${idx === 0 ? "A" : "B"}`
|
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 (
|
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">
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue