Live nå — kun teecup_frontend redeployet, begge containere friske, teeoff.no upåvirket. Statusdokumentene er oppdatert med rotårsaken (nestet <form> → nettleseren slo sammen skjemaene → «Søk» submittet det ytre økt-skjemaet som ekte sidenavigasjon → vasket bort org-parameteren).
This commit is contained in:
parent
b28f604fe9
commit
0b6e7e4219
4 changed files with 118 additions and 10 deletions
|
|
@ -247,7 +247,8 @@
|
||||||
"Bash(awk -F: '$1>=782 && $1<=980')",
|
"Bash(awk -F: '$1>=782 && $1<=980')",
|
||||||
"Bash(python3 -)",
|
"Bash(python3 -)",
|
||||||
"Bash(python3 -c \"import json,sys; d=json.load\\(sys.stdin\\); print\\(len\\(d\\), 'facilities'\\); print\\(d[:2]\\)\")",
|
"Bash(python3 -c \"import json,sys; d=json.load\\(sys.stdin\\); print\\(len\\(d\\), 'facilities'\\); print\\(d[:2]\\)\")",
|
||||||
"Bash(rm -f /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch.env /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch_app_pw.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_user.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_pass.txt /tmp/scratch_cookies2.txt)"
|
"Bash(rm -f /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch.env /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/scratch_app_pw.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_user.txt /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/minio_pass.txt /tmp/scratch_cookies2.txt)",
|
||||||
|
"Bash(curl -s \"https://teecup.teeoff.no/tournaments/73b6bf38-90cd-4ef7-9535-8b8e108eaa9a?org=test-org-id&name=Test\")"
|
||||||
],
|
],
|
||||||
"additionalDirectories": [
|
"additionalDirectories": [
|
||||||
"/opt/teeoff/deploy",
|
"/opt/teeoff/deploy",
|
||||||
|
|
|
||||||
49
CLAUDE.md
49
CLAUDE.md
|
|
@ -732,11 +732,54 @@ Ferdig og verifisert:
|
||||||
prinsipp som handicap-snapshotten (ADR-007). Server-til-server-kall
|
prinsipp som handicap-snapshotten (ADR-007). Server-til-server-kall
|
||||||
(`teecup_api` → `http://teeoff_api:8000`, internt Docker-nettverk, ingen
|
(`teecup_api` → `http://teeoff_api:8000`, internt Docker-nettverk, ingen
|
||||||
auth trengs). Ny migrasjon `010` (unik `external_course_ref` per org,
|
auth trengs). Ny migrasjon `010` (unik `external_course_ref` per org,
|
||||||
hindrer dupliserte importer). Bygges nå — se ADR-019 for alle fem
|
hindrer dupliserte importer). Se ADR-019 for alle fem delbeslutningene.
|
||||||
delbeslutningene.
|
**Bygget, scratch-verifisert MOT EKTE `teeoff_api`** (ikke en simulert
|
||||||
|
respons — ekte HTTP-kall til den kjørende produksjonscontaineren, kun
|
||||||
|
lesing): søkte opp "Borregaard" i teeoff sine 174 publiserte anlegg,
|
||||||
|
hentet Borregaard Golfklubb sin hovedbane (18 hull, 4 tee-farger ×
|
||||||
|
kjønn), importerte den til en scratch-org — alle 18 hull med riktig
|
||||||
|
par/stroke-index (1-18, unik), alle 8 tee/tee_rating-rader med riktig
|
||||||
|
full_18 course/slope-rating verifisert direkte i databasen, opprettet
|
||||||
|
deretter en ekte økt med den importerte banen som `course_id` (beviser
|
||||||
|
hele veien til handicap-motoren fungerer, ikke bare selve importen).
|
||||||
|
Reimport av samme bane korrekt avvist (409 DUPLICATE, migrasjon 010 sin
|
||||||
|
indeks). Kryss-org-isolasjon bekreftet, ukjent teeoff-slug ga korrekt 404,
|
||||||
|
`test_isolation.sql` 12/12. Ekte typesjekket produksjonsbuild av
|
||||||
|
frontend-utvidelsen (bane-søk i program-skjemaet: søk anlegg → velg bane →
|
||||||
|
importer, samme UI-mønster som spiller-/bane-type-ahead ellers i appen).
|
||||||
|
**Rullet ut live 2026-07-18**, bruker bekreftet eksplisitt: migrasjon 010
|
||||||
|
kjørt mot ekte `teecup_db` (kun én ny partiell unik-indeks, ingen
|
||||||
|
eksisterende rader rørt), begge containere bygget+redeployet, live
|
||||||
|
sjekker OK, `teeoff.no` upåvirket. **"Bygg i rekkefølgen ting brukes"-
|
||||||
|
planen gjenopptas nå** — blind draw-skjermen er neste steg.
|
||||||
|
**Reell produksjonsbug funnet OG fikset samme dag, rapportert av bruker
|
||||||
|
som faktisk brukte funksjonen:** brukeren klikket seg korrekt via
|
||||||
|
dashbord → turnering → Program-fane (bekreftet med skjermbilde + full
|
||||||
|
klikk-sti, ikke gjettet), åpnet "Hent bane fra teeoff", søkte "Tjøme", og
|
||||||
|
fikk feilmeldingen "Mangler organisasjon i lenken" — URL-en hadde da
|
||||||
|
MISTET `?org=...&name=...`. Root-cause: `OfficialCourseSearch` sitt eget
|
||||||
|
søke-`<form onSubmit={runSearch}>` var rendret INNI `CreateSessionCard`
|
||||||
|
sitt eksisterende `<form onSubmit={handleSubmit}>` -- nestede
|
||||||
|
`<form>`-elementer er ugyldig HTML. Nettleseren slår sammen de to
|
||||||
|
skjemaene i den faktiske DOM-en, så "Søk"-knappen submittet i praksis det
|
||||||
|
YTRE økt-skjemaet som en ekte native side-navigasjon (GET til gjeldende
|
||||||
|
sti, ingen navngitte felt => tom spørrestreng) -- dette vasket bort
|
||||||
|
org-parameteren og landet brukeren på siden sin egen org-guard. **Fikset**
|
||||||
|
ved å fjerne det indre `<form>`-elementet helt (vanlig `<div>` +
|
||||||
|
Enter-tast-håndtering på inputet + `type="button"` i stedet for
|
||||||
|
`type="submit"` på søkeknappen) -- gjør nestede skjemaer strukturelt
|
||||||
|
umulig fremover for denne komponenten. Ekte typesjekket
|
||||||
|
produksjonsbuild kjørt på nytt, kun `teecup_frontend` redeployet (ingen
|
||||||
|
backend-endring). **Lærdom for fremtidige skjermer:** en ny
|
||||||
|
søk-/underskjema-widget som skal plasseres INNI et eksisterende skjema
|
||||||
|
(slik som denne bane-søk-widgeten ligger inni økt-opprett-skjemaet) må
|
||||||
|
ALDRI være et eget `<form>` -- bruk `<div>` + eksplisitt
|
||||||
|
klikk-/Enter-håndtering.
|
||||||
|
|
||||||
Neste steg:
|
Neste steg:
|
||||||
1. Flere V0-skjermer (blind draw, scorekort, leaderboard) — samme mønster:
|
1. Blind draw-skjermen (neste i "bygg i rekkefølgen ting brukes", gjenopptatt
|
||||||
|
etter ADR-019-sidespranget) — deretter scorekort, leaderboard. Samme
|
||||||
|
mønster:
|
||||||
design i V0 (fortsett i samme prosjekt), FORVENT en full re-eksport hver
|
design i V0 (fortsett i samme prosjekt), FORVENT en full re-eksport hver
|
||||||
gang — diff mot live-treet i et scratch-område før noe pakkes ut over
|
gang — diff mot live-treet i et scratch-område før noe pakkes ut over
|
||||||
eksisterende filer, og sjekk om V0-skjermen bygger inn handlinger backend
|
eksisterende filer, og sjekk om V0-skjermen bygger inn handlinger backend
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
| Dedikert app-rolle (ingen superuser/BYPASSRLS) | ✅ | migrasjon 002. |
|
| Dedikert app-rolle (ingen superuser/BYPASSRLS) | ✅ | migrasjon 002. |
|
||||||
| API: oppsett (spillere/lag/roster/økter/matcher/blind draw) | ✅ | Verifisert for ekte mot scratch-db + engangscontainer. |
|
| API: oppsett (spillere/lag/roster/økter/matcher/blind draw) | ✅ | Verifisert for ekte mot scratch-db + engangscontainer. |
|
||||||
| API: scoring (hole_score/match_hole_result, matchstatus, handicap-beregning) | ✅ | ADR-012/014. Verifisert for ekte, inkl. fourball better-ball og race-sikker recompute. |
|
| API: scoring (hole_score/match_hole_result, matchstatus, handicap-beregning) | ✅ | ADR-012/014. Verifisert for ekte, inkl. fourball better-ball og race-sikker recompute. |
|
||||||
| Banedata fra teeoff via API | 🔨 | ADR-004 → **ADR-019** (design besluttet 2026-07-18, bygges nå — sidesprang fra "bygg i rekkefølgen ting brukes", blind draw-skjermen er fortsatt neste steg etterpå). Import (kopi) ved eksplisitt organisator-valg, ikke live oppslag — se ADR-019 i ARCHITECTURE_DECISIONS.md for alle fem delbeslutningene og CLAUDE.md-status for kartleggingen av teeoff sitt `/api/facilities`-API. |
|
| Banedata fra teeoff via API | ✅ | ADR-004 → **ADR-019**, LIVE 2026-07-18. Import (kopi) ved eksplisitt organisator-valg, ikke live oppslag ved hver bruk. Se egen seksjon under. |
|
||||||
| Konfigurerbar handicap-pipeline (4 brytere) | ✅ | ADR-014. Bygget i `app/handicap.py`, brukt av scoring-runden. |
|
| Konfigurerbar handicap-pipeline (4 brytere) | ✅ | ADR-014. Bygget i `app/handicap.py`, brukt av scoring-runden. |
|
||||||
| Ekte autentisering (magic-link + JWT-sesjon) | ✅ | ADR-009. `app/routers/auth.py` + migrasjon `004_auth.sql`. `X-Debug-User-Id`-stubben er helt fjernet. |
|
| Ekte autentisering (magic-link + JWT-sesjon) | ✅ | ADR-009. `app/routers/auth.py` + migrasjon `004_auth.sql`. `X-Debug-User-Id`-stubben er helt fjernet. |
|
||||||
| RLS-tomstreng-fiks (`app_current_org()`) | ✅ | Migrasjon `005_rls_null_guard.sql`. Se detaljer under. |
|
| RLS-tomstreng-fiks (`app_current_org()`) | ✅ | Migrasjon `005_rls_null_guard.sql`. Se detaljer under. |
|
||||||
|
|
@ -502,6 +502,52 @@ andre skjermen når den ble generert i en egen prompt.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Offisiell banedata fra teeoff — ✅ BYGGET OG LIVE 2026-07-18 (ADR-019)
|
||||||
|
|
||||||
|
Bevisst sidesprang fra "bygg i rekkefølgen ting brukes" rett etter
|
||||||
|
program-skjermen: brukeren påpekte at ADR-004s teeoff-integrasjon fortsatt
|
||||||
|
bare var vedtatt, ikke bygget. Full design i ARCHITECTURE_DECISIONS.md
|
||||||
|
ADR-019 (fem delbeslutninger). Kort: organisator søker blant teeoff sine
|
||||||
|
baner i program-skjemaet, velger én, og teecup KOPIERER bane+hull+tee+
|
||||||
|
tee_rating inn i `teecup_db` (`source='official'`) — ikke et live oppslag
|
||||||
|
ved hver bruk. Ny `app/teeoff_client.py` (ren HTTP-klient mot
|
||||||
|
`http://teeoff_api:8000`, internt Docker-nettverk, ingen auth trengs — begge
|
||||||
|
containere deler allerede `teeoff_default`). To nye endepunkter i
|
||||||
|
`app/routers/courses.py`: `GET .../courses/official-search[/{slug}]` og
|
||||||
|
`POST .../courses/official-import`. Migrasjon `010` (unik
|
||||||
|
`external_course_ref` per org, hindrer dupliserte importer).
|
||||||
|
|
||||||
|
**Bevisste avgrensninger for denne runden:** kun 18-hulls baner kan
|
||||||
|
importeres (teeoffs skjema har ingen egen 9-hulls-inndeling); kun
|
||||||
|
`full_18`-rating importeres (teeoff har ingen separat front9/back9-rating,
|
||||||
|
samme valg som ADR-008 allerede tok for egendefinerte baner); ufullstendige
|
||||||
|
teeoff-data (manglende par/hcp_index på et hull, eller en tee uten NOEN
|
||||||
|
rating) avviser hele importen tydelig (`EXTERNAL_DATA_INCOMPLETE`) FØR noe
|
||||||
|
skrives, ikke en delvis importert bane.
|
||||||
|
|
||||||
|
**Verifisert grundig, inkludert mot EKTE `teeoff_api`** (ikke en simulert
|
||||||
|
respons): søk, anlegg-/banevalg, og import kjørt reelt mot den kjørende
|
||||||
|
produksjonscontaineren (kun lesing) — importerte Borregaard Golfklubb sin
|
||||||
|
hovedbane, bekreftet alle 18 hull + 8 tee/tee_rating-rader riktig i
|
||||||
|
databasen, og opprettet en ekte økt med den importerte banen (beviser hele
|
||||||
|
veien til handicap-motoren, ikke bare selve importen). Reimport avvist
|
||||||
|
(409), kryss-org-isolasjon bekreftet, ukjent teeoff-slug ga 404,
|
||||||
|
`test_isolation.sql` 12/12, ekte typesjekket produksjonsbuild av
|
||||||
|
frontend-utvidelsen.
|
||||||
|
|
||||||
|
**Rullet ut live**, bruker bekreftet eksplisitt: migrasjon 010 mot ekte
|
||||||
|
`teecup_db`, begge containere redeployet, `teeoff.no` upåvirket.
|
||||||
|
|
||||||
|
**Reell bug funnet og fikset samme dag, av en bruker som faktisk testet
|
||||||
|
funksjonen:** bane-søkeboksen ("Hent bane fra teeoff") var et `<form>`
|
||||||
|
rendret INNI det ytre økt-opprett-skjemaet — ugyldig, nestet HTML. Å klikke
|
||||||
|
"Søk" submittet i praksis det ytre skjemaet som en ekte side-navigasjon og
|
||||||
|
vasket bort `?org=...`-parameteren fra URL-en. Fikset ved å fjerne det
|
||||||
|
indre `<form>`-elementet (vanlig `<div>` + Enter-tast/knapp-klikk i
|
||||||
|
stedet). Se CLAUDE.md-status for full root cause.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## UX / frontend (senere fase)
|
## UX / frontend (senere fase)
|
||||||
|
|
||||||
- 📋 Høy kontrast, dark/light, store +/- knapper, stor «Neste hull»-knapp
|
- 📋 Høy kontrast, dark/light, store +/- knapper, stor «Neste hull»-knapp
|
||||||
|
|
|
||||||
|
|
@ -811,8 +811,7 @@ function OfficialCourseSearch({
|
||||||
const [importing, setImporting] = useState(false)
|
const [importing, setImporting] = useState(false)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
async function runSearch(e?: React.FormEvent) {
|
async function runSearch() {
|
||||||
e?.preventDefault()
|
|
||||||
setSearching(true)
|
setSearching(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
try {
|
try {
|
||||||
|
|
@ -901,18 +900,37 @@ function OfficialCourseSearch({
|
||||||
|
|
||||||
{!selectedFacility ? (
|
{!selectedFacility ? (
|
||||||
<>
|
<>
|
||||||
<form onSubmit={runSearch} className="flex items-center gap-2">
|
{/* Bevisst IKKE et eget <form> her -- denne komponenten rendres
|
||||||
|
inne i CreateSessionCard sitt <form onSubmit={handleSubmit}>,
|
||||||
|
og nestede <form>-elementer er ugyldig HTML. Nettleseren
|
||||||
|
slår da sammen de to skjemaene i den faktiske DOM-en, som
|
||||||
|
gjorde at "Søk"-knappen i praksis submittet det YTRE
|
||||||
|
økt-skjemaet i stedet -- en ekte native side-navigasjon som
|
||||||
|
vasket bort ?org=...-parameteren fra URL-en (funnet ved en
|
||||||
|
reell feilrapport fra bruker mot produksjon). */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder="Søk anleggsnavn…"
|
placeholder="Søk anleggsnavn…"
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault()
|
||||||
|
runSearch()
|
||||||
|
}
|
||||||
|
}}
|
||||||
className="h-11 flex-1 rounded-xl text-base"
|
className="h-11 flex-1 rounded-xl text-base"
|
||||||
/>
|
/>
|
||||||
<Button type="submit" disabled={searching} className="h-11 shrink-0 rounded-xl font-bold">
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() => runSearch()}
|
||||||
|
disabled={searching}
|
||||||
|
className="h-11 shrink-0 rounded-xl font-bold"
|
||||||
|
>
|
||||||
{searching ? "Søker…" : "Søk"}
|
{searching ? "Søker…" : "Søk"}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</div>
|
||||||
{facilities && (
|
{facilities && (
|
||||||
<ul className="flex max-h-56 flex-col overflow-auto rounded-xl border border-border">
|
<ul className="flex max-h-56 flex-col overflow-auto rounded-xl border border-border">
|
||||||
{facilities.map((f) => (
|
{facilities.map((f) => (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue