Compliance-pass shipped and live. Summary:
"Deg Deg" fixed — the name is now always the real display name; the "Deg" badge alone marks your own row. This also fixed the same problem in the wizard header/context chips/footer, which had no badge at all and were showing "Deg" instead of the roster-context full name the spec requires. Score button now follows the golfscore language — reused ScoreMark's exact primary/brand-orange fill-vs-tint rules instead of an unconditional green circle. 13 smaller gaps fixed — 2 missing tabular-nums, 11 tap targets brought up to the 44px floor. Spec's §1 (scorecard-as-grid) deliberately left out — that's a bigger structural call, not something to slip in under a "fix known bugs" pass. Typechecked build was clean, both containers redeployed and booted cleanly, /health//dashboard → 200, teeoff.no unaffected. Docs updated (CLAUDE.md status + autoritative kilder, FEATURE_BACKLOG.md pointer). Same honest caveat as the rest of this week's hand-coded work: no real browser interaction test — worth a manual look, especially the score button's shape change at the 56px size.
This commit is contained in:
parent
6bfd986964
commit
a11c638fda
3 changed files with 159 additions and 33 deletions
81
CLAUDE.md
81
CLAUDE.md
|
|
@ -19,6 +19,16 @@ Les dette først i hver økt. Det koder hva vi har bestemt og hvordan vi jobber.
|
|||
turneringsformatene som ennå ikke er designet (Københavner/High-low-high/
|
||||
Robbins/Try all, se FEATURE_BACKLOG.md), siden disse dokumentene trolig
|
||||
dekker akkurat de reglene som trengs der.
|
||||
- **Design-dokumentasjon, to filer med bevisst motsatt retning (2026-07-27):**
|
||||
`DESIGN_SYSTEM.md` (innsjekket i git) er DESKRIPTIV — hva som faktisk ER i
|
||||
koden i dag (farger/typografi/komponentmønstre/golfscore-språket), avledet
|
||||
direkte fra `globals.css`/`components/ui/*`. `teecup-scorekort-og-entry-
|
||||
spec.md` (lastet opp av brukeren til prosjektroten, IKKE innsjekket i git)
|
||||
er PRESKRIPTIV — hva scorekort-/score-entry-skjermene BØR bli, basert på en
|
||||
sammenligning mot fem etablerte golf-apper. Der de to avviker vinner
|
||||
kildekoden (DESIGN_SYSTEM.md beskriver den), men spec-dokumentets §3
|
||||
"Compliance-pass" er en konkret sjekkliste over kjente avvik — les den FØR
|
||||
videre visuelt arbeid på scorekort-/score-entry-skjermene.
|
||||
|
||||
## Sikkerhetsregler (ufravikelige)
|
||||
- Rør ALDRI `teeoff`-databasen eller den ekte `teecup_db` uten at brukeren
|
||||
|
|
@ -3495,6 +3505,77 @@ Ferdig og verifisert:
|
|||
ingen migrasjon, `docker compose up -d --build teecup_frontend`
|
||||
(gjenskapte også `teecup_api` som vanlig bivirkning). Begge containere
|
||||
boot-et rent, `/health`/`/dashboard` → 200, `teeoff.no` upåvirket.
|
||||
- **`DESIGN_SYSTEM.md` opprettet (2026-07-27):** brukeren ba om en ny fil
|
||||
som viser designsystemet arbeidet tar utgangspunkt i. Skrevet fra bunnen,
|
||||
DESKRIPTIVT (hva som ER i koden, ikke et mål) — grunnet direkte i
|
||||
`globals.css`/`components/ui/*.tsx` og etablerte tvers-fil-mønstre:
|
||||
fargetoken (inkl. OKLCH-opprinnelsen til `primary`/`brand-orange` fra
|
||||
ADR-009/016), typografi, avstand/hjørner/lag (inkl. "én sammenhengende
|
||||
liste med `divide-y`, ikke separate kort"-regelen fra 2026-07-26s
|
||||
leaderboard-fiks), lokale komponentmønstre (NumberPicker/Stepper/
|
||||
ChoiceRow/DirectionCross — bevisst per-fil, ikke delte importer),
|
||||
golfscore-språket (`ScoreMark`/`ToParMark`/`PointsMark`/`HoleMark`),
|
||||
ikonografi, tilbakemelding/tilstander, og navneformat (pekt til CLAUDE.md).
|
||||
- **`teecup-scorekort-og-entry-spec.md` lest og evaluert, delvis BYGGET SOM
|
||||
COMPLIANCE-PASS SAMME DAG (2026-07-27):** brukeren lastet opp et
|
||||
PRESKRIPTIVT motstykke til DESIGN_SYSTEM.md (5-app-sammenligning: Golf
|
||||
GameBook/Golf Pad/Golfshot/Hole 19/18Birdies) og spurte om det ga mening
|
||||
og kunne forbedre TeeCup. Vurdert som reelt verdifullt — sylskarpeste nye
|
||||
innsikt: "grønt-på-grønt"-diagnosen (§0.2) forklarer noe av "fortsatt
|
||||
rotete"-følelsen fra v1-v3-rundene bedre enn tetthet alene: `primary`
|
||||
(grønn) var brukt om hverandre for BÅDE "aktiv tilstand" og "generisk
|
||||
fylt/positiv", uten at begge betydde det samme sted. Foreslo (og
|
||||
brukeren bekreftet) å fikse spec-dokumentets §3 "Compliance-pass" FØR den
|
||||
større strukturelle §1-omleggingen (scorekort som grid) vurderes som egen,
|
||||
senere runde.
|
||||
**To av sjekklistens fem punkter var KONKRETE, VERIFISERBARE bugs, bekreftet
|
||||
direkte i koden (ikke antatt fra spec-teksten alene) FØR de ble fikset:**
|
||||
1. **"Deg Deg"-duplikat:** `playerLabel()` (`round-detail.tsx`) erstattet
|
||||
tidligere selve navnet med "Deg" for viewer-relativ egen rad, OG en
|
||||
separat `<Badge>Deg</Badge>` sto ved siden av samme sted (spillerkort,
|
||||
scoringslisten) — bekreftet duplikat, matchet et tidligere skjermbilde
|
||||
brukeren delte. **Fikset:** `playerLabel()` returnerer nå alltid det
|
||||
faktiske `display_name` (aldri "Deg") — Badge-en er nå ENESTE
|
||||
selv-indikator. Dette retter samtidig et videre, ikke tidligere flagget
|
||||
avvik: spec-dokumentet krever eksplisitt "roster-kontekst → fullt navn"
|
||||
for BÅDE scorekort-gridet og score-entry-headeren — veiviserens header/
|
||||
kontekst-rad/"Neste: {navn}"/fullført-banneret viste tidligere "Deg" i
|
||||
stedet for et fullt navn der også, uten noen badge til å disambiguere
|
||||
(reelt forvirrende på en delt telefon som sendes rundt en flight).
|
||||
Det nå overflødige `rawName`-feltet (var identisk med `name` etter
|
||||
fiksen) fjernet, tre kallsteder oppdatert.
|
||||
2. **Score-knappen fulgte ikke `§Golfscore-språket`:** den store runde
|
||||
knappen i den kompakte scoringslisten (`round-detail.tsx`, bygget i
|
||||
v2-runden) var `rounded-full`+grønn UANSETT om resultatet var under,
|
||||
over eller på par — bogey og eagle så identiske ut. **Fikset:** ny
|
||||
`scoreMarkClasses(diff)`-hjelpefunksjon som gjenbruker EKSAKT samme
|
||||
`primary`/`brand-orange`-fargespråk og fylt-vs-border+10%-tint-omfangs-
|
||||
regel som `ScoreMark` i `round-scorecard.tsx` (bekreftet ved å lese
|
||||
`ScoreMark` sin kildekode direkte, ikke gjettet) — sirkel under par,
|
||||
nøytral sirkel på par, `rounded-2xl` (bevisst mildere enn scorekortets
|
||||
`rounded-[4px]`, for å matche denne skjermens øvrige 56px-trykkflate-
|
||||
avrunding) over par, fylt ved 2+ slag fra par.
|
||||
**Resten av sjekklisten (44px-trykkgulv, `tabular-nums`) auditert
|
||||
systematisk mot AKKURAT denne filen** (samme fil brukerens skjermbilder
|
||||
viste) — 2 manglende `tabular-nums` (HCP/tildelte slag i spillerkortet)
|
||||
og 11 knapper/lenker under 44px (fane-bryteren `min-h-10`→`min-h-11`,
|
||||
`EditRoundPanel`s lukk-ikon `size-8`→`size-11`, feilside-tilbakelenken,
|
||||
og åtte knapper i bane-bytte-/legg-til-medspiller-skjemaene) rettet.
|
||||
**Bevisst UTENFOR omfang denne runden:** spec-dokumentets §1 (scorekort
|
||||
som fullt grid, celle åpner veiviseren) — en STØRRE strukturell endring
|
||||
som fortjener et eget, bevisst ja fra brukeren, ikke bygget stille inn i
|
||||
en "fiks kjente bugs"-runde.
|
||||
**Verifisert:** ekte typesjekket produksjonsbuild (samme `Dockerfile` som
|
||||
deployes) kompilerte rent, alle 24 ruter listet. **Samme ærlige
|
||||
begrensning som ALLE håndkodede runder denne uken:** ingen ekte
|
||||
nettleser-interaksjonstest av det faktiske visuelle resultatet (kun kode-
|
||||
lesing + build-verifisering + bevisst gjenbruk av en allerede lest,
|
||||
eksisterende komponents fargespråk for å holde risikoen lav).
|
||||
**Rullet ut live 2026-07-27**, bruker bekreftet eksplisitt: ingen
|
||||
migrasjon, `docker compose up -d --build teecup_frontend` (gjenskapte
|
||||
også `teecup_api` som vanlig bivirkning, ingen backend-kode rørt). Begge
|
||||
containere boot-et rent, `/health`/`/dashboard` → 200, `teeoff.no`
|
||||
upåvirket.
|
||||
|
||||
Neste steg:
|
||||
0a. **Spillerliste-redesign — LIVE, men ikke nettleser-testet
|
||||
|
|
|
|||
|
|
@ -1326,6 +1326,18 @@ ARCHITECTURE_DECISIONS.md (ADR-033-oppdatering 2026-07-26), full
|
|||
verifiserings-/utrullingsdetalj i CLAUDE.md sin statuslogg — ikke
|
||||
duplisert her.
|
||||
|
||||
**Oppfølging 2026-07-27 — `teecup-scorekort-og-entry-spec.md` (nytt,
|
||||
brukeropplastet PRESKRIPTIVT motstykke til `DESIGN_SYSTEM.md`, basert på
|
||||
en 5-app-sammenligning) evaluert, §3 "Compliance-pass" delvis BYGGET SAMME
|
||||
DAG — ✅ LIVE:** "Deg Deg"-badge-duplikat fjernet (`playerLabel()` viser nå
|
||||
alltid ekte navn), scoringslistens score-knapp gjort om til å følge
|
||||
`§Golfscore-språket` (gjenbruker `ScoreMark`s fargespråk fra
|
||||
`round-scorecard.tsx`), pluss et systematisk `tabular-nums`/44px-
|
||||
trykkgulv-avvik-audit fikset (13 steder). Spec-dokumentets §1 (scorekort
|
||||
som fullt grid, egen skjerm) bevisst IKKE bygget — større strukturell
|
||||
endring, egen fremtidig runde. Full detalj i CLAUDE.md sin statuslogg
|
||||
(2026-07-27) — ikke duplisert her.
|
||||
|
||||
---
|
||||
|
||||
## En tredje (informasjons-)farge til designet — 🧠 DRØFTET 2026-07-25, IKKE besluttet
|
||||
|
|
|
|||
|
|
@ -52,12 +52,10 @@ type PuttBucket = "<1m" | "<2m" | "<3m" | "<5m" | "<8m" | "8m+"
|
|||
|
||||
type Player = {
|
||||
id: string
|
||||
// Visningsnavn -- "Deg" for spilleren SOM SER PÅ (viewer-relativt, se
|
||||
// playerLabel()), IKKE nødvendigvis den faktiske display_name.
|
||||
// Alltid den faktiske display_name (2026-07-27: fjernet "Deg"-erstatning
|
||||
// her -- se playerLabel() -- brøt "roster-kontekst → fullt navn" og ga en
|
||||
// "Deg Deg"-duplikat der en egen Badge også viste "Deg" ved siden av).
|
||||
name: string
|
||||
// Den faktiske, urørte display_name -- ALDRI "Deg". Brukt til å
|
||||
// forhåndsutfylle "Rediger"-flytens navnefelt for en gjest (2026-07-26).
|
||||
rawName: string
|
||||
userId: string | null
|
||||
guestEmail: string | null
|
||||
isOwner: boolean
|
||||
|
|
@ -264,11 +262,17 @@ function statToPatchBody(s: HoleStat) {
|
|||
}
|
||||
}
|
||||
|
||||
// Viewer-relativt (ADR-036 fase 3-utvidelsen, 2026-07-26): "Deg" skal vises
|
||||
// for DEN SOM SER PÅ, ikke alltid runde-eieren -- en lenket medspiller som
|
||||
// åpner runden skal se seg selv som "Deg" og eieren under sitt eget navn.
|
||||
function playerLabel(p: ApiParticipant, viewerId: string | null): string {
|
||||
return viewerId !== null && p.user_id === viewerId ? "Deg" : p.display_name
|
||||
// Alltid det faktiske visningsnavnet -- ALDRI "Deg" (2026-07-27, compliance-
|
||||
// pass mot teecup-scorekort-og-entry-spec.md). "Deg" var tidligere en
|
||||
// viewer-relativ erstatning for selve navnet, men det produserte en ekte
|
||||
// "Deg Deg"-duplikat der navnet OGSÅ sto ved siden av en egen "Deg"-badge
|
||||
// (spillerkort, scoringslisten) -- og brøt spesifikasjonens eksplisitte
|
||||
// "roster-kontekst → fullt navn"-regel i skjermer uten noen badge i det
|
||||
// hele tatt (veiviserens header/kontekst-rad, "Neste: {navn}", fullført-
|
||||
// banneret). `isSelf` (se players-konstruksjonen under) er fortsatt
|
||||
// viewer-relativ og brukes alene til å vise "Deg"-badgen der det trengs.
|
||||
function playerLabel(p: ApiParticipant): string {
|
||||
return p.display_name
|
||||
}
|
||||
|
||||
// --- Component -------------------------------------------------------------
|
||||
|
|
@ -414,8 +418,7 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
|||
const players: Player[] =
|
||||
round?.participants.map((p) => ({
|
||||
id: p.id,
|
||||
name: playerLabel(p, viewerId),
|
||||
rawName: p.display_name,
|
||||
name: playerLabel(p),
|
||||
userId: p.user_id,
|
||||
guestEmail: p.guest_email,
|
||||
isOwner: p.is_owner,
|
||||
|
|
@ -651,7 +654,10 @@ 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="/my-rounds" className="text-base font-semibold text-primary underline underline-offset-2">
|
||||
<Link
|
||||
href="/my-rounds"
|
||||
className="flex min-h-11 items-center text-base font-semibold text-primary underline underline-offset-2"
|
||||
>
|
||||
Tilbake til egne runder
|
||||
</Link>
|
||||
</div>
|
||||
|
|
@ -726,7 +732,7 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
|||
onClick={() => setPageTab("score")}
|
||||
aria-pressed={pageTab === "score"}
|
||||
className={cn(
|
||||
"min-h-10 rounded-full px-4 text-sm font-bold transition-colors",
|
||||
"min-h-11 rounded-full px-4 text-sm font-bold transition-colors",
|
||||
pageTab === "score" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground",
|
||||
)}
|
||||
>
|
||||
|
|
@ -737,7 +743,7 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
|||
onClick={() => setPageTab("manage")}
|
||||
aria-pressed={pageTab === "manage"}
|
||||
className={cn(
|
||||
"min-h-10 rounded-full px-4 text-sm font-bold transition-colors",
|
||||
"min-h-11 rounded-full px-4 text-sm font-bold transition-colors",
|
||||
pageTab === "manage" ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground",
|
||||
)}
|
||||
>
|
||||
|
|
@ -905,6 +911,11 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
|||
const playerApiHole = playerHoles?.find((h) => h.hole_number === activeHole) ?? null
|
||||
const hasScore = Boolean(playerApiHole?.played) && playerApiHole?.score !== null
|
||||
const running = computeRunningToPar(playerHoles)
|
||||
// Golfscore-språket (2026-07-27, compliance-pass): sirkel
|
||||
// under par / rundet firkant over par / nøytral sirkel på
|
||||
// par -- ALDRI samme grønne fylte sirkel uansett resultat
|
||||
// (bogey og dobbel bogey skal ikke se ut som birdie).
|
||||
const diff = hasScore && hole ? (playerApiHole!.score as number) - hole.par : null
|
||||
return (
|
||||
<li key={player.id} className="flex items-center gap-3 p-3 sm:p-4">
|
||||
<span className="flex min-w-0 flex-1 flex-col">
|
||||
|
|
@ -935,14 +946,12 @@ export function RoundDetail({ roundId }: { roundId: string }) {
|
|||
}}
|
||||
aria-label={
|
||||
hasScore
|
||||
? `Rediger ${player.name} sin registrering på hull ${activeHole}, ${playerApiHole?.score} slag`
|
||||
? `Rediger ${player.name} sin registrering på hull ${activeHole}, ${playerApiHole?.score} slag${diff !== null && hole ? `, ${golfTermForScore(playerApiHole!.score as number, hole.par) ?? ""}` : ""}`
|
||||
: `Registrer score for ${player.name} på hull ${activeHole}`
|
||||
}
|
||||
className={cn(
|
||||
"flex size-14 shrink-0 items-center justify-center rounded-full border-2 text-xl font-extrabold tabular-nums transition-colors disabled:opacity-60",
|
||||
hasScore
|
||||
? "border-primary bg-primary text-primary-foreground"
|
||||
: "border-dashed border-border text-muted-foreground hover:border-primary/60 hover:text-primary",
|
||||
"flex size-14 shrink-0 items-center justify-center border-2 text-xl font-extrabold tabular-nums transition-colors disabled:opacity-60",
|
||||
scoreMarkClasses(diff),
|
||||
)}
|
||||
>
|
||||
{hasScore ? playerApiHole?.score : "–"}
|
||||
|
|
@ -1669,7 +1678,7 @@ function PlayerList({
|
|||
Utslag: <span className="font-semibold text-foreground">{player.teeName}</span>
|
||||
{" · "}
|
||||
HCP:{" "}
|
||||
<span className="font-semibold text-foreground">
|
||||
<span className="font-semibold tabular-nums text-foreground">
|
||||
{player.hcp !== null ? player.hcp.toFixed(1).replace(".", ",") : "ikke satt"}
|
||||
</span>
|
||||
{/* Tildelte slag (course handicap) -- KUN vist når HCP
|
||||
|
|
@ -1678,7 +1687,7 @@ function PlayerList({
|
|||
{player.courseHandicap !== null && (
|
||||
<>
|
||||
{" · "}
|
||||
Tildelte slag: <span className="font-semibold text-foreground">{player.courseHandicap}</span>
|
||||
Tildelte slag: <span className="font-semibold tabular-nums text-foreground">{player.courseHandicap}</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
|
|
@ -1862,7 +1871,7 @@ function AddParticipantForm({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setMode("guest")}
|
||||
className="text-sm font-semibold text-foreground underline-offset-2 hover:underline"
|
||||
className="flex min-h-11 items-center text-sm font-semibold text-foreground underline-offset-2 hover:underline"
|
||||
>
|
||||
Legg til uten konto (gjest)
|
||||
</button>
|
||||
|
|
@ -1904,7 +1913,7 @@ function AddGuestForm({
|
|||
<button
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
className="self-start text-sm font-semibold text-muted-foreground hover:text-foreground"
|
||||
className="flex min-h-11 items-center self-start text-sm font-semibold text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
← Søk i stedet
|
||||
</button>
|
||||
|
|
@ -2043,7 +2052,7 @@ function EditRoundPanel({
|
|||
<div className="flex flex-col gap-4 rounded-2xl border border-border bg-card p-4 sm:p-5">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="text-sm font-bold text-foreground">Rediger runde</span>
|
||||
<Button type="button" variant="ghost" size="icon" onClick={onClose} className="size-8 rounded-lg text-muted-foreground" aria-label="Lukk">
|
||||
<Button type="button" variant="ghost" size="icon" onClick={onClose} className="size-11 rounded-lg text-muted-foreground" aria-label="Lukk">
|
||||
<X aria-hidden="true" className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -2185,7 +2194,7 @@ function EditParticipantPanel({
|
|||
const [teeName, setTeeName] = useState(player.teeName)
|
||||
const [hcp, setHcp] = useState(player.hcp !== null ? String(player.hcp).replace(".", ",") : "")
|
||||
const [statLevel, setStatLevel] = useState<StatLevel>(player.statLevel)
|
||||
const [guestName, setGuestName] = useState(player.rawName)
|
||||
const [guestName, setGuestName] = useState(player.name)
|
||||
const [gender, setGender] = useState<Gender>(player.gender)
|
||||
const [guestEmail, setGuestEmail] = useState(player.guestEmail ?? "")
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
|
@ -2223,7 +2232,7 @@ function EditParticipantPanel({
|
|||
if (statLevel !== player.statLevel) body.stat_level = statLevel
|
||||
if (isGuest) {
|
||||
const trimmedName = guestName.trim()
|
||||
if (trimmedName && trimmedName !== player.rawName) body.guest_name = trimmedName
|
||||
if (trimmedName && trimmedName !== player.name) body.guest_name = trimmedName
|
||||
if (gender !== player.gender) body.gender = apiGender
|
||||
const trimmedEmail = guestEmail.trim()
|
||||
const normalizedEmail = trimmedEmail === "" ? null : trimmedEmail
|
||||
|
|
@ -2510,7 +2519,7 @@ function ChangeCourseForm({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => pickFacility(f)}
|
||||
className="flex w-full flex-col px-3 py-2.5 text-left hover:bg-accent/60"
|
||||
className="flex min-h-11 w-full flex-col px-3 py-2.5 text-left hover:bg-accent/60"
|
||||
>
|
||||
<span className="text-sm font-semibold text-foreground">{f.name}</span>
|
||||
{(f.city || f.county) && (
|
||||
|
|
@ -2527,7 +2536,11 @@ function ChangeCourseForm({
|
|||
|
||||
{source === "teeoff" && selectedFacility && !selectedOfficialCourse && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<button type="button" onClick={() => setSelectedFacility(null)} className="self-start text-sm font-semibold text-muted-foreground">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedFacility(null)}
|
||||
className="flex min-h-11 items-center self-start text-sm font-semibold text-muted-foreground"
|
||||
>
|
||||
← {selectedFacility.name}
|
||||
</button>
|
||||
{officialCourses === null ? (
|
||||
|
|
@ -2539,7 +2552,7 @@ function ChangeCourseForm({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setSelectedOfficialCourse(c)}
|
||||
className="flex w-full items-center justify-between px-3 py-2.5 text-left text-sm font-semibold text-foreground hover:bg-accent/60"
|
||||
className="flex min-h-11 w-full items-center justify-between px-3 py-2.5 text-left text-sm font-semibold text-foreground hover:bg-accent/60"
|
||||
>
|
||||
{c.name}
|
||||
</button>
|
||||
|
|
@ -2565,7 +2578,7 @@ function ChangeCourseForm({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => pickCustomCourse(c)}
|
||||
className="flex w-full px-3 py-2.5 text-left text-sm font-semibold text-foreground hover:bg-accent/60"
|
||||
className="flex min-h-11 w-full items-center px-3 py-2.5 text-left text-sm font-semibold text-foreground hover:bg-accent/60"
|
||||
>
|
||||
{c.name}
|
||||
</button>
|
||||
|
|
@ -2593,7 +2606,7 @@ function ChangeCourseForm({
|
|||
onClick={() => setTeeName(t.name)}
|
||||
aria-pressed={teeName === t.name}
|
||||
className={cn(
|
||||
"h-10 rounded-lg border px-3 text-sm font-semibold transition-colors",
|
||||
"h-11 rounded-lg border px-3 text-sm font-semibold transition-colors",
|
||||
teeName === t.name
|
||||
? "border-primary bg-primary text-primary-foreground"
|
||||
: "border-border bg-background text-foreground hover:bg-accent/50",
|
||||
|
|
@ -2610,7 +2623,7 @@ function ChangeCourseForm({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<Button type="button" variant="ghost" onClick={onCancel} className="h-10 self-start rounded-xl text-sm font-semibold text-muted-foreground">
|
||||
<Button type="button" variant="ghost" onClick={onCancel} className="h-11 self-start rounded-xl text-sm font-semibold text-muted-foreground">
|
||||
Avbryt
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -2684,6 +2697,26 @@ function golfTermForScore(n: number, par: number): string | null {
|
|||
return null
|
||||
}
|
||||
|
||||
// Golfscore-språket (§Golfscore-språket i DESIGN_SYSTEM.md, håndhevet
|
||||
// eksplisitt av teecup-scorekort-og-entry-spec.md §0.3/§3): form encoder
|
||||
// retning (sirkel = under par, "firkant" = over par) slik at betydningen
|
||||
// overlever selv uten farge; fylt vs. border+10%-tint encoder omfang (fylt =
|
||||
// 2 slag eller mer fra par). Samme klassifisering som `ScoreMark` i
|
||||
// round-scorecard.tsx -- egen kopi her siden denne knappen (size-14,
|
||||
// trykkbar) trenger andre grunnklasser enn scorekortets små tabell-merker,
|
||||
// men SAMME fargespråk (2026-07-27, compliance-pass). Bevisst `rounded-2xl`
|
||||
// for "over par" i stedet for scorekortets `rounded-[4px]` -- fortsatt
|
||||
// tydelig IKKE en sirkel, men matcher denne skjermens øvrige store
|
||||
// trykkflate-avrunding (NumberPicker/hull-panel bruker samme radius).
|
||||
function scoreMarkClasses(diff: number | null): string {
|
||||
if (diff === null) return "rounded-full border-dashed border-border text-muted-foreground hover:border-primary/60 hover:text-primary"
|
||||
if (diff <= -2) return "rounded-full border-primary bg-primary text-primary-foreground"
|
||||
if (diff === -1) return "rounded-full border-primary bg-primary/10 text-primary"
|
||||
if (diff === 0) return "rounded-full border-border bg-card text-foreground"
|
||||
if (diff === 1) return "rounded-2xl border-brand-orange bg-brand-orange/10 text-brand-orange"
|
||||
return "rounded-2xl border-brand-orange bg-brand-orange text-brand-orange-foreground"
|
||||
}
|
||||
|
||||
function NumberPicker({
|
||||
label,
|
||||
value,
|
||||
|
|
|
|||
Loading…
Reference in a new issue