Ja, den gir mye mening — og jeg sjekket to av påstandene direkte mot koden i stedet for å ta dem på tro:
Bekreftet, reelt: "Deg Deg"-duplikaten er ekte. playerLabel() erstatter selve NAVNET med teksten "Deg" for din egen rad, OG kortet legger på en egen "Deg"-badge ved siden av — du ser den bokstavelig to ganger, akkurat som i skjermbildet du delte tidligere. Samme sted i koden (spillerkortet OG den nye scoringslisten). Også bekreftet: score-sirkelen i den nye listen er rounded-full + grønn UANSETT om resultatet er under, over eller på par — den følger ikke golfscore-språket i det hele tatt (sirkel skal bety under par, firkant over par). Det forklarer noe jeg ikke helt klarte å sette fingeren på selv: fargen grønn er kraftig overbrukt — aktiv hull-fane, valgt tall, denne sirkelen, "Deg"-badgen, rangeringsmerket for lederen, primærknapper — alt er grønt, uansett om det faktisk betyr "aktiv/positivt" eller bare er standard-stilen. Det er en skarpere diagnose enn noe jeg selv har formulert denne økten, og den forklarer trolig mye av "fortsatt rotete"-følelsen din utover selve tettheten. Resten (tetthetsprinsippet, "verktøy ikke nettside", scorekort-som-grid) er en sammenhengende, godt begrunnet retning — men det er en STØRRE strukturell endring (bytte fra dagens "faner + kompakt liste" til et fullt scorekort-grid som primærvisning, med veiviseren åpnet fra en hvilken som helst celle). Jeg foreslår å dele dette i to: Compliance-passet (seksjon 3) nå — konkrete, allerede bekreftede feil, lav risiko, rask gevinst. Grid-redesignet som egen, større runde — fortjener et bevisst ja fra deg først, siden det er en helt annen informasjonsarkitektur enn det jeg bygde i går.
This commit is contained in:
parent
9ec6f1c9ed
commit
6bfd986964
6 changed files with 704 additions and 98 deletions
|
|
@ -2817,6 +2817,97 @@ ekte https, `teeoff.no` upåvirket.
|
||||||
boot-et rent, `/health`/`/dashboard`/`/my-rounds`/`/account` → 200 over
|
boot-et rent, `/health`/`/dashboard`/`/my-rounds`/`/account` → 200 over
|
||||||
ekte https, `teeoff.no` upåvirket.
|
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 `<main>`), 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
|
## ADR-035: Dashbord-redesign — organisasjon blir implisitt
|
||||||
|
|
|
||||||
30
CLAUDE.md
30
CLAUDE.md
|
|
@ -3465,6 +3465,36 @@ Ferdig og verifisert:
|
||||||
ingen migrasjon, `docker compose up -d --build teecup_api
|
ingen migrasjon, `docker compose up -d --build teecup_api
|
||||||
teecup_frontend`. Begge containere boot-et rent, `/health`/`/dashboard`
|
teecup_frontend`. Begge containere boot-et rent, `/health`/`/dashboard`
|
||||||
→ 200, `teeoff.no` upåvirket.
|
→ 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:
|
Neste steg:
|
||||||
0a. **Spillerliste-redesign — LIVE, men ikke nettleser-testet
|
0a. **Spillerliste-redesign — LIVE, men ikke nettleser-testet
|
||||||
|
|
|
||||||
233
DESIGN_SYSTEM.md
Normal file
233
DESIGN_SYSTEM.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -738,8 +738,11 @@ tillatelse kreves:**
|
||||||
> universelt gjenkjent symbol, MEN skal ha en beskrivende
|
> universelt gjenkjent symbol, MEN skal ha en beskrivende
|
||||||
> `aria-label` som inkluderer antall uleste).
|
> `aria-label` som inkluderer antall uleste).
|
||||||
|
|
||||||
**Status: In-app varslingssenter BYGGET OG SCRATCH-VERIFISERT 2026-07-26,
|
**Status: In-app varslingssenter BYGGET, SCRATCH-VERIFISERT OG RULLET UT
|
||||||
IKKE ENNÅ RULLET UT.** V0 kjørte prompten (zip 20), backend bygget for å
|
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`,
|
matche eksakt: migrasjon `026_notifications.sql` (tabell `notification`,
|
||||||
`plain_connection()`-mønster, ingen RLS — samme som personlig profil/
|
`plain_connection()`-mønster, ingen RLS — samme som personlig profil/
|
||||||
runder/venner), ny `app/routers/notifications.py` (`create_notification()`
|
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
|
**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.
|
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å
|
Brukeren ba eksplisitt om å få leaderboardet for frittstående runder på
|
||||||
plass (kun runder, ikke turneringer — turnering-spørsmålet over fortsatt
|
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
|
> store trykkflater (min. 44px), aldri kun farge for å formidle
|
||||||
> informasjon (rangering/over-under par), lesbar uten briller.
|
> informasjon (rangering/over-under par), lesbar uten briller.
|
||||||
|
|
||||||
**Ikke avgjort ennå, avklares når zip-en er klar til integrering:**
|
**Rettet i selve prompten 2026-07-26, FØR den ble kjørt i v0.app:** den
|
||||||
nøyaktig plassering av lenke til full leaderboard-side (trolig
|
opprinnelige "Deg"-merke-instruksen antok feilaktig at kun eieren ser sin
|
||||||
`round-detail.tsx`, som en lenke/knapp — ikke inline, samme "unngå for
|
egen runde -- ADR-036 fase 3 (bygget samme dag) gjør nå at en lenket
|
||||||
mye stablet oppå hverandre"-lærdom som tidligere runder denne uken),
|
medspiller også kan se leaderboardet. Byttet til to uavhengige merker
|
||||||
og ruten (foreslått `/my-rounds/[id]/leaderboard`, samme mønster som
|
("Eier" fra `is_owner`, "Deg" fra en egen `participant_id`-prop
|
||||||
`/my-rounds/[id]/stats`/`/scorecard`).
|
komponenten mottar utenfra) -- se prompten over.
|
||||||
|
|
||||||
**Rettet i selve prompten 2026-07-26 (samme dag, fanget FØR den ble kjørt i
|
**Oppdatering, samme dag: HÅNDKODET i stedet for kjørt i v0.app.**
|
||||||
v0.app):** den opprinnelige "Deg"-merke-instruksen antok feilaktig at kun
|
Brukeren gikk tom for V0-credits (samme situasjon som spillerliste-
|
||||||
eieren ser sin egen runde -- ADR-036 fase 3 (bygget samme dag, se over) gjør
|
redesignet rett over) og ba meg bygge direkte etter nøyaktig samme
|
||||||
nå at en lenket medspiller også kan se leaderboardet. Byttet til to
|
designspesifikasjon som prompten over. Ny `components/round-leaderboard.tsx`
|
||||||
uavhengige merker ("Eier" fra `is_owner`, "Deg" fra en egen
|
+ rute `/my-rounds/[id]/leaderboard`, pluss en `RoundLeaderboardMini`
|
||||||
`participant_id`-prop komponenten mottar utenfra) -- se oppdatert prompt
|
(topp 3 + "og N til", lenket fra selve rundens detaljside) og en ny
|
||||||
over. Fortsatt ikke kjørt i v0.app.
|
"Se leaderboard"-lenke på hvert rundekort i `/my-rounds`-listen (krevde å
|
||||||
|
gjøre om `round-card.tsx` sin ytre `<Link>` til en `<div>` med to separate
|
||||||
|
lenker -- unngår en nestet `<a>`). 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)
|
interaksjonstest (brutto/netto-veksling, faktisk visuell høyde/form-språk)
|
||||||
er utført.
|
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
|
## En tredje (informasjons-)farge til designet — 🧠 DRØFTET 2026-07-25, IKKE besluttet
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,15 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
||||||
// hull 1 uansett faktisk starthull).
|
// hull 1 uansett faktisk starthull).
|
||||||
const [currentHole, setCurrentHole] = useState<number | null>(null)
|
const [currentHole, setCurrentHole] = useState<number | null>(null)
|
||||||
const [showAddGuest, setShowAddGuest] = useState(false)
|
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 [completing, setCompleting] = useState(false)
|
||||||
const [deleting, setDeleting] = useState(false)
|
const [deleting, setDeleting] = useState(false)
|
||||||
const [showEditRound, setShowEditRound] = useState(false)
|
const [showEditRound, setShowEditRound] = useState(false)
|
||||||
|
|
@ -707,62 +716,122 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Kompakt leaderboard-forhåndsvisning (2026-07-26) -- kun for
|
{/* To faner: "Score" (default, KUN hull-navigasjon + registrering --
|
||||||
runder med flere enn én deltaker (komponenten returnerer selv
|
det eneste de fleste trenger midt i en runde) og "Spillere og
|
||||||
null ellers), lenke til hele leaderboardet. */}
|
runde" (alt administrativt, samlet ett sted i stedet for å ligge
|
||||||
<RoundLeaderboardMini roundId={roundId} />
|
i veien øverst på scoringssiden). */}
|
||||||
|
<div className="mb-5 inline-flex gap-1 rounded-full bg-muted p-1">
|
||||||
{/* Rediger/Fullfør/Slett samlet ØVERST, bevisst tonet ned (samme
|
<button
|
||||||
nøytrale trigger-stil som hverandre) -- disse lå tidligere som
|
type="button"
|
||||||
store, fremtredende knapper RETT under "Neste hull"-navigasjonen
|
onClick={() => setPageTab("score")}
|
||||||
nederst i hull-panelet, som gjorde det altfor lett å trykke feil
|
aria-pressed={pageTab === "score"}
|
||||||
ved et uhell mens man bare skulle bla mellom hull (rapportert av
|
className={cn(
|
||||||
bruker 2026-07-25). Ved å flytte dem hit må man aktivt scrolle OPP
|
"min-h-10 rounded-full px-4 text-sm font-bold transition-colors",
|
||||||
forbi hull-registreringen for å nå dem i det hele tatt. */}
|
pageTab === "score" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground",
|
||||||
<div className="mb-5 flex flex-col gap-3">
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{isOwnerViewer && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowEditRound((v) => !v)}
|
|
||||||
aria-expanded={showEditRound}
|
|
||||||
className="inline-flex min-h-11 items-center gap-1.5 rounded-xl border border-dashed border-border bg-card px-3 text-sm font-semibold text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground"
|
|
||||||
>
|
|
||||||
<Settings2 aria-hidden="true" className="size-4" />
|
|
||||||
Rediger runde
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
{!completed && (
|
Score
|
||||||
<button
|
</button>
|
||||||
type="button"
|
<button
|
||||||
disabled={completing}
|
type="button"
|
||||||
onClick={finishRound}
|
onClick={() => setPageTab("manage")}
|
||||||
className="inline-flex min-h-11 items-center gap-1.5 rounded-xl border border-dashed border-border bg-card px-3 text-sm font-semibold text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground disabled:opacity-60"
|
aria-pressed={pageTab === "manage"}
|
||||||
>
|
className={cn(
|
||||||
<Check aria-hidden="true" className="size-4" />
|
"min-h-10 rounded-full px-4 text-sm font-bold transition-colors",
|
||||||
{completing ? "Fullfører…" : "Fullfør runde"}
|
pageTab === "manage" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground",
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
{isOwnerViewer && (
|
Spillere og runde
|
||||||
<button
|
</button>
|
||||||
type="button"
|
|
||||||
disabled={deleting}
|
|
||||||
onClick={deleteRound}
|
|
||||||
className="inline-flex min-h-11 items-center gap-1.5 rounded-xl border border-dashed border-border bg-card px-3 text-sm font-semibold text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive disabled:opacity-60"
|
|
||||||
>
|
|
||||||
<Trash2 aria-hidden="true" className="size-4" />
|
|
||||||
{deleting ? "Sletter…" : "Slett runde"}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showEditRound && isOwnerViewer && (
|
|
||||||
<EditRoundPanel round={round} onPatch={patchRound} onClose={() => setShowEditRound(false)} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{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. */}
|
||||||
|
<RoundLeaderboardMini roundId={roundId} />
|
||||||
|
|
||||||
|
<div className="mb-5 flex flex-col gap-3">
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{isOwnerViewer && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowEditRound((v) => !v)}
|
||||||
|
aria-expanded={showEditRound}
|
||||||
|
className="inline-flex min-h-11 items-center gap-1.5 rounded-xl border border-dashed border-border bg-card px-3 text-sm font-semibold text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground"
|
||||||
|
>
|
||||||
|
<Settings2 aria-hidden="true" className="size-4" />
|
||||||
|
Rediger runde
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!completed && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={completing}
|
||||||
|
onClick={finishRound}
|
||||||
|
className="inline-flex min-h-11 items-center gap-1.5 rounded-xl border border-dashed border-border bg-card px-3 text-sm font-semibold text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground disabled:opacity-60"
|
||||||
|
>
|
||||||
|
<Check aria-hidden="true" className="size-4" />
|
||||||
|
{completing ? "Fullfører…" : "Fullfør runde"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isOwnerViewer && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={deleting}
|
||||||
|
onClick={deleteRound}
|
||||||
|
className="inline-flex min-h-11 items-center gap-1.5 rounded-xl border border-dashed border-border bg-card px-3 text-sm font-semibold text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive disabled:opacity-60"
|
||||||
|
>
|
||||||
|
<Trash2 aria-hidden="true" className="size-4" />
|
||||||
|
{deleting ? "Sletter…" : "Slett runde"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showEditRound && isOwnerViewer && (
|
||||||
|
<EditRoundPanel round={round} onPatch={patchRound} onClose={() => setShowEditRound(false)} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 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. */}
|
||||||
|
<PlayerList
|
||||||
|
roundId={roundId}
|
||||||
|
players={players}
|
||||||
|
activePlayerId={activePlayer.id}
|
||||||
|
onSelect={setActivePlayerId}
|
||||||
|
onRemove={removeGuest}
|
||||||
|
onAdd={() => 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 && <ScoreSoFar holes={apiHoles} order={holeOrder} />}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{pageTab === "score" && (
|
||||||
|
<>
|
||||||
{/* Completed summary banner */}
|
{/* Completed summary banner */}
|
||||||
{completed && (
|
{completed && (
|
||||||
<CompletedBanner
|
<CompletedBanner
|
||||||
|
|
@ -772,35 +841,6 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 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. */}
|
|
||||||
<PlayerList
|
|
||||||
roundId={roundId}
|
|
||||||
players={players}
|
|
||||||
activePlayerId={activePlayer.id}
|
|
||||||
onSelect={setActivePlayerId}
|
|
||||||
onRemove={removeGuest}
|
|
||||||
onAdd={() => 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 && <ScoreSoFar holes={apiHoles} order={holeOrder} />}
|
|
||||||
|
|
||||||
{/* Hole navigation */}
|
{/* Hole navigation */}
|
||||||
{holes.length === 0 ? (
|
{holes.length === 0 ? (
|
||||||
<div className="mt-5 flex justify-center py-8">
|
<div className="mt-5 flex justify-center py-8">
|
||||||
|
|
@ -926,7 +966,8 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Skjermovertagende scoringsveiviser (2026-07-26) -- rendres HELT
|
{/* Skjermovertagende scoringsveiviser (2026-07-26) -- rendres HELT
|
||||||
|
|
|
||||||
167
teecup-scorekort-og-entry-spec.md
Normal file
167
teecup-scorekort-og-entry-spec.md
Normal file
|
|
@ -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.
|
||||||
Loading…
Reference in a new issue