Update Todos
Diagnostisere de to feilmeldingene fra skjermbildene
Flytte frontend-sidene fra /rounds/* til /my-rounds/* (kollisjon med API-proxyen)
Oppdatere alle interne lenker/router-kall til nye stier
Oppdatere next.config.mjs-kommentaren med denne lærdommen
Typesjekket produksjonsbuild
Rulle ut live etter bekreftelse
Oppdatere statusdokumentasjon
Fant og fikset rot-årsaken til begge feilmeldingene — en reell, alvorlig bug, ikke bare kosmetikk:
/rounds var samtidig navnet på både frontend-listesiden og backend-APIets ressursprefiks. Next.js sin rewrite-presedens ("afterFiles") gjorde at:
GET/POST /rounds fra klienten aldri nådde backend — den statiske siden vant, så fetch() fikk Next sin egen HTML tilbake i stedet for JSON. Det ga akkurat de to feilmeldingene i skjermbildene.
/rounds/[id] (rundedetaljsiden) hadde det motsatte problemet — der vant rewriten, så selve siden var fullstendig uoppnåelig (ville vist rå backend-JSON i stedet for UI-et om du hadde kommet så langt).
Dette er nøyaktig samme klasse feil som medlemsside-hendelsen under ADR-016, bare i begge retninger samtidig siden det denne gangen var en eksakt navnekollisjon, ikke bare nøsting.
Fikset: flyttet alle tre frontend-sidene til /my-rounds/* (list, new, [id]) — API-et beholder /rounds uendret. Oppdatert alle interne lenker (round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx, dashboard.tsx), lagt inn en tydelig advarsel i next.config.mjs for å hindre at dette gjentar seg. Bekreftet: «55/50/44/32» i det andre skjermbildet er ikke en bug — det er Tjøme Golfklubb sine faktiske utslagsnavn i teeoff (lengde i hundremeter).
Typesjekket produksjonsbuild kompilerer rent med de nye rutene.
This commit is contained in:
parent
7301de72c5
commit
6e688c6440
12 changed files with 30 additions and 9 deletions
|
|
@ -361,7 +361,13 @@
|
|||
"Bash(curl -s -o /dev/null -w \"teecup.teeoff.no/rounds: %{http_code}\\\\n\" https://teecup.teeoff.no/rounds)",
|
||||
"Read(//home/**)",
|
||||
"Bash(rm -f \"tee-cup-login-screen \\(10\\).zip\" \"tee-cup-login-screen \\(11\\).zip\" \"tee-cup-login-screen \\(12\\).zip\")",
|
||||
"Bash(curl -s -o /dev/null -w \"teecup.teeoff.no/rounds/new: %{http_code}\\\\n\" https://teecup.teeoff.no/rounds/new)"
|
||||
"Bash(curl -s -o /dev/null -w \"teecup.teeoff.no/rounds/new: %{http_code}\\\\n\" https://teecup.teeoff.no/rounds/new)",
|
||||
"Read(//tmp/claude-1000/**)",
|
||||
"Bash(docker exec teeoff_api python3 -c ' *)",
|
||||
"Bash(curl -s -D - https://teecup.teeoff.no/rounds -o /tmp/rounds_get.out)",
|
||||
"Bash(curl -s -D - https://teecup.teeoff.no/rounds/00000000-0000-0000-0000-000000000000 -o /tmp/rounds_id_get.out)",
|
||||
"Bash(grep -n '\"/rounds\\\\|`/rounds\\\\|href=\"/rounds\\\\|href={`/rounds' components/own-rounds.tsx components/new-round.tsx components/round-detail.tsx components/round-card.tsx components/dashboard.tsx app/page.tsx)",
|
||||
"Bash(grep -n '\"/rounds\\\\|`/rounds\\\\|href=\"/rounds\\\\|href={`/rounds\\\\|router\\\\.\\\\\\(push\\\\|replace\\\\\\)\\(.*rounds' components/own-rounds.tsx components/new-round.tsx components/round-detail.tsx components/round-card.tsx components/dashboard.tsx)"
|
||||
],
|
||||
"additionalDirectories": [
|
||||
"/opt/teeoff/deploy",
|
||||
|
|
|
|||
BIN
Screenshot_20260723-113235.png
Normal file
BIN
Screenshot_20260723-113235.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
BIN
Screenshot_20260723-113302.png
Normal file
BIN
Screenshot_20260723-113302.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 151 KiB |
|
|
@ -233,7 +233,7 @@ export function Dashboard() {
|
|||
<Wordmark compact />
|
||||
<div className="flex items-center gap-1">
|
||||
<Link
|
||||
href="/rounds"
|
||||
href="/my-rounds"
|
||||
className="inline-flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-sm font-semibold text-muted-foreground transition-colors hover:text-foreground"
|
||||
>
|
||||
<Flag aria-hidden="true" className="size-4" />
|
||||
|
|
@ -298,7 +298,7 @@ export function Dashboard() {
|
|||
function PersonalRoundsEntry() {
|
||||
return (
|
||||
<Link
|
||||
href="/rounds"
|
||||
href="/my-rounds"
|
||||
className="group flex items-center gap-4 rounded-2xl border border-border bg-card p-4 shadow-sm shadow-black/5 transition-colors hover:border-primary/50 sm:p-5"
|
||||
>
|
||||
<div className="flex size-11 shrink-0 items-center justify-center rounded-xl bg-primary/15">
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ export function NewRound() {
|
|||
<div className="flex min-h-[100dvh] flex-col bg-background">
|
||||
<header className="sticky top-0 z-10 border-b border-border bg-background/80 backdrop-blur">
|
||||
<div className="mx-auto flex w-full max-w-3xl items-center justify-between gap-4 px-5 py-4">
|
||||
<BackLink step={step} setStep={setStep} onExit={() => router.push("/rounds")} />
|
||||
<BackLink step={step} setStep={setStep} onExit={() => router.push("/my-rounds")} />
|
||||
<Wordmark compact />
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -275,7 +275,7 @@ export function NewRound() {
|
|||
course={course}
|
||||
ownGender={ownGender}
|
||||
onSubmit={submitRound}
|
||||
onCreated={(id) => router.replace(`/rounds/${id}`)}
|
||||
onCreated={(id) => router.replace(`/my-rounds/${id}`)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export function OwnRounds() {
|
|||
</div>
|
||||
|
||||
<Button
|
||||
render={<Link href="/rounds/new" />}
|
||||
render={<Link href="/my-rounds/new" />}
|
||||
size="lg"
|
||||
className="h-12 shrink-0 gap-2 rounded-2xl px-6 text-base font-bold shadow-sm"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export function RoundCard({ round }: { round: Round }) {
|
|||
|
||||
return (
|
||||
<Link
|
||||
href={`/rounds/${round.id}`}
|
||||
href={`/my-rounds/${round.id}`}
|
||||
className="group flex min-h-[88px] w-full items-center gap-4 rounded-2xl border border-border bg-card p-5 text-left shadow-sm shadow-black/5 transition-colors hover:border-primary/60 hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-3">
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
|||
return (
|
||||
<div className="flex min-h-[100dvh] flex-col items-center justify-center gap-4 bg-background px-5 text-center">
|
||||
<p className="text-base font-medium text-destructive">{error}</p>
|
||||
<Link href="/rounds" className="text-base font-semibold text-primary underline underline-offset-2">
|
||||
<Link href="/my-rounds" className="text-base font-semibold text-primary underline underline-offset-2">
|
||||
Tilbake til egne runder
|
||||
</Link>
|
||||
</div>
|
||||
|
|
@ -347,7 +347,7 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
|||
<header className="sticky top-0 z-20 border-b border-border bg-background/90 backdrop-blur">
|
||||
<div className="mx-auto flex w-full max-w-3xl items-center gap-3 px-5 py-4">
|
||||
<Link
|
||||
href="/rounds"
|
||||
href="/my-rounds"
|
||||
aria-label="Tilbake til egne runder"
|
||||
className="flex size-11 shrink-0 items-center justify-center rounded-xl border border-border bg-card text-foreground transition-colors hover:bg-accent/50"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,21 @@ const nextConfig = {
|
|||
// 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å).
|
||||
//
|
||||
// SAMME FELLE, VERRE UTSLAG (funnet 2026-07-23, ADR-033): en EKSAKT
|
||||
// kollisjon (ikke bare nøsting) mellom en frontend-side og et
|
||||
// rewrite-prefiks slår begge veier. `/rounds` var opprinnelig BÅDE en
|
||||
// frontend-liste-side OG rounds.py sitt API-prefiks -- statisk side vs.
|
||||
// eksakt rewrite: SIDEN vant, så `fetch("/rounds")`/`POST /rounds` fra
|
||||
// klienten traff aldri backend (fikk Next sin egen HTML tilbake, JSON-
|
||||
// parsing feilet stille). `/rounds/[id]` (DYNAMISK side) vs.
|
||||
// `/rounds/:path*`: REWRITEN vant, så selve rundedetalj-siden var
|
||||
// fullstendig uoppnåelig (rå backend-JSON i stedet for UI-et). Løst ved
|
||||
// å flytte frontend-sidene til et HELT ANNET toppnivå-prefiks
|
||||
// (`/my-rounds/*`) som ikke overlapper `/rounds/*` i det hele tatt --
|
||||
// API-et beholder `/rounds` uendret. Lærdom: et rewrite-prefiks og en
|
||||
// frontend-sides toppnivå-segment må ALDRI være identisk streng, selv
|
||||
// om det virker naturlig (f.eks. "runder" for begge).
|
||||
async rewrites() {
|
||||
return [
|
||||
{ source: "/auth/:path*", destination: `${API_ORIGIN}/auth/:path*` },
|
||||
|
|
|
|||
Loading…
Reference in a new issue