From c498bfca8f2b0082e6d79b5e6a5cd5fd7e16345f Mon Sep 17 00:00:00 2001 From: Erol Haagenrud Date: Sun, 16 Aug 2026 08:19:46 +0200 Subject: [PATCH] Tilbake-navigering til en tidligere spiller i scoringsveiviseren (ADR-077) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kontekst-raden i ScoringWizard (alle spillerne, aktiv fremhevet) var bevisst ikke-interaktiv ved bygging. Trygt å gjøre interaktiv nå: hvert felt lagres allerede for seg med en gang (PATCH per felt), så bytte spiller midt i et steg gir ingen datatap. Løser at man måtte fullføre hele samlebånd-flyten for å rette opp en tidligere spillers feil. tsc + 45/45 vitest, scratch-verifisert i nettleser (lys+mørk). Rullet ut. Co-Authored-By: Claude Sonnet 5 --- ARCHITECTURE_DECISIONS.md | 56 ++++++++++++++++++++++++++++ CHANGELOG.md | 25 +++++++++++++ frontend/components/round-detail.tsx | 44 +++++++++++++++++++--- 3 files changed, 119 insertions(+), 6 deletions(-) diff --git a/ARCHITECTURE_DECISIONS.md b/ARCHITECTURE_DECISIONS.md index 57fc02e..2279326 100644 --- a/ARCHITECTURE_DECISIONS.md +++ b/ARCHITECTURE_DECISIONS.md @@ -7605,6 +7605,62 @@ turneringer (ADR-075), offentlig visning (dette dokumentet). --- +## ADR-077: Tilbake-navigering til en tidligere spiller i scoringsveiviseren ("Steg 1" av tre forbedringer i score-registreringen) (2026-08-16) + +Foranledning: bruker observerte at når man fører score for flere spillere +etter hverandre i samlebånd-flyten (`ScoringWizard`, frittstående runder) +og har trykket "Neste" videre forbi en spiller, er det umulig å rette opp +en feilregistrering hos den spilleren INNE i selve veiviser-flyten — man +må fullføre veiviseren helt til slutten, tilbake på Score-fanen lete opp +riktig spillers kort for det aktuelle hullet, og åpne det på nytt. Første +av tre planlagte forbedringer (se `~/.claude/plans/joyful-wishing-crab.md` +for hele planen) — kun denne er round-only: org-turneringer har ingen +tilsvarende kjede-flyt (`HoleGrid` velger ett hull+én spiller om gangen), +og ADR-065 (per-deltaker-fullføring) er eksplisitt bygget kun for +frittstående runder. + +**Løsning**: kontekst-raden i `ScoringWizard` (alle spillerne, aktiv +fremhevet) var BEVISST ikke-interaktiv ved bygging (2026-07-26-kommentaren: +"bytte spiller midt i et steg ville latt uferdige felt henge løst") — +denne antakelsen viste seg foreldet: `updateWizardStat` har siden lagret +hvert felt for seg MED EN GANG (PATCH per felt, ikke batch-lagring), så +det er trygt å bytte spiller når som helst uten datatap. Raden er nå +interaktiv: hver spiller-chip er en knapp, trykk på en ANNEN spiller enn +den aktive (inkludert allerede "Ferdig"-markerte -- bekreftet at backend +ikke har noen hard sperre mot å redigere en fullført spillers data, kun +runde-nivå `readOnly` blokkerer) bytter veiviseren dit via en ny +`switchWizardPlayer()`-funksjon i `RoundDetail` (samme mål-oppløsning som +`openPlayerOrTeamSideEntry`, inkl. scramble-solo-lag-siden-avviket, men +uten å røre `currentHole`). Steget nullstilles automatisk til "Slag" for +den nye spilleren via den allerede eksisterende `useEffect(() => +setStepIndex(0), [player.id])`. "Ferdig"-markerte spillere vises med en +tekst-etikett ("· Ferdig") i raden, ikke bare farge (tilgjengelighetsregel +-- aldri fargekoding alene). + +Ingen ny fil, ingen V0 (gjør et eksisterende, bevisst +ikke-interaktivt element interaktivt -- ingen ny visuell flate). + +**Verifisert:** `tsc --noEmit` rent + 45/45 vitest. Egen scratch-database ++ scratch `teecup_api` (port 18104, live-montert kode, +`TEECUP_DEV_LOG_MAGIC_LINKS=true`) + lokal `next dev` (port 13104): runde +med tre spillere. Registrerte Par for spiller 1, byttet midt i steget til +spiller 2 (Bogey) via kontekst-raden -- spiller 1 sin allerede lagrede +score forble urørt. Byttet tilbake til spiller 1 FRA MIDT I spiller 2 sin +flyt, rettet scoren til Birdie -- lagret korrekt, spiller 2 sin Bogey +urørt. Trykket "Avslutt for Erol Scratch" (ADR-065), åpnet spiller 2 sin +veiviser på nytt, bekreftet at spiller 1 nå vises som "Erol Scratch · +Ferdig" i kontekst-raden OG fortsatt er trykkbar -- åpnet ham, viste +korrekt lagret Birdie (ikke nullstilt), ingen sperre. Lys+mørk bekreftet. +Scratch-stacken fullstendig revet ned (database, rolle, container, +MinIO-bøtte, `next dev`) -- 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) 1. ✅ Land tenant-modell → **Organisasjon** (ADR-001/002/003) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ad6e43..ae0fbfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11663,3 +11663,28 @@ Neste steg: Med dette er alle tre punktene fra ADR-043 sin opprinnelige "gjenstår"-liste fullført. + +93. **Tilbake-navigering til en tidligere spiller i scoringsveiviseren + (ADR-077) — 2026-08-16.** Første av tre forbedringer i score- + registreringen brukeren ba om etter OOM. I samlebånd-flyten + (`ScoringWizard`, frittstående runder) var det umulig å rette opp en + feilregistrering hos en tidligere spiller uten å fullføre hele + veiviseren og lete opp riktig kort manuelt etterpå. + + Kontekst-raden med alle spillerne (bevisst ikke-interaktiv ved bygging + i 2026-07-26-runden) er nå interaktiv -- trykk på en annen spiller + (inkl. "Ferdig"-markerte) bytter veiviseren dit umiddelbart. Trygt + fordi hvert felt allerede lagres for seg med en gang (PATCH per felt, + ikke batch) -- ingen datatap ved bytte. Round-only (org-turneringer + har ingen tilsvarende kjede-flyt). + + **Verifisert:** `tsc --noEmit` rent + 45/45 vitest. Scratch-database + + scratch `teecup_api` (port 18104) + lokal `next dev` (port 13104): tre + spillere, byttet spiller midt i steg uten datatap, rettet en allerede + lagret score etter bytte, bekreftet "Ferdig"-markerte spillere fortsatt + er trykkbare og viser korrekt lagret data (ikke nullstilt). Lys+mørk + bekreftet. Scratch-stacken fullstendig revet ned. + + **Ingen migrasjon.** + + **Rullet ut**: ikke ennå. diff --git a/frontend/components/round-detail.tsx b/frontend/components/round-detail.tsx index aa97e2a..13958e6 100644 --- a/frontend/components/round-detail.tsx +++ b/frontend/components/round-detail.tsx @@ -1130,6 +1130,21 @@ export function RoundDetail({ roundId }: { roundId: string }) { setWizardPlayerId(playerId) } + // Tilbake til en tidligere spiller fra INNI veiviseren (ADR-077, + // 2026-08-15) -- samme mål-oppløsning som openPlayerOrTeamSideEntry over, + // men rører IKKE currentHole (vi er allerede på riktig hull, kalt fra + // kontekst-raden i selve veiviseren). + function switchWizardPlayer(playerId: string) { + const target = players.find((p) => p.id === playerId) + if (scrambleSoloTeamSideId && target?.roundSideId === scrambleSoloTeamSideId) { + setWizardPlayerId(null) + setWizardSideId(scrambleSoloTeamSideId) + return + } + setActivePlayerId(playerId) + setWizardPlayerId(playerId) + } + // Samlebånd-fremdrift (2026-07-26): kalles fra veiviserens siste steg for // siste spiller på hullet -- bytter til NESTE spiller i rekkefølgen (samme // veiviser, nullstilt til steg 1), eller -- er aktiv spiller den siste -- @@ -1874,6 +1889,7 @@ export function RoundDetail({ roundId }: { roundId: string }) { onUpdate={updateWizardStat} onClose={() => setWizardPlayerId(null)} onAdvance={advanceWizardPlayer} + onSwitchPlayer={switchWizardPlayer} readOnly={readOnly} ownBagClubs={ownBagClubs} isPending={pendingParticipantHoles.has(`${wizardPlayer.id}:${activeHole}`)} @@ -1931,6 +1947,7 @@ function ScoringWizard({ onUpdate, onClose, onAdvance, + onSwitchPlayer, readOnly, ownBagClubs, isPending, @@ -1945,6 +1962,11 @@ function ScoringWizard({ onUpdate: (patch: Partial) => void onClose: () => void onAdvance: () => void + // Tilbake til en TIDLIGERE (eller "Ferdig"-markert) spiller midt i + // samlebånd-flyten (ADR-077) -- se kontekst-raden under. Trygt fordi + // hvert felt allerede lagres for seg med en gang (updateWizardStat), + // ingen fare for å miste uferdige felt ved bytte. + onSwitchPlayer: (playerId: string) => void readOnly: boolean ownBagClubs: string[] // Offline scoreregistrering (ADR-028-utvidelse, 2026-07-28) -- sann når @@ -2059,21 +2081,31 @@ function ScoringWizard({ {/* Kontekst-rad: alle spillerne, aktiv fremhevet -- samme prinsipp som konkurrentappen: man mister aldri oversikten over hvem som allerede - er ferdig på hullet. Ren informasjon, ikke en snarvei -- bytte - spiller midt i et steg ville latt uferdige felt henge løst. */} + er ferdig på hullet. Interaktiv (ADR-077, 2026-08-15) -- trykk på + en ANNEN spiller (inkl. "Ferdig"-markerte) bytter veiviseren dit, + for å rette opp allerede registrerte data uten å måtte fullføre + hele samlebåndet først og lete opp kortet på nytt etterpå. Trygt + siden hvert felt allerede lagres for seg med en gang (se + onSwitchPlayer). */}
{players.map((p) => ( - onSwitchPlayer(p.id)} + aria-current={p.id === player.id ? "true" : undefined} className={cn( - "flex shrink-0 items-center rounded-full border px-3 py-1.5 text-sm font-bold", + "flex min-h-11 shrink-0 items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm font-bold transition-colors", p.id === player.id ? "border-primary bg-primary text-primary-foreground" - : "border-border bg-card text-muted-foreground", + : "border-border bg-card text-muted-foreground hover:bg-accent/50 hover:text-foreground", )} > {p.name} - + {p.completed && p.id !== player.id && ( + · Ferdig + )} + ))}