Dashbordet er bygget (zip 18 integrert): kun dashboard.tsx var reelt nytt fra V0, og jeg fant en genuin merge-situasjon i tournament-card.tsx — V0s nye "Arrangør"-merkelapp lagt til ved siden av den eksisterende offentlig/innlogget-lenkelogikken, ikke en revert. "Ny turnering" implementerer nå selve ADR-035-mekanismen (usynlig org-opprettelse ved innsending), "Statistikk"/"Spilte baner" er ren klientside-utledning fra data vi allerede har, og "Venner" viser en ærlig tom-tilstand siden ADR-036 ikke er bygget ennå. Typesjekket produksjonsbuild kompilerte rent.

Notert og bekreftet:

Medspillere skal kunne registrere score for hele flighten, ikke bare egen rad — oppdatert i ADR-036 (sletting av runden/bane-bytte forblir eier-eksklusivt).
To nye backlog-notater: flytte spillere mellom lag i turneringsoppsett, og flere flighter i én frittstående runde (sistnevnte har en reell modelleringsspenning mellom to retninger — ikke besluttet, bør trolig vente til ADR-036 fase 1 er bygget).
This commit is contained in:
Erol Haagenrud 2026-07-25 07:51:32 +02:00
parent f88d0a1469
commit c8c4ad29b6
7 changed files with 1220 additions and 580 deletions

13
.claude/settings.json Normal file
View file

@ -0,0 +1,13 @@
{
"permissions": {
"allow": [
"Bash(find / -maxdepth 3 -iname \"*zip*18*\")",
"Bash(find / -maxdepth 4 -iname \"*.zip\" -mmin -120)",
"Bash(rm -rf /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/zip18)",
"Bash(mkdir -p /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/zip18)",
"Bash(unzip -q \"/opt/teecup/tee-cup-login-screen \\(18\\).zip\")",
"Bash(python3 -m zipfile -e \"/opt/teecup/tee-cup-login-screen \\(18\\).zip\" .)",
"Bash(grep -v \"ui/dropdown-menu\\\\|^.*DropdownMenuItem,$\\\\|import\")"
]
}
}

View file

@ -2808,6 +2808,242 @@ ekte https, `teeoff.no` upåvirket.
---
## ADR-035: Dashbord-redesign — organisasjon blir implisitt
Direkte oppfølging av en refleksjonsrunde 2026-07-25: brukeren spurte
eksplisitt hvorfor organisasjon i det hele tatt trengs, gitt at en enkelt
bruker som vil sette opp ÉN turnering for en vennegjeng ikke får noe igjen
for opprettelses-seremonien. To alternativer ble veid opp mot hverandre:
- **A — bruker-eide turneringer** (samme mønster som ADR-033 sine
frittstående runder, `user_id`-eierskap, ingen RLS): AVVIST. Praktisk
talt HELE turnering-apparatet (lag/roster/blind draw/scoring/chat/
leaderboard/offentlig side, se CLAUDE.md sin arkitektur-invariant om
`organization_id` på alle domenetabeller) er bygget rundt RLS og
org-medlemskap. Å gjøre turneringer bruker-eide ville krevd enten en
full duplisering av dette apparatet i en parallell, RLS-fri variant,
eller å gjøre `organization_id` valgfri overalt — et brudd på
invarianten CLAUDE.md eksplisitt krever en ny ADR for, med en
reverseringskostnad som går GALT begge veier (migrere eksisterende
bruker-eide turneringer inn i organisasjoner senere, eller gjenoppfinne
medadministrasjon fra bunnen om den viser seg nødvendig uansett).
- **B — organisasjon beholdes, men opprettelsen gjøres usynlig/automatisk**:
VALGT. Rører verken skjema, RLS eller noen av de ni routerne som
allerede er bygget rundt organisasjon — alt fortsetter å virke uendret.
Kun UX-seremonien fjernes.
### Beslutning A — Hva "usynlig organisasjon" betyr konkret
`POST /orgs` (`app/routers/organizations.py`) krever i dag KUN `name`
verifisert direkte i koden: `slug`/`public_profile` settes separat via
`PATCH /orgs/{id}` (ADR-018), ikke ved opprettelse. Dette betyr at
"usynlig opprettelse" krever **null backend-endring** — kun en endret
frontend-orkestrering:
- Trykker en bruker "Ny turnering" og har PRESIS ÉN organisasjon fra før:
den gjenbrukes direkte, ingen synlig org-steg.
- Har brukeren INGEN organisasjon ennå: `POST /orgs` kalles automatisk med
et generert navn (`"{fornavn} {etternavn}s turneringer"`, eller
`"Mine turneringer"` som fallback hvis navn mangler) FØR turnering-
opprettelsen vises — brukeren ser aldri et eget "opprett organisasjon"-
skjema.
- Har brukeren FLERE organisasjoner (f.eks. fordi de også er invitert inn
i en ekte klubb, ADR-022): et lett org-valg vises FØRST da — samme
`OrganizationView`-mønster som i dag, men kun synlig i dette ene
tilfellet, ikke som standard.
- Alt annet er UENDRET: en bruker som ønsker en ekte klubb-identitet
(navn, slug, offentlig side, medadministratorer) kan fortsatt navngi
orgen sin og bygge den ut senere — B fjerner kun ceremonien ved
FØRSTE opprettelse, ikke noen av de eksisterende org-funksjonene.
### Reversibilitet
B kan reverseres på en ettermiddag: legg navnesteget tilbake i UI-flyten
FØR "Ny turnering" fullføres. Ingen datamigrering — eksisterende org-rader
er identiske uansett om de ble navngitt av brukeren eller auto-generert.
**Status: 📋 DESIGNET 2026-07-25, IKKE BYGGET.** Se
"Dashbord: tom-tilstand"-seksjonen i FEATURE_BACKLOG.md for det fulle
blokk-forslaget og V0-prompten som ble skrevet i samme runde.
---
## ADR-036: Venner, kategorisert deling av runder, og tiered personsøk for medspillere
Reist av brukeren 2026-07-25, samme runde som ADR-035. To sammenhengende
behov: (1) når man legger til en medspiller på en frittstående runde, skal
det søkes opp EKTE personer — venner FØRST, deretter samme hjemmeklubb,
deretter samme land, til slutt globalt, uavhengig av om for- eller
etternavn skrives først, i samme UI-mønster som bane-/klubbsøket; (2)
appen skal ha et fullverdig vennekonsept, der venner (med mindre runden er
satt til "Privat") kan se livescoren din, og der DU kan kategorisere hver
venn i én eller flere av et fast sett grupper — uten at vennen selv vet
hvilke grupper du har puttet dem i.
**Direkte oppfølging av et allerede notert, men avvist punkt:** ADR-033
Beslutning D satte `round_participant.user_id` i skjemaet, men avgrenset
bevisst til KUN gjeste-deltakere i v1 ("en deltaker med `user_id` satt får
IKKE egen tilgang til runden"), med e-post-kobling notert som en mulig,
ikke-designet senere utvidelse. Denne ADR-en erstatter e-post-kobling-
ideen med noe som passer bedre til det brukeren faktisk ba om: ekte
personsøk (samme mønster som bane/klubb), ikke en e-postadresse man må
kjenne på forhånd.
### Beslutning A — Vennskap er gjensidig; kategorisering er privat og ensidig
Et vennskap krever en forespørsel + aksept (samme grunnmønster som
organisasjon-invitasjoner, ADR-022) — "venner ser livescoren din" gir kun
mening som et GJENSIDIG, bekreftet forhold, ikke en ensidig følging.
Kategoriseringen ("Make", "Golfvenner" osv.) er derimot et HELT SEPARAT,
privat attributt EIET av den som kategoriserer — A kan sette B i
"Golfvenner" uten at B noensinne får vite det, og B kategoriserer A helt
uavhengig (kan sette A i en annen gruppe, eller ingen). Dette er bevisst
samme idé som Facebooks "nære venner"-lister: vennskapet er symmetrisk,
men grupperingen er det ikke.
**Datamodell (arbeidsnavn, avgjøres ved migrasjonsskriving):**
- `friendship`: `requester_user_id`, `addressee_user_id`, `status`
(`pending`/`accepted`/`declined`), tidsstempler. Et UNIK
uttrykks-indeks på `(LEAST(requester_user_id, addressee_user_id),
GREATEST(...))` hindrer duplikate forespørsler i begge retninger
samtidig.
- Kategori er IKKE en egen tabell — et fast, CHECK-constrained sett
(samme mønster som `BAG_CLUBS`/`gender`/`stat_level` ellers i appen),
ikke brukerdefinerbart: `spouse` (Make), `close_family` (Nær familie),
`extended_family` (Storfamilie), `close_friends` (Nære venner),
`golf_friends` (Golfvenner), `colleagues` (Kollegaer), `business`
(Forretningsforbindelser), `classmates` (Studiekamerater),
`acquaintances` (Perifere bekjente), `other` (Ymse).
- `friend_categorization`: `owner_user_id`, `friend_user_id`, `category`
— én rad PER kategori en venn er satt i (en venn kan ha flere rader,
"de skal kunne være tilknyttet forskjellige kategorier"). Håndheves i
app-laget (skriving krever en `accepted`-vennskapsrad mellom de to),
ikke en direkte FK til `friendship` (unngår å måtte holde styr på
requester/addressee-retning to steder).
### Beslutning B — Rundevisibilitet: tre nivåer, samme struktur som turnering, men egen mekanisme
`round` får `visibility_mode` (`public`/`private`/`friends`, default
`private` — trygg standard, samme filosofi som RLS-policyenes "se
ingenting" ved manglende kontekst). Når `friends` er valgt: hvilke
KATEGORIER som får se runden velges eksplisitt (`round_visible_category`,
`round_id`+`category`) — ikke "alle venner", siden brukeren eksplisitt ba
om å velge blant gruppene sine ved rundestart.
**Viktig presisering, unngår en reell forvekslingsfelle:** dette er
ADSKILT fra om noen er lagt til som faktisk MEDSPILLER (Beslutning C
under). En medspiller du har lagt til ser ALLTID runden dere spiller
sammen, uavhengig av `visibility_mode` — visibility-nivået styrer kun
TREDJEPARTS innsyn (venner/offentligheten), ikke de som faktisk er med i
flighten.
**Autorisasjonssjekk** (app-lag, `plain_connection()`-mønsteret fra
ADR-033 Beslutning A — INGEN RLS, samme begrunnelse som der):
1. `viewer == round.owner_user_id` → alltid tilgang.
2. `viewer` er en lenket medspiller (`round_participant.user_id ==
viewer`) → alltid tilgang til DEN runden.
3. `visibility_mode == 'public'` → alle, også anonyme (samme mønster som
turneringers offentlige side).
4. `visibility_mode == 'private'` → kun 1+2.
5. `visibility_mode == 'friends'` → krever innlogget bruker MED en
`accepted`-vennskapsrad til eieren OG minst én
`friend_categorization`-rad (`owner_user_id=eier, friend_user_id=
viewer`) hvor kategorien finnes i `round_visible_category` for akkurat
denne runden. Legg merke til retningen: det er EIERENS kategorisering
av VIEWER som brukes, ikke omvendt — riktig, siden det er eieren som
begrenser hvem som får se, basert på eierens egen gruppering.
**"Livescore"** — forstått som sanntidsoppdatering mens runden pågår,
samme idé som turnering sin `/t/[id]/live` (ADR-027). Gjenbruker samme
kringkastingsmønster (`app/realtime.py`, `broadcast_live_update`) — en
tilsvarende funksjon for runder, kringkastet ved hver hull-PATCH når
`visibility_mode != 'private'`, med et WS-endepunkt gated av samme
autorisasjonssjekk som over.
### Beslutning C — Tiered personsøk: delt mellom "finn venn" og "legg til medspiller"
Samme underliggende endepunkt brukes til BEGGE formål (finn en venn å
sende forespørsel til, OG søk opp en medspiller å legge til på en runde)
— begge er i essens "finn en person", kun hva som skjer ETTER valg
skiller dem. Foreslått: `GET /people/search?q=...`.
**Navnerekkefølge-uavhengig matching:** spørringen splittes i tokens
(mellomrom-separert). For HVER token må minst ett av `first_name`/
`last_name` matche (`ILIKE token||'%'`) — ALLE tokens må matche (AND på
tvers av tokens, OR på tvers av feltene per token). Dette gjør at "Erol
Haagenrud" og "Haagenrud Erol" gir identisk treff, uten noen spesiell
"gjett rekkefølgen"-logikk — det faller naturlig ut av at hvert ord kan
matche HVILKET SOM HELST av de to feltene.
**Tiered rangering, én spørring:** en beregnet prioritet per kandidat —
0 hvis venn (uavhengig av kategorisering — ALLE venner, ikke bare de i
en bestemt gruppe, siden dette er søk-for-å-legge-til, ikke visibility-
sjekken over), 1 hvis samme `home_club` som søkeren (nå en pålitelig
eksakt streng siden ADR-025-runden 2026-07-25 gjorde Hjemmeklubb til en
ekte dropdown-verdi i stedet for fritekst — retroaktivt en god
begrunnelse for den endringen), 2 hvis samme `country`, 3 ellers.
`ORDER BY prioritet, fornavn, etternavn LIMIT 20` gir venner-først uten
behov for separate spørringer per nivå.
**Personvernhensyn, bevisst innebygd i designet, ikke tilføyd i
etterkant:**
- Svaret inneholder KUN navn, avatar, hjemmeklubb — ALDRI e-post/mobil/
fødselsdato.
- Foreslått minimum 2 tegn i søket før noe returneres i det hele tatt
(hindrer triviell enumerering av alle brukere via ett enkelt
bokstavsøk) — MIN anbefaling, ikke bekreftet med bruker.
- Kun innloggede brukere kan søke (`get_current_user`, ikke anonymt).
### Beslutning D — Byggerekkefølge (foreslått, IKKE bekreftet)
Gitt omfanget (nytt vennekonsept + ny rundevisibilitet + nytt delt
søkeendepunkt + sanntidsutvidelse) foreslås tre uavhengig leverbare faser,
samme "bygg i rekkefølgen ting brukes"-prinsipp som resten av prosjektet:
1. **Venner-kjernen**: `friendship`+`friend_categorization`, forespørsel/
aksept/fjern-endepunkter, `/people/search`, en ny `/friends`-side
(søk, forespørsler, kategoriser). Leverbar og nyttig helt alene.
2. **Rundevisibilitet**: `round.visibility_mode`+`round_visible_category`,
valg ved rundestart, `can_view_round`-gatede lese-endepunkter for
ikke-eiere, sanntidsutvidelse for live-visning. Avhenger av fase 1.
3. **Ekte medspillere** (ikke bare gjester): utvid `POST .../participants`
til å godta et søkt `user_id` i tillegg til `guest_name`. Avhenger av
fase 1 (samme søk). **Avklart med bruker 2026-07-25: JA** — en
lagt-til ekte medspiller skal se runden i SIN EGEN "Egne runder"-liste,
ikke bare eieren.
**Konkret teknisk konsekvens, presisert her siden det ikke er
opplagt:** `list_rounds` filtrerer i dag KUN på `owner_user_id`, og
`RoundOut` sine `owner_holes_played`/`owner_total_score`/
`owner_score_to_par`-felt (lagt til 2026-07-25, se status) er alltid
utledet fra EIERENS `round_participant`-rad. For en medspiller som
ser runden i SIN liste må disse tallene i stedet vise DERES EGEN
score i runden, ikke eierens — feltene må derfor bli
"viewer-relative" (utledet fra HVILKEN SOM HELST deltaker-rad som
matcher innlogget bruker, enten `is_owner` eller lenket `user_id`),
ikke hardkodet til `is_owner=true`-raden. `list_rounds`-spørringen må
utvides til `WHERE owner_user_id = $1 OR id IN (SELECT round_id FROM
round_participant WHERE user_id = $1)`. Ingen endring i selve
eierskapet (`round.owner_user_id` er fortsatt entydig én person).
**Skrivetilgang avklart med bruker 2026-07-25: en medspiller skal
kunne registrere score for ALLE i flighten**, ikke bare sin egen rad
— samme skrivetilgang som eieren allerede har i dag (via
`_get_owned_round_or_404`), nå utvidet til å gjelde ENHVER lenket
ekte deltaker (`round_participant.user_id`), ikke kun eieren. Praktisk
presisering: dette gjelder KUN hull-registrering
(`PATCH .../participants/{id}/holes/{hole}`) og legg-til-deltaker —
sletting av selve runden og bane-/utslagsbytte (`DELETE /rounds/{id}`,
`PATCH /rounds/{id}` sine bane-felt) forblir eier-eksklusivt, siden
disse er destruktive/strukturelle handlinger uavhengig av hvem som
fører score. Autorisasjonssjekken for hull-PATCH blir dermed:
`viewer == owner_user_id OR viewer IN (SELECT user_id FROM
round_participant WHERE round_id = $1 AND user_id IS NOT NULL)`.
**Status: 📋 DESIGNET 2026-07-25, IKKE BYGGET.** Se "Venner og
kategorisert deling"-seksjonen i FEATURE_BACKLOG.md for åpne spørsmål og
videre kontekst.
---
## Åpne spørsmål (ikke besluttet ennå)
Disse må avklares før eller under de relevante fasene:

View file

@ -2574,17 +2574,86 @@ Ferdig og verifisert:
12/12, begge containere redeployet, `/health`/`/dashboard`/`/my-rounds`/
`/account` → 200, `teeoff.no` upåvirket.
- **Refleksjonsrunde 2026-07-25: hvorfor organisasjon? + venner/deling —
DESIGNET, IKKE bygget.** Brukeren spurte hvorfor organisasjon i det
hele tatt trengs, gitt at ADR-033 nå gjør en enkelt bruker fullt
selvstendig. Veide A (bruker-eide turneringer, som runder) mot B
(organisasjon beholdt, men opprettelsen gjøres usynlig/automatisk) —
A avvist (ville krevd duplisering av HELE turnering-apparatet som er
bygget rundt RLS/`organization_id`, og er ikke reversibelt i noen
retning), B valgt (rører verken skjema eller de ni eksisterende
routerne, kun frontend-orkestrering — `POST /orgs` krever allerede kun
`name`). Skrevet som **ADR-035** (organisasjon-B-beslutningen + et
konkret 7-blokks dashbord-forslag: Hurtighandlinger/Kommende runder/
Kommende turneringer/Statistikk/Spilte baner/Venner/Organisasjoner —
sistnevnte nedtonet, kun synlig ved reelt flere org-er) og **ADR-036**
(nytt vennekonsept — gjensidig forespørsel/aksept, privat
kategorisering i faste grupper som Make/Nær familie/Golfvenner/osv.,
ny rundevisibilitet `public`/`private`/`friends` med eksplisitt
gruppevalg, og et tiered personsøk — venner→samme klubb→samme
land→globalt, navnerekkefølge-uavhengig — delt mellom "finn venn" og
en fremtidig "legg til ekte medspiller"-utvidelse av
`round_participant.user_id`, som allerede lå ubrukt i skjemaet som en
eksplisitt notert "v1-avgrensning" i ADR-033). V0-prompt for det nye
dashbordet skrevet (i FEATURE_BACKLOG.md), IKKE sendt til V0 ennå.
**Ingen kode skrevet** — bevisst en design-/dokumentasjonsrunde, ikke
en byggerunde, på brukerens eksplisitte instruks. Full detalj i
ARCHITECTURE_DECISIONS.md (ADR-035/036) og FEATURE_BACKLOG.md (åpne
spørsmål, foreslått 3-fase byggerekkefølge for venner-delen).
- **Oppfølging samme dag:** bruker bekreftet at en lagt-til ekte
medspiller SKAL se runden i sin egen "Egne runder"-liste (ADR-036 fase
3, tidligere bevisst uavklart). Presiserte samtidig en ny teknisk
konsekvens i ARCHITECTURE_DECISIONS.md: `RoundOut` sine
`owner_*`-statistikkfelt må bli viewer-relative, ikke alltid eierens,
og et NYTT åpent spørsmål dukket opp — skal medspilleren også kunne
SKRIVE egne hull-tall, ikke bare lese? Ikke avgjort. Fortsatt ingen
kode skrevet.
- **Dashbord-redesign (ADR-035) BYGGET, IKKE ENNÅ RULLET UT (2026-07-25):**
zip 18 mottatt og integrert — kun `dashboard.tsx` var reelt nytt (samme
full-reeksport-mønster som alltid), pluss en genuin MERGE (ikke revert)
i `tournament-card.tsx`: V0s nye `organizer`-merkelapp lagt til SIDE OM
SIDE med den eksisterende `orgId`-baserte lenkelogikken (offentlig vs.
innlogget kontekst) som V0 ikke kjente til. Datalag skrevet fra bunnen:
"Kommende turneringer" slår sammen deltaker- (`me.my_tournaments`) og
arrangør-turneringer (hentet per org, alle organisasjoner brukeren er
medlem i) til ÉN tidssortert liste, filtrert til `draft`/`active`-status.
"Statistikk"/"Spilte baner" er REN klientside-utledning fra eksisterende
`GET /rounds` + `GET /auth/profile/handicap-history` — ingen nye
endepunkter trengt. "Ny turnering"-hurtighandlingen implementerer selve
ADR-035-mekanismen: oppretter/gjenbruker organisasjon USYNLIG først
(kun ved faktisk innsending, ikke når skjemaet åpnes — unngår en
foreldreløs org ved avbrutt skjema), deretter turneringen, deretter
navigerer rett inn i den. "Bli med med kode" gjenbruker samme
`by-code`-oppslag som `login-form.tsx` sin `JoinByCode`, nå som en
dashbord-lokal variant. "Venner"-seksjonen vises med en ærlig
tom-tilstand (ADR-036 er ikke bygget ennå) — CTA-en er bevisst uten
href/onClick, ikke en lenke til noe som ikke finnes. "Spilte baner"
fikk sine listeelementer gjort om til ren visning (ikke lenker) siden
API-et ikke eksponerer noen stabil bane-id å lenke til per runde.
Ekte typesjekket produksjonsbuild kompilerte rent, alle 21 ruter
listet. **IKKE rullet ut ennå** — venter på brukerens bekreftelse
(ren frontend-endring, ingen migrasjon).
- **Oppfølging samme runde, avklart med bruker:** medspillere skal kunne
registrere score for HELE flighten (ikke bare egen rad) når ADR-036
fase 3 bygges — oppdatert i ARCHITECTURE_DECISIONS.md/FEATURE_BACKLOG.md.
To nye notater lagt i FEATURE_BACKLOG.md (ingen design/bygging ennå,
kun fanget opp): (1) turneringsoppsett mangler en vei til å FLYTTE en
rostret spiller til et annet lag (kun fjerning finnes i dag), (2) et
reelt modelleringsspørsmål om flere flighter i én frittstående runde
(f.eks. "min flight + vennenes flight bak oss samme dag") — to
prinsipielt ulike retninger skissert, ingen valgt.
Neste steg:
1. **Pauset, venter på retning:** dashbordets tom-tilstand ved første
innlogging. Brukeren ba om å justere det opprinnelige 2026-07-20-
forslaget i lys av et dypere spørsmål — bør organisasjon fortsatt være
"det som meldes først"? Min vurdering (se FEATURE_BACKLOG.md): nei,
bør bli ett likestilt valg blant flere. **Delvis besvart 2026-07-22:**
den ALLER første tingen en ny/ufullstendig bruker nå ser er den
obligatoriske profil-fullføringen (se status over), ikke selve
dashbordet — men spørsmålet om HVA dashbordets tom-tilstand skal vise
for en bruker som HAR fullført profilen, men ennå ikke har noen
organisasjon/turnering, står fortsatt åpent.
1. **Klart for bygging, venter på brukerens go-ahead:** dashbord-redesign
(ADR-035) + venner/kategorisert deling (ADR-036), begge designet
2026-07-25 (se status over og FEATURE_BACKLOG.md). Retningen er
avklart (organisasjon blir usynlig/automatisk, dashbordet får syv
konkrete blokker, venner bygges i tre uavhengige faser) — men INGEN
av delene er bekreftet klar til bygging ennå. Naturlig neste steg:
send V0-prompten for dashbordet (se FEATURE_BACKLOG.md) til v0.app,
ELLER start på venner-kjernen (fase 1 av ADR-036), etter brukerens
valg.
3. **Frittstående rundeføring + detaljert statistikk — ADR-033 skrevet
2026-07-22, IKKE bygget.** Brukeren avklarte 2026-07-22 at dette skal
bli appens HOVEDFOKUS (turneringsoppsett skal bli ekstremt enkelt

View file

@ -1329,7 +1329,7 @@ og vil derfor begge se profil-fullførings-skjemaet ved neste innlogging.
---
## Dashboard: tom-tilstand ved første innlogging — 📋 UNDER REVURDERING (2026-07-21), IKKE bygget
## Dashboard: tom-tilstand ved første innlogging — 📋 KONKRET FORSLAG LAGT FREM (2026-07-25), IKKE bygget
Brukeren påpekte 2026-07-20 at dagens tomme-tilstand ("Du har ingen
organisasjon ennå — opprett en") er organisator-vridd og ikke stemmer med
@ -1381,6 +1381,222 @@ tom-skjermens endelige form kan bestemmes.
---
### 2026-07-25 — avhengigheten er løst, konkret forslag lagt frem (📋 DESIGNET, IKKE BYGGET)
Frittstående runder (ADR-033) er nå ferdig bygget (backend+frontend, alle
oppfølgingsrunder), så blokkeringen over er borte. Brukeren reiste samtidig
det dypere spørsmålet "hvorfor har vi organisasjon i det hele tatt" —
besvart og designet som **ADR-035** (organisasjon beholdes, men
opprettelsen gjøres usynlig/automatisk — Beslutning B, se
ARCHITECTURE_DECISIONS.md for full A-vs-B-avveining og
reversibilitetsvurdering).
**Konkret blokk-forslag for det nye dashbordet** (rekkefølge, topp til
bunn):
1. **Hurtighandlinger** — tre likestilte kort/knapper: "Ny runde", "Ny
turnering" (oppretter/gjenbruker organisasjon usynlig, ADR-035), "Bli
med med kode" (ADR-020). Ingen av de tre skal kreve noe org-steg
synlig for brukeren.
2. **Kommende runder** — egne runder som ikke er fullført
(`completed_at IS NULL`), sortert på dato, inntil 3 vist + "se alle"
til `/my-rounds`. Tomtilstand: kort tekst + snarvei til "Ny runde".
3. **Kommende turneringer** — SLÅR SAMMEN turneringer man er deltaker i
(dagens "Mine runder"-data) OG turneringer man arrangerer (dagens
organisasjons-turneringsliste, på tvers av ALLE organisasjoner
brukeren er medlem i, flatt) til ÉN tidssortert liste. Kort man
arrangerer får en liten "Arrangør"-merkelapp. Tomtilstand: snarvei til
"Ny turnering"/"Bli med med kode".
4. **Statistikk** — smått aggregert: antall runder spilt, HCP-trend
(sparkline fra den eksisterende `handicap_history`-tabellen, kun vist
ved ≥2 datapunkter), snitt til par siste 5 runder. Tomtilstand til
minst én runde er fullført.
5. **Spilte baner** — utledet fra `round.course_name_snapshot`, gruppert
med besøksantall + sist spilt. Ingen ny datamodell trengs.
6. **Venner** — kort med antall ventende forespørsler + snarvei til en ny
`/friends`-side (se ADR-036 under). Tomtilstand: "Du har ingen venner
ennå — søk etter noen".
7. **Organisasjoner** — KUN vist hvis brukeren er medlem i mer enn den
auto-opprettede sin egen (dvs. har en ekte, navngitt klubb-tilknytning)
— én liten, nedtonet lenke, ikke en egen fremtredende seksjon. Dette er
selve poenget med ADR-035: organisasjon skal ikke lenger dominere
dashbordet.
**V0-prompt skrevet, IKKE sendt til V0 ennå** (venter på brukerens
gjennomgang av blokk-forslaget over først):
> Design et nytt dashbord for TeeCup (golf-app, Next.js + Tailwind +
> shadcn/ui, mobil-først, eksisterende merkevarefarger: grønn primær,
> oransje sekundær — bruk appens eksisterende design-tokens, ikke nye
> farger). Dette ERSTATTER dagens dashbord, som feilaktig satte
> "organisasjon" som det første og viktigste en bruker møtte — ny
> retning: brukeren er først og fremst en GOLFSPILLER, organisasjon er en
> liten, valgfri detalj lengre ned.
>
> Innhold, i denne rekkefølgen, som distinkte kort/seksjoner (ikke faner):
> 1. Hurtighandlinger: tre like store, likestilte knapper/kort side ved
> side (stables på smal skjerm) — "Ny runde", "Ny turnering", "Bli med
> med kode". Tydelige, tekstede (ikke kun ikon), store trykkflater.
> 2. "Kommende runder" — liste over inntil 3 pågående/ikke-fullførte
> runder (banenavn eller eget rundenavn, dato, en liten
> fremdriftsindikator "X/18 hull"), med en "se alle"-lenke. Vis en
> tydelig, vennlig tomtilstand med snarvei til "Ny runde" hvis ingen.
> 3. "Kommende turneringer" — liste over turneringer brukeren enten
> deltar i eller arrangerer, sortert på dato, en liten
> "Arrangør"-merkelapp på kort man selv arrangerer. Tomtilstand med
> snarveier til "Ny turnering"/"Bli med med kode".
> 4. "Statistikk" — en kompakt rad med 2-3 nøkkeltall (antall runder,
> HCP nå + en liten trendpil/sparkline, snitt til par) i staselige
> tall-fliser (stort, fet skrift, høy kontrast).
> 5. "Spilte baner" — en kompakt liste/rutenett av baner med
> besøksantall og sist spilt-dato.
> 6. "Venner" — et lite kort: avatar-stabel av noen få venner (om noen),
> et tall-merke for ventende forespørsler, snarvei "Se venner".
> Tomtilstand: oppfordring til å søke opp noen.
> 7. "Organisasjoner" — KUN når relevant: én liten, nedtonet tekstlenke
> nederst, IKKE et fremtredende kort — dette skal se ut som en
> bakgrunnsdetalj, ikke en hovedseksjon.
>
> Tilgjengelighet er et ufravikelig krav, ikke en estetisk sluttpuss: god
> kontrast, stor nok skrift, store trykkflater (min. 44px), ALDRI
> ikon-only uten tekstlabel på viktige handlinger, appen skal være
> brukbar uten finmotorikk eller skarpt syn. Design ALLE tomtilstander
> eksplisitt, ikke bare den fylte varianten — de fleste nye brukere vil
> se flere tomme blokker samtidig, og det skal fortsatt se innbydende ut,
> ikke ufullstendig.
**Åpne spørsmål før bygging:**
- Skal blokk 3 og 4 lenke til nye, dedikerte sider (f.eks. en egen
aggregert statistikk-side), eller er de rene dashbord-widgets uten
"se mer"? Statistikk-tallene over er enkle å beregne, men en FULL
aggregert statistikk-side (grafer over tid på tvers av alle runder) er
et større, eget stykke arbeid — ikke inkludert i dette forslaget.
- Nøyaktig terskel for når "Organisasjoner"-lenken vises (mer enn 1 org
totalt? Eller kun når minst én org har et eksplisitt satt `slug`/
`public_profile`, dvs. faktisk er gjort til en "ekte" klubb?).
---
## Venner, kategorisert deling av runder, og tiered personsøk — 📋 DESIGNET 2026-07-25, IKKE bygget
Reist av brukeren samme runde som dashbord-forslaget over. Fullt design
skrevet som **ADR-036** i ARCHITECTURE_DECISIONS.md — se der for
datamodell, autorisasjonslogikk og søke-algoritme i detalj. Kort
oppsummert her, pluss åpne spørsmål og foreslått byggerekkefølge.
**Tre sammenhengende deler:**
1. Et ekte vennekonsept — gjensidig forespørsel/aksept (som org-
invitasjoner), pluss et fast sett kategorier en venn kan settes i
(flere samtidig): Make, Nær familie, Storfamilie, Nære venner,
Golfvenner, Kollegaer, Forretningsforbindelser, Studiekamerater,
Perifere bekjente, Ymse. **Kategoriseringen er privat** — vennen vet
ikke hvilken/hvilke grupper du har satt dem i.
2. Rundevisibilitet — ny `round.visibility_mode`
(`public`/`private`/`friends`, default `private`). Ved `friends`
velger man EKSPLISITT hvilke av gruppene sine som får se runden (ikke
"alle venner"). Styrer kun tredjeparts innsyn — en faktisk lagt-til
medspiller ser alltid runden uansett innstilling.
3. Tiered personsøk — samme søkbare-liste-mønster som bane-/klubbsøket
(`HomeClubField`/`OfficialSearchStep`), navnerekkefølge-uavhengig
(skriv for- ELLER etternavn først, begge treffer), rangert: venner →
samme hjemmeklubb → samme land → globalt. Ett delt endepunkt brukt
BÅDE til "finn en venn" og (i en senere fase) "legg til medspiller på
en runde".
**Reell synergi funnet under design, ikke tilfeldig:** forrige rundes
redesign av "Hjemmeklubb" til en ekte dropdown-verdi (i stedet for
fritekst) gjør nå "samme klubb"-rangeringen i søket pålitelig — et
eksakt strengmatch fungerer nå, noe det ikke ville gjort med den gamle
fritekst-versjonen.
**Foreslått byggerekkefølge (IKKE bekreftet), tre uavhengig leverbare
faser:**
1. Venner-kjernen (vennskap, kategorisering, `/people/search`, ny
`/friends`-side) — leverbar og nyttig helt alene.
2. Rundevisibilitet (visibility-valg ved rundestart, gatede
lese-endepunkter, sanntids-livevisning for venner/offentlighet).
3. Ekte medspillere (ikke bare gjester) — utvider
`POST /rounds/{id}/participants` til å godta et søkt `user_id`.
**Avklart med bruker 2026-07-25: JA, runden skal vises i spillerens
egen rundeliste.** Teknisk konsekvens (se ADR-036 for full detalj):
`list_rounds` må utvides fra kun `owner_user_id` til også lenkede
`round_participant.user_id`-rader, og `RoundOut` sine
`owner_*`-statistikkfelt må bli "viewer-relative" (vise DEN som ser
på sin egen score, ikke alltid eierens). **Skrivetilgang også
avklart 2026-07-25: en medspiller skal kunne registrere score for
ALLE i flighten**, ikke bare egen rad — samme rettighet som eieren
har i dag, utvidet til enhver lenket ekte deltaker. Sletting av
runden og bane-/utslagsbytte forblir eier-eksklusivt (se ADR-036 for
presis autorisasjonssjekk).
**Andre åpne spørsmål (se ADR-036 for full drøfting):**
- Skal en bruker kunne gjøre seg "usøkbar"? Foreslått default: alle med
fullført profil er søkbare, ingen opt-out i første omgang.
- Minimum antall tegn før søket returnerer globale (ikke-venn/klubb/land)
treff — foreslått 2, for å hindre triviell enumerering av alle
brukere.
- Skal en "offentlig" runde være synlig for ANONYME besøkende (som
turneringers offentlige side), eller kreve innlogging? Foreslått:
samme mønster som turnering — anonymt tilgjengelig.
**Ingen kode skrevet ennå** — dette var en design-/dokumentasjonsrunde,
ikke en byggerunde, på brukerens eksplisitte instruks.
---
## Turneringsoppsett: flytte/slette spillere mellom lag — 📋 NOTERT 2026-07-25, IKKE bygget
Reist av brukeren samme runde som dashbord-integreringen. I dag finnes
`DELETE .../roster/{roster_id}` (fjerner en spiller fra ETT lag) og
`PATCH .../roster/{roster_id}` (kun `is_captain`, ADR-023) — men INGEN vei
til å FLYTTE en allerede rostret spiller til et ANNET lag i samme
turnering i én operasjon. I dag må organisatoren gjøre det som to separate
kall (fjern fra lag A, legg til på lag B), og det finnes ingen slik
UI-handling i `tournament-detail.tsx` sin `TeamPanel` i det hele tatt —
kun fjerning.
**Ikke designet i detalj ennå** — rent notert som et reelt hull. Naturlig
retning ved bygging: enten et eget `POST .../roster/{roster_id}/move`
(target-team-id) som gjør begge operasjonene atomisk (unngår en
mellomtilstand der spilleren midlertidig ikke er rostret noe sted), eller
en UI-snarvei som bare kjører de to eksisterende kallene i sekvens — det
første er tryggere (ingen delvis fullført tilstand ved feil midtveis).
---
## Frittstående runder: flere flighter i én "vanlig" runde — 📋 NOTERT 2026-07-25, IKKE bygget
Reist av brukeren samme runde: eksempel gitt — "jeg går i den første
flighten sammen med tre andre, mens fire venner går i flighten bak."
Dagens datamodell (`round`, ADR-033 Beslutning A) er implisitt ÉN flight
= ÉN runde: `round.owner_user_id` er entydig, og alle
`round_participant`-rader (eier + gjester/medspillere) spiller sammen i
SAMME flight på SAMME hull-for-hull-registrering.
**Reell modelleringsspenning, ikke bare en UI-mangel:** å støtte flere
flighter "i samme runde" krever et valg mellom to prinsipielt ulike
retninger:
1. **Løs gruppering av flere separate `round`-rader** — hver flight er
fortsatt sin egen `round` (egen eier, egne deltakere, egen
hull-registrering, uendret datamodell), men et nytt, tynt
"delt arrangement"-konsept binder flere runder sammen visuelt (samme
dag, samme bane, "spilt sammen med disse flightene") — minst
invasivt, gjenbruker alt som allerede er bygget og scratch-verifisert.
2. **`round` blir en beholder for flere flighter** — ligner
turneringers `session`→`match`-struktur (én økt, flere matcher).
Større omskriving: `round_participant` må da vite hvilken flight den
hører til, og eierskap/autorisasjon (i dag: "eieren av runden ser/
redigerer alt") må revurderes for en modell med flere selvstendige
flighter under samme paraply.
**Ikke besluttet hvilken retning** — kun notert som et reelt, ikke-trivielt
spørsmål. Henger dessuten sammen med det pågående ADR-036-arbeidet
(medspillere/venner) — en avklaring bør trolig vente til minst fase 1 av
ADR-036 er bygget, siden "hvem er i min flight" og "hvem er min venn/
medspiller" er beslektede, men ikke identiske spørsmål.
---
## Frittstående rundeføring + detaljert statistikk (uten turnering/organisasjon) — ✅ BACKEND + FRONTEND LIVE, løpende oppfølging t.o.m. 2026-07-25 (se ADR-033 for full detalj per dag)
**Fremdrift 2026-07-22:** HCP-indeks-motor (43/43 tester), databaseskjema

File diff suppressed because it is too large Load diff

View file

@ -33,12 +33,17 @@ function formatDateRange(start?: string, end?: string) {
export function TournamentCard({
tournament,
orgId,
organizer = false,
}: {
tournament: Tournament
// Satt (dashbordet, innlogget) -> lenker til den innloggede detaljsiden.
// Utelatt (klubb-landingsside, offentlig) -> lenker til den offentlige
// /t/{id}-ruten i stedet. Samme kort, to kontekster.
orgId?: string
// Vis en "Arrangør"-merkelapp (dashbordets slåtte-sammen "Kommende
// turneringer"-liste, 2026-07-25 -- deltaker- og arrangør-turneringer i
// ÉN liste, merkelappen skiller dem visuelt).
organizer?: boolean
}) {
const dateRange = formatDateRange(tournament.startDate, tournament.endDate)
const href = orgId
@ -56,6 +61,12 @@ export function TournamentCard({
{tournament.name}
</h3>
<TournamentStatusBadge status={tournament.status} />
{organizer && (
<span className="inline-flex items-center gap-1.5 rounded-full bg-brand-orange/15 px-2.5 py-1 text-xs font-semibold text-foreground">
<span aria-hidden="true" className="size-1.5 rounded-full bg-brand-orange" />
Arrangør
</span>
)}
</div>
{dateRange ? (
<div className="flex items-center gap-1.5 text-sm text-muted-foreground">

Binary file not shown.