Dokumenter dashbord/BottomNav/veiviser/login/deling/slagmåling i CHANGELOG.md

Punkt 39-45: dashbord+BottomNav-reskin, ny-runde-veiviser + offisiell-
banesøk-produksjonsbug, /logg-inn som ekte innloggingsside, rundedeling-
kategoriregel-fiks (ADR-036 E), og slag-for-slag-avstandsmåling
inngangspunkt 1. Se de fem foregående commitene for koden.
This commit is contained in:
Erol Haagenrud 2026-08-08 12:38:31 +02:00
parent 9538dfec7f
commit 2d1d0ba1d0

View file

@ -8683,9 +8683,10 @@ Neste steg:
eksplisitt bekreftet at ekte `teecup_db` sin `datacl` var uendret eksplisitt bekreftet at ekte `teecup_db` sin `datacl` var uendret
etterpå). etterpå).
**Ikke rullet ut ennå** -- venter på samlet bekreftelse sammen med **Rullet ut 2026-08-08** sammen med punkt 40 og 41 (`docker compose
resten av det som gjenstår i denne økten (slagmåling-frontend, build teecup_frontend` + `up -d` -- ingen migrasjon, kun ny
dashbord-V0-beslutning). frontend-image). Bekreftet ingen konsollfeil på
`https://teecup.golf/` etter omstart.
39. **Dashbordet reskinnet til "clubhouse"-paletten — 2026-08-07, bruker 39. **Dashbordet reskinnet til "clubhouse"-paletten — 2026-08-07, bruker
bekreftet eksplisitt "full overtagelse, også bakgrunnen".** Egen, bekreftet eksplisitt "full overtagelse, også bakgrunnen".** Egen,
@ -8745,7 +8746,424 @@ Neste steg:
utvidet korrekt og det eksisterende skjemaet (uendret) fungerte som før. utvidet korrekt og det eksisterende skjemaet (uendret) fungerte som før.
Ingen konsollfeil. Scratch-miljøet ryddet opp fullstendig. Ingen konsollfeil. Scratch-miljøet ryddet opp fullstendig.
**Ikke rullet ut ennå** -- venter på BottomTabBar-V0-eksporten (så **Rullet ut 2026-08-08** sammen med BottomNav (punkt 40) og
hele dashbordet, inkludert navigasjonen, kan rulles ut samlet i én ny-runde-veiviseren (punkt 41), som planlagt -- se punkt 41 for
omgang i stedet for i to synlig usammenhengende steg) og på samlet utrullingsdetaljer.
bekreftelse sammen med resten av det som gjenstår denne økten.
40. **BottomTabBar erstattet med `BottomNav` fra egen V0-prompt —
2026-08-08.** Bruker ba eksplisitt om at bunn-navigasjonens utseende
skulle avgjøres av V0 selv, ikke av meg (se punkt 39) -- prompt skrevet
med eksplisitt kontekst om at komponenten deles mellom clubhouse- og
Forest Green-sider og må fungere rimelig godt mot begge, uten å få
oppgitt en fasit-retning. V0 valgte en bevisst palett-nøytral løsning:
en "flytende hvit flate" (halvtransparent hvit, backdrop-blur, egen
skygge) som ikke arver noen sideb palett -- kun den grønne
`tee`/`tee-strong`-aksenten (felles for begge paletter) brukes for
aktiv-tilstand.
Ny fil `components/teecup/bottom-nav.tsx`, eksporterer `BottomNav`
(ikke lenger `BottomTabBar`). Strukturell endring fra forgjengeren:
aktiv fane leses nå fra `usePathname()` internt (ikke en `active`-prop
utenfra) -- hrefs rettet fra V0s plassholdere (`/hjem`, `/runder` osv.)
til de faktiske rutene under integrering. Fjernet den gamle
`BottomTabBar`-definisjonen (og dens nå-ubrukte `C`-fargekonstant og
fire lucide-ikon-importer) fra `dashboard.tsx`; alle seks sider
(`dashboard.tsx`, `own-rounds.tsx`, `friends.tsx`, `account-
settings.tsx`, `feed.tsx`, `notifications.tsx`, `more-menu.tsx`)
importerer nå `BottomNav` fra den nye filen i stedet, uten `active`-
prop.
**To reelle bugs funnet og rettet UNDER scratch-verifisering (ikke i
selve V0-eksporten -- begge i MIN EGEN integreringskode):**
1. Forsøkte først å "forbedre" aktiv-fane-sammenligningen ved å
strippe bort et evt. `#hash` før sammenligning (siden "Turneringer"
sin href er `/dashboard#kommende-turneringer`). Dette var FEIL --
browser-verifisert til å få BÅDE "Hjem" og "Turneringer" til å vise
som aktive samtidig på `/dashboard`, siden begge da strippet til
samme sti. Reversert til V0s opprinnelige, rå strengsammenligning
(som aldri hadde dette problemet -- "Turneringer" matcher rett og
slett aldri via ren pathname, akkurat som forgjengeren uansett aldri
fremhevet den).
2. `/my-friends`, `/my-feed` og `/my-notifications` viste INGEN fane
som aktiv i det hele tatt (ren pathname-matching kjenner dem ikke
igjen som noen fanes mål) -- forgjengeren løste dette implisitt ved
at hver side selv sendte inn riktig `active`-prop. Lagt til en ny,
liten `matchPaths`-mekanisme på `Tab`-typen; "Mer" sin oppføring
fikk `matchPaths: ["/my-friends", "/my-feed", "/my-notifications"]`
-- speiler nøyaktig hvilke tre sider `more-menu.tsx` selv allerede
lister som sine undersider, ingen ny gruppering oppfunnet.
`tsc --noEmit` rent. **Scratch-verifisert i ekte nettleser** (tiende
scratch-miljø denne økten, samme migrasjon-002-forsiktighet som
punkt 37-39, `teecup_db` bekreftet uendret): logget inn, besøkt alle
syv sider som bruker `BottomNav` (dashbord, my-rounds, my-friends,
account, my-feed, my-notifications, more) -- riktig (og ETT AV GANGEN,
etter fiks 1) fane fremhevet på hver, ingen konsollfeil. Scratch-miljøet
ryddet opp fullstendig.
**Rullet ut 2026-08-08** sammen med punkt 39 og 41 -- se punkt 41 for
utrullingsdetaljer.
41. **`/my-rounds/new` erstattet med ny-runde-veiviser fra egen V0-prompt
— 2026-08-08.** Samme mønster som punkt 40: prompt skrevet med full
teknisk spesifikasjon (alle 18 spilleformer, alle steg/felt/
valideringsregler, alle 12 backend-endepunkt, `submitWizard()`-
kontrakten) og eksplisitt UTEN visuell føring -- V0 fikk velge
utseendet fritt. Resultat: en ny, selvstendig palett (`--nr-*` i
`globals.css`, kald blå/grå, `--nr-accent: #1f5fd6`), additiv og
scoped til veiviseren alene, samme "co-eksisterende paletter via
Tailwind arbitrary-value-syntaks"-mønster som clubhouse-paletten
(punkt 39) -- ingen `@theme inline`-kobling, ingen påvirkning på
resten av appen.
Gammel `components/new-round.tsx` (3364 linjer) SLETTET -- bekreftet
via grep at kun `app/my-rounds/new/page.tsx` importerte den (de tre
andre grep-treffene, i `course-template-editor.tsx`/
`round-leaderboard.tsx`/`round-detail.tsx`, var bare beskrivende
norske kommentarer, urørt). Ny modul under
`components/ny-runde/` (`wizard-shell.tsx`, `wizard-context.tsx`,
fem steg-filer, `primitives.tsx`, `progress.tsx`, `footer-bar.tsx`,
`create-course-form.tsx`) + `lib/ny-runde/` (`types.ts`,
`formats.ts`, `api.ts`).
V0s eksport brukte mock-data gjennomgående -- hele portingsjobben
var å koble hvert steg til de faktiske backend-kontraktene fra
`app/routers/rounds.py` og den gamle `new-round.tsx`, IKKE bare et
overflatisk bytte av komponentnavn: `Gender`-oversettelse
(`"m"|"f"` API ↔ `"mann"|"kvinne"|"annet"` UI), `Tee`-formen
forenklet til kun `{id, name, genders}` (aldri CR/Slope til klienten),
`CourseMeta`-discriminated-union for teeoff- vs. egen-bane, ekte
`POST /rounds``POST .../sides``POST .../participants`
`PATCH .../participants/{id}`-sekvens i `submit()`, ekte
kontosøk/gjeste-oppslag i steg 3, ekte banesøk (teeoff-fasilitet +
egne baner, inkl. geolokasjon for "i nærheten") i steg 1.
**Fire reelle bugs funnet og rettet under scratch-verifisering:**
1. `TextInput` i `primitives.tsx` manglet `forwardRef` (feil i selve
V0-eksporten, ikke i portingen) -- ga 3 TS-feil der nedstrøms kode
sendte `ref` til komponenten. Rettet ved å pakke inn med
`forwardRef`, `tsc --noEmit` gikk fra 3 feil til rent.
2. Steg 3 viste "Utslag: Ikke valgt" for eieren selv etter at tee var
valgt i steg 1 -- min egen portingsfeil, jeg hadde ikke tatt med
den ekte `chooseCourse()`s side-effekt som synker valgt tee inn i
spillerlisten. Rettet ved å legge `players:
state.players.map(...)` til i `pickCourse()`
(`step1-course-time.tsx`). Verifisert rettet ved reload.
3. Manglende "Ingen"/"Alle"-hurtigknapp og "ingen kategori
valgt"-advarsel i steg 5s kategorivelger (samme mangel som ble
oppdaget og rettet i selve appen 2026-08-06, se punkt 36 -- V0s
eksport hadde ikke fått denne konteksten). Lagt til i
`step5-sharing.tsx`, samme mønster som punkt 36 (`role="group"`,
`aria-pressed`, `role="alert"` ved null valgt). Browser-verifisert
i scratch: "Ingen" tømmer alle 10 avkrysninger og viser advarselen,
"Alle" gjenoppretter alle.
4. Fulgte først en blindvei: trodde "Legg til gjest"-knappen i steg 3
var usynlig/klikk-slukende bak den klissede (`sticky bottom-0`)
`FooterBar`-en (samme feilklasse som `pb-32`-saken 2026-08-02,
nevnt i den gamle `new-round.tsx`s egne kommentarer) -- la til
`pb-28``<main>`. Padding-fiksen var faktisk RIKTIG og
nødvendig (uten den er det for lite scroll-klaring til å noensinne
få knappen helt fri av footeren), men den første reproduksjonen av
"feilen" var selv et testverktøy-artefakt: et koordinat-basert
klikk uten forutgående scroll traff footeren fordi den, ved
`scrollY: 0`, visuelt ligger over knappen (klissete element som
ikke har "festet seg" ennå fordi normal dokumentflyt allerede
plasserer det nederst i viewport). Bekreftet ved å måle
`getBoundingClientRect()` for begge før/etter scroll: uten scroll
overlapper de nesten fullstendig (y:776 vs. top:775); etter
`scrollIntoView` (174px, alt tilgjengelig scroll) står knappen på
y:602, godt klar. Et ekte, koordinat-basert klikk etter scroll
fungerte perfekt. Konklusjon: `pb-28`-fiksen var korrekt og er
beholdt (den gir nødvendig klaring når brukeren scroller helt ned),
ingen ytterligere kodefeil forelå.
`tsc --noEmit` rent. **Scratch-verifisert i ekte nettleser** (ellevte
scratch-miljø denne økten, samme migrasjon-002-forsiktighet som
punkt 37-40, `teecup_db`s ACL bekreftet uendret før og etter): full
veiviser-gjennomkjøring for Fourball -- egen bane opprettet og valgt,
tee/HCP riktig synket til eier, gjest uten konto lagt til, begge
spillere tildelt hver sin side i steg 4, "Ingen"/"Alle"-toggle testet
i steg 5, fullført innsending. **DB-verifisert direkte**: `round`-rad
med riktig `play_format`/`visibility_mode`/bane-/tee-snapshot,
to `round_side`-rader, begge `round_participant`-rader korrekt
knyttet til hver sin `round_side_id` med riktig
HCP-/tee-/stat_level-snapshot. Ingen konsollfeil. Scratch-miljøet
(containere, images, DB, rolle) ryddet opp fullstendig etterpå.
**Rullet ut 2026-08-08** sammen med punkt 39 og 40, etter eksplisitt
brukerbekreftelse ("Rull ut alle tre samlet"): `docker compose build
teecup_frontend && docker compose up -d teecup_frontend` (kun
frontend-image, ingen migrasjon). Bekreftet `teecup_api`s image
UENDRET (bygget 2026-08-07, ikke i dag) -- containeren ble kun
re-opprettet fra samme eksisterende image pga. avhengighetskjeden i
`docker-compose.yml`, ikke bygget på nytt, så de uncommittede
backend-endringene som lå i arbeidstreet (`rounds.py`,
`handicap_engine.py` m.fl., urelatert til denne økten) ble IKKE
utilsiktet rullet ut. `https://teecup.golf/` bekreftet oppe, ingen
konsollfeil på innloggingssiden. Autentisert gjennomgang av
dashbord/BottomNav/ny-runde-veiviseren i produksjon overlatt til
brukeren selv (krever ekte pålogging).
42. **`/logg-inn` gjort til den EKTE innloggingssiden (erstatter `/` sin
gamle `LoginForm`) — 2026-08-08.** Brukeren la merke til at
innloggingssiden fortsatt viste "det gamle grensesnittet" etter
punkt 39-41s utrulling -- undersøkelse avdekket at `/logg-inn`
(clubhouse-palett, `TeeCupAuth`) var en fullstendig FORELDRELØS V0-
utforskning: INGENTING i appen lenket eller redirectet dit (bekreftet
ved grep), alle ~11 uautentisert-steder pekte fortsatt til `/` (gammel
`LoginForm`, Forest Green). Se dashboard.tsx sin egen kommentar
(linje 26-34) som allerede erkjente dette -- "fra en tidligere, ennå
ikke integrert /logg-inn-utforskning".
**Kritisk oppdagelse FØR arbeidet startet:** `TeeCupAuth` var 100 %
mock -- `apiSendMagicLink`/`apiPasswordLogin`/`apiJoinByCode` var alle
`sleep()`-baserte stubber (hardkodet demo-passord `"teecup123"`,
hardkodede demo-koder `TEECUP`/`RYDER-25`/`HOST2026`, en falsk
"2FA-kode sendes"-tekst som ALDRI faktisk sendte noe). Dette ble
eksplisitt flagget til brukeren (AskUserQuestion) FØR noe ble bygget,
siden dette er sikkerhetskritisk kode og omfanget var langt større enn
en ren redirect-ombytting -- brukeren bekreftet "gjør full port nå".
**Full port utført:**
- `components/teecup/teecup-auth.tsx` skrevet om fra bunnen: ekte
`POST /auth/request-link` (magic link), ekte
`POST /auth/login-password` (med `LoginResult`-status-håndtering
identisk med den gamle `LoginForm`), ekte
`GET /public/tournaments/by-code/{code}`. Alle demo-hint/mock-tekster
fjernet. `TwoFactorVerifyForm`/`TwoFactorSetupForm`
(`components/two-factor-flow.tsx`) gjenbrukt UENDRET -- samme
"delt komponent ikke re-stylet"-mønster som `RoundCard`/
`TournamentCard` i dashbord-reskinnet (punkt 39) -- lavest mulig
risiko for sikkerhetskritisk 2FA-kode.
- `app/logg-inn/page.tsx`: fikk samme server-side allerede-innlogget-
sjekk (`cookies()` + `/auth/me`) som `/`-siden hadde.
- `app/page.tsx` (root): redusert til en tynn videresending
(autentisert → `/dashboard`/`/account`, uautentisert → `/logg-inn`)
-- beholdt for gamle bokmerker/lenker til `teecup.golf/`. Gamle
`LoginForm`/`components/login-form.tsx` SLETTET (bekreftet ingen
andre importer via grep).
- Alle 11 `router.replace("/")`-steder (401-håndtering + logout) i
`dashboard.tsx`, `friends.tsx`, `more-menu.tsx`,
`account-settings.tsx`, `notifications.tsx`, `course-rounds.tsx`,
`own-rounds.tsx`, `rounds-stats-summary.tsx` byttet til
`router.replace("/logg-inn")`. `verify-form.tsx` sin
"Be om en ny lenke"-fallback-lenke (`href="/"`) byttet til
`/logg-inn`.
**Én reell bug funnet og rettet under scratch-verifisering:**
`app/logg-inn/page.tsx` kalte `redirect()` INNI `try`-blokken som
henter `/auth/me` -- Next.js sin `redirect()` fungerer ved å kaste en
egen `NEXT_REDIRECT`-kontrollflyt-exception som MÅ boble videre
urørt til Next.js sin render-maskineri. Den tomme `catch {}`-en rundt
slukte denne stille, så en allerede innlogget bruker som besøkte
`/logg-inn` fikk se innloggingsskjemaet på nytt i stedet for å bli
sendt videre -- oppdaget fordi en ekte innlogget test-bruker IKKE ble
omdirigert i scratch, mens en direkte nettleser-`fetch("/auth/me")`
(som går via `next.config.mjs` sin rewrite, ikke gjennom denne
server-komponentens egen kode) bekreftet sesjonen var helt gyldig --
avslørte at feilen satt i AKKURAT denne serverkomponentens egen
try/catch-struktur. Den opprinnelige `/`-sidens ekvivalente kode
unngikk dette ved å KUN sette `authenticated`/`profileComplete`-
variabler inni try/catch og kalle `redirect()` etterpå, UTENFOR
blokken -- samme mønster gjeninnført her. `tsc --noEmit` rent etter
fiks.
**Scratch-verifisert i ekte nettleser** (tolvte scratch-miljø denne
økten, samme migrasjon-002-forsiktighet som punkt 37-41 --
`teecup_db`s ACL bekreftet uendret før og etter; `TEECUP_DEV_LOG_MAGIC_LINKS=true`
brukt for å hente ekte magic-link-tokens fra API-loggen i stedet for å
sende ekte e-post til testadresser): full runde -- magic-link-
forespørsel + `/verify?token=`-innlogging (ny bruker auto-opprettet,
korrekt sendt til `/account` pga. ufullstendig profil), passord-
innlogging med feil passord (ekte feilmelding "E-post eller passord
er feil." fra `/auth/login-password`), invitasjonskode med ugyldig
kode (ekte feilmelding fra `/public/tournaments/by-code/`), logout
(→ `/logg-inn`), uautentisert besøk til `/dashboard` (→ `/logg-inn`,
ikke lenger `/`), `/` og `/logg-inn` besøkt allerede innlogget med
ufullstendig profil (→ `/account`) og med fullført profil
(→ `/dashboard`, satt via direkte `PATCH /auth/profile`-kall for å
unngå å klikke gjennom fødselsdato-datepickeren manuelt). Ingen
konsollfeil. **Ikke click-through-testet:** `2fa_required`-grenen
(krever en konto med 2FA allerede aktivert) -- vurdert lav risiko
siden `TwoFactorVerifyForm`/`TwoFactorSetupForm` er gjenbrukt helt
uendret fra den allerede beviste `LoginForm`-implementasjonen, kun
kablingen frem til dem (`handleLoginResult`) er ny kode, og den er
identisk portert fra `LoginForm`. Scratch-miljøet (containere, images,
DB, rolle) ryddet opp fullstendig etterpå, inkl. en disk-full-hendelse
underveis (`docker builder prune` frigjorde 11,75 GB build-cache fra
denne øktens mange scratch-bygg -- ingen kjørende containere eller
volumer berørt).
**Rullet ut 2026-08-08**, etter egen, eksplisitt brukerbekreftelse
separat fra punkt 39-41s batch-bekreftelse (sikkerhetskritisk --
autentisering). `docker compose build teecup_frontend && up -d` --
denne gangen ble kun `teecup_frontend` gjenskapt (`teecup_api` forble
"Running" med bekreftet uendret image-tidsstempel før/etter, ulikt
forrige utrulling i punkt 39-41 der en avhengighets-kjede-bivirkning
gjenskapte -- men ikke bygde om -- `teecup_api`). `https://teecup.golf/`
bekreftet å redirecte til `/logg-inn` med clubhouse-designet, ingen
konsollfeil.
43. **PRODUKSJONSBUG: "Laster baner…" hang for alltid ved offisiell
banevalg i ny-runde-veiviseren — funnet og fikset 2026-08-08.**
Brukeren rapporterte (skjermbilde) at steget etter å ha valgt et
teeoff-anlegg ("Baner") aldri kom videre fra "Laster baner…". Dette
hadde vært live siden punkt 41s utrulling -- treffer ALLE nye runder
som starter fra en offisiell (teeoff-) bane, ikke egne baner.
**Rotårsak:** `fetchFacilityCourses()` i `lib/ny-runde/api.ts` antok
at `GET /rounds/official-search/{slug}` returnerer banene direkte som
en array (`data.map(...)`), men det ekte endepunktet returnerer et
OBJEKT -- `OfficialFacilityDetail = {slug, name, courses: [...]}`
(`app/routers/rounds.py` linje 530). `data.map` er ikke en funksjon
på et objekt, så kallet kastet en `TypeError` -- og siden
`OfficialCourses` sin `useEffect` i `step1-course-time.tsx` kun hadde
`.then(...)` uten `.catch(...)`, ble denne exceptionen en stille,
ufanget promise-rejection: `setCourses` ble aldri kalt, og
`courses === null`-grenen ("Laster baner…") viste seg for alltid.
Bug i min egen porting (punkt 41) -- **denne konkrete stien
(offisiell teeoff-bane) ble aldri scratch-testet den runden**, kun
"egen bane"-veien ble klikket gjennom (se punkt 41s test-notat).
**To rettelser:**
1. `fetchFacilityCourses()`: leser nå `data.courses.map(...)` i
stedet for `data.map(...)`.
2. `OfficialCourses` (`step1-course-time.tsx`): la til en reell
`.catch()` + en `loadError`-tilstand (`role="alert"`, samme
`--nr-danger`-stil som resten av veiviseren) -- uten denne ville
ENHVER fremtidig feil her (f.eks. teeoff nede, 502) gitt nøyaktig
samme uendelige "Laster baner…"-hang på nytt, uansett om
datakontrakt-bugen over er rettet. Dette er defensivt, ikke bare
en fiks for det spesifikke tilfellet.
`tsc --noEmit` rent. **Scratch-verifisert i ekte nettleser** (trettende
scratch-miljø denne økten, samme migrasjon-002-forsiktighet, `teecup_db`
bekreftet uendret) MOT EKTE teeoff-data (samme `teeoff_db`/`teeoff_api`
som produksjon deler, nådd direkte på `teeoff_default`-nettverket):
søkte opp "Hvaler Golfklubb" (reell fasilitet), banen ("Hovedbanen, 2
utslag") lastet korrekt i stedet for å henge, gikk videre til
felt-steget og bekreftet ekte utslag (Gul/Rød) hentet riktig fra
teeoff. Ingen konsollfeil (kun én forhåndseksisterende, ikke-relatert
"Deprecated feature"-info-melding fra nettleseren selv). Scratch-miljøet
ryddet opp fullstendig.
**Rullet ut umiddelbart** (produksjonsbug som traff alle live
brukere som prøvde å starte en runde på en offisiell bane) --
`docker compose build teecup_frontend && up -d`, kun frontend-image,
`teecup_api`s image-tidsstempel bekreftet uendret før/etter. Kunne
ikke click-through-verifisere selve produksjonssiden med ekte
brukerkonto (krever brukerens egen pålogging) -- basert på grundig
scratch-verifisering mot ekte teeoff-data rett før utrulling.
44. **PRODUKSJONSBUG: rundedeling med venner i flere kategorier virket
ikke som forventet — funnet og fikset 2026-08-08, se ADR-036
Beslutning E.** Brukeren rapporterte at en runde delt med kategorien
"Make" ikke ble synlig for vedkommendes ektefelle, til tross for at
"Make" var huket av. Diagnostisert direkte mot ekte `teecup_db`
(skrivebeskyttede spørringer): eieren (Erol) hadde kategorisert
ektefellen (Gina) under TRE kategorier (`close_family`,
`extended_family`, `spouse`), mens runden kun delte
(`close_family`, `golf_friends`, `spouse`) -- `extended_family`
manglet. `_can_view_round` krevde den gang at ALLE en venns
kategorier måtte være i rundens synlige sett (presisert av bruker
2026-07-29, kun dokumentert i kode-kommentarer, ALDRI i
ARCHITECTURE_DECISIONS.md -- selve dokumentasjonshullet som gjorde
dette vanskelig å spore tilbake), ikke bare én relevant kategori.
Brukeren fikk vist mekanismen konkret (hvilke kategorier Gina var
tagget i, hvilke runden delte, den strenge AND-regelen) og valgte via
et eksplisitt spørsmål å reversere til "minst én kategori er nok"
(som var den OPPRINNELIGE ADR-036 Beslutning B-regelen fra
2026-07-25 -- 2026-07-29-presiseringen hadde altså strammet inn en
regel utover det som noensinne ble ordentlig dokumentert som en
bevisst arkitekturbeslutning).
**Rettet i alle fire duplikate SQL-steder** (samme
kopier-inn-i-SQL-mønster som allerede omtalt i ADR-036 Beslutning B):
`app/routers/rounds.py` sin `_can_view_round` (selve
tilgangssjekken), `_friends_who_can_see_round` (varsel-fan-out ved
rundeopprettelse/fullføring), `list_friends_on_course`
(dashbordets "Venner på banen"); `app/routers/round_messages.py` sin
`/feed`-listing. Alle gikk fra "`NOT EXISTS` en kategori UTENFOR
synlig sett" til et enklere "`EXISTS` en kategori INNENFOR synlig
sett" -- enklere spørringer, ikke bare annen semantikk.
**Scratch-verifisert i ekte API-kall** (fjortende scratch-miljø denne
økten, samme migrasjon-002-forsiktighet, `teecup_db` bekreftet
uendret): gjenskapte den ekte Erol/Gina-situasjonen (venn kategorisert
i to kategorier, runde deler kun én av dem) -- vennen fikk nå 200 på
`GET /public/rounds/{id}` (tidligere 403). Bekreftet at motsatt
tilfelle (venn med INGEN overlappende kategori) fortsatt korrekt gir
403 -- ingen utilsiktet åpning av tilgangskontrollen. `/friends/
on-course` og `/feed` (de to andre endrede spørringene) begge 200
uten SQL-feil. Scratch-miljøet ryddet opp fullstendig.
**Rullet ut umiddelbart** (produksjonsbug, brukerens egen delte runde
var konkret berørt) -- `docker compose build teecup_api && up -d`,
KUN backend-image (ingen migrasjon, `visibility_mode`/
`round_visible_category` er uendret skjema), `teecup_frontend`s
image-tidsstempel bekreftet uendret før/etter. **Bekreftet direkte
mot ekte data etter utrulling**: samme spørring som
`_can_view_round` nå bruker, kjørt skrivebeskyttet mot den ekte
Erol/Gina/runde-situasjonen, returnerer nå `true`.
45. **Slag-for-slag GPS-avstandsmåling — inngangspunkt 1 av 2, i
scoring-veiviseren — 2026-08-08 (ADR-048).** Backend (migrasjon
`060_round_shot.sql`, `ShotIn`/`ShotOut`/`ShotShareIn`-endepunktene i
`rounds.py`, `lib/geo.ts` Haversine, Mapbox-token-plumbing) var
allerede bygget og scratch-verifisert fra tidligere i økten (se
ADR-048 i ARCHITECTURE_DECISIONS.md). Denne runden: selve
frontend-flaten.
V0-prompt skrevet med `DESIGN_SYSTEM.md`s tokens som en HARD
begrensning (i motsetning til dashbord-/veiviser-/login-promptene
tidligere i økten, som bevisst fikk null designføring) -- dette
arket lever INNI den eksisterende Forest Green-skjermen
(`round-detail.tsx`), ikke som en ny frittstående side. Eksporten
(zip 4) var meget tro mot spesifikasjonen: `next/dynamic({ssr:false})`
for kartsteget, ingen `mapbox-gl`-import i det hele tatt på
GPS-only-stien, kartet mountes kun én gang per arkåpning
(tom-deps `useEffect`, klikk/drag re-initialiserer aldri).
`components/shot/shot-measurement-sheet.tsx` +
`components/shot/map-point-picker.tsx` kopiert inn uendret (kun
én import-sti rettet: V0s egen plassholder-`ClubPicker` byttet til
den nå faktisk utrukne, delte `components/teecup/club-picker.tsx`
-- ren utrekking fra `round-detail.tsx`s tidligere lokale kopi,
ingen atferdsendring for veiviserens eksisterende bruk). Ny
`components/ui/textarea.tsx`-shadcn-primitiv lagt til (fantes ikke
fra før).
Koblet inn som `ScoringWizard`s nye `roundId`-prop + lokal
`shotSheetOpen`/`shotCount`-state: "Mål et slag"-knapp rett etter
kølle-plukkeren i detalj-steget, henter eksisterende slag-antall on
mount (`GET .../holes/{n}/shots`), sender til ekte
`POST .../holes/{n}/shots` ved innsending og (hvis deling valgt)
en påfølgende `POST /rounds/{id}/shots/{shot_id}/share`.
**Scratch-verifisert** (femtende scratch-miljø denne økten, samme
migrasjon-002-forsiktighet, `teecup_db` bekreftet uendret): full
klikk-gjennomgang fra "Mål et slag" til arket åpner riktig, korrekt
feilmelding+"Prøv igjen" når GPS-tillatelse mangler (bekreftet ekte
-- CDP-automatiserte nettlesersesjoner har `geolocation`-tillatelse
permanent `denied` uten noen dialog å akseptere, en verktøy-
begrensning i selve test-miljøet, ikke i appen). Siden selve
GPS-suksess-stien derfor ikke lot seg klikke gjennom i denne
økten, ble de eksakte kallene `submitShot()` sender (opprett slag,
list slag, del) i stedet verifisert direkte mot API-et med samme
data-kontrakt -- alle tre 200/201, ingen feil i API-loggen. Bekreftet
i ekte nettleser at slag-tellingen faktisk oppdateres og vises i
knappeteksten ("Mål et slag (1 målt)") etter et slag er opprettet.
Scratch-miljøet ryddet opp fullstendig.
**Ikke rullet ut** -- `round_shot`-tabellen finnes ennå ikke i ekte
`teecup_db` (migrasjonen er aldri kjørt der), og `.env` har fortsatt
tomme Mapbox-token-plassholdere (kart-veien vil ikke fungere i
produksjon før brukeren skaffer et ekte token). Gjenstår før dette
kan vurderes for utrulling: inngangspunkt 2 (alltid-synlig
"N slag målt"-merkelapp på selve hull-kortet, for retroaktiv måling
OG for lagformater/side-eide hull -- denne `ScoringWizard`-veien
dekker kun deltaker-eide hull, siden delt-ball-formater bruker en
egen, enklere side-veiviser uten dette detalj-steget), ekte
Mapbox-tokens, og migrering av `060_round_shot.sql` mot ekte
`teecup_db` (krever egen, eksplisitt brukerbekreftelse).