Tilbake-navigering til en tidligere spiller i scoringsveiviseren (ADR-077)
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 <noreply@anthropic.com>
This commit is contained in:
parent
f740814833
commit
c498bfca8f
3 changed files with 119 additions and 6 deletions
|
|
@ -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)
|
## Utviklingsplan (rekkefølge)
|
||||||
|
|
||||||
1. ✅ Land tenant-modell → **Organisasjon** (ADR-001/002/003)
|
1. ✅ Land tenant-modell → **Organisasjon** (ADR-001/002/003)
|
||||||
|
|
|
||||||
25
CHANGELOG.md
25
CHANGELOG.md
|
|
@ -11663,3 +11663,28 @@ Neste steg:
|
||||||
|
|
||||||
Med dette er alle tre punktene fra ADR-043 sin opprinnelige
|
Med dette er alle tre punktene fra ADR-043 sin opprinnelige
|
||||||
"gjenstår"-liste fullført.
|
"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å.
|
||||||
|
|
|
||||||
|
|
@ -1130,6 +1130,21 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
||||||
setWizardPlayerId(playerId)
|
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
|
// Samlebånd-fremdrift (2026-07-26): kalles fra veiviserens siste steg for
|
||||||
// siste spiller på hullet -- bytter til NESTE spiller i rekkefølgen (samme
|
// siste spiller på hullet -- bytter til NESTE spiller i rekkefølgen (samme
|
||||||
// veiviser, nullstilt til steg 1), eller -- er aktiv spiller den siste --
|
// veiviser, nullstilt til steg 1), eller -- er aktiv spiller den siste --
|
||||||
|
|
@ -1874,6 +1889,7 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
||||||
onUpdate={updateWizardStat}
|
onUpdate={updateWizardStat}
|
||||||
onClose={() => setWizardPlayerId(null)}
|
onClose={() => setWizardPlayerId(null)}
|
||||||
onAdvance={advanceWizardPlayer}
|
onAdvance={advanceWizardPlayer}
|
||||||
|
onSwitchPlayer={switchWizardPlayer}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
ownBagClubs={ownBagClubs}
|
ownBagClubs={ownBagClubs}
|
||||||
isPending={pendingParticipantHoles.has(`${wizardPlayer.id}:${activeHole}`)}
|
isPending={pendingParticipantHoles.has(`${wizardPlayer.id}:${activeHole}`)}
|
||||||
|
|
@ -1931,6 +1947,7 @@ function ScoringWizard({
|
||||||
onUpdate,
|
onUpdate,
|
||||||
onClose,
|
onClose,
|
||||||
onAdvance,
|
onAdvance,
|
||||||
|
onSwitchPlayer,
|
||||||
readOnly,
|
readOnly,
|
||||||
ownBagClubs,
|
ownBagClubs,
|
||||||
isPending,
|
isPending,
|
||||||
|
|
@ -1945,6 +1962,11 @@ function ScoringWizard({
|
||||||
onUpdate: (patch: Partial<HoleStat>) => void
|
onUpdate: (patch: Partial<HoleStat>) => void
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
onAdvance: () => 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
|
readOnly: boolean
|
||||||
ownBagClubs: string[]
|
ownBagClubs: string[]
|
||||||
// Offline scoreregistrering (ADR-028-utvidelse, 2026-07-28) -- sann når
|
// 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
|
{/* Kontekst-rad: alle spillerne, aktiv fremhevet -- samme prinsipp som
|
||||||
konkurrentappen: man mister aldri oversikten over hvem som allerede
|
konkurrentappen: man mister aldri oversikten over hvem som allerede
|
||||||
er ferdig på hullet. Ren informasjon, ikke en snarvei -- bytte
|
er ferdig på hullet. Interaktiv (ADR-077, 2026-08-15) -- trykk på
|
||||||
spiller midt i et steg ville latt uferdige felt henge løst. */}
|
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). */}
|
||||||
<div className="-mx-1 flex shrink-0 gap-2 overflow-x-auto border-b border-border px-4 py-2.5">
|
<div className="-mx-1 flex shrink-0 gap-2 overflow-x-auto border-b border-border px-4 py-2.5">
|
||||||
{players.map((p) => (
|
{players.map((p) => (
|
||||||
<span
|
<button
|
||||||
key={p.id}
|
key={p.id}
|
||||||
|
type="button"
|
||||||
|
onClick={() => onSwitchPlayer(p.id)}
|
||||||
|
aria-current={p.id === player.id ? "true" : undefined}
|
||||||
className={cn(
|
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
|
p.id === player.id
|
||||||
? "border-primary bg-primary text-primary-foreground"
|
? "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.name}
|
||||||
</span>
|
{p.completed && p.id !== player.id && (
|
||||||
|
<span className="text-xs font-semibold">· Ferdig</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue