diff --git a/ARCHITECTURE_DECISIONS.md b/ARCHITECTURE_DECISIONS.md index ef867c0..5ed3cde 100644 --- a/ARCHITECTURE_DECISIONS.md +++ b/ARCHITECTURE_DECISIONS.md @@ -2817,6 +2817,97 @@ ekte https, `teeoff.no` upåvirket. boot-et rent, `/health`/`/dashboard`/`/my-rounds`/`/account` → 200 over ekte https, `teeoff.no` upåvirket. +### Oppdatering 2026-07-26: utslag/HCP per deltaker, gjeste-kontaktinfo, og scoringsflyten redesignet (v2) + +Tre beslutninger denne dagen utvider ADR-033 uten å endre grunn- +arkitekturen (Beslutning A-G står uendret). + +**1. Utslagssted er nå PER DELTAKER, ikke per runde.** Reelt hull +oppdaget ved bruk: alle deltakere delte tidligere `round. +tee_name_snapshot` uansett kjønn/faktisk valg — en medspiller kunne +aldri spille fra et annet utslag enn eieren. Migrasjon `028_round_ +participant_tee.sql` gir `round_participant` sitt eget `tee_name_ +snapshot`, backfylt fra rundens eksisterende utslag for eksisterende +rader. `ParticipantCreate`/`ParticipantUpdate` fikk et valgfritt +`tee_name`; et bane-bytte (eksisterende `RoundUpdate`-mekanisme) +nullstiller nå eksplisitt ALLE deltakeres per-deltaker-utslag til det +nye standardutslaget (et bane-bytte gjør individuelle valg fra den +gamle banen meningsløse). HCP kan nå også redigeres per deltaker for +KUN denne runden (samme reproduserbarhets-unntak som resten av +`round_participant` — endrer aldri spillerens faktiske profil), +avvist etter at runden er fullført (samme presedens som bane-bytte). + +**2. Gjester (`user_id IS NULL`) fikk et valgfritt kontaktfelt + +redigerbart navn/kjønn.** Migrasjon `029_round_participant_guest_ +email.sql` (`guest_email`, nullable). `ParticipantUpdate` fikk `guest_ +name`/`gender`/`guest_email` — eksplisitt avvist (400) for en LENKET +deltaker (disse feltene gir ingen mening for noen med egen konto). +`gender`-endring inngår i samme rating-rekalkulering som utslag/HCP +(påvirker hvilken rating som er gyldig). + +**3. Scoringsflyten redesignet til en skjermovertagende veiviser -- +to iterasjoner, andre erstattet den første samme dag.** Brukeren delte +en skjermopptaksvideo av en konkurrentapp (Golf GameBook) sin +scoreregistrering og spurte om prinsippene kunne forbedre TeeCup. +Videoen ble analysert bilde for bilde (ffmpeg i en engangs Docker- +container) og avdekket et "samlebånd"-mønster: taltastatur med +kontekstuelle golf-termer (Eagle/Birdie/Par/Bogey ut fra hullets par), +og at fullført registrering for én spiller automatisk åpner NESTE +spillers registrering for samme hull. +**Første forsøk (v1), BEVISST avvist fra en full modal:** vurdert som +unødvendig risikofylt å bygge en skjermovertagende steg-for-steg-flyt +korrekt uten visuell testing (intet nettleserverktøy tilgjengelig i +denne økten). I stedet: golf-term-taltastatur lagt til der det allerede +var (`NumberPicker`s nye `showGolfTerms`-modus), pluss en "Neste: +{navn}"/"Neste hull"-knapp nederst på den EKSISTERENDE, lange +inline-siden. +**Brukeren testet v1 live og avviste den eksplisitt:** "ingen forbedring +i det hele tatt", "visuelt like overveldende og rotete" — den +forsiktige, risikoreduserte tilnærmingen var utilstrekkelig; en ekte +skjermovertagende veiviser var det som faktisk krevdes, sammen med et +nytt, eksplisitt krav: akkumulert score-så-langt for RUNDEN synlig for +HVER spiller samtidig (ikke bare én valgt), matchende konkurrentappens +vedvarende "E"/"+1"-visning ved siden av hvert navn. +**v2 bygget samme dag, ERSTATTER v1 helt:** ny `ScoringWizard`-komponent +(fullskjerm, egen DOM-stack utenfor `
`), tre steg maks drevet av +`stat_level` ("strokes_only" → kun Slag; "strokes_and_putts" → + Putter/ +avstand første putt; "full" → + ett samlet detalj-steg). Bevisst FÆRRE, +grovere steg enn konkurrentens egne 5-6 skjermer — en risikoreduksjon +uten visuell testing, og TeeCups stat_level-modell gjør en finere +oppdeling mindre naturlig uansett. Alle spillerne vises som en fast, +ikke-trykkbar kontekst-rad øverst i veiviseren (speiler konkurrent- +appens "mist aldri oversikten"-prinsipp — bevisst IKKE trykkbar, siden +GameBooks egen video heller ikke viste spillerbytte midt i et steg). +Hovedsiden forenklet radikalt: det gamle Slag/Putter/"flere detaljer"- +skjemaet er fjernet, erstattet med én kompakt liste (navn, "HCP X · +{til-par} så langt (N hull)", en stor rund knapp som åpner veiviseren). +Datalasting utvidet til å hente ALLE deltakeres hull med det samme +(ikke lenger lat lasting kun for aktiv spiller), nødvendig for at +akkumulert score kan vises for alle samtidig. +**Reell driftsfeil funnet OG rettet SAMME dag, rapportert av bruker som +testet live:** den gamle `ScoreSoFar`("Så langt i runden")-boksen ble +ved en feil IKKE fjernet/flyttet i selve v2-bygget — den lå fortsatt +øverst, uendret, og fikk brukeren til å rapportere "ingen synlig +endring i det hele tatt" (toppen av siden var reelt uendret). Bekreftet +presist ved å hente den FAKTISK kjørende JS-bunten i produksjon og +søke i den — begge de gamle OG de nye tekst-strengene lå i samme bunt, +som beviste dette var en ekte plasseringsfeil, ikke en cache-/ +utrullingsfeil. Rettet ved å flytte `ScoreSoFar` til UNDER scorings- +seksjonen (fortsatt tilgjengelig, bare ikke lenger i veien for +hovedoppgaven) — samme klasse feil hele denne rundens redesign forsøkte +å løse. +**Bevisst utenfor omfang, IKKE bygget:** de mest granulære trekkene fra +konkurrentappen (separate skjermer per detalj-felt, en illustrert +"bommet green"-grafikk, spillerbytte midt i et steg) — vurdert som for +stor risiko å bygge presist uten visuell testing, og TeeCups egen +stat_level-gruppering gjør en så fin oppdeling mindre naturlig uansett. +**Ingen ekte nettleser-interaksjonstest utført av noen av iterasjonene** +(intet slikt verktøy tilgjengelig denne økten) — kun logikk (Node- +simulering av stegmaskinen, rangering, akkumulert-score-beregning) og +et ekte API-rundtur-script (nøyaktig samme feltkombinasjon som +veiviseren sender) er verifisert. Full detalj i CLAUDE.md sin +statuslogg (2026-07-26). + --- ## ADR-035: Dashbord-redesign — organisasjon blir implisitt diff --git a/CLAUDE.md b/CLAUDE.md index 35aef7d..0a445cd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3465,6 +3465,36 @@ Ferdig og verifisert: ingen migrasjon, `docker compose up -d --build teecup_api teecup_frontend`. Begge containere boot-et rent, `/health`/`/dashboard` → 200, `teeoff.no` upåvirket. +- **Scoringsflyt v3: administrasjon og scoring adskilt i to faner, BYGGET, + IKKE ENNÅ RULLET UT (2026-07-26) -- direkte svar på at bruker fortsatt + fant siden "bråkete og lite intuitiv" etter v2.** Brukeren viste et NYTT + sidestilt skjermbilde-par (samme mønster som første gang) og påpekte at + TeeCup fortsatt hadde mye synlig FØR selve scoringslisten (leaderboard- + forhåndsvisning, Rediger/Fullfør/Slett, hele spillerlisten med Rediger- + ikoner, "+Medspiller") -- nøyaktig det jeg selv identifiserte som GameBooks + kjerneprinsipp i den aller første analysen ("administrasjon og scoring er + ADSKILTE tabs"), men aldri fullt ut gjennomførte i v2 (kun `ScoreSoFar` + ble flyttet forrige runde, resten av det administrative innholdet ble + stående igjen øverst). + **Fikset denne gangen for ekte:** ny `pageTab`-state (`"score" | "manage"`, + default `"score"`), en enkel fanevelger rett under feilmeldingen. "Score" + (default) inneholder nå KUN: fullført-banner (hvis relevant), hull- + navigasjon, og selve hull-panelet (header + scoringslisten fra v2 + + Forrige/Neste hull) -- ingenting annet. "Spillere og runde" samler + leaderboard-forhåndsvisning, Rediger/Fullfør/Slett, hele spillerlisten + (`PlayerList`), OG `ScoreSoFar` (flyttet HIT fra forrige rundes + "under scoringslisten"-plassering, siden den er detaljert stats-innsyn, + ikke selve registreringsoppgaven). + **Verifisert:** ekte typesjekket produksjonsbuild kompilerte rent, samme + engangs `next dev`-container-sjekk ga 200, full regresjon (35-punkts + co-player-testsuite) fortsatt grønn, `test_isolation.sql` 12/12 (ren + frontend-omrokkering, ingen migrasjon). + **Samme ærlige begrensning som v1/v2:** ingen ekte nettleser- + interaksjonstest av selve fane-følelsen. + **Rullet ut mot ekte systemer 2026-07-26**, bruker bekreftet eksplisitt: + ingen migrasjon, `docker compose up -d --build teecup_frontend` + (gjenskapte også `teecup_api` som vanlig bivirkning). Begge containere + boot-et rent, `/health`/`/dashboard` → 200, `teeoff.no` upåvirket. Neste steg: 0a. **Spillerliste-redesign — LIVE, men ikke nettleser-testet diff --git a/DESIGN_SYSTEM.md b/DESIGN_SYSTEM.md new file mode 100644 index 0000000..0ad002d --- /dev/null +++ b/DESIGN_SYSTEM.md @@ -0,0 +1,233 @@ +# TeeCup — designsystem (referanse) + +> Dette dokumentet beskriver det VISUELLE grunnlaget for alt frontend- +> arbeid i TeeCup — fargetoken, typografi, avstand, komponentmønstre og +> tilgjengelighetsregler. Det er en beskrivelse av hva som ER etablert i +> koden (`frontend/app/globals.css`, `frontend/components/ui/*`, og +> mønstrene brukt på tvers av `components/*.tsx`), ikke et nytt forslag — +> hensikten er at både V0-prompter og håndkodet arbeid skal kunne +> forankres i akkurat dette, i stedet for å gjettes på nytt hver gang. +> +> Ufravikelig grunnregel (fra CLAUDE.md, gjelder ALT, eksisterende og +> fremtidig): appen skal være lesbar, forståelig og betjenbar for noen med +> noe redusert syn UTEN briller. God kontrast, stor nok skrift, store nok +> trykkflater, ikke ikon-only uten tekstlabel for viktige handlinger, ikke +> avhengig av finmotorikk/skarpt syn. Enhver visuell beslutning under +> veies mot dette først. + +## Merkevare-opprinnelse + +Fargeformen/-tonen er hentet fra Teeoff-logoen (samme merkevarefamilie, +egne farger — se ADR-009/ADR-016), IKKE navn eller logo. Basisfargene ble +regnet ut som presise OKLCH-verdier fra de opprinnelige hex-fargene: + +- Grønn (primær): `#8bc24a` +- Oransje (sekundær/"brand-orange"): `#ff5722` + +Disse to er de ENESTE to kjernefargene i merkevaren. Et forslag om en +tredje ("informasjons"-)farge ble vurdert 2026-07-25 og IKKE besluttet — +se FEATURE_BACKLOG.md. Anbefalingen der var å gjenbruke `--chart-3` +(allerede en blåtone i statistikk-skalaen) fremfor å finne på noe nytt. + +## Fargetoken (`frontend/app/globals.css`) + +Alle farger er definert som OKLCH-variabler, med egne verdier for lys og +mørk modus (`:root` / `.dark` / `@media (prefers-color-scheme: dark)`, +alle tre holdt i synk). Bruk ALLTID token-navnene under (Tailwind-klasser +som `bg-primary`, `text-muted-foreground` osv.) — aldri rå hex/oklch i ny +kode. + +| Token | Lys modus | Rolle | +|---|---|---| +| `background` / `foreground` | nesten hvit / mørk grønn-grå | sideflate / brødtekst | +| `card` / `card-foreground` | hvit / mørk grønn-grå | kort, paneler, seksjoner | +| `primary` / `primary-foreground` | `oklch(0.7512 0.1613 130.33)` (grønn) / mørk | hovedhandling, valgt tilstand, "under par"/positiv | +| `secondary` / `secondary-foreground` | lys grønn-tint | sekundær vekt, sjeldent brukt alene | +| `muted` / `muted-foreground` | lys grå-grønn / dempet | bakgrunnstoner, sekundær tekst/bildetekst | +| `accent` / `accent-foreground` | lys grønn-tint | hover-tilstand på nøytrale flater | +| `destructive` | rød-oransje | slett/fare-handlinger | +| `border` / `input` | lys grå-grønn | kant på kort, felt, delelinjer | +| `ring` | samme som `primary` | fokus-ring (tastatur-tilgjengelighet) | +| `brand-orange` / `brand-orange-foreground` | `oklch(0.6792 0.2128 36.53)` | sekundærfarge — "over par"/advarsel, IKKE feil (det er `destructive`) | +| `chart-1` … `chart-6` | grønn→blå→gul→oransje→rød-skala | statistikk-/fordelingsvisualiseringer (ADR-033), ALDRI brukt for kategoriske UI-tilstander | + +**Farge-semantikk, konsekvent på tvers av hele appen:** +- `primary` (grønn) = valgt/aktiv, positivt resultat (under par, ledende, + bekreftet), primærhandling. +- `brand-orange` = nest viktigst/kontrast-pol i et par (over par, "Poeng" + i noen sammenhenger), IKKE en feilfarge. +- `destructive` = KUN faktiske slette-/fare-handlinger (Slett runde, + fjern spiller), aldri "over par" eller lignende domenetilstand. +- `muted-foreground` = all sekundær/bildetekst-tekst, aldri brødtekstens + hovedfarge. + +**Aldri farge alene.** Hver gang farge bærer betydning (over/under par, +leder, valgt/uvalgt), følges den av FORM (sirkel vs. firkant, fylt vs. +bordered), tekst (+N/−N/E, "Deg"/"Eier"), eller et ikon — se +"Golfscore-språket" under. Dette er ikke en stilistisk preferanse, det +er et direkte utslag av tilgjengelighetsregelen øverst. + +## Typografi + +- **Skrifttype:** Nunito (`next/font/google`, lastet i `app/layout.tsx`, + eksponert som `--font-sans`) — rund, vennlig, god lesbarhet på små + trykkflater. Ingen sekundær/monospace-font i UI-et (tabular-nums + brukes for tallinnretting i stedet for en egen tallfont — se under). +- **Skala** (Tailwind-klasser, brukt konsekvent): + - `text-sm` — bildetekst, sekundær informasjon, knapp-tekst i tette + kontekster + - `text-base` — brødtekst, standard knapp-/felt-tekst, listeelementer + - `text-lg` / `text-xl` — kortoverskrifter, seksjonstitler + - `text-2xl` / `text-3xl` — sideoverskrifter, hull-header, store + resultat-tall +- **Vekt:** `font-semibold`/`font-bold` for all vektlagt tekst, + `font-extrabold` for tall og hovedoverskrifter. Vanlig (regular) vekt + brukes nesten aldri i UI-kontroller — selv sekundærtekst er ofte + `font-semibold` for lesbarhet på avstand/i sollys. +- **`tabular-nums` på ALL numerisk visning** (skår, HCP, datoer, + rangeringer) — hindrer tall fra å "hoppe" i bredde når de endres. +- **Golfvis fortegn:** til-par-tall formateres ALLTID som "E" (jevnt med + par), "+N" (over), "−N" — ekte minustegn (U+2212), ALDRI en vanlig + bindestrek eller et bart negativt tall. Samme regel for HCP/ + differensial-tall der fortegn er meningsbærende. + +## Avstand, hjørner og lag + +- **Hjørneradius** (fra `--radius: 0.625rem`, skalert opp via + `--radius-sm/md/lg/xl/2xl/3xl/4xl`): `rounded-xl` for felt/knapper i + tette rader, `rounded-2xl` for kort/paneler/knapper med normal vekt, + `rounded-3xl` for store side-nivå-containere (hull-panel, rundekort). + `rounded-full` reservert for sirkulære tilstander (se golfscore-språket) + og pille-formede brytere/merker. +- **Kort/panel-basismønster:** `rounded-2xl` eller `rounded-3xl`, `border + border-border`, `bg-card`, `p-4` (tett) til `p-5 sm:p-6` (romslig), ofte + `shadow-sm shadow-black/5` for lett løft fra bakgrunnen. +- **Sammenhengende lister** (leaderboard, spillerliste): ÉN container + (`rounded-2xl border border-border overflow-hidden`) med + `divide-y divide-border` mellom radene — IKKE separate kort med + mellomrom, det gir en "kort-følelse" som virker mer oppstykket enn + nødvendig når radene i praksis hører sammen (bevisst rettet opp + 2026-07-26 på leaderboardet, se CLAUDE.md). +- **Stiplet kant** (`border border-dashed border-border`) markerer en + "legg til"/tom-tilstand-affordance (f.eks. "+ Medspiller"), ALDRI + brukt på innhold som allerede finnes. + +## Trykkflater og avstand mellom elementer + +- **44px er det ufravikelige gulvet** for enhver trykkbar flate + (`min-h-11` i Tailwind-tall siden 1 enhet = 4px). Primærhandlinger er + ofte enda større: `h-12`–`h-16` for tallvelgere/hovedknapper. +- Runde ikon-only-knapper (lukk, fjern) er minimum `size-11`. +- Gap mellom relaterte kontroller: `gap-2`/`gap-2.5`/`gap-3` normalt, + `gap-4`/`gap-6` mellom distinkte seksjoner. + +## Layout-containere + +- Innloggede/app-sider: `mx-auto w-full max-w-3xl` for innholdsrike + sider (runde-detalj), `max-w-xl` for enklere/lesevisninger + (leaderboard, scorekort, statistikk). +- Sticky header: `sticky top-0 z-10` (eller `z-20` når flere lag stables), + `border-b border-border`, `bg-background/90` til `/95` + `backdrop-blur` + — innhold forblir lesbart under skrolling uten å bli en solid, brå kant. +- Fullskjerm-overlegg (scoringsveiviser): `fixed inset-0 z-50 flex + flex-col bg-background`, med `header`/`main` (scroller, + `flex-1 overflow-y-auto`) /`footer` (handling-rad, `shrink-0`) — samme + tre-delt struktur uansett hvor i appen et slikt overlegg brukes. +- Fast-per-rad-høyde når kort skal se enhetlige ut i en liste + (`min-h-[Npx]` + `truncate` på tekstlinjer i stedet for fri wrapping) — + brukt bevisst på spillerkort 2026-07-26 for at rader med ulikt antall + merker/lengde på navn likevel blir like høye. + +## Komponentmønstre (gjenbrukes, ikke gjenoppfinnes) + +Disse er alle allerede bygget som lokale funksjoner i de respektive +`components/*.tsx`-filene (ikke delte imports på tvers av filer — hver +fil har sin egen lokale kopi tilpasset sin kontekst, et bevisst valg +gjentatt flere ganger denne økten fremfor tidlig abstraksjon). + +- **`NumberPicker`** (`round-detail.tsx`) — 3-kolonners rutenett av store + (`h-16`) tallknapper, valgt tilstand = fylt `bg-primary`, med en + valgfri liten bildetekst under tallet (par-merke, eller — Slag- + varianten — kontekstuell golf-term: Albatross/Eagle/Birdie/Par/Bogey/ + Dobbel bogey relativt til hullets par). Utvidbar "10+"-knapp for + sjeldne høye verdier, holder standardvisningen kort. +- **`Stepper`** (+/−) — pille-formet rad med to `size-11`-knapper som + flankerer et sentrert tall. Brukt for løpende antall (putter, chip, + bunker, straffeslag) der en full talltavle ville vært overkill. +- **`ChoiceRow`** — horisontal rad av segmenterte knapper + (`flex-1`, valgt = `bg-primary`), for korte, faste alternativlister + (avstand første putt, kjønn, statistikknivå). +- **`DirectionCross`** — 3×3-rutenett med retningsikoner (`ArrowUp/Left/ + Right/Down` + `Target` i sentrum) for utslags-/innspillretning. Tekst- + label på hver knapp (ALDRI kun ikon), matcher tilgjengelighetsregelen. +- **`ModeToggle`/segmentert bryter** — `inline-flex gap-1 rounded-full + bg-muted p-1`, hver fane `rounded-full`, valgt = `bg-primary text- + primary-foreground`. Brukt for Brutto/Netto/Poeng, Score/Spillere-faner + osv. +- **`Badge`** (delt shadcn-komponent, `components/ui/badge.tsx`) — + `variant="default"` (fylt grønn) for "Deg", `variant="outline"` + (bordered) for "Eier" — de to skal ALLTID være visuelt distinkte + varianter av hverandre, ikke samme stil i to farger. + +## Golfscore-språket (form + farge, aldri farge alene) + +Det viktigste, mest gjenbrukte visuelle mønsteret i appen — brukt i +`round-scorecard.tsx` (`ScoreMark`), `round-leaderboard.tsx` +(`ToParMark`/`PointsMark`/`HoleMark`), og flere steder til: + +- **Sirkel (`rounded-full`)** = under par / positivt resultat. +- **Firkant med liten radius (`rounded-[4px]`–`rounded-[6px]`)** = over + par / negativt resultat. +- **Ren tekst, ingen ramme** = nøyaktig par ("E"). +- **Fylt bakgrunn** (`bg-primary`/`bg-brand-orange` + kontrastfarget + tekst) = ekstra vekt (eagle+/dobbel bogey+, eller lederen i en + rangering) — fortsatt SAMME form som den vanlige varianten, kun + fyllingen endres. Vanlig variant = `border-2` + `/10`-tint bakgrunn + + farget tekst. +- **Poeng (stableford) har ingen retning** (høyere er alltid bedre) — + får derfor kun ÉN fast form (sirkel), samme fylt/bordered-emphasis- + logikk som resten. +- Rangeringstall bruker et eget, nøytralt merke (`bg-muted`/`bg-primary` + ved leder) — ikke samme form-språk som selve resultatet, for å unngå + at rangering og resultat blandes visuelt sammen. + +## Ikonografi + +- **`lucide-react`**, konsekvent gjennom hele appen — ingen annen + ikonpakke blandet inn. +- Ethvert ikon som er rent dekorativt får `aria-hidden="true"`. +- **Ikon-only er ALDRI lov for en viktig handling** — en tekstlabel følger + alltid med (unntak eksplisitt vurdert og akseptert: bjelleikonet for + varsler, fordi det er universelt gjenkjent OG har en beskrivende + `aria-label` med antall uleste). + +## Tilbakemelding og tilstander + +- **Lasting:** sentrert spinner, `size-8`–`size-10 animate-spin + rounded-full border-4 border-primary/20 border-t-primary`. +- **Feil:** `role="alert"`, `text-destructive`, alltid som ren tekst + (aldri kun et ikon eller en farget kant). +- **Tomme lister:** en kort forklarende tekst + evt. en stiplet + "legg til"-affordance, aldri en stille blank flate. +- **Sanntid (WebSocket):** ingen egen visuell "oppdatert!"-indikator — + data hentes på nytt og erstatter stille, siden radene allerede har + stabile nøkler/layout (unngår blunking). + +## Navneformat (innhold, ikke et rent visuelt punkt — men styrer hva som vises hvor) + +Direkte adressering av brukeren (dashbord-hilsen, e-post/varsel rettet +TIL mottakeren) bruker KUN fornavn. Alt annet (lister, roster, chat, +administrasjonsvisninger) bruker fullt navn/initialer — se CLAUDE.md for +den fulle, ufravikelige regelen. Nevnt her fordi det er en like fast +STANDING forventning til fremtidig UI som resten av dette dokumentet. + +## Hva dette dokumentet bevisst IKKE er + +- Ikke en erstatning for CLAUDE.md sin tilgjengelighetsregel (den er + autoritativ og gjengitt her kun i sammendrag). +- Ikke en frys av dagens design — nye mønstre legges til etter hvert som + de bygges (både via V0 og håndkodet), men skal alltid først sjekkes mot + det som ALLEREDE er etablert her fremfor å finne opp noe nytt. +- Ikke en komponent-API-referanse — se selve kildefilene + (`components/ui/*.tsx`, og de lokale komponentene i hver + `components/*.tsx`) for eksakte prop-signaturer. diff --git a/FEATURE_BACKLOG.md b/FEATURE_BACKLOG.md index 71c4d72..f37a8ca 100644 --- a/FEATURE_BACKLOG.md +++ b/FEATURE_BACKLOG.md @@ -738,8 +738,11 @@ tillatelse kreves:** > universelt gjenkjent symbol, MEN skal ha en beskrivende > `aria-label` som inkluderer antall uleste). -**Status: In-app varslingssenter BYGGET OG SCRATCH-VERIFISERT 2026-07-26, -IKKE ENNÅ RULLET UT.** V0 kjørte prompten (zip 20), backend bygget for å +**Status: In-app varslingssenter BYGGET, SCRATCH-VERIFISERT OG RULLET UT +LIVE 2026-07-26** (bruker bekreftet eksplisitt, se CLAUDE.md sin +statuslogg for utrullingsdetalj — denne linjen var utdatert helt til den +ble rettet i en senere gjennomgang samme dag). V0 kjørte prompten (zip +20), backend bygget for å matche eksakt: migrasjon `026_notifications.sql` (tabell `notification`, `plain_connection()`-mønster, ingen RLS — samme som personlig profil/ runder/venner), ny `app/routers/notifications.py` (`create_notification()` @@ -931,7 +934,7 @@ hjem alene. To ideer, ikke gjensidig utelukkende: **Status: ingen kode, ingen V0-prompt ennå** — venter på at retning (og særlig turnering-spørsmålet over) avklares før noe designes ferdig. -### Oppdatering 2026-07-26: backend for RUNDE-leaderboardet BYGGET, V0-prompt sendt +### Oppdatering 2026-07-26: RUNDE-leaderboardet HELT FERDIG (håndkodet, ikke V0 -- se under) Brukeren ba eksplisitt om å få leaderboardet for frittstående runder på plass (kun runder, ikke turneringer — turnering-spørsmålet over fortsatt @@ -1036,20 +1039,38 @@ stemte eksakt). > store trykkflater (min. 44px), aldri kun farge for å formidle > informasjon (rangering/over-under par), lesbar uten briller. -**Ikke avgjort ennå, avklares når zip-en er klar til integrering:** -nøyaktig plassering av lenke til full leaderboard-side (trolig -`round-detail.tsx`, som en lenke/knapp — ikke inline, samme "unngå for -mye stablet oppå hverandre"-lærdom som tidligere runder denne uken), -og ruten (foreslått `/my-rounds/[id]/leaderboard`, samme mønster som -`/my-rounds/[id]/stats`/`/scorecard`). +**Rettet i selve prompten 2026-07-26, FØR den ble kjørt i v0.app:** den +opprinnelige "Deg"-merke-instruksen antok feilaktig at kun eieren ser sin +egen runde -- ADR-036 fase 3 (bygget samme dag) gjør nå at en lenket +medspiller også kan se leaderboardet. Byttet til to uavhengige merker +("Eier" fra `is_owner`, "Deg" fra en egen `participant_id`-prop +komponenten mottar utenfra) -- se prompten over. -**Rettet i selve prompten 2026-07-26 (samme dag, fanget FØR den ble kjørt i -v0.app):** den opprinnelige "Deg"-merke-instruksen antok feilaktig at kun -eieren ser sin egen runde -- ADR-036 fase 3 (bygget samme dag, se over) gjør -nå at en lenket medspiller også kan se leaderboardet. Byttet til to -uavhengige merker ("Eier" fra `is_owner`, "Deg" fra en egen -`participant_id`-prop komponenten mottar utenfra) -- se oppdatert prompt -over. Fortsatt ikke kjørt i v0.app. +**Oppdatering, samme dag: HÅNDKODET i stedet for kjørt i v0.app.** +Brukeren gikk tom for V0-credits (samme situasjon som spillerliste- +redesignet rett over) og ba meg bygge direkte etter nøyaktig samme +designspesifikasjon som prompten over. Ny `components/round-leaderboard.tsx` ++ rute `/my-rounds/[id]/leaderboard`, pluss en `RoundLeaderboardMini` +(topp 3 + "og N til", lenket fra selve rundens detaljside) og en ny +"Se leaderboard"-lenke på hvert rundekort i `/my-rounds`-listen (krevde å +gjøre om `round-card.tsx` sin ytre `` til en `
` med to separate +lenker -- unngår en nestet ``). Gjenbrukte bevisst etablerte mønstre +(`ScoreMark`s form+farge-språk fra `round-scorecard.tsx`, WS-sanntid- +mønsteret fra `round-stats.tsx`) fremfor å finne opp nye. +**To oppfølgingspunkter samme dag, begge bygget:** (1) leaderboardet +skulle vise hullscorer per spiller -- løst som en utvidbar rad (klikk for +å vise en horisontal strip med hull-for-hull brutto-merker), krevde en +liten backend-utvidelse (`LeaderboardHoleOut`/`holes`-felt, data var +allerede hentet, bare ikke eksponert). (2) et tredje "Poeng" +(stableford)-modus lagt til ved siden av Brutto/Netto (rangert synkende, +siden høyere poengsum er bedre) -- krevde `strokes_received` per hull + +`total_points` på hver leaderboard-rad (også backend, samme +utvidelsesmønster). Netto/poeng vises nå også som en rolig sekundærlinje +RETT under det fremhevede brutto-hullmerket (inspirert av et +referansebilde fra en konkurrentapp bruker delte, bevisst IKKE en kopi av +fargevalg/layout). +Full verifiserings- og utrullingsdetalj i CLAUDE.md sin statuslogg +(2026-07-26) -- ikke gjentatt her for å unngå duplisering. --- @@ -1282,6 +1303,29 @@ feilside. **Samme ærlige begrensning som over:** ingen ekte nettleser- interaksjonstest (brutto/netto-veksling, faktisk visuell høyde/form-språk) er utført. +**Oppfølging samme dag: scoringsflyten redesignet til en skjermovertagende +veiviser (v1 avvist av bruker, v2 erstattet den helt) — ✅ LIVE +2026-07-26.** Brukeren delte en skjermopptaksvideo av en konkurrentapp +(Golf GameBook) sin scoreregistrering. Første forsøk (v1) la kun til +golf-term-taltastatur + en "Neste spiller"-knapp på den EKSISTERENDE lange +inline-siden — brukeren testet den live og avviste den eksplisitt ("ingen +forbedring i det hele tatt", "visuelt like overveldende og rotete"). v2 +bygget samme dag: en ekte skjermovertagende `ScoringWizard` (tre steg +maks, drevet av `stat_level`), hovedsiden erstattet med én kompakt +spillerliste (navn, akkumulert til-par-så-langt, en stor rund knapp som +åpner veiviseren). Krevde også å laste ALLE deltakeres hull med det +samme (ikke lenger lat lasting), slik at akkumulert score kan vises for +alle samtidig. **Reell driftsfeil funnet OG rettet samme dag:** den gamle +"Så langt i runden"-boksen (`ScoreSoFar`) ble ved en feil IKKE flyttet i +v2-bygget — lå fortsatt øverst, uendret, og fikk brukeren til å +rapportere "ingen synlig endring i det hele tatt" (bekreftet presist ved +å hente den faktisk kjørende JS-bunten i produksjon og søke i den — ikke +en cache-feil, en ekte plasseringsfeil). Rettet ved å flytte boksen til +under scoringsseksjonen. Full beslutnings-/begrunnelsesdetalj i +ARCHITECTURE_DECISIONS.md (ADR-033-oppdatering 2026-07-26), full +verifiserings-/utrullingsdetalj i CLAUDE.md sin statuslogg — ikke +duplisert her. + --- ## En tredje (informasjons-)farge til designet — 🧠 DRØFTET 2026-07-25, IKKE besluttet diff --git a/frontend/components/round-detail.tsx b/frontend/components/round-detail.tsx index 5beaf46..9b70567 100644 --- a/frontend/components/round-detail.tsx +++ b/frontend/components/round-detail.tsx @@ -294,6 +294,15 @@ export function RoundDetail({ roundId }: { roundId: string }) { // hull 1 uansett faktisk starthull). const [currentHole, setCurrentHole] = useState(null) const [showAddGuest, setShowAddGuest] = useState(false) + // Administrasjon (leaderboard-forhåndsvisning/Rediger/Fullfør/Slett/ + // spillerliste) og selve scoringen er nå to ADSKILTE faner (2026-07-26, + // etterspurt av bruker etter å ha sammenlignet direkte med en + // konkurrentapp -- forrige runde flyttet kun ScoreSoFar, men lot alt det + // ANDRE administrative innholdet stå igjen øverst på samme side som + // scoringen, som fortsatt gjorde siden "bråkete" før man i det hele tatt + // når frem til oppgaven). "Score" er default -- den ENESTE fanen de + // fleste trenger under selve spillingen. + const [pageTab, setPageTab] = useState<"score" | "manage">("score") const [completing, setCompleting] = useState(false) const [deleting, setDeleting] = useState(false) const [showEditRound, setShowEditRound] = useState(false) @@ -707,62 +716,122 @@ export function RoundDetail({ roundId }: { roundId: string }) {

)} - {/* Kompakt leaderboard-forhåndsvisning (2026-07-26) -- kun for - runder med flere enn én deltaker (komponenten returnerer selv - null ellers), lenke til hele leaderboardet. */} - - - {/* Rediger/Fullfør/Slett samlet ØVERST, bevisst tonet ned (samme - nøytrale trigger-stil som hverandre) -- disse lå tidligere som - store, fremtredende knapper RETT under "Neste hull"-navigasjonen - nederst i hull-panelet, som gjorde det altfor lett å trykke feil - ved et uhell mens man bare skulle bla mellom hull (rapportert av - bruker 2026-07-25). Ved å flytte dem hit må man aktivt scrolle OPP - forbi hull-registreringen for å nå dem i det hele tatt. */} -
-
- {isOwnerViewer && ( - + {/* To faner: "Score" (default, KUN hull-navigasjon + registrering -- + det eneste de fleste trenger midt i en runde) og "Spillere og + runde" (alt administrativt, samlet ett sted i stedet for å ligge + i veien øverst på scoringssiden). */} +
+ + > + Score + + - )} -
- - {showEditRound && isOwnerViewer && ( - setShowEditRound(false)} /> - )} + > + Spillere og runde +
+ {pageTab === "manage" && ( + <> + {/* Kompakt leaderboard-forhåndsvisning (2026-07-26) -- kun for + runder med flere enn én deltaker (komponenten returnerer selv + null ellers), lenke til hele leaderboardet. */} + + +
+
+ {isOwnerViewer && ( + + )} + + {!completed && ( + + )} + + {isOwnerViewer && ( + + )} +
+ + {showEditRound && isOwnerViewer && ( + setShowEditRound(false)} /> + )} +
+ + {/* Spillerliste (2026-07-26, erstatter tidligere horisontal + fane-rad) -- vertikal, ett kort per spiller. Utslag/HCP/ + statistikknivå + "Rediger" ligger nå INNI hvert kort (samme + kort er også selve velg-aktiv-spiller-trykkflaten), i stedet + for en egen rad under listen som kun gjaldt aktiv spiller. */} + setShowAddGuest((v) => !v)} + addOpen={showAddGuest} + readOnly={readOnly} + canManage={isOwnerViewer} + editingPlayerId={editingPlayerId} + onToggleEdit={(id) => setEditingPlayerId((prev) => (prev === id ? null : id))} + onPatchParticipant={patchParticipant} + onAddGuest={addGuest} + onAddSearched={addSearchedParticipant} + /> + + {/* Flyttet HIT (2026-07-26, samme redesign-runde) -- lå tidligere + på selve scoringssiden (først øverst, deretter lenger ned), + men er detaljert stats-innsyn (fairway/innspill/kølle- + fordeling for VALGT spiller), ikke selve registrerings- + oppgaven. Hver spillers rad i scoringslisten viser allerede + løpende til-par, så dette er nå rendyrket "se mer detaljer" + -- hører hjemme i administrasjons-fanen, ikke i veien for + score-registreringen. */} + {!completed && } + + )} + + {pageTab === "score" && ( + <> {/* Completed summary banner */} {completed && ( )} - {/* Spillerliste (2026-07-26, erstatter tidligere horisontal fane-rad) - -- vertikal, ett kort per spiller. Utslag/HCP/statistikknivå + - "Rediger" ligger nå INNI hvert kort (samme kort er også selve - velg-aktiv-spiller-trykkflaten), i stedet for en egen rad under - listen som kun gjaldt aktiv spiller. */} - setShowAddGuest((v) => !v)} - addOpen={showAddGuest} - readOnly={readOnly} - canManage={isOwnerViewer} - editingPlayerId={editingPlayerId} - onToggleEdit={(id) => setEditingPlayerId((prev) => (prev === id ? null : id))} - onPatchParticipant={patchParticipant} - onAddGuest={addGuest} - onAddSearched={addSearchedParticipant} - /> - - {/* Skjules når runden er fullført (2026-07-25, rapportert av bruker - som "veldig mye dobbel informasjon") -- CompletedBanner sin - "Se full rundestatistikk"-lenke dekker nå akkurat det samme, - langt grundigere. Fortsatt nyttig som live fremdriftsoversikt - mens runden pågår. */} - {!completed && } - {/* Hole navigation */} {holes.length === 0 ? (
@@ -926,7 +966,8 @@ export function RoundDetail({ roundId }: { roundId: string }) { )} )} - + + )}
{/* Skjermovertagende scoringsveiviser (2026-07-26) -- rendres HELT diff --git a/teecup-scorekort-og-entry-spec.md b/teecup-scorekort-og-entry-spec.md new file mode 100644 index 0000000..1067cab --- /dev/null +++ b/teecup-scorekort-og-entry-spec.md @@ -0,0 +1,167 @@ +# TeeCup — skjerm-spec: Scorekort + Score-entry + +> **Status: PRESKRIPTIV (mål, ikke beskrivelse).** Dette dokumentet er +> bevisst det motsatte av `DESIGN_SYSTEM.md`: der DS beskriver hva som ER +> i koden, beskriver dette hva skjermene SKAL være. Det gir repoet et tak +> å sikte mot, ikke bare et gulv å reprodusere. +> +> **Til Claude Code:** behandle token- og prop-navn her som *intensjon*. +> Der de avviker fra faktisk `globals.css` / `components/*.tsx`, vinner +> kildekoden — avstem, ikke gjett. Alt visuelt skal fortsatt gå gjennom +> tokenene og komponentene i `DESIGN_SYSTEM.md`; dette dokumentet endrer +> *komposisjon og tetthet*, ikke fargespråket. +> +> Grunnlaget er en gjennomgang av fem etablerte golf-apper (Golf GameBook, +> Golf Pad, Golfshot, Hole 19, 18Birdies). Reglene under er der de fem +> konvergerer — altså «slik løser kategorien det», ikke smak. + +--- + +## 0. Prinsipper (gjelder begge skjermene) + +Fire ting skiller de fem appene fra dagens TeeCup-skjerm. Alt under er +utledet av disse: + +1. **Tetthet framfor høflighet.** Ingen av de fem lar en flate stå halvtom. + Entry- og visningskontrollene fyller skjermen. Et enkelt kort øverst med + død luft under er ikke et mønster i kategorien — det er tegnet på en + nettside, ikke et verktøy. **Regel:** ingen score-flate skal ha mer enn + ~1 skjermhøyde tom plass under fold. Har runden få hull ført, vis alle + 18 (kommende hull grået ut), ikke whitespace. + +2. **Ett aksent-poeng på nøytralt underlag.** Alle fem bruker et nesten + monokromt underlag + ÉN aksent (blå/grønn/teal). TeeCup-score-skjermen + leser som grønt-på-grønt fordi sirkler, faner, hull-velger og knapper + deler samme grønn. **Regel:** `primary` (grønn) reserveres for aktiv + tilstand + under-par/positivt. `brand-orange` er kontrast-polen + (over par). Alt annet er nøytralt: `bg-card`, `bg-muted`, + `border-border`, `muted-foreground`. Hvis to naboelementer begge er + grønne uten at begge betyr «aktiv/positiv», er ett av dem feil. + +3. **Tall vises alltid med term og/eller til-par — aldri farge alene.** + Alle fem viser Eagle/Birdie/Par/Bogey og/eller +N/−N/E ved siden av + tallet. Dette er allerede din `§Golfscore-språket`; håndhev den. En bar + grønn sirkel med råtall (dagens skjerm) er det ene mønsteret ingen av de + fem bruker. + +4. **Verktøy, ikke nettside.** Hard, tydelig header; klare skiller mellom + rader/kolonner; kraftig `font-extrabold tabular-nums` på resultat-tall. + Myke, like avrundede kort overalt = generisk. + +--- + +## 1. Scorekort-skjerm (score-VISNING) + +**Mål:** erstatt ett-hull-kortet med et tett, scrollbart scorekort-grid. +Dette er skjermens største enkeltløft. + +### Layout +- Container: `mx-auto w-full max-w-xl` (per DS for scorekort-visninger). +- **Sticky header** (`sticky top-0 z-10 border-b border-border + bg-background/95 backdrop-blur`): banenavn, dato, evt. format + (Slag/Stableford). Ikke et stort kort — en tett topplinje. +- **Grid, ikke kort-stabel.** Én container + (`rounded-2xl border border-border overflow-hidden`) — DS sier eksplisitt + at sammenhengende lister skal være én container med `divide-y`, ikke + separate kort med mellomrom. + - **Spillere = rader.** Venstre kolonne er *sticky* (`sticky left-0`): + fullt navn (roster-kontekst → fullt navn, ikke fornavn) + HCP under, i + `text-sm muted-foreground`. Fast rad-høyde (`min-h-[Npx]` + `truncate`) + så rader med ulik navnelengde blir like høye (DS-mønsteret fra + 2026-07-26). + - **Hull = kolonner**, horisontalt scrollbare. Kolonnetopp: + hull-nr (`font-bold`), Par og evt. HCP-indeks under i + `text-sm muted-foreground`. Aktivt/valgt hull markeres med `bg-primary` + på kolonnetoppen (én grønn, ikke grønn overalt). + - **Celle** = score for (spiller × hull): brutto som hovedtall + (`font-extrabold tabular-nums`) med net som liten senket subscript. + Score-merket følger `§Golfscore-språket` UBRYTELIG: + - under par → sirkel (`rounded-full`) + - over par → firkant (`rounded-[4px]`) + - E → ren tekst, ingen ramme + - eagle+/dobbel bogey+/leder → *fylt* (`bg-primary`/`bg-brand-orange`), + samme form, kun fyllingen endres. Vanlig = `border-2` + `/10`-tint. + - **Sum-kolonner:** Ut / Inn / Total i en høyre *sticky* kolonne + (`sticky right-0`), `tabular-nums font-extrabold`, til-par som E/+N/−N. +- **Fyll flaten:** vis alle 18 hull-kolonner alltid. Uspilte hull = + tomme celler på nøytralt underlag, ikke utelatt. + +### Farge +Underlaget er nøytralt (`bg-card`). Grønn (`primary`) opptrer kun i: +aktivt hulls kolonnetopp, under-par-merker, og lederens sum. Over-par- +merker bruker firkant-form + `brand-orange`-tint. Ingenting annet er grønt. + +### Interaksjon +Tapp en celle (eller en rad-header) → åpne score-entry-overlegget (§2) for +det hullet. Overgang, ikke ny side. + +--- + +## 2. Score-entry-skjerm (per hull) + +**Mål:** tett, fokusert entry som fyller skjermen — komponentene finnes +allerede (`NumberPicker`, `Stepper`, `ChoiceRow`, `DirectionCross`); dette +er ren komposisjon. + +### Struktur — DS' fullskjerm-overlegg +`fixed inset-0 z-50 flex flex-col bg-background`, tredelt: + +**`header` (shrink-0)** +- Hull · Par · HCP-indeks, og «til par så langt» (`tabular-nums`, E/+N/−N). +- Lukk-knapp, `size-11`, ikon + `aria-label`. +- Aktiv spillers navn + HCP (roster-kontekst → fullt navn). + +**`main` (`flex-1 overflow-y-auto`)** — i denne rekkefølgen, alt synlig med +minst mulig scroll: +1. **Score** — `NumberPicker` (eksisterende): 3-kol grid, `h-16`, valgt = + `bg-primary`, term-caption relativt til hullets par + (Albatross/Eagle/Birdie/Par/Bogey/Dobbel bogey). «10+»-utvidelse + beholdes. +2. **Putter** — `Stepper` (+/−, `size-11`). +3. **Fairway** — `ChoiceRow` eller `DirectionCross`: Bom venstre / Treff / + Bom høyre. Alltid tekstlabel (DS: aldri ikon alene). +4. **Green (GIR)** — `DirectionCross` «Bommet green? Hvor» (Lang/Kort/ + Venstre/Høyre + treff i sentrum), eller `ChoiceRow` hvis enklere. +5. **Bunkerslag / Straffeslag / Chip** — `Stepper` hver. +6. **Sand save / Up & down** — toggles (pille, `size-11`-treffflate). + +**`footer` (shrink-0)** — handling-rad: +- Sekundær «Forrige» (`bg-muted`/nøytral) + primær «Neste»/«Ferdig» + (`bg-primary`), begge full bredde-andel, `min-h-12`. + +### Multiplayer +Behold **per-spiller fullskjerm-fokus** (GameBook-mønsteret) framfor inline- +rader — det matcher størrelsen på `NumberPicker` og tilgjengelighetsgulvet +bedre enn tette inline-steppere. «Neste» går til neste spiller på samme +hull, deretter neste hull. (18Birdies' inline-alle-spillere er et alternativ +å vurdere senere, men krever mindre trykkflater — sekundært valg.) + +### Tetthet +Score + Putter + Fairway skal være synlige uten scroll på en vanlig telefon. +Resten scroller i `main`. Ingen halvtom skjerm. + +--- + +## 3. Compliance-pass (kjør FØR nybygg) + +Dette er avvik mellom `DESIGN_SYSTEM.md` og faktisk kode som allerede er +synlige i skjermbildene. Fiks dem først — de gir størst effekt per innsats: + +- [ ] **`ScoreMark` brukes på ALLE score-visninger.** Auditér + `round-scorecard.tsx` og score-visnings-skjermen: dagens grønne sirkler + med råtall bryter `§Golfscore-språket`. Bogey og dobbel bogey skal IKKE se + like ut, og over-par skal være firkant. +- [ ] **Badge-dublett «Deg Deg».** `Badge`: `default` (fylt) = «Deg», + `outline` = «Eier». Fjern at navnet «Deg» får en «Deg»-badge oppå seg. +- [ ] **`tabular-nums`** på alle score-/HCP-/sum-tall. +- [ ] **44px trykkgulv** (`min-h-11`) på alt trykkbart; `size-11` på + ikon-only. +- [ ] **Farge aldri alene** — hver farge-bærende tilstand har også form + eller tekst. + +--- + +## 4. Referanser +Legg GameBook- + de fire andre skjermdumpene i `design/references/` og pek +Claude Code på dem: «scorekort-skjermen skal matche tetthet og hierarki i +disse». Visuell referanse forankrer bedre enn prosa alene.