V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
"use client"
|
|
|
|
|
|
|
|
|
|
// Frittstående rundeføring (ADR-033) -- runder eid direkte av en BRUKER
|
|
|
|
|
// (app_user), ikke en organisasjon. Presentasjon fra V0, datalag skrevet om
|
|
|
|
|
// fra mock til ekte fetch mot /rounds (samme mønster som resten av appen).
|
|
|
|
|
|
|
|
|
|
import { useEffect, useState } from "react"
|
|
|
|
|
import { useRouter } from "next/navigation"
|
|
|
|
|
import Link from "next/link"
|
|
|
|
|
import { Loader2, Plus, ClipboardList } from "lucide-react"
|
|
|
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
|
import { Wordmark } from "@/components/wordmark"
|
|
|
|
|
import { RoundCard, type Round } from "@/components/round-card"
|
|
|
|
|
import { ArrowLeft } from "lucide-react"
|
|
|
|
|
|
|
|
|
|
type ApiRoundParticipant = {
|
|
|
|
|
id: string
|
2026-07-26 08:22:41 +02:00
|
|
|
user_id: string | null
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
is_owner: boolean
|
|
|
|
|
guest_name: string | null
|
Nytt horisontalt scorekort (/my-rounds/[id]/scorecard) — ekte HTML-tabell med faste kolonnebredder, ingen scroll i det hele tatt. Score-cellene bruker form (sirkel = under par, firkant = over par) + fylt/ufylt i stedet for farge alene, med egen symbolforklaring. Ut/Inn-blokker for 18-hulls runder, én blokk for 9-hulls. Egen dedikert side (ikke sammenslått med statistikk-siden), lenket fra både "Runde fullført"-banneret og fra statistikk-siden. Byttet ut V0s egen forenklede HCP-formel med backends allerede beregnede strokes_received, så det ikke finnes to ulike slagfordelings-implementasjoner i appen. Den gamle vertikale scorekort-tabellen i statistikk-siden er fjernet — nå bor det rå scorekortet kun ett sted.
Anywayslag-fiksen din: lagt til som en tredje flis ved siden av Bunkerslag/Straffeslag, og seksjonen omdøpt fra "Chip, bunker og straffeslag" til "Annet" — notert i .md-filene at et notatfelt trolig kommer dit senere.
Ekte typesjekket build, ingen backend-endring nødvendig, teeoff.no upåvirket.
2026-07-24 23:33:41 +02:00
|
|
|
counts_for_handicap: boolean
|
|
|
|
|
score_differential: number | null
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ApiRound = {
|
|
|
|
|
id: string
|
2026-07-25 06:29:09 +02:00
|
|
|
name: string | null
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
course_name_snapshot: string
|
|
|
|
|
tee_name_snapshot: string
|
|
|
|
|
played_at: string
|
|
|
|
|
holes_planned: number
|
|
|
|
|
completed_at: string | null
|
|
|
|
|
participants: ApiRoundParticipant[]
|
2026-07-26 08:22:41 +02:00
|
|
|
my_holes_played: number
|
|
|
|
|
my_total_score: number | null
|
|
|
|
|
my_score_to_par: number | null
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-26 08:22:41 +02:00
|
|
|
// viewerId er null helt til /auth/me har svart -- faller da tilbake til
|
|
|
|
|
// eierens rad (samme oppførsel som før ADR-036 fase 3-utvidelsen).
|
All done and verified live in the browser. Summary of this round:
"Kommende runder" → "Runder" on the dashboard.
Tjøme course-name duplicate fixed in the real database (one round's stale course_name_snapshot corrected) — all three of your rounds now group under one "Tjøme Golfklubb" entry.
Two new colors, per your request for two: lifted the already-validated --chart-3 (blue → --info) and --chart-4 (gold → --gold) into core design tokens. First real uses: a blue "Hcp spilt til X" badge on round cards, and a gold "Personlig rekord" badge on your best completed round.
Clicking a course under "Spilte baner" now opens a new page listing every round played there (/my-rounds/course/[name]) — caught and fixed a real double-encoding bug here via an actual browser screenshot before shipping.
Aggregated statistics, clickable from the dashboard's "Statistikk" section (/my-rounds/stats): rounds completed, avg to-par, putts/18 holes (implementing your padding rule exactly — unplayed holes count as 2 putts, only for rounds where putt-tracking was on), fairway%, GIR%, one-putt%, scrambling%, sand save%, and chip/bunker/penalty/anywayslag averages per round.
Everything is typechecked, the putts-padding logic is verified against a hand-computed synthetic dataset, and all new screens were checked live in the browser with no console errors.
2026-07-28 07:04:56 +02:00
|
|
|
function toRound(r: ApiRound, viewerId: string | null, personalBestRoundId: string | null): Round {
|
2026-07-26 08:22:41 +02:00
|
|
|
const me = (viewerId && r.participants.find((p) => p.user_id === viewerId)) || r.participants.find((p) => p.is_owner)
|
|
|
|
|
const differential = me?.counts_for_handicap ? me.score_differential : null
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
return {
|
|
|
|
|
id: r.id,
|
2026-07-25 06:29:09 +02:00
|
|
|
name: r.name,
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
courseName: r.course_name_snapshot,
|
|
|
|
|
status: r.completed_at ? "completed" : "active",
|
|
|
|
|
teeName: r.tee_name_snapshot,
|
|
|
|
|
holes: r.holes_planned === 9 ? 9 : 18,
|
|
|
|
|
date: r.played_at,
|
|
|
|
|
playerCount: r.participants.length,
|
2026-07-26 08:22:41 +02:00
|
|
|
holesPlayed: r.my_holes_played,
|
|
|
|
|
totalScore: r.my_total_score ?? undefined,
|
|
|
|
|
toPar: r.my_score_to_par ?? undefined,
|
Nytt horisontalt scorekort (/my-rounds/[id]/scorecard) — ekte HTML-tabell med faste kolonnebredder, ingen scroll i det hele tatt. Score-cellene bruker form (sirkel = under par, firkant = over par) + fylt/ufylt i stedet for farge alene, med egen symbolforklaring. Ut/Inn-blokker for 18-hulls runder, én blokk for 9-hulls. Egen dedikert side (ikke sammenslått med statistikk-siden), lenket fra både "Runde fullført"-banneret og fra statistikk-siden. Byttet ut V0s egen forenklede HCP-formel med backends allerede beregnede strokes_received, så det ikke finnes to ulike slagfordelings-implementasjoner i appen. Den gamle vertikale scorekort-tabellen i statistikk-siden er fjernet — nå bor det rå scorekortet kun ett sted.
Anywayslag-fiksen din: lagt til som en tredje flis ved siden av Bunkerslag/Straffeslag, og seksjonen omdøpt fra "Chip, bunker og straffeslag" til "Annet" — notert i .md-filene at et notatfelt trolig kommer dit senere.
Ekte typesjekket build, ingen backend-endring nødvendig, teeoff.no upåvirket.
2026-07-24 23:33:41 +02:00
|
|
|
differential,
|
All done and verified live in the browser. Summary of this round:
"Kommende runder" → "Runder" on the dashboard.
Tjøme course-name duplicate fixed in the real database (one round's stale course_name_snapshot corrected) — all three of your rounds now group under one "Tjøme Golfklubb" entry.
Two new colors, per your request for two: lifted the already-validated --chart-3 (blue → --info) and --chart-4 (gold → --gold) into core design tokens. First real uses: a blue "Hcp spilt til X" badge on round cards, and a gold "Personlig rekord" badge on your best completed round.
Clicking a course under "Spilte baner" now opens a new page listing every round played there (/my-rounds/course/[name]) — caught and fixed a real double-encoding bug here via an actual browser screenshot before shipping.
Aggregated statistics, clickable from the dashboard's "Statistikk" section (/my-rounds/stats): rounds completed, avg to-par, putts/18 holes (implementing your padding rule exactly — unplayed holes count as 2 putts, only for rounds where putt-tracking was on), fairway%, GIR%, one-putt%, scrambling%, sand save%, and chip/bunker/penalty/anywayslag averages per round.
Everything is typechecked, the putts-padding logic is verified against a hand-computed synthetic dataset, and all new screens were checked live in the browser with no console errors.
2026-07-28 07:04:56 +02:00
|
|
|
isPersonalBest: personalBestRoundId !== null && r.id === personalBestRoundId,
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
All done and verified live in the browser. Summary of this round:
"Kommende runder" → "Runder" on the dashboard.
Tjøme course-name duplicate fixed in the real database (one round's stale course_name_snapshot corrected) — all three of your rounds now group under one "Tjøme Golfklubb" entry.
Two new colors, per your request for two: lifted the already-validated --chart-3 (blue → --info) and --chart-4 (gold → --gold) into core design tokens. First real uses: a blue "Hcp spilt til X" badge on round cards, and a gold "Personlig rekord" badge on your best completed round.
Clicking a course under "Spilte baner" now opens a new page listing every round played there (/my-rounds/course/[name]) — caught and fixed a real double-encoding bug here via an actual browser screenshot before shipping.
Aggregated statistics, clickable from the dashboard's "Statistikk" section (/my-rounds/stats): rounds completed, avg to-par, putts/18 holes (implementing your padding rule exactly — unplayed holes count as 2 putts, only for rounds where putt-tracking was on), fairway%, GIR%, one-putt%, scrambling%, sand save%, and chip/bunker/penalty/anywayslag averages per round.
Everything is typechecked, the putts-padding logic is verified against a hand-computed synthetic dataset, and all new screens were checked live in the browser with no console errors.
2026-07-28 07:04:56 +02:00
|
|
|
// Samme regel som dashboardet (2026-07-28): laveste til-par blant minst to
|
|
|
|
|
// fullførte runder -- én enkelt fullført runde er ikke ennå en "rekord".
|
|
|
|
|
function findPersonalBestRoundId(rounds: ApiRound[]): string | null {
|
|
|
|
|
const completed = rounds.filter((r) => r.completed_at !== null && r.my_score_to_par !== null)
|
|
|
|
|
if (completed.length < 2) return null
|
|
|
|
|
let best: { id: string; toPar: number } | null = null
|
|
|
|
|
for (const r of completed) {
|
|
|
|
|
const toPar = r.my_score_to_par as number
|
|
|
|
|
if (!best || toPar < best.toPar) best = { id: r.id, toPar }
|
|
|
|
|
}
|
|
|
|
|
return best?.id ?? null
|
|
|
|
|
}
|
|
|
|
|
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
export function OwnRounds() {
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
const [rounds, setRounds] = useState<Round[] | null>(null)
|
|
|
|
|
const [error, setError] = useState<string | null>(null)
|
2026-07-26 08:22:41 +02:00
|
|
|
const [viewerId, setViewerId] = useState<string | null>(null)
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
let cancelled = false
|
|
|
|
|
fetch("/auth/me", { credentials: "include" })
|
|
|
|
|
.then((res) => (res.ok ? res.json() : null))
|
|
|
|
|
.then((data: { id: string } | null) => {
|
|
|
|
|
if (!cancelled && data) setViewerId(data.id)
|
|
|
|
|
})
|
|
|
|
|
.catch(() => {})
|
|
|
|
|
return () => {
|
|
|
|
|
cancelled = true
|
|
|
|
|
}
|
|
|
|
|
}, [])
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
let cancelled = false
|
|
|
|
|
async function load() {
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch("/rounds", { credentials: "include" })
|
|
|
|
|
if (res.status === 401) {
|
|
|
|
|
router.replace("/")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (!res.ok) throw new Error(`rounds: ${res.status}`)
|
|
|
|
|
const data: ApiRound[] = await res.json()
|
All done and verified live in the browser. Summary of this round:
"Kommende runder" → "Runder" on the dashboard.
Tjøme course-name duplicate fixed in the real database (one round's stale course_name_snapshot corrected) — all three of your rounds now group under one "Tjøme Golfklubb" entry.
Two new colors, per your request for two: lifted the already-validated --chart-3 (blue → --info) and --chart-4 (gold → --gold) into core design tokens. First real uses: a blue "Hcp spilt til X" badge on round cards, and a gold "Personlig rekord" badge on your best completed round.
Clicking a course under "Spilte baner" now opens a new page listing every round played there (/my-rounds/course/[name]) — caught and fixed a real double-encoding bug here via an actual browser screenshot before shipping.
Aggregated statistics, clickable from the dashboard's "Statistikk" section (/my-rounds/stats): rounds completed, avg to-par, putts/18 holes (implementing your padding rule exactly — unplayed holes count as 2 putts, only for rounds where putt-tracking was on), fairway%, GIR%, one-putt%, scrambling%, sand save%, and chip/bunker/penalty/anywayslag averages per round.
Everything is typechecked, the putts-padding logic is verified against a hand-computed synthetic dataset, and all new screens were checked live in the browser with no console errors.
2026-07-28 07:04:56 +02:00
|
|
|
const personalBestRoundId = findPersonalBestRoundId(data)
|
|
|
|
|
if (!cancelled) setRounds(data.map((r) => toRound(r, viewerId, personalBestRoundId)))
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
} catch {
|
|
|
|
|
if (!cancelled) setError("Klarte ikke å hente rundene dine. Prøv igjen om litt.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void load()
|
|
|
|
|
return () => {
|
|
|
|
|
cancelled = true
|
|
|
|
|
}
|
2026-07-26 08:22:41 +02:00
|
|
|
}, [router, viewerId])
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<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">
|
|
|
|
|
<Link
|
|
|
|
|
href="/dashboard"
|
|
|
|
|
className="inline-flex min-h-[44px] items-center gap-2 rounded-xl px-2 py-2 text-base font-semibold text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
|
|
|
>
|
|
|
|
|
<ArrowLeft aria-hidden="true" className="size-5" />
|
|
|
|
|
Til dashbord
|
|
|
|
|
</Link>
|
|
|
|
|
<Wordmark compact />
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<main className="mx-auto w-full max-w-3xl flex-1 px-5 py-8 sm:py-10">
|
|
|
|
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between sm:gap-6">
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
<h1 className="text-3xl font-extrabold tracking-tight text-foreground text-balance">
|
|
|
|
|
Egne runder
|
|
|
|
|
</h1>
|
|
|
|
|
<p className="max-w-prose text-lg leading-relaxed text-muted-foreground text-pretty">
|
|
|
|
|
Frittstående golfrunder du har registrert selv, uavhengig av turnering og klubb.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Button
|
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.
2026-07-23 12:07:40 +02:00
|
|
|
render={<Link href="/my-rounds/new" />}
|
V0-integrasjonen er ferdig. Kort om hva som skjedde:
Diff mot levende tre (samme rutine som alltid): alle tre eksportene var fulle re-eksporter av hele prosjektet, bare fire filer var faktisk nye/endret for dette formålet: round-card.tsx, own-rounds.tsx, new-round.tsx, round-detail.tsx (+ tre app/rounds*/page.tsx-ruter). Alt annet (login-form, dashboard, config osv.) var forventede full-reverts og ble ikke rørt. Ett kjent V0-avvik dukket opp igjen (app/clubs/[id]/page.tsx med feil parameternavn — samme feil som ble rettet i klubbsiderunden) — hoppet bevisst over den, som vanlig.
Erstattet mine hånd-bygde skjermer med V0s presentasjon, datalag skrevet om fra mock til ekte fetch, blant annet:
Egen-bane-søket er nå et ekte to-stegs oppslag (søk → hent detalj med utslag) siden V0s modell antok baner allerede hadde utslagene ferdig innbakt i søkeresultatet.
La til «Annet» som et tredje kjønnsvalg for gjestespillere (V0 hadde kun mann/kvinne) — matcher appens ellers etablerte konvensjon.
Beholdt merge-før-PATCH-sikringen fra forrige runde (hull-endepunktet skriver alle felt hver gang).
Fjernet V0s dev-only forhåndsvisningsbrytere («Forhåndsvis: Pågår/Fullført» osv.) — samme opprydning som i tidligere runder.
Verifisert: ekte typesjekket produksjonsbuild (docker build --target builder) kompilerte rent, alle ruter listet. Ingen backend-endring i denne runden, så API-kontrakten er allerede scratch-bevist fra forrige runde — jeg har ikke kjørt en ny interaktiv nettleser-test (intet slikt verktøy tilgjengelig), kun kodegjennomgang + typesjekk.
Zip-filene er slettet fra prosjektroten.
2026-07-23 11:27:37 +02:00
|
|
|
size="lg"
|
|
|
|
|
className="h-12 shrink-0 gap-2 rounded-2xl px-6 text-base font-bold shadow-sm"
|
|
|
|
|
>
|
|
|
|
|
<Plus aria-hidden="true" className="size-5" />
|
|
|
|
|
Ny runde
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{error && (
|
|
|
|
|
<p role="alert" className="mt-4 text-base font-medium text-destructive">
|
|
|
|
|
{error}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="mt-8">
|
|
|
|
|
{rounds === null ? (
|
|
|
|
|
<LoadingState />
|
|
|
|
|
) : rounds.length === 0 ? (
|
|
|
|
|
<EmptyState />
|
|
|
|
|
) : (
|
|
|
|
|
<ul className="flex flex-col gap-4">
|
|
|
|
|
{rounds.map((round) => (
|
|
|
|
|
<li key={round.id}>
|
|
|
|
|
<RoundCard round={round} />
|
|
|
|
|
</li>
|
|
|
|
|
))}
|
|
|
|
|
</ul>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function LoadingState() {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
role="status"
|
|
|
|
|
aria-live="polite"
|
|
|
|
|
className="flex flex-col items-center justify-center gap-4 py-20 text-center"
|
|
|
|
|
>
|
|
|
|
|
<Loader2 aria-hidden="true" className="size-9 animate-spin text-primary" />
|
|
|
|
|
<span className="text-base font-medium text-muted-foreground">Laster runder…</span>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function EmptyState() {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex flex-col items-center gap-5 rounded-3xl border border-dashed border-border bg-card/50 px-6 py-16 text-center">
|
|
|
|
|
<div className="flex size-16 items-center justify-center rounded-2xl bg-primary/15">
|
|
|
|
|
<ClipboardList aria-hidden="true" className="size-8 text-primary" />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex max-w-md flex-col gap-2">
|
|
|
|
|
<h2 className="text-xl font-bold text-foreground">Ingen runder ennå</h2>
|
|
|
|
|
<p className="text-lg leading-relaxed text-muted-foreground text-pretty">
|
|
|
|
|
Her dukker rundene dine opp etter hvert som du registrerer dem. Trykk på{" "}
|
|
|
|
|
<span className="font-semibold text-foreground">«Ny runde»</span> øverst for å komme i
|
|
|
|
|
gang.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|