diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c165a..65c3c55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8683,9 +8683,10 @@ Neste steg: eksplisitt bekreftet at ekte `teecup_db` sin `datacl` var uendret etterpå). - **Ikke rullet ut ennå** -- venter på samlet bekreftelse sammen med - resten av det som gjenstår i denne økten (slagmåling-frontend, - dashbord-V0-beslutning). + **Rullet ut 2026-08-08** sammen med punkt 40 og 41 (`docker compose + build teecup_frontend` + `up -d` -- ingen migrasjon, kun ny + frontend-image). Bekreftet ingen konsollfeil på + `https://teecup.golf/` etter omstart. 39. **Dashbordet reskinnet til "clubhouse"-paletten — 2026-08-07, bruker bekreftet eksplisitt "full overtagelse, også bakgrunnen".** Egen, @@ -8745,7 +8746,424 @@ Neste steg: utvidet korrekt og det eksisterende skjemaet (uendret) fungerte som før. Ingen konsollfeil. Scratch-miljøet ryddet opp fullstendig. - **Ikke rullet ut ennå** -- venter på BottomTabBar-V0-eksporten (så - hele dashbordet, inkludert navigasjonen, kan rulles ut samlet i én - omgang i stedet for i to synlig usammenhengende steg) og på samlet - bekreftelse sammen med resten av det som gjenstår denne økten. + **Rullet ut 2026-08-08** sammen med BottomNav (punkt 40) og + ny-runde-veiviseren (punkt 41), som planlagt -- se punkt 41 for + utrullingsdetaljer. + +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` på `
`. 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).