De to gjenværende UI-hullene fra 2026-07-19-runden (ingen vei til å opprette en ANDRE organisasjon, og intet sammendrag for "alle runder har dato") er de mest opplagte å ta først — begge er ferdig diagnostisert, null designtvil, og rene tilføyelser i frontend uten backend-endring.

Det jeg egentlig vil trekke frem som det strategisk viktigste, er derimot deltaker-tilgang til lag-chat og scorekort uten org-medlemskap — den naturlige fortsettelsen av dagens ADR-031-arbeid («Mine runder» viser nå turneringen, men en ren spiller kan fortsatt ikke åpne laget sin chat eller scorekortet derfra, siden begge fortsatt krever get_authorized_org). Tradeoffen: det er en bredere og mer risikofylt endring enn dagens papirkutt, siden get_authorized_org brukes av dusinvis av endepunkter — bør gjøres som en egen, forsiktig gjennomgått runde, ikke tas i forbifarten.
This commit is contained in:
Erol Haagenrud 2026-07-21 09:03:52 +02:00
parent 45850a97fa
commit 3f888f1e10
6 changed files with 161 additions and 42 deletions

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -424,7 +424,7 @@ function OrganizationView({
<div className="flex items-center gap-2">
<Link
href={`/orgs/${org.organization_id}/members?name=${encodeURIComponent(org.name)}`}
href={`/organizations/${org.organization_id}/members?name=${encodeURIComponent(org.name)}`}
className="inline-flex h-11 items-center gap-1.5 rounded-2xl border border-border bg-card px-4 text-sm font-semibold text-foreground transition-colors hover:bg-accent/50"
>
<Users aria-hidden="true" className="size-4" />

View file

@ -9,6 +9,14 @@ const nextConfig = {
images: {
unoptimized: true,
},
// MERK (funnet 2026-07-19, se FEATURE_BACKLOG.md): en plain array her gir
// implisitt "afterFiles"-semantikk -- statiske filer/sider sjekkes FØR
// disse rewrites, men DYNAMISKE Next.js-sider (app/x/[id]/page.tsx)
// sjekkes ETTER. En frontend-SIDE må derfor ALDRI nestes direkte under
// et av prefiksene under (f.eks. /orgs/[id]/members) -- rewrite-regelen
// vinner presedens og siden blir uoppnåelig (rå API-404 i stedet).
// Legg nye frontend-sider et sted UTENFOR disse prefiksene (se
// app/organizations/[id]/members for eksempelet dette ble rettet på).
async rewrites() {
return [
{ source: "/auth/:path*", destination: `${API_ORIGIN}/auth/:path*` },