Compare commits
2 commits
43482d9ba9
...
861a2ec1a7
| Author | SHA1 | Date | |
|---|---|---|---|
| 861a2ec1a7 | |||
| ad3c47bfa3 |
3 changed files with 70 additions and 5 deletions
31
CHANGELOG.md
31
CHANGELOG.md
|
|
@ -9229,3 +9229,34 @@ Neste steg:
|
||||||
(migrasjonen aldri kjørt der, krever egen brukerbekreftelse), og
|
(migrasjonen aldri kjørt der, krever egen brukerbekreftelse), og
|
||||||
`TEECUP_MAPBOX_SECRET_TOKEN` (for delings-satellittbilder) mangler
|
`TEECUP_MAPBOX_SECRET_TOKEN` (for delings-satellittbilder) mangler
|
||||||
fortsatt.
|
fortsatt.
|
||||||
|
|
||||||
|
47. **Ny-runde-veiviser: utslag lagt til direkte i gjesteskjemaet —
|
||||||
|
2026-08-08, brukertilbakemelding.** Brukeren rapporterte at
|
||||||
|
utslag-valget for en nyopprettet gjest ("midlertidig spiller") ikke
|
||||||
|
var intuitivt -- måtte inn i det ferdigopprettede gjestekortet
|
||||||
|
("Rediger") for i det hele tatt å SE hvilket utslag som var valgt.
|
||||||
|
|
||||||
|
Viste seg at `addGuest()` allerede valgte utslag automatisk
|
||||||
|
(`compatibleTees(course, gender)[0]?.id`), men helt stille -- selve
|
||||||
|
gjesteskjemaet (`GuestForm` i `step3-players.tsx`) hadde aldri et
|
||||||
|
synlig Utslag-felt, kun Fornavn/Etternavn/E-post/Kjønn/HCP/
|
||||||
|
Statistikk. Lagt til et kontrollert `teeId`-felt direkte i skjemaet,
|
||||||
|
samme `NativeSelect`-mønster som `PlayerCard`s tilsvarende felt,
|
||||||
|
med samme kjønn→utslag-resynk-logikk (`stillOk`-sjekk) som
|
||||||
|
`PlayerCard` allerede hadde -- inkludert i "Fyll inn automatisk"-
|
||||||
|
knappen for kjente gjester (samme feilklasse, ikke tidligere
|
||||||
|
rettet der).
|
||||||
|
|
||||||
|
`tsc --noEmit` rent. **Scratch-verifisert i ekte nettleser**
|
||||||
|
(syttende scratch-miljø denne økten, samme migrasjon-002-
|
||||||
|
forsiktighet, `teecup_db` bekreftet uendret): opprettet en egen bane
|
||||||
|
med kjønns-eksklusive utslag (Gul kun menn, Rød kun kvinner) for å
|
||||||
|
bevise resynk-logikken fungerer -- byttet kjønn til Kvinne i
|
||||||
|
gjesteskjemaet, utslag-feltet oppdaterte seg umiddelbart til "Rød",
|
||||||
|
la til gjesten, og det ferdige spillerkortet viste "Utslag: Rød"
|
||||||
|
med det samme, uten å måtte åpne "Rediger". Ingen konsollfeil.
|
||||||
|
Scratch-miljøet ryddet opp fullstendig.
|
||||||
|
|
||||||
|
**Ikke rullet ut ennå** -- venter på samlet bekreftelse sammen med
|
||||||
|
resten av det som gjenstår (Mapbox-hemmelig-token, migrering av
|
||||||
|
`060_round_shot.sql`).
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { cn } from "@/lib/utils"
|
||||||
import { ChevronDown, Search, Sparkles, UserPlus, X } from "lucide-react"
|
import { ChevronDown, Search, Sparkles, UserPlus, X } from "lucide-react"
|
||||||
import { useEffect, useMemo, useRef, useState } from "react"
|
import { useEffect, useMemo, useRef, useState } from "react"
|
||||||
import { Avatar, Field, NativeSelect, Pill, TextInput, ToggleButton, useDebouncedValue } from "./primitives"
|
import { Avatar, Field, NativeSelect, Pill, TextInput, ToggleButton, useDebouncedValue } from "./primitives"
|
||||||
|
import type { Tee } from "@/lib/ny-runde/types"
|
||||||
import { useWizard } from "./wizard-context"
|
import { useWizard } from "./wizard-context"
|
||||||
|
|
||||||
const GENDERS: { value: Gender; label: string }[] = [
|
const GENDERS: { value: Gender; label: string }[] = [
|
||||||
|
|
@ -272,6 +273,7 @@ function AddPlayerPanel({
|
||||||
const [gender, setGender] = useState<Gender>("mann")
|
const [gender, setGender] = useState<Gender>("mann")
|
||||||
const [hcp, setHcp] = useState("")
|
const [hcp, setHcp] = useState("")
|
||||||
const [stat, setStat] = useState<StatLevel>(defaultStat)
|
const [stat, setStat] = useState<StatLevel>(defaultStat)
|
||||||
|
const [teeId, setTeeId] = useState<string | undefined>(() => compatibleTees(course, "mann")[0]?.id)
|
||||||
const firstRef = useRef<HTMLInputElement>(null)
|
const firstRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -330,7 +332,7 @@ function AddPlayerPanel({
|
||||||
isGuest: true,
|
isGuest: true,
|
||||||
email: email.trim() || undefined,
|
email: email.trim() || undefined,
|
||||||
hcp: hcp.trim() === "" ? null : Number.parseFloat(hcp.replace(",", ".")),
|
hcp: hcp.trim() === "" ? null : Number.parseFloat(hcp.replace(",", ".")),
|
||||||
teeId: compatibleTees(course, gender)[0]?.id,
|
teeId,
|
||||||
statLevel: stat,
|
statLevel: stat,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -400,6 +402,7 @@ function AddPlayerPanel({
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<GuestForm
|
<GuestForm
|
||||||
|
course={course}
|
||||||
first={first}
|
first={first}
|
||||||
setFirst={setFirst}
|
setFirst={setFirst}
|
||||||
last={last}
|
last={last}
|
||||||
|
|
@ -412,6 +415,8 @@ function AddPlayerPanel({
|
||||||
setHcp={setHcp}
|
setHcp={setHcp}
|
||||||
stat={stat}
|
stat={stat}
|
||||||
setStat={setStat}
|
setStat={setStat}
|
||||||
|
teeId={teeId}
|
||||||
|
setTeeId={setTeeId}
|
||||||
firstRef={firstRef}
|
firstRef={firstRef}
|
||||||
onBackToSearch={() => setMode("search")}
|
onBackToSearch={() => setMode("search")}
|
||||||
onSubmit={addGuest}
|
onSubmit={addGuest}
|
||||||
|
|
@ -422,6 +427,7 @@ function AddPlayerPanel({
|
||||||
}
|
}
|
||||||
|
|
||||||
function GuestForm({
|
function GuestForm({
|
||||||
|
course,
|
||||||
first,
|
first,
|
||||||
setFirst,
|
setFirst,
|
||||||
last,
|
last,
|
||||||
|
|
@ -434,10 +440,13 @@ function GuestForm({
|
||||||
setHcp,
|
setHcp,
|
||||||
stat,
|
stat,
|
||||||
setStat,
|
setStat,
|
||||||
|
teeId,
|
||||||
|
setTeeId,
|
||||||
firstRef,
|
firstRef,
|
||||||
onBackToSearch,
|
onBackToSearch,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}: {
|
}: {
|
||||||
|
course?: Course
|
||||||
first: string
|
first: string
|
||||||
setFirst: (v: string) => void
|
setFirst: (v: string) => void
|
||||||
last: string
|
last: string
|
||||||
|
|
@ -450,10 +459,13 @@ function GuestForm({
|
||||||
setHcp: (v: string) => void
|
setHcp: (v: string) => void
|
||||||
stat: StatLevel
|
stat: StatLevel
|
||||||
setStat: (v: StatLevel) => void
|
setStat: (v: StatLevel) => void
|
||||||
|
teeId: string | undefined
|
||||||
|
setTeeId: (v: string | undefined) => void
|
||||||
firstRef: React.RefObject<HTMLInputElement | null>
|
firstRef: React.RefObject<HTMLInputElement | null>
|
||||||
onBackToSearch: () => void
|
onBackToSearch: () => void
|
||||||
onSubmit: () => void
|
onSubmit: () => void
|
||||||
}) {
|
}) {
|
||||||
|
const tees = compatibleTees(course, gender)
|
||||||
// kjent-gjest-oppslag på e-post (scoped til DENNE brukeren server-side)
|
// kjent-gjest-oppslag på e-post (scoped til DENNE brukeren server-side)
|
||||||
const debouncedEmail = useDebouncedValue(email, 350)
|
const debouncedEmail = useDebouncedValue(email, 350)
|
||||||
const [match, setMatch] = useState<KnownGuest | null>(null)
|
const [match, setMatch] = useState<KnownGuest | null>(null)
|
||||||
|
|
@ -508,8 +520,12 @@ function GuestForm({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFirst(match.firstName)
|
setFirst(match.firstName)
|
||||||
setLast(match.lastName)
|
setLast(match.lastName)
|
||||||
if (match.gender === "m") setGender("mann")
|
const matchedGender = match.gender === "m" ? "mann" : match.gender === "f" ? "kvinne" : null
|
||||||
else if (match.gender === "f") setGender("kvinne")
|
if (matchedGender) {
|
||||||
|
const stillOk = compatibleTees(course, matchedGender).some((t) => t.id === teeId)
|
||||||
|
setGender(matchedGender)
|
||||||
|
if (!stillOk) setTeeId(compatibleTees(course, matchedGender)[0]?.id)
|
||||||
|
}
|
||||||
if (match.hcp != null) setHcp(String(match.hcp))
|
if (match.hcp != null) setHcp(String(match.hcp))
|
||||||
}}
|
}}
|
||||||
className="mt-1 text-[13px] font-semibold text-[var(--nr-accent)] underline-offset-2 hover:underline"
|
className="mt-1 text-[13px] font-semibold text-[var(--nr-accent)] underline-offset-2 hover:underline"
|
||||||
|
|
@ -521,7 +537,25 @@ function GuestForm({
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<Field label="Kjønn">
|
<Field label="Kjønn">
|
||||||
<GenderChoice value={gender} onChange={setGender} />
|
<GenderChoice
|
||||||
|
value={gender}
|
||||||
|
onChange={(g) => {
|
||||||
|
const stillOk = compatibleTees(course, g).some((t) => t.id === teeId)
|
||||||
|
setGender(g)
|
||||||
|
if (!stillOk) setTeeId(compatibleTees(course, g)[0]?.id)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label="Utslag" htmlFor="g-tee">
|
||||||
|
<NativeSelect id="g-tee" value={teeId ?? ""} onChange={(e) => setTeeId(e.target.value || undefined)}>
|
||||||
|
<option value="">Velg utslag</option>
|
||||||
|
{tees.map((t: Tee) => (
|
||||||
|
<option key={t.id} value={t.id}>
|
||||||
|
{t.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</NativeSelect>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Field label="HCP" optional htmlFor="g-hcp">
|
<Field label="HCP" optional htmlFor="g-hcp">
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue