diff --git a/CLAUDE.md b/CLAUDE.md index 6473d99..c50fab1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4936,6 +4936,159 @@ Ferdig og verifisert: nøkkel, `GET /rounds/{ukjent-id}/flight-group` ga korrekt `401 NOT_AUTHENTICATED` (ikke en rå 404 — bekrefter ruten faktisk når FastAPI), `teeoff.no` upåvirket. +- **Designinstruksen erstattet/formalisert + seks brukerpunkter, ALLE + BYGGET OG LIVE (2026-07-29):** `alternativ designinstruks.md` (fra + dagen før) erstattet med en revidert versjon fra bruker (allerede + forankret i det eksisterende token-systemet, ikke lenger i konflikt med + oransje/hardkodet-slate-spørsmålene) — INNHOLDET er nå slått sammen inn + i `DESIGN_SYSTEM.md` som den gjeldende fasiten (8-punkts rutenett, + `shadow-md shadow-black/8`, `font-normal` OK ved `text-base`+god + kontrast, `:active`-tilstander, safe-area, 16px input, `transition-all + duration-200`). `alternativ designinstruks.md` selv beholdt kun som + arkiv/historikk. **Konsistens-sveip:** det gamle svake skygge-mønsteret + (`shadow-sm shadow-black/5`) erstattet mekanisk på tvers av 20 filer + (`sed`, verifisert 0 gjenværende treff) — IKKE en fullstendig + strukturell gjennomgang av alle 27 skjermer, kun dette ene, trygge, + mekaniske mønsteret. + **Hurtighandlinger:** dashbordets tre knapper er nå `grid-cols-3` også + på mobil (var `grid-cols-1 sm:grid-cols-3`, tok unødvendig mye plass) — + mindre ikon/tekst/høyde for å fortsatt være lesbare i smalere kolonner. + **Par/stroke-index manglet på rundeleaderboardet** (`/my-rounds/{id}/ + leaderboard`) — ny `HoleReferenceStrip` viser Hull/Par/Hcp ÉN gang + (banedata, identisk for alle deltakere) rett over den rangerte listen. + **Venner: kategorisering nå OBLIGATORISK fra vennskapet inngås** + (presisert av bruker: "det skal ikke finnes ukategoriserte venner") — + `POST /friends` og `POST /friends/{id}/accept` krever begge minst én + kategori (`Field(min_length=1)`), satt AV BEGGE PARTER på hvert sitt + naturlige tidspunkt (avsender ved sending, mottaker ved aksept) — ikke + en frivillig senere handling. `PUT .../categories` nekter også å sette + et tomt sett. Ny delt `CategoryPicker`-komponent (friends.tsx, alle + forhåndsvalgt — "man må heller velge bort", presisert av bruker) brukt + tre steder (send/godta/rediger), nekter å fjerne SISTE avkrysning. + Selv-helbredende sikkerhetsnett for venner fra FØR denne regelen: ny + advarselsbanner øverst i `/my-friends` ("N venner mangler kategori"), + tvinger vedkommendes kategori-panel åpent til det er løst — bekreftet + reelt nødvendig i produksjon (Erol hadde aldri kategorisert Tore + Morell, sin faste matchspill-motstander — vil nå bli fanget opp og + tvunget løst neste gang `/my-friends` besøkes). + **Synlighetsregelen for "Venner"-runder endret fra "minst én treffende + kategori" til "ALLE vennens kategorier må være i det synlige settet"** + (presisert av bruker: "'ikke vise' overstyrer 'vise'") — en venn med + ÉN ikke-valgt kategori ekskluderes nå helt, selv om en annen av + kategoriene deres er valgt. En venn med NULL kategorier vises ALDRI + (avklart eksplisitt med bruker via spørsmål — skal i praksis aldri + forekomme lenger pga. regelen over, kun en igjenværende tilstand fra + FØR den ble håndhevet). `_can_view_round`/`_friends_who_can_see_round` + (rounds.py) omskrevet til denne AND-semantikken (fra tidligere OR). + `new-round.tsx` sin kategori-multiselect for "Venner"-synlighet starter + nå med ALLE forhåndsvalgt (samme "velg heller bort"-prinsipp). + **"Match leaderboard" — undersøkt grundig, IKKE en backend-bug:** + bekreftet direkte mot ekte `teecup_db` (kun lesing) at brukerens egen + Tjøme-matchrunde (`d857289c-...`) hadde begge sider korrekt satt opp + med beregnet `playing_handicap` — `format-result`-endepunktet ville + altså allerede regnet ut riktig "1 UP (A)"-status. Det reelle hullet + var at `FormatResultPanel` (matchstatus/skins-tavle, med hull-for-hull + vinner/AS-visning) KUN lå under "Spillere og runde"-fanen, usynlig fra + "Score"-fanen der scoring naturlig skjer. Fikset ved å vise samme + komponent (ikke duplisert logikk) øverst på BEGGE faner. + **Scratch-verifisert grundig** (isolert rolle+MinIO+engangs API- + container, samme mønster som hele prosjektet): full kategori-håndheving + (422 uten kategorier ved både send/godta/rediger, 200 med), presis + eksklusjon-overstyrer-inklusjon-test (venn med golf_friends+close_family + ekskludert når kun golf_friends er synlig, inkludert når begge er + synlige — bekreftet mot BÅDE rå SQL og de faktiske API-endepunktene), + match-runde med sider satt opp fra bunnen ga korrekt "1 UP (A)". + Deretter en FULL, ekte nettleser-gjennomgang (Chrome DevTools, mobil + 390×844): 3-kolonners hurtighandlinger, matchstatus-banner synlig på + Score-fanen, Hull/Par/Hcp-referanserad på leaderboardet, hele venne- + søk→kategorivelger(alle forhåndsvalgt, avkrysning fungerer)→send- + flyten, og selv-helbredende-banner-flyten (simulerte en gammel + ukategorisert venn direkte i databasen, bekreftet banneret dukket opp + OG forsvant igjen etter kategorisering via UI-et). + **Rullet ut live 2026-07-29**, ingen migrasjon (kun eksisterende felt/ + logikk endret), `docker compose up -d --build teecup_api + teecup_frontend`. Begge containere boot-et rent, `/health`/`/dashboard` + → 200, anonymt `POST /friends` ga korrekt `401` (ikke en rå 404 — + bekrefter ruten når FastAPI), `teeoff.no` upåvirket. + +- **Match-scorekort redesignet på tvers av appen (frittstående runde + + turnering), BYGGET, GRUNDIG SCRATCH-/BROWSERVERIFISERT OG LIVE + (2026-07-29):** brukeren delte et referansebilde av en konkurrentapp sitt + 1v1-matchscorekort (horisontalt rutenett, farget etter hvem som vant + hvert hull, løpende "Stilling"-rad AS/X UP/X&Y, navn+HCP-banner) og ba om + det tilsvarende for ALLE match-scorekort i TeeCup, uten direkte plagiat. + **Bevisst IKKE en kopi:** beholdt appens egne, allerede etablerte + fargespråk i stedet for referansens røde/blå -- frittstående runder bruker + primær/oransje (samme "side A/side B"-konvensjon som `ResultChip`/ + `FormatResultPanel` fra tidligere runder), turnering-matcher bruker lagets + faktiske `team.color` (samme konvensjon som resten av turnering-UI-et). + Droppet bevisst referansens "Matchplay NET/Stableford NET"-fane (ga ikke + entydig mening for delt-ball-formater) og "Lik/Kommentar til + spillfeeden/Spillere"-ikonraden (en helt ny sosial funksjon, ikke en + scorekort-redesign -- utenfor denne rundens omfang). + **Kjernemekanisme, ny og delt idé (separat implementert i begge filer, + ikke faktisk delt kode siden filene allerede har egne lokale typer per + etablert konvensjon):** `computeRunning()` speiler + `handicap_engine.py` sin `compute_match_state()`/`describe()` presist, + men regnet ETT PREFIKS om gangen client-side (backend cacher i dag kun + SLUTT-tilstanden) -- gir en løpende AS/X UP/dormie/X&Y-status per hull i + stedet for kun et sluttresultat. + **`round-scorecard.tsx`:** `MatchProgressTable` (vertikal hull-for-hull- + **`round-scorecard.tsx`:** den generiske `ScoreBlock`-tabellen erstattet med `MatchScorecardGrid` -- horisontalt rutenett (Hull/ + liste) erstattet med `MatchScorecardGrid` -- horisontalt rutenett (Hull/ + Par/en rad per spiller-ELLER-side/Stilling), identitetsbanner (navn+HCP, + farget dot, løpende sentral status + "Ferdig"-merke når avgjort). Fargen + på scorecellen følger HVEM SOM VANT hullet (fylt sirkel), ikke over/under + par -- egen semantikk fra `ScoreMark` med vilje, siden dette er en match, + ikke en individuell runde. `ApiParticipant` fikk `playing_handicap` + (allerede eksponert av backend, kun en frontend-typeutvidelse). + **`session-scorecard.tsx`:** `HoleSummaryTable`/`OutcomeBadge`/ + `grossPairLabel` erstattet med `TournamentMatchGrid` (samme struktur, + brukt av BÅDE den pågående "Vis full oversikt"-seksjonen og + `DecidedView`s "Hull for hull" -- én komponent, ikke to). `Unit`-typen + fikk `playingHandicap`. + **Ekte, nødvendig backend-endring:** `match_participant.playing_handicap` + ble beregnet og lagret siden ADR-039, men var ALDRI eksponert i + `MatchParticipantOut` (matches.py) -- lagt til i modellen og i begge + SELECT-spørringene som populerer den (`fetch_matches` og + `add_participant`). **Reelt funn, fikset FØR utrulling:** + `add_participant` sin `row` hentes FØR + `compute_and_store_side_handicaps()` kjører -- en naiv + `MatchParticipantOut(**dict(row))` ville derfor alltid returnert + `playing_handicap: null` for en NYLIG lagt til deltaker, selv når verdien + faktisk ble beregnet et øyeblikk senere i samme kall. Fikset med et + eksplisitt re-oppslag av `playing_handicap` RETT FØR responsen bygges. + **Scratch-verifisert grundig** (isolert `teecup_app_scratch`-rolle + + isolert scratch-MinIO + engangs API-container, alle 37 migrasjoner kjørt + friskt, samme mønster som resten av prosjektet): en full match bygget fra + bunnen for BEGGE surfacene (en frittstående match-runde med to sider, OG + en full turnering-scaffold -- org/bane-import/tee/tournament/to lag/ + roster/singel-økt/match/deltakere -- bygget fra API-et for FØRSTE gang i + et testskript denne uken, siden frittstående runder aldri har trengt det + apparatet før). 8-9 hull registrert med et bevisst blandet vinn/tap/delt- + mønster, bekreftet at `format-result`/`scorecard` sine per-hull + resultater matchet forventet handicap-justert utfall. + **Deretter en FULL, ekte nettleser-gjennomgang** (Chrome DevTools MCP): + et REELT, tidkrevende miljøproblem ble diagnostisert og løst underveis -- + React-komponentenes egne `useEffect`-datahentinger kjørte ALDRI (evig + lastespinner på HVER side, ikke bare de nye) når dev-serveren ble besøkt + via `127.0.0.1:3100` i stedet for `localhost:3100`; Next.js 16 sin + `allowedDevOrigins`-beskyttelse blokkerer stille dev-ressurser (HMR- + websocket m.m.) for det som oppfattes som et fremmed opphav, og dette + fikk hele klient-hydreringen til å henge uten en eneste konsollfeil. + Løst ved å konsekvent bruke `localhost` i stedet for `127.0.0.1` -- + ren miljø-lærdom for fremtidige scratch-frontend-økter, ikke en bug i + selve appen. Etter fiksen: bekreftet BEGGE match-scorekortene visuelt, + i BÅDE lys og mørk modus, i BÅDE pågående (løpende "2 UP"/farget + Stilling-rad) og avgjort tilstand ("8 UP"/"Ferdig"-merke, cachet + banner), inkl. et helt nytt 2FA-oppsett fullført på fersk konto via en + engangs e-post-2FA-kode lest fra dev-loggen (org-eier/admin krever 2FA, + ADR-021) for å nå frem til turnering-siden. Ingen konsollfeil i noen av + rundene. + **Rullet ut live 2026-07-29**, bruker bekreftet eksplisitt: ingen + migrasjon, `docker compose up -d --build teecup_api teecup_frontend`. + Begge containere boot-et rent, `/health`/`/dashboard` → 200, `teeoff.no` + → 200 upåvirket. Neste steg: 0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet diff --git a/DESIGN_SYSTEM.md b/DESIGN_SYSTEM.md index 0ad002d..fe1dd46 100644 --- a/DESIGN_SYSTEM.md +++ b/DESIGN_SYSTEM.md @@ -14,6 +14,14 @@ > trykkflater, ikke ikon-only uten tekstlabel for viktige handlinger, ikke > avhengig av finmotorikk/skarpt syn. Enhver visuell beslutning under > veies mot dette først. +> +> **2026-07-29:** dette dokumentet ble revidert og formalisert basert på +> "alternativ designinstruks.md" (brukerens eget forslag, forsøkt først på +> dashbordet, deretter godkjent som ny fasit). Innholdet under ER derfor nå +> den gjeldende regelen — men er ikke nødvendigvis rullet ut på ALLE +> skjermer ennå (se `CLAUDE.md`-status for hvor langt konsistens-sjekken har +> kommet). Rett opportunistisk opp eldre skjermer mot dette dokumentet når +> de likevel røres, samme mønster som tilgjengelighetsregelen over. ## Merkevare-opprinnelse @@ -34,8 +42,11 @@ se FEATURE_BACKLOG.md. Anbefalingen der var å gjenbruke `--chart-3` 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. +som `bg-primary`, `text-muted-foreground` osv.) — aldri rå hex/oklch, og +ALDRI hardkodede Tailwind-fargeklasser som `slate-500`/`gray-100` i ny +kode. Dette er ikke bare en stilregel: hardkodede farger bryter den +automatiske mørk/lys-tema-logikken (se "Lyst og mørkt tema" under), som +ellers virker helt av seg selv så lenge token-klassene brukes. | Token | Lys modus | Rolle | |---|---|---| @@ -80,10 +91,14 @@ er et direkte utslag av tilgjengelighetsregelen øverst. - `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. +- **Vekt (revidert 2026-07-29):** ved `text-base` (16px) og god kontrast + (`text-foreground`) er `font-normal` helt akseptabelt og FORETRUKKET for + brødtekst — unngår visuell støy av at alt roper likt. Hierarki skapes + primært med STØRRELSE og FARGE (kontrast), ikke bare vekt. `text-sm` + eller mindre (bildetekst/metadata) bør derimot ofte gis `font-medium` + for å kompensere for den mindre størrelsen — aldri `font-normal` under + `text-base`. Overskrifter/tall/primærhandlinger beholder + `font-bold`/`font-extrabold` som før. - **`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 @@ -93,6 +108,17 @@ er et direkte utslag av tilgjengelighetsregelen øverst. ## Avstand, hjørner og lag +- **8-punkts rutenett (2026-07-29):** marger, padding og `gap` følger + 8-gangen (`p-2`=8px, `p-4`=16px, `p-6`=24px, `p-8`=32px) for + forutsigbar rytme. Halv-steg (`gap-1.5`, `py-2.5` osv.) unngås i nytt + arbeid — brukes kun der et etablert mønster (f.eks. shadcn sine egne + primitiver) allerede definerer det. +- **Kort får tydeligere løft (2026-07-29):** `shadow-md shadow-black/8` + (opp fra `shadow-sm shadow-black/5`) på kort/paneler i lyst tema — mer + synlig separasjon fra bakgrunnen. Se `round-card.tsx`/ + `tournament-card.tsx`/`install-prompt.tsx`/`dashboard.tsx` for det + gjeldende mønsteret; eldre skjermer som fortsatt har `shadow-sm + shadow-black/5` bør rettes opp neste gang de røres. - **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, @@ -121,6 +147,43 @@ er et direkte utslag av tilgjengelighetsregelen øverst. - Gap mellom relaterte kontroller: `gap-2`/`gap-2.5`/`gap-3` normalt, `gap-4`/`gap-6` mellom distinkte seksjoner. +## Interaksjon (2026-07-29) + +- Alle interaktive elementer skal ha en definert `:hover`- OG + `:active`-tilstand — `:active` (f.eks. `active:scale-[0.98]` på kort/ + knapper, `active:opacity-70` på lenker/ikon-knapper) gir umiddelbar, + taktil bekreftelse på at trykket faktisk ble registrert. shadcn sin + `Button`-primitiv har allerede `active:translate-y-px` innebygd; egne + håndbygde trykkflater (kort, ``-baserte rader) må legge det til + eksplisitt siden de ikke arver primitivens klasser. +- `disabled` bruker `disabled:opacity-50 disabled:pointer-events-none` + (allerede standard i `Input`/`Button`). +- Bevegelse: `transition-all duration-200 ease-in-out` for state- + endringer på knapper/kort — kjapt og "snappy", ikke en treg nettside- + følelse. Ingen animasjon/fade lenger enn 300ms. + +## Skjemaer og inndata (mobil-først) + +- Tekst i ``/`