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).
|
teecup_frontend` (gjenskapte også `teecup_api` som vanlig bivirkning).
|
||||||
Begge containere boot-et rent, `/health`/`/my-rounds/new` → 200,
|
Begge containere boot-et rent, `/health`/`/my-rounds/new` → 200,
|
||||||
`teeoff.no` upåvirket.
|
`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:
|
Neste steg:
|
||||||
0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet
|
0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue