diff --git a/CLAUDE.md b/CLAUDE.md index e94aeae..f0f8bd2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5232,6 +5232,78 @@ Ferdig og verifisert: Begge containere boot-et rent, `/health`/`/dashboard` → 200, `teeoff.no` upåvirket. +- **Match-identitetsbanneret: territorium-bar i stedet for flat 50/50-boks, + BYGGET, GRUNDIG BROWSERVERIFISERT (inkl. en reell overlapp-bug funnet OG + fikset) OG LIVE (2026-07-29), samme dag:** brukeren lastet opp to bilder + av dagens banner (samme identitetskort som ble bygget dagen før -- navn+ + HCP i hver ende, en sentrert statusboks) og en "VELDIG DÅRLIG, kun + illustrerende" skisse av eget forslag: den ledende sidens halvdel bør + markeres OVER på motstanderens halvdel (ikke en statisk 50/50-boks), og + ved AS skal begge sider markeres likt (nøytralt). + **Design:** ny `leadZoneFraction(lead, totalHoles)` -- `0.5 + (lead / + totalHoles) * 0.5`, klippet til `[0.18, 0.82]` for å alltid holde begge + navn lesbare selv ved en ekstrem ledelse. Dominant side får en solid, + mettet farge (`bg-primary`/`bg-brand-orange`, hvit/kontrastfarget tekst + via allerede eksisterende `--primary-foreground`/`--brand-orange- + foreground`-tokens) og strekker seg proporsjonalt forbi midtlinjen; + underlegne side beholder samme fargetone som en svak, lys tint + (`/12`-opacity) med vanlig temafarget tekst. Ved AS (eller ingen hull + spilt ennå): begge soner nøyaktig 50/50 og nøytralt `bg-muted` -- ingen + side ser ut til å lede. Egen lokal kopi i alle tre filer der et match- + identitetsbanner finnes (samme "én fil, én kopi"-konvensjon som + MatchScorecardCell-arbeidet dagen før): `round-detail.tsx` sin + `FormatResultPanel` (primary/brand-orange-tokens), `round-scorecard.tsx` + sin nye delte `LeadBar`+`LeadZone` (samme tokens, portert fra den + tidligere `IdentitySide`+`CenterBadge`-duoen), `session-scorecard.tsx` + sin `IdentityBlock` (FAKTISK lagets `team.color`-hex i stedet for faste + tokens -- dominant sone `backgroundColor: color` + hvit tekst, svak sone + `color-mix(in srgb, ${color} 15%, transparent)`, samme kontrastvalg som + den eksisterende `SegmentedBar` i tournament-leaderboard.tsx). + **Reell overlapp-bug funnet OG fikset UNDER selve browserverifiseringen, + ikke antatt riktig fra kodegjennomgang alene:** første forsøk brukte en + `position:absolute`-sentrert statusboks OPPÅ to `width:X%`-delte soner -- + et ekte skjermbilde på en smal (390px) mobil-viewport viste at et langt + navn ("Erol Haagenrud") ble delvis SKJULT bak statusboksen ("l Haagenrud" + vist i stedet, ikke en ellipse-trunkering, et reelt visuelt overlapp). + Første fiksforsøk (CSS Grid med `minmax(0,X fr) auto minmax(0,Y fr)` i + stedet for absolutt posisjonering) løste IKKE problemet alene -- fortsatt + samme overlapp ved re-test. Rot-årsaken var dypere: identitetsboksen + brukte `items-start`/`items-end` på sin YTRE flex-kolonne, som sizer + boksen etter INNHOLDETS egen bredde (shrink-to-fit) i stedet for sonens + faktiske tildelte bredde -- innholdet kunne dermed visuelt strekke seg + utover sin egen sone og inn i midt-kolonnen uansett hvor korrekt selve + grid-/bredde-fordelingen var. Fikset ved at boksen alltid STREKKER SEG + (fjernet items-start/items-end helt), med venstre/høyre-justering i + stedet løst via `justify-start`/`justify-end` på navne-raden og + `text-left`/`text-right` på HCP-teksten -- INNI en boks som nå alltid har + sonens fulle, korrekte bredde, slik at `truncate` faktisk virker presist. + **Verifisert grundig i en isolert scratch-nettleserøkt** (fersk + `teecup_scratch`-database + isolert scratch-MinIO + engangs API- + container, ekte `next dev` mot scratch-backend, Chrome DevTools MCP, 390× + 844 mobil-viewport): bygget en ekte match-runde fra bunnen via API (egen- + definert 18-hulls bane, Erol HCP 10 vs. gjest "Tore Morell" HCP 54, + samme HCP-mønster som brukerens skjermbilde) og testet re-render etter + HVER kodeendring, ikke bare én gang til slutt -- fanget nettopp DERFOR + både at CSS Grid-fiksen alene ikke var nok, og at stretch-fiksen + faktisk løste det. Bekreftet: normal ledelse ("1 UP", dominant sone + moderat bredere), en EKSTREM ledelse (avgjort "9&7", dominant sone + nesten fyller hele baren, underlegne navn korrekt trunkert "T…"/"HCP…" + i stedet for å overlappe), AS/ingen-hull-spilt (nøytral 50/50, `bg- + muted`), BÅDE lys og mørk modus (kontrast bekreftet i begge), og samme + fiks bekreftet å fungere identisk i `round-scorecard.tsx` sin + `MatchScorecardGrid`-visning (samme runde, `/my-rounds/{id}/scorecard`). + `session-scorecard.tsx` sin tournament-variant IKKE egen nettleser- + testet denne runden (identisk kodemønster, kun `team.color`-hex i stedet + for faste tokens -- lavere risiko, men flagget ærlig som ikke eget + bevist). Ekte typesjekket produksjonsbuild kjørt (to runder -- én etter + første, mislykkede CSS Grid-only-forsøk, én etter den faktiske stretch- + fiksen), alle 24 ruter listet begge ganger. + **Rullet ut live 2026-07-29**, bruker bekreftet eksplisitt: ingen + migrasjon (ren frontend), `docker compose up -d --build + teecup_frontend` (gjenskapte også `teecup_api` som vanlig bivirkning, + ingen backend-kode rørt). Begge containere boot-et rent, `/health`/ + `/dashboard` → 200, `teeoff.no` upåvirket. + Neste steg: 0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet (2026-07-27, full 22-skjerms gjennomgang):** rendrer korrekt, ingen diff --git a/frontend/components/round-detail.tsx b/frontend/components/round-detail.tsx index 684b74c..edb3bfe 100644 --- a/frontend/components/round-detail.tsx +++ b/frontend/components/round-detail.tsx @@ -2396,6 +2396,74 @@ function IdentitySide({ identity, side }: { identity: { name: string; hcp: numbe ) } +// Territorium-bar (2026-07-29, erstatter det tidligere flate, likt-delte +// banneret) -- den ledende sidens sone strekker seg PROPORSJONALT forbi +// midtlinjen inn på motstanderens halvdel (brukerens eget forslag: "hele +// den halve delen som tilhører den som er en opp er markert over på +// motstanderens halvdel"), i stedet for en statisk 50/50-boks med tekst i +// midten. Ved AS (eller ingen hull spilt ennå) er begge soner nøyaktig like +// store OG likt (nøytralt) farget -- ingen side skal se ut til å lede. +// Bruker samme proporsjonale to-fargede-bar-idé som SegmentedBar +// (tournament-leaderboard.tsx), tilpasset lead-i-hull i stedet for poeng. +function leadZoneFraction(lead: number, totalHoles: number): number { + if (totalHoles <= 0) return 0.5 + const fraction = 0.5 + (lead / totalHoles) * 0.5 + return Math.min(0.82, Math.max(0.18, fraction)) +} + +function LeadZone({ + identity, + side, + dominant, +}: { + identity: { name: string; hcp: number | null } + side: "a" | "b" + dominant: boolean +}) { + const textTone = dominant ? (side === "a" ? "text-primary-foreground" : "text-brand-orange-foreground") : "text-foreground" + const subTone = dominant + ? side === "a" + ? "text-primary-foreground/75" + : "text-brand-orange-foreground/75" + : "text-muted-foreground" + // Merk: den ytre boksen STREKKER SEG (ingen items-start/items-end her -- + // det ville sizet boksen etter innholdets egen bredde, ikke etter sonens + // faktiske tildelte bredde, og kunne visuelt lekke inn i midt-kolonnen på + // smale skjermer). Venstre/høyre-justering skjer i stedet med + // justify-start/justify-end og text-align INNI en boks som alltid har + // sonens fulle, korrekte bredde -- truncate virker da presist. + return ( +
+
+ {!dominant && ( +
+ {identity.hcp !== null && ( + + HCP {identity.hcp} + + )} +
+ ) +} + type ApiFlightSummary = { round_id: string name: string | null @@ -2579,10 +2647,9 @@ function FormatResultPanel({ ) } - // Identitetsbanner (2026-07-29, erstatter den tidligere plainere teksten + - // den nå overflødige hull-for-hull-sirkel-listen -- selve scorekortet - // under viser dette per hull nå). Navn+HCP enten side, stor sentrert - // løpende status, "Ferdig"-merke når avgjort -- samme visuelle språk som + // Identitetsbanner (2026-07-29, territorium-bar-versjon -- se leadZoneFraction + // over). Navn+HCP i hver sone, stor sentrert løpende status flytende oppå + // begge soner, "Ferdig"-merke når avgjort -- samme visuelle språk (nå) som // MatchScorecardGrid (round-scorecard.tsx) og TournamentMatchGrid // (session-scorecard.tsx), egen lokal kopi per prosjektets konvensjon. const identityA = sideIdentity(round, players, "a") @@ -2594,39 +2661,61 @@ function FormatResultPanel({ const isDormie = result.match_is_dormie ?? false const tone: "a" | "b" | "neutral" = lead > 0 ? "a" : lead < 0 ? "b" : "neutral" const statusLabel = holesPlayed > 0 ? formatMatchStatus(lead, holesRemaining, isClosed, isDormie) : null + const totalHoles = holesPlayed + holesRemaining + const widthA = holesPlayed > 0 ? leadZoneFraction(lead, totalHoles) * 100 : 50 + const widthB = 100 - widthA + const zoneAClass = tone === "a" ? "bg-primary" : tone === "b" ? "bg-primary/12" : "bg-muted" + const zoneBClass = tone === "b" ? "bg-brand-orange" : tone === "a" ? "bg-brand-orange/12" : "bg-muted" return (
{FORMAT_LABELS[round.play_format] ?? round.play_format} -
- -
- {statusLabel ? ( - <> - - {statusLabel} - - {isClosed ? ( - - Ferdig - - ) : ( - - {holesPlayed} hull spilt - {holesRemaining > 0 && isDormie && " · dormie"} - - )} - - ) : ( - Ingen hull spilt ennå - )} + {/* CSS Grid, ikke absolutt-posisjonert overlegg (2026-07-29-fiks) -- + en tidligere versjon la statusen som en absolutt sentrert boks OPPÅ + navnetekst, som kunne overlappe et langt navn på smale skjermer. + Med grid-kolonner `minmax(0,X fr) auto minmax(0,Y fr)` reserverer + nettleseren ALLTID nøyaktig midt-kolonnens egen bredde til status- + boksen -- soneandelen forblir proporsjonal (X:Y), men kan aldri + overlappe, og lange navn trunkeres i stedet (samme `truncate`- + mekanisme som ellers). */} +
+
+ +
+
+
+ {statusLabel ? ( + <> + + {statusLabel} + + {isClosed ? ( + + Ferdig + + ) : ( + + {holesPlayed} hull spilt + {holesRemaining > 0 && isDormie && " · dormie"} + + )} + + ) : ( + Ingen hull spilt ennå + )} +
+
+
+
-
) diff --git a/frontend/components/round-scorecard.tsx b/frontend/components/round-scorecard.tsx index f057b47..984fdab 100644 --- a/frontend/components/round-scorecard.tsx +++ b/frontend/components/round-scorecard.tsx @@ -443,42 +443,121 @@ function computeRunning(results: ("a" | "b" | "halved")[], totalHoles: number): type HeaderIdentity = { name: string; hcp: number | null } -function IdentitySide({ identity, side }: { identity: HeaderIdentity; side: "a" | "b" }) { +// Territorium-bar (2026-07-29, brukerens eget forslag: den ledende sidens +// sone strekker seg proporsjonalt forbi midtlinjen inn på motstanderens +// halvdel, i stedet for en statisk 50/50-boks med tekst i midten). Ved AS +// eller ingen hull avgjort ennå er begge soner like store og nøytralt +// farget. Egen lokal kopi av samme idé som round-detail.tsx sin +// FormatResultPanel, per prosjektets "én fil, én kopi"-konvensjon. +function leadZoneFraction(lead: number, totalHoles: number): number { + if (totalHoles <= 0) return 0.5 + const fraction = 0.5 + (lead / totalHoles) * 0.5 + return Math.min(0.82, Math.max(0.18, fraction)) +} + +function LeadZone({ identity, side, dominant }: { identity: HeaderIdentity; side: "a" | "b"; dominant: boolean }) { + const textTone = dominant ? (side === "a" ? "text-primary-foreground" : "text-brand-orange-foreground") : "text-foreground" + const subTone = dominant + ? side === "a" + ? "text-primary-foreground/75" + : "text-brand-orange-foreground/75" + : "text-muted-foreground" + // Boksen STREKKER SEG (ingen items-start/items-end) -- ellers sizes den + // etter innholdets egen bredde, ikke sonens faktiske tildelte bredde, og + // kan visuelt lekke inn i midt-kolonnen på smale skjermer. Justering skjer + // med justify-start/justify-end + text-align inni en boks som alltid har + // full, korrekt bredde -- truncate virker da presist. return ( -
-
-