Update Todos
Skrive migrasjon 052 (guest_first_name/guest_last_name + backfill) Bygge backend: splitt navn i ParticipantCreate/Update, auto-synk guest_name Bygge backend: GET known-guest-oppslag (e-post-basert, scoped til egen bruker) Bygge backend: retroaktiv e-post-kobling ved innlogging (auth.py) Bygge backend: HTML-e-post-støtte i email.py (multipart/alternative) Bygge backend: scorekort/statistikk-e-post ved complete_round (individuell + delt-ball) Frontend: e-post-felt + navnesplitt + autofyll fra søk i AddGuestForm Frontend: "vi kjenner igjen denne e-posten"-forslag i AddGuestForm Scratch-verifisere hele kjeden (migrasjon+backend+frontend+e-post) end-to-end Kjøre migrasjon mot ekte teecup_db og rulle ut (etter bekreftelse) Alt seks punkter er live: e-postfelt, navnesplitt, retroaktiv kobling (med bekreftelse-før-HCP-telling), automatisk oppsummerings-e-post ved fullføring, autofyll fra søket, og det personvern-scopede "gjenkjenn denne e-posten"-forslaget. Migrasjonen kjørte rent mot ekte data (3 eksisterende gjester korrekt splittet), test_isolation.sql fortsatt 12/12, teeoff.no upåvirket.
This commit is contained in:
parent
d395f86753
commit
e74fe5d949
1 changed files with 122 additions and 0 deletions
122
CHANGELOG.md
122
CHANGELOG.md
|
|
@ -6336,6 +6336,128 @@ Ferdig og verifisert:
|
|||
teecup_frontend` (gjenskapte også `teecup_api` som vanlig bivirkning).
|
||||
Begge containere boot-et rent, `/health`/`/my-rounds/new` → 200,
|
||||
`teeoff.no` upåvirket.
|
||||
- **Midlertidige spillere (gjester på frittstående runder): full runde
|
||||
ferdig -- e-post, navnesplitt, retroaktiv kobling, autofyll, "gjenkjenn
|
||||
gjest"-oppslag, BYGGET, GRUNDIG SCRATCH-/BROWSERVERIFISERT OG LIVE
|
||||
(2026-08-03):** brukeren ba om seks ting samtidig (e-post som valgfritt
|
||||
felt manglet, navnesplitt for-/etternavn, retroaktiv lagring/kobling av
|
||||
gjeste-runder til en fremtidig konto, automatisk e-post med scorekort+
|
||||
statistikk+invitasjon ved fullføring, autofyll fra søkefeltet inn i
|
||||
gjesteskjemaet, og "gjenkjenn en tidligere registrert gjeste-e-post") og
|
||||
ba eksplisitt om innspill på hva mer som var lurt. Grundig
|
||||
kodeutforskning FØR noe ble bygget avdekket presist hva som faktisk
|
||||
manglet: `guest_email` fantes ALLEREDE i backend (2026-07-26), bare
|
||||
aldri eksponert i selve "legg til gjest"-skjemaet; `guest_name` var ETT
|
||||
enkelt tekstfelt brukt i 10+ spørringer.
|
||||
**Tre load-bærende avklaringer bekreftet av bruker** (AskUserQuestion):
|
||||
(A) en retroaktivt koblet runde (noen registrerte deg som gjest FØR du
|
||||
hadde konto) teller IKKE automatisk mot faktisk HCP -- `exclude_from_
|
||||
handicap` settes til `true` som default ved kobling, personen må selv
|
||||
slå den på (samme mekanisme ADR-038 allerede bygget, kun default
|
||||
snudd for denne ene banen inn). Begrunnelse: en org-turnering sin
|
||||
eksisterende `link_player_by_email`-presedens (ADR-017) er trygg fordi
|
||||
org-scoring aldri teller mot faktisk HCP -- frittstående runder GJØR
|
||||
det (ADR-038), så blind auto-inkludering ville latt en fremmed
|
||||
påvirke noens HCP uten samtykke. (B) treffer en gjeste-e-post en
|
||||
EKSISTERENDE konto, opprettes gjesten likevel -- kobles ved neste
|
||||
innlogging, ikke et eget "denne personen har konto"-forgreiningssteg
|
||||
(enklere, bevisst valgt fremfor det opprinnelig anbefalte). (C) bygget
|
||||
i én samlet runde (skjema→backend→frontend→e-post lagvis, samme
|
||||
disiplin som ellers i prosjektet).
|
||||
**Migrasjon `052_guest_name_split.sql`:** `round_participant.
|
||||
guest_first_name`/`guest_last_name` lagt til, `guest_name` beholdt
|
||||
UENDRET som et auto-synkronisert, lagret "fullt navn" (samme mønster
|
||||
som `app_user.display_name` synkes fra `first_name`/`last_name`,
|
||||
2026-07-25-bugfiksen) -- unngikk å måtte røre alle eksisterende
|
||||
SELECT-steder. Backfill: "første ord = fornavn, resten = etternavn".
|
||||
**Retroaktiv kobling** (`app/routers/auth.py`, ny
|
||||
`_link_round_participants_by_email`, kalt fra BEGGE innloggingsveiene
|
||||
-- magic-link og passord, samme "kjør trygt på hver innlogging"-
|
||||
idempotens som `link_player_by_email`): INGEN SECURITY DEFINER-bro
|
||||
trengs siden `round`/`round_participant` ikke har RLS (ADR-033
|
||||
Beslutning A) -- en rett UPDATE er nok. `NOT EXISTS`-vaktet mot en
|
||||
allerede eksisterende ekte deltaker-rad på samme runde (ville ellers
|
||||
brutt migrasjon 027 sin UNIQUE-indeks). **Reell bug funnet OG fikset
|
||||
UNDER scratch-testing:** en første versjon nullet `gender` sammen med
|
||||
de andre gjeste-feltene ved kobling -- krasjet med en NOT NULL-
|
||||
violation, siden `gender` er en påkrevd snapshot for ALLE deltakere
|
||||
(brukt til utslags-rating-oppslag), ikke bare gjester. Rettet ved å la
|
||||
`gender` stå urørt (uansett låst mot videre endring av `update_
|
||||
participant` sin egen `guest_only_fields`-sjekk så snart `user_id` er
|
||||
satt).
|
||||
**HTML-e-post** (`app/email.py`, FØRSTE i appen): `_send_sync` fikk en
|
||||
valgfri `html_body`-parameter (`EmailMessage.add_alternative`,
|
||||
multipart/alternative -- ekte tekst-fallback bevart). Ny
|
||||
`send_round_summary_email` -- scorekort som HTML-tabell, statistikk
|
||||
gradert etter `stat_level`/individuell- vs. delt-ball (kalleren bygger
|
||||
`stat_lines`, e-post-modulen antar ingenting), ekte magic-link-
|
||||
innlogging (samme token-mønster som `send_scorecard_invitations`,
|
||||
ADR/CLAUDE.md 2026-07-28). **Navneformat (ufravikelig regel):**
|
||||
hilsenen er "Hei {fornavn}," -- direkte adressering, kun fornavn, aldri
|
||||
fullt navn. Trigget fra en ny `_send_guest_round_summaries`, kalt fra
|
||||
`complete_round` for hver deltaker med `guest_email` satt -- bygger
|
||||
scorekortet fra ENTEN `_build_participant_holes` (individuell) ELLER
|
||||
`_build_side_holes` (delt-ball, ADR-039), gjenbruker eksisterende
|
||||
hjelpefunksjoner uendret, ingen egen regnelogikk.
|
||||
**Known-guest-oppslag** (`GET /rounds/guests/known`, statisk rute
|
||||
plassert FØR `/rounds/{round_id}` i filen, samme mønster som
|
||||
`/rounds/stats/summary`): bevisst scoped til KUN den spørrende
|
||||
brukerens EGNE tidligere registrerte gjester (`WHERE r.owner_user_id =
|
||||
$1`) -- et globalt oppslag ville latt hvem som helst skrive inn en
|
||||
tilfeldig e-post og se navn/kjønn/HCP en HELT ANNEN organisator har
|
||||
registrert, en reell personvernlekkasje unngått FØR bygging (flagget
|
||||
proaktivt til bruker, bekreftet enig).
|
||||
**Frontend** (`round-detail.tsx`): `AddGuestForm` skrevet om -- separate
|
||||
Fornavn/Etternavn-felt (etternavn valgfritt), nytt E-post-felt med
|
||||
debounced known-guest-oppslag + en "Bruk disse opplysningene"-forslags-
|
||||
boks, `initialName`-prop fra søkefeltet (`AddParticipantForm`s `query`)
|
||||
autofyller navnefeltene ved "Legg til uten konto (gjest)" via en ny
|
||||
`splitName()`-heuristikk (samme "første ord/resten" som backfillen).
|
||||
`EditParticipantPanel` oppdatert til samme to-felts navnestruktur (var
|
||||
i ferd med å bli en regresjon siden backend ikke lenger godtar
|
||||
`guest_name` direkte). **Reelt, uforespurt funn fanget under
|
||||
implementering:** rundeoppsett-veiviseren (`new-round.tsx`) sender
|
||||
OGSÅ `guest_name` ved opprettelse av spillere i Steg 3 -- ville brutt
|
||||
hvis ikke oppdatert samtidig; løst med samme splitt-heuristikk direkte
|
||||
ved innsending (ingen UI-endring i wizarden denne runden, kun
|
||||
kontraktsrettelse).
|
||||
**Scratch-verifisert grundig, 41/41 sjekker** (isolert
|
||||
`teecup_app_scratch`-rolle + isolert scratch-MinIO + engangs API-
|
||||
container, alle 52 migrasjoner kjørt friskt, samme mønster som hele
|
||||
prosjektet): navnesplitt+auto-synk, delvis PATCH bevarer det andre
|
||||
feltet, validering (verken/begge user_id+guest_first_name avvist),
|
||||
known-guest-oppslag (funnet for egen bruker, IKKE synlig for en annen
|
||||
organisator -- personvern-scopingen bekreftet presist), full
|
||||
fullførings-e-post-syklus (dev-log bekreftet både magic-link OG
|
||||
rundeoppsummering), retroaktiv kobling (user_id satt, gjeste-felt
|
||||
nullstilt, `gender` BEVART, `exclude_from_handicap=true`, `display_
|
||||
name` viser nå kontoens ekte navn), idempotent gjeninnlogging, OG et
|
||||
eget delt-ball-scenario (foursome) som bekreftet ingen krasj ved
|
||||
fullføring og korrekt sideoppsummering i e-post-loggen.
|
||||
**Deretter en FULL, ekte nettleser-gjennomgang** (Chrome DevTools MCP):
|
||||
hele "+ Medspiller"→"Legg til uten konto"-flyten klikket gjennom med et
|
||||
navn som ikke fantes -- bekreftet AUTOFYLL fungerte (Fornavn/Etternavn
|
||||
korrekt splittet), skrev inn en kjent gjeste-e-post og bekreftet
|
||||
"Vi fant Anna fra en tidligere runde..."-forslaget dukket opp, trykket
|
||||
"Bruk disse opplysningene" og bekreftet navn/kjønn ble overskrevet
|
||||
korrekt (HCP forble tomt, siden Anna ikke hadde noen registrert),
|
||||
sendte inn skjemaet og bekreftet gjesten dukket opp i spillerlisten
|
||||
UTEN konsollfeil, åpnet rediger-panelet og bekreftet det viser samme
|
||||
to-felts navnestruktur korrekt forhåndsutfylt. E-postens faktiske
|
||||
HTML/tekst-innhold generert og inspisert direkte (ikke bare at
|
||||
utsendingen ble trigget) -- bekreftet riktig "Hei Kari,"-hilsen,
|
||||
korrekt hull-tabell inkl. et uspilt hull vist som "–", og korrekt
|
||||
byggede statistikklinjer.
|
||||
**Rullet ut mot ekte systemer 2026-08-03**, bruker bekreftet
|
||||
eksplisitt (plan vist FØR migrasjonen, per CLAUDE.md sin ufravikelige
|
||||
regel): migrasjon 052 kjørt mot ekte `teecup_db` (nye kolonner
|
||||
bekreftet, backfill verifisert nøyaktig mot de 3 eksisterende gjeste-
|
||||
radene i produksjon -- "Christer Heitun"→"Christer"/"Heitun", "Sigurd"
|
||||
→"Sigurd"/null, "Vidar Hoksrød"→"Vidar"/"Hoksrød" -- `test_isolation.sql`
|
||||
fortsatt 12/12), deretter `docker compose up -d --build teecup_api
|
||||
teecup_frontend`. Begge containere boot-et rent, `/health`/`/dashboard`
|
||||
→ 200, `GET /rounds/guests/known` anonymt ga korrekt `401` gjennom hele
|
||||
produksjonsstacken (ikke en rå 404), `teeoff.no` upåvirket.
|
||||
|
||||
Neste steg:
|
||||
0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet
|
||||
|
|
|
|||
Loading…
Reference in a new issue