diff --git a/.claude/settings.local.json b/.claude/settings.local.json index afba321..81737f7 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -331,7 +331,10 @@ "Bash(python3 /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/test_profile_and_myrounds.py)", "Bash(curl -s -o /dev/null -w \"account: %{http_code}\\\\n\" https://teecup.teeoff.no/account)", "Bash(python3 /tmp/claude-1000/-opt-teecup/a8bd2fc3-4b9c-4682-a2be-cf36e143de78/scratchpad/test_email_mobile.py)", - "Bash(curl -s -o /dev/null -w \"verify-email: %{http_code}\\\\n\" https://teecup.teeoff.no/verify-email)" + "Bash(curl -s -o /dev/null -w \"verify-email: %{http_code}\\\\n\" https://teecup.teeoff.no/verify-email)", + "Bash(grep -rln \"orgs/\\\\[id\\\\]/members\\\\|orgs/\\\\${.*}/members\\\\|/orgs/\\\\\" .*members\" /opt/teecup/frontend --include=\"*.tsx\" --include=\"*.ts\")", + "Bash(curl -s http://localhost:18734/organizations/00000000-0000-0000-0000-000000000000/members?name=Test)", + "Bash(curl -s http://localhost:18734/orgs/00000000-0000-0000-0000-000000000000/members)" ], "additionalDirectories": [ "/opt/teeoff/deploy", diff --git a/CLAUDE.md b/CLAUDE.md index 49ad700..71d5feb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2067,33 +2067,59 @@ Ferdig og verifisert: boot-et rent, `/health`/`/dashboard`/`/account`/`/verify-email` → 200, `teeoff.no` upåvirket. +- **Medlemsside-ruten FIKSET OG LIVE (2026-07-20):** brukeren ba eksplisitt + om å ta fatt på dette (det mest presserende av de fire UI-hullene notert + 2026-07-19 — siden var helt utilgjengelig). Root cause var allerede + presist diagnostisert: `next.config.mjs` sin `rewrites()` (plain array, + implisitt "afterFiles") fanger `/orgs/:path*` FØR Next.js sine egne + DYNAMISKE sider sjekkes, så `app/orgs/[id]/members/page.tsx` ble aldri + nådd — kallet gikk til FastAPI i stedet, som ga en rå 404. + **Fikset:** siden flyttet til `app/organizations/[id]/members/page.tsx` + (utenfor `/orgs/*`-prefikset), eneste lenke (`dashboard.tsx`) oppdatert. + Lagt til en forklarende kommentar i `next.config.mjs` sin `rewrites()` + for å forhindre samme feil ved en fremtidig ny side. + **Verifisert med ekte produksjonsbuild + container-boot** (ikke bare + typesjekk): den nye ruten (`/organizations/{id}/members`) rendrer + faktisk `OrgMembers`-komponenten med riktig `organizationId`/`orgName` + i RSC-payloaden, IKKE en 404 eller innloggingssiden. + **Rullet ut live**, ren frontend-endring, ingen migrasjon, `teeoff.no` + upåvirket. + Neste steg: -1. **Personlig landingsside + profil (ADR-031) OG e-post/mobil (ADR-032) - er BEGGE LIVE.** Naturlig oppfølging: deltaker-tilgang (uten - org-medlemskap) til lag-chat/scorekort — bevisst utenfor omfang denne - runden, se FEATURE_BACKLOG.md. -2. **Punkt 2 fra samme 2026-07-20-runde (lavere prioritet enn #1):** - midlertidige spillere + automatisk etter-runde-e-post — forslag klart +1. **Venter på brukerens retningsbekreftelse:** redesign av dashbordets + tom-tilstand ved første innlogging (mindre organisator-vridd, se + FEATURE_BACKLOG.md) — forslag lagt frem 2026-07-20, ikke bygget. +2. **Notert, IKKE designet i detalj:** én person med flere e-post-adresser + — legge til en verifisert sekundær-adresse fra dashbordet, med + turneringer/data som "dukker opp"/slås sammen deretter, og fremtidige + innlogginger med enten adresse. Fanget grundig i FEATURE_BACKLOG.md, + inkl. et reelt uløst spørsmål (ekte konto-sammenslåing hvis adressen + allerede tilhører en annen eksisterende konto) som trenger egen, + separat designrunde. +3. Personlig landingsside + profil (ADR-031) OG e-post/mobil (ADR-032) er + BEGGE LIVE. Naturlig oppfølging: deltaker-tilgang (uten org-medlemskap) + til lag-chat/scorekort — bevisst utenfor omfang, se FEATURE_BACKLOG.md. +4. **Punkt 2 fra 2026-07-20-runden (midlertidige spillere):** forslag klart i FEATURE_BACKLOG.md, tre åpne spørsmål, ingen kode skrevet. -3. Oppfølgingspunkt fra PWA-runden, eksplisitt notert på brukerens +5. Oppfølgingspunkt fra PWA-runden, eksplisitt notert på brukerens forespørsel: offline-scoreregistrering er ALDRI browser-testet i praksis (kun kodegjennomgang + build-verifisering, se ADR-028). Bruker bør selv åpne et scorekort, skru på Chrome DevTools sin Offline-bryter, registrere et par slag, skru nettet på igjen, og bekrefte at de faktisk synkes — først da er flyten reelt bevist. -4. Fire UI-/UX-hull notert 2026-07-19 (se over) — ingen fikset ennå. - Rewrite/medlemsside-bugen (#2) er den mest presserende siden siden er - helt utilgjengelig i dag. -5. Fortsatt åpne beslutninger fra FEATURE_BACKLOG.md: kode-regenerering for +6. To av fire UI-/UX-hull notert 2026-07-19 er fikset (datovisning ADR-030, + medlemsside-ruten). Gjenstår: "opprett en ANDRE organisasjon" (ingen UI + ennå) og et sammendrag for "alle runder har dato". +7. Fortsatt åpne beslutninger fra FEATURE_BACKLOG.md: kode-regenerering for ADR-020, korrigering-godkjenning fra motpart, video/1-til-1-meldinger (bevisst utsatt i ADR-025). -6. Flere turneringsformater utover Ryder Cup (Københavner/High-low-high/ +8. Flere turneringsformater utover Ryder Cup (Københavner/High-low-high/ Robbins/Try all, notert 2026-07-19) — ingen ADR-runde startet ennå. -7. **Merk for neste økt:** `deploy/Caddyfile`-endringen (ny `/ws/*`-rute, +9. **Merk for neste økt:** `deploy/Caddyfile`-endringen (ny `/ws/*`-rute, ADR-025) ligger uncommitted i det SEPARATE `/opt/teeoff`-repoet, ikke i `teecup`-repoet — samme fallgruve som ADR-016-runden sin Caddy-endring, lett å glemme siden denne økten ellers kun har jobbet i `/opt/teecup`. -8. Ved fremtidige nye V0-skjermer/-design i V0 (fortsett i samme prosjekt), +10. Ved fremtidige nye V0-skjermer/-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 eksisterende filer, og sjekk om V0-skjermen bygger inn handlinger backend ikke støtter ennå FØR diff --git a/FEATURE_BACKLOG.md b/FEATURE_BACKLOG.md index 191d803..79eb025 100644 --- a/FEATURE_BACKLOG.md +++ b/FEATURE_BACKLOG.md @@ -932,29 +932,22 @@ er fikset i denne runden — kun dokumentert slik at de ikke går i glemmeboken. satt `start_date`/`end_date`, som fortsatt vinner om det noensinne settes). Se ADR-030 for full detalj og verifisering. -2. **`/orgs/{id}/members`-siden gir en rå API-404 (`{"detail":"Not - Found"}`), ikke medlemssiden.** Bekreftet root cause, ikke bare - reprodusert: `frontend/next.config.mjs` sin `rewrites()` returnerer en - PLAIN ARRAY (implisitt "afterFiles"-semantikk i Next.js) — det betyr at - ikke-dynamiske filer/sider sjekkes FØR rewrites, men DYNAMISKE sider - (som `app/orgs/[id]/members/page.tsx`) sjekkes ETTER. Rewrite-regelen - `{ source: "/orgs/:path*", destination: ".../orgs/:path*" }` (satt opp i - ADR-016 for å proxye API-kall) fanger derfor `/orgs/{id}/members` FØR - Next.js noensinne når frem til den faktiske siden, og sender kallet til - FastAPI i stedet — som naturligvis ikke har noen `GET /orgs/{id}/members`- - rute (kun `/orgs/{id}/memberships`), derav den rå FastAPI-404-formen - (ikke engang appens egen `app_error`-kontrakt, siden ruten ikke matcher - noe sted i det hele tatt). Selve siden (`org-members.tsx`, lenken fra - dashbordet) er ellers riktig bygget — dette er en ren - rewrite/dynamisk-rute-presedens-krasj, samme klasse fallgruve som - ADR-016 sin opprinnelige "alt nytt API-prefiks må inn i rewrites"-lærdom, - bare i motsatt retning (en frontend-SIDE ble skjult AV en rewrite). Dette - er den FØRSTE frontend-siden som noensinne har blitt nestet direkte under - et allerede proxyet prefiks (`/orgs/*`) — ingen tidligere skjerm har - truffet dette. Sannsynlig fiks: flytt siden til en ikke-proxyet sti - (f.eks. `/organizations/[id]/members`), ELLER gjør rewrites-regelen mer - presis (kun kjente API-undermønstre som `/orgs/:id/tournaments`, - `/orgs/:id/memberships` osv., ikke et bredt `:path*`). +2. **✅ FIKSET 2026-07-20: `/orgs/{id}/members`-siden ga en rå API-404 + (`{"detail":"Not Found"}`), ikke medlemssiden.** Bekreftet root cause, + ikke bare reprodusert: `frontend/next.config.mjs` sin `rewrites()` + returnerer en PLAIN ARRAY (implisitt "afterFiles"-semantikk i Next.js) + — statiske filer/sider sjekkes FØR rewrites, men DYNAMISKE sider (som + `app/orgs/[id]/members/page.tsx`) sjekkes ETTER. Rewrite-regelen + `{ source: "/orgs/:path*", ... }` (ADR-016) fanget derfor kallet FØR + Next.js noensinne nådde selve siden, og sendte det til FastAPI i + stedet (som naturligvis ikke har noen `GET /orgs/{id}/members`-rute). + **Fikset:** siden flyttet til `app/organizations/[id]/members/page.tsx` + (utenfor det proxyede `/orgs/*`-prefikset), eneste lenke til den + (`dashboard.tsx`) oppdatert tilsvarende. Lagt til en forklarende + kommentar direkte i `next.config.mjs` sin `rewrites()` slik at samme + feil ikke gjentas for en fremtidig ny side. Verifisert med ekte + produksjonsbuild + container-boot: den nye ruten rendrer faktisk + `OrgMembers`-komponenten (ikke en 404 eller innloggingssiden). 3. **Dashboard: ingen vei til å opprette/legge til en ANDRE organisasjon.** Bekreftet i `dashboard.tsx`: `CreateOrganizationState` @@ -974,9 +967,9 @@ er fikset i denne runden — kun dokumentert slik at de ikke går i glemmeboken. bakenforliggende datamodell-begrensning (all nødvendig data finnes allerede i `GET .../sessions`). -**Ingenting av dette er fikset ennå** — kun diagnostisert og notert på -brukerens eksplisitte instruks, for å ikke gå i glemmeboken mens PWA-runden -prioriteres. Punkt 1 (datovisning) senere ✅ fikset, se over (ADR-030). +Punkt 1 (datovisning, ADR-030) og punkt 2 (medlemsside-ruten) er nå ✅ +fikset, se over. Punkt 3 (opprett en ANDRE organisasjon) og punkt 4 (ingen +sammendrag for "alle runder har dato") står fortsatt åpne. --- @@ -1131,6 +1124,95 @@ koblingen skal skje). --- +## Dashboard: tom-tilstand ved første innlogging — 📋 FORESLÅTT 2026-07-20, IKKE bygget + +Brukeren påpekte at dagens tomme-tilstand ("Du har ingen organisasjon ennå — +opprett en") er organisator-vridd og ikke stemmer med hva en fersk bruker +faktisk trenger å se/gjøre. Bedt om en refleksjon FØR bygging. + +**Analyse av hvem som faktisk lander på tom-skjermen:** en spiller invitert +med kode (ADR-020) går allerede utenom (login-siden sender dem rett til +turneringen). En spiller som er ROSTRET på et lag dukker nå opp i "Mine +runder" (ADR-031). Men en spiller som kun er PÅMELDT (`tournament_ +registration`, ADR-017) og ikke ennå rostret, dukker IKKE opp — "Mine +runder" v1 ser kun på `team_roster` (kjent, allerede notert begrensning, +se ADR-031 sitt "naturlig neste steg"-punkt over). Denne gruppen — trolig +en av de vanligste måtene en helt fersk bruker faktisk møter TeeCup på — +faller derfor rett igjennom til "opprett organisasjon"-skjermen i dag. + +**Foreslått, IKKE bygget:** +1. Utvid "Mine runder" til også å vise rene påmeldinger (ikke bare + rostrede lag) — tetter selve hullet, reduserer hvor ofte noen i det + hele tatt havner på tom-skjermen. +2. Gjør selve tom-skjermen nøytral: to likestilte valg side ved side — + "Har du en kode?" (gjenbruker samme oppslag som login-siden sin + `JoinByCode`) og "Skal du arrangere selv? Opprett organisasjon" — i + stedet for at organisator-veien er det eneste synlige alternativet. + +**Venter på brukerens bekreftelse på retning** før bygging. + +--- + +## Én person, flere e-postadresser — 📋 NOTERT 2026-07-20, IKKE designet/bygget + +Reist av brukeren rett etter ADR-032 (verifisert e-postbytte). Et beslektet, +men DISTINKT behov: én person kan ha flere e-postadresser i omløp samtidig +(f.eks. registrert seg privat med én adresse, men fått en turnering- +invitasjon rettet mot en jobb-adresse organisator la inn) — ikke et BYTTE +(ADR-032 sin løsning), men en TILLEGGS-tilknytning. I dag oppretter et +magic-link-innlogg på en ny adresse alltid en HELT NY, tom `app_user`-konto +(ADR-009) — nøyaktig det som gjør at spilleren aldri "finner" turneringen +sin med sin vanlige, primære konto. + +**Brukerens beskrevne flyt, ordrett fanget:** innlogget med +eksempel@domene.no, sier "jeg eier også test@domain.com". Ved dette kravet +sendes en e-post til test@domain.com med en nøkkel som limes inn et sted på +dashbordet. Etter bekreftelse dukker inviterte turneringer på den adressen +opp. Annen data (spilte runder, personlig informasjon) skal "forespørres +slått sammen eller justert". Fremtidige innlogginger skal kunne gjøres med +ENHVER av de tilknyttede adressene. + +**Foreslått retning, basert på gjenbruk av allerede bygget mønster:** +samme token-i-e-post-bevis-eierskap-mekanisme som ADR-032 sin +e-postbytte-flyt (`email_change_token`), men ADDITIV i stedet for +ERSTATTENDE — en ny tabell for verifiserte SEKUNDÆRE e-poster knyttet til +kontoen (i stedet for å overskrive `app_user.email`). Login (`/auth/ +request-link` m.fl.) må da slå opp BÅDE primær- og sekundær-e-poster. +`link_player_by_email()`/organisasjonsinvitasjon-aksept (som i dag kun +kjører mot `app_user.email`) må kjøres for HVER av kontoens verifiserte +adresser — naturlig utløst rett etter en ny adresse er bekreftet, og +sannsynligvis også trygt å kjøre på nytt ved hver innlogging (idempotent, +samme mønster som i dag). + +**Det virkelig vanskelige, uløste spørsmålet, IKKE adressert av brukerens +beskrevne flyt:** hva skjer hvis den "krevde" adressen ALLEREDE er +primær- (eller sekundær-)adressen til en ANNEN, eksisterende `app_user`- +konto — altså at spilleren faktisk har logget inn med DEN adressen +tidligere og dermed har to helt separate kontoer med egen historikk +(ulike org-medlemskap, ulike spillerkoblinger, kanskje ulikt passord/2FA)? +Da holder det ikke å bare "legge til" adressen — det er en ekte KONTO- +SAMMENSLÅING (slå sammen organisasjonsmedlemskap uten å bryte "én rolle +per bruker per org"-unikheten, deduplisere spiller-koblinger, avgjøre +hvilken konto som "vinner" for tvetydige felt som `preferred_locale`/2FA +når begge har satt noe ulikt). Dette er en betydelig større og mer +risikofylt operasjon enn "legg til en frisk, ukrevd adresse" — bør +utredes og besluttes som en egen, separat sak, ikke antas løst av samme +runde som det enkle tilfellet. + +**Plassering:** brukeren presiserte eksplisitt at dette må skje FRA +dashbord-siden (ikke `/account`, der ADR-032 sin e-postbytte-flyt ellers +naturlig ville hørt hjemme) — trolig fordi selve GEVINSTEN (nye turneringer +dukker opp) er noe som vises på dashbordet, så handlingen bør ligge der +resultatet vises. + +**Ingenting designet i detalj eller bygget ennå** — kun fanget grundig her +slik at det ikke går i glemmeboken. Bør trolig deles i to separate runder: +(1) legg til en frisk, ukrevd sekundær-e-post (rimelig godt avgrenset, +gjenbruker ADR-032 sitt mønster direkte), (2) ekte konto-sammenslåing for +det vanskelige tilfellet over (egen, større designrunde). + +--- + ## UX / frontend (senere fase) - 📋 Høy kontrast, dark/light, store +/- knapper, stor «Neste hull»-knapp diff --git a/frontend/app/orgs/[id]/members/page.tsx b/frontend/app/organizations/[id]/members/page.tsx similarity index 100% rename from frontend/app/orgs/[id]/members/page.tsx rename to frontend/app/organizations/[id]/members/page.tsx diff --git a/frontend/components/dashboard.tsx b/frontend/components/dashboard.tsx index cf13d35..f80d9e5 100644 --- a/frontend/components/dashboard.tsx +++ b/frontend/components/dashboard.tsx @@ -424,7 +424,7 @@ function OrganizationView({