Score-fanen: fjern Deg-badge, begrens historikk til eget kort, fiks kompakt hindrings-ikoner, fjern duplikat manage-knapp

Fire brukerpåpekte oppfølgingsfunn rett etter forrige runde (#133):
Deg-badgen på spillerkort er unødvendig, historikk-panelet viste seg
feilaktig på medspilleres kort, kompakt avstandsmåler falt tilbake til
en generisk varseltrekant i stedet for ekte hindrings-ikoner, og den
nye Spillere-og-runde-vekslen duplikerte RoundHeader sin Administrer-
knapp. Se CHANGELOG.md #134 for full detalj og verifisering.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Erol Haagenrud 2026-08-20 06:51:19 +02:00
parent 05599aafe6
commit 2e4cb9733c
3 changed files with 106 additions and 58 deletions

View file

@ -13292,3 +13292,65 @@ Neste steg:
**Rullet ut 2026-08-20** -- bruker bekreftet. Ren frontend. **Rullet ut 2026-08-20** -- bruker bekreftet. Ren frontend.
`docker compose build teecup_frontend && up -d`, rene logger, 200 OK. `docker compose build teecup_frontend && up -d`, rene logger, 200 OK.
134. **Score-fanen: fire oppfølgingsfunn på spillerkortene rett etter
#133, 2026-08-20.** Bruker så det ferdige resultatet av #133 og
meldte fire konkrete ting samme runde:
- **"Deg"-badgen ved siden av eget navn i spillerkortet** oppfattet
som unødvendig ("knappen med 'deg' må bort") -- fjernet fra
`PlayerHoleCards` sin kortheader (`round-detail.tsx`). Ikke rørt
andre steder den samme badgen brukes (`PlayerList`,
`ScorecardGrid`) -- kun spilt-kortet på Score-fanen var i scope.
- **Historikk-panelet ("Din historikk på dette hullet...") vist på
ALLE spilleres kort**, ikke bare egen -- reelt feil siden teksten
allerede eksplisitt sier "DIN", uansett hvilken spillers data som
faktisk vises. Rettet: `HoleHistoryPanel` rendres nå kun når
`player.isSelf`.
- **Hindrings-ikonene i kompakt avstandsmåler-visning (fra #133)
viste generisk `TriangleAlert` for alle hindringstyper** i stedet
for de egentlige type-ikonene (bunker/vann/kratt/tre osv.) som
alltid har fungert i full-visningen -- ren regresjon, siden
compact-modusen (introdusert i #133) aldri hadde fått samme
ikon-logikk som full-modusen, og ingen tidligere kalte
`size="compact"` for at det skulle synes. `hole-diagram-view.tsx`:
kompakt-listen bruker nå samme `HazardRow`-komponent (ikon,
bane, forkant/bakkant) som full-visningen, i stedet for en egen,
forenklet rad -- bruker sammenlignet de to skjermbildene direkte
og påpekte at de tar omtrent like mye plass, så den forenklede
raden ga ingen reell besparelse for tapt informasjon.
- **Den nye "Spillere og runde"/"Tilbake til score"-veksleknappen
fra #133 er selv et duplikat** av RoundHeader sin egen
"Administrer"-knapp (øverst til høyre, lenker til samme
`?tab=manage`-visning) -- bruker pekte dette ut selv
("Den ligger jo i toppmenyen oppe til høyre?"), presiserte at kun
selve RADEN skulle bort, ikke funksjonen. Løst ved å fjerne
knapperaden helt og heller gjøre `pageTab`-synkroniseringen mot
`?tab=manage`-parameteren TOVEIS (`round-detail.tsx`) i stedet for
den opprinnelige ensrettede varianten fra #133 -- den ensrettede
varianten eksisterte kun for å skåne knappens EGEN lokale
tilstand fra å bli overstyrt av URL-en; siden knappen nå er
borte, er den beskyttelsen unødvendig, og toveis-synkronisering
gir samtidig RoundHeader sin "Score"-fane en reell funksjon som
"tilbake fra manage" (fungerte ikke før -- fanen linket til bare
URL-en, men ingenting nullstilte `pageTab` når parameteret
forsvant).
**Verifisert i scratch:** `tsc --noEmit` rent etter alle fire.
Badge-fjerning, historikk-restriksjon og fjerning av veksle-raden
bekreftet visuelt i en mocket `RoundDetail`-forhåndsvisning
(samme mock-fetch-teknikk som #133). Hindrings-ikon-fiksen verifisert
isolert ved å rendre `HoleDiagram` direkte med `size="compact"` og
alle hindringstyper -- sammenlignet før/etter-skjermbilde, alle
typer viser nå riktig ikon. Toveis-URL-synkroniseringen ble
bekreftet REAKTIVT for inn-veien (Administrer -> manage, samme
side, ingen remount) via faktisk klikk-gjennomgang; selve
tilbake-retningen (manage -> bar URL) kunne ikke skjermbilde-
bekreftes -- forhåndsvisnings-nettleseren hang gjentatte ganger på
akkurat den navigasjonen uavhengig av trigger (både `router.push`
og ekte `<Link>` testet), mens serveren i alle tilfeller svarte
riktig og raskt (bekreftet i dev-server-loggen) -- vurdert som et
miljø-/verktøyproblem i forhåndsvisnings-nettleseren, ikke en reell
kodefeil, siden logikken er en direkte, symmetrisk speiling av den
allerede beviste `initialTab`-beregningen ved førstegangslasting.
Flagget eksplisitt til bruker som ikke fullt skjermbilde-verifisert.

View file

@ -212,17 +212,14 @@ export function HoleDiagram({ distances, playerPosition, hazards, size = "full",
<CompactNumber label="Bak" value={distances.back} /> <CompactNumber label="Bak" value={distances.back} />
</div> </div>
{hazards.length > 0 && ( {hazards.length > 0 && (
<ul className="flex flex-col gap-1 border-t border-clubhouse-border pt-2"> <div className="flex flex-col gap-2 border-t border-clubhouse-border pt-2">
{hazards.map((h) => ( <h3 className="text-xs font-bold uppercase tracking-wide text-clubhouse-muted">Hindringer</h3>
<li key={h.key} className="flex items-center gap-1.5 text-sm"> <ul className="flex flex-col divide-y divide-clubhouse-border">
<TriangleAlert aria-hidden="true" className="size-3.5 shrink-0 text-cup-strong" /> {hazards.map((h) => (
<span className="min-w-0 flex-1 truncate text-clubhouse-muted">{h.label}</span> <HazardRow key={h.key} hazard={h} />
<span className="shrink-0 font-bold tabular-nums text-clubhouse-ink"> ))}
{formatMeters(h.below)} </ul>
</span> </div>
</li>
))}
</ul>
)} )}
</div> </div>
) )

View file

@ -452,8 +452,17 @@ export function RoundDetail({ roundId }: { roundId: string }) {
// (rapportert av bruker via video 2026-08-03, bekreftet: URL-en endret // (rapportert av bruker via video 2026-08-03, bekreftet: URL-en endret
// seg riktig til ?tab=manage, men selve fanen forble "Score"). Fikset // seg riktig til ?tab=manage, men selve fanen forble "Score"). Fikset
// med en effekt som reagerer på selve søkeparameter-ENDRINGEN, ikke // med en effekt som reagerer på selve søkeparameter-ENDRINGEN, ikke
// bare mount -- ensrettet (kun til "manage"), rører aldri brukerens // bare mount.
// egen lokale "Score"-valg via pillen etterpå. // 2026-08-20: effekten var opprinnelig ensrettet (kun TIL "manage") for
// å skåne en egen, lokal "Spillere og runde"-veksleknapp på selve
// Score-siden fra å bli overstyrt av URL-en. Den knappen er nå fjernet
// (brukerkorrigering -- reelt duplikat av "Administrer"-knappen i
// RoundHeader, som allerede lenker hit) -- eneste vei INN i "manage" er
// nå denne URL-parameteren, så effekten kan trygt speile den begge
// veier. Det gir samtidig "Score"-fanen i RoundHeader (lenker til bare
// `/my-rounds/{id}`, uten parameteret) en reell funksjon som "tilbake"
// fra manage-panelet -- fungerte ikke før, siden ingenting nullstilte
// `pageTab` når parameteret forsvant.
const pathname = usePathname() const pathname = usePathname()
const searchParams = useSearchParams() const searchParams = useSearchParams()
const initialTab = searchParams.get("tab") === "manage" ? "manage" : "score" const initialTab = searchParams.get("tab") === "manage" ? "manage" : "score"
@ -535,12 +544,11 @@ export function RoundDetail({ roundId }: { roundId: string }) {
const [pageTab, setPageTab] = useState<"score" | "manage">(initialTab) const [pageTab, setPageTab] = useState<"score" | "manage">(initialTab)
// Fikser "Administrer"-snarveien fra selve Score-siden (se begrunnelse // Fikser "Administrer"-snarveien fra selve Score-siden (se begrunnelse
// ved searchParams over) -- reagerer på selve parameter-ENDRINGEN i // ved searchParams over) -- reagerer på selve parameter-ENDRINGEN i
// stedet for kun mount-tidspunktet. Ensrettet med vilje: hopper TIL // stedet for kun mount-tidspunktet. Toveis siden 2026-08-20 (se
// "manage" når URL-en ber om det, men tvinger aldri tilbake til "score" // begrunnelse ved searchParams over) -- speiler URL-en nøyaktig samme
// om parameteren mangler/fjernes (det ville kunnet rive brukeren vekk // vei som `initialTab` allerede gjorde ved mount.
// fra et bevisst lokalt "Score"-valg de allerede har gjort).
useEffect(() => { useEffect(() => {
if (searchParams.get("tab") === "manage") setPageTab("manage") setPageTab(searchParams.get("tab") === "manage" ? "manage" : "score")
}, [searchParams]) }, [searchParams])
// Fanger opp nettleserens frem/tilbake-navigasjon (URL-en endrer seg uten // Fanger opp nettleserens frem/tilbake-navigasjon (URL-en endrer seg uten
// at `setHoleAndUrl` kalte den) -- speiler `?hole=`-parameteren inn i // at `setHoleAndUrl` kalte den) -- speiler `?hole=`-parameteren inn i
@ -1503,34 +1511,15 @@ export function RoundDetail({ roundId }: { roundId: string }) {
</div> </div>
)} )}
{/* Ett enkelt, sekundært veksle-valg -- IKKE en fanerad (2026-08-20, {/* 2026-08-20: den forrige egne veksleknappen ("Spillere og
reelt brukerfunn: en andre pille-fanerad her, rett under runde"/"Tilbake til score") her er FJERNET (brukerkorrigering --
RoundHeader sin egen Score/Scorekort/Leaderboard-rad, leste som reelt duplikat av "Administrer"-knappen i RoundHeader øverst,
to duplikate "Score"-kontroller rett oppå hverandre). "Score" som allerede lenker til nøyaktig samme "manage"-visning via
(hull-navigasjon + registrering, default) og "Spillere og runde" ?tab=manage). Selve TO-FANE-FUNKSJONEN (pageTab under)
(alt administrativt) er fortsatt to distinkte visninger denne beholdes uendret -- kun den ekstra, synlige raden er borte. Se
siden -- kun selve KONTROLLEN er en enkelt, tydelig ANNERLEDES searchParams-effekten over: RoundHeader sin egen "Score"-fane
knapp (ramme + ikon, ikke en pille) i stedet for en segmentert (lenker til bare-URL-en, uten parameteret) er den fungerende
fanerad som konkurrerer visuelt med hovedfanene over. */} veien tilbake fra manage-panelet. */}
<div className="mb-5 flex justify-end">
<button
type="button"
onClick={() => setPageTab(pageTab === "score" ? "manage" : "score")}
className="flex h-11 items-center gap-2 rounded-xl border border-border bg-card px-3 text-sm font-bold text-foreground transition-colors hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
{pageTab === "score" ? (
<>
<Settings2 aria-hidden="true" className="size-4 shrink-0 text-primary" />
Spillere og runde
</>
) : (
<>
<ChevronLeft aria-hidden="true" className="size-4 shrink-0" />
Tilbake til score
</>
)}
</button>
</div>
{/* Offline scoreregistrering (ADR-028-utvidelse) -- synlig uansett {/* Offline scoreregistrering (ADR-028-utvidelse) -- synlig uansett
fane, siden offline-tilstanden er global for hele siden. */} fane, siden offline-tilstanden er global for hele siden. */}
@ -6833,11 +6822,6 @@ function PlayerHoleCards({
/> />
)} )}
<span className="truncate text-base font-bold tracking-tight text-foreground">{player.name}</span> <span className="truncate text-base font-bold tracking-tight text-foreground">{player.name}</span>
{player.isSelf && (
<Badge variant="default" className="shrink-0 px-1.5 py-0 text-[10px]">
Deg
</Badge>
)}
</div> </div>
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs font-medium text-muted-foreground tabular-nums"> <div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs font-medium text-muted-foreground tabular-nums">
<span>HCP {player.hcp !== null ? player.hcp.toFixed(1).replace(".", ",") : ""}</span> <span>HCP {player.hcp !== null ? player.hcp.toFixed(1).replace(".", ",") : ""}</span>
@ -6923,13 +6907,18 @@ function PlayerHoleCards({
slagvinduet ADR-079, 2026-08-16) -- skjermen FØR slagvinduet ADR-079, 2026-08-16) -- skjermen FØR
registrering/"Avslutt for {navn}", ikke inni selve registrering/"Avslutt for {navn}", ikke inni selve
slagvinduet. Skjuler seg selv når banen er custom eller slagvinduet. Skjuler seg selv når banen er custom eller
spilleren ikke har historikk ennå. */} spilleren ikke har historikk ennå. KUN scorekortførers
<div className="px-4 pb-2"> (isSelf) eget kort (2026-08-20, brukerkorrigering --
<HoleHistoryPanel panelets tekst sier "Din historikk", som var feil for
url={`/rounds/${roundId}/participants/${player.id}/holes/${currentHole}/history`} andre spilleres kort). */}
onOpenFull={() => setHistoryPlayerId(player.id)} {player.isSelf && (
/> <div className="px-4 pb-2">
</div> <HoleHistoryPanel
url={`/rounds/${roundId}/participants/${player.id}/holes/${currentHole}/history`}
onOpenFull={() => setHistoryPlayerId(player.id)}
/>
</div>
)}
<ParticipantCompletionActions <ParticipantCompletionActions
player={player} player={player}
holesPlayed={list?.filter((h) => h.played).length ?? 0} holesPlayed={list?.filter((h) => h.played).length ?? 0}