Rutenett-stil for spillerpool-tabellen, ingen sidescroll på bred skjerm (tillegg til ADR-094)
EditTable (player-import-view.tsx) får samme grid-stil som spillertabellen, med individuell kolonnebredde per felt slik at alle 13 spillerpool-felt får plass uten sidescroll. mode.kind==="roster"- grenen bevart ved manuell fletting siden V0-eksporten var en eldre snapshot uten den varianten. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
47e3874198
commit
7632f53702
3 changed files with 111 additions and 56 deletions
|
|
@ -9627,6 +9627,29 @@ navn) -- ren visuell omskriving, ingen endring i `SetupTab` sin
|
|||
sammenkobling nødvendig. Verifisert i ny lokal scratch-visning (bred
|
||||
skjerm bekreftet fylt uten sidescroll med 3 runde-kolonner, lys+mørk).
|
||||
|
||||
**Tillegg 2026-08-19 -- samme rutenett-mønster utvidet til spillerpool-
|
||||
tabellen (`player-import-view.tsx` sin `EditTable`).** Bruker: "Jeg
|
||||
ønsker at tabellen skal være så bred at jeg slipper å scrolle
|
||||
sidelengs" -- presisert etter en første runde der jeg feilaktig hadde
|
||||
bedt V0 om å BEHOLDE horisontal scroll (misforsto "mye bredere" som
|
||||
"behold bredden", ikke "fyll bredden slik at scroll blir unødvendig").
|
||||
Denne tabellen viser opptil 13 datafelt + slett-knapp samtidig i
|
||||
spillerpool-modus (ingen filtrering på kolonnetilknytning der, ulikt
|
||||
CSV-import-modus) -- retting: samme `table-fixed`+`<colgroup>`-teknikk
|
||||
som over, men med EN fast pikselbredde PER felt (`COL_W`, 70-190px
|
||||
avhengig av innholdstype) i stedet for få brede kolonner, slik at alle
|
||||
13 feltene faktisk får plass på en bred skjerm. V0-eksporten denne
|
||||
runden var en eldre prosjekt-snapshot uten `"roster"`-varianten av
|
||||
`Mode`-typen (kun `team`/`individual`) -- fletting gjort manuelt i
|
||||
stedet for å lime inn rått, for å bevare `mode.kind === "roster"`-
|
||||
grenen (annen hjelpetekst, ingen lag/klasse-kolonner) og selve
|
||||
`Mode`/`FieldKey`-typene uendret. `FIELD_MIN_W`-konstanten (nå erstattet
|
||||
av `COL_W`) fjernet som dødt kode. Verifisert i lokal scratch (5 rader,
|
||||
alle 13 spillerpool-felt) -- bekreftet ALLE kolonner synlige uten
|
||||
sidescroll på 1900px, riktig `mode.kind === "roster"`-hjelpetekst
|
||||
bevart, redigering og sticky navnekolonne fungerer, lys+mørk, smal
|
||||
skjerm faller korrekt tilbake til sidescroll.
|
||||
|
||||
---
|
||||
|
||||
## Utviklingsplan (rekkefølge)
|
||||
|
|
|
|||
22
CHANGELOG.md
22
CHANGELOG.md
|
|
@ -13039,3 +13039,25 @@ Neste steg:
|
|||
**Rullet ut 2026-08-19** -- bruker bekreftet. Ren frontend.
|
||||
`docker compose build teecup_frontend && up -d`, rene logger,
|
||||
200 OK.
|
||||
|
||||
128. **Samme rutenett-stil utvidet til spillerpool-tabellen -- 2026-08-19
|
||||
(tillegg til ADR-094).** Bruker: "Jeg ønsker at tabellen skal være
|
||||
så bred at jeg slipper å scrolle sidelengs" -- presisering etter at
|
||||
jeg først (feilaktig) hadde bedt V0 om å BEHOLDE sidescroll for
|
||||
denne tabellen. `player-import-view.tsx` sin `EditTable` (opptil
|
||||
13 felt + slett-knapp samtidig i spillerpool-modus) fikk samme
|
||||
`table-fixed`+`<colgroup>`-teknikk som spillertabellen (#127), men
|
||||
med individuelt tilpasset pikselbredde per felt (70-190px) i stedet
|
||||
for få brede kolonner -- alle 13 feltene får nå plass uten
|
||||
sidescroll på en bred skjerm. V0-eksporten manglet `"roster"`-
|
||||
varianten av `Mode`-typen (eldre snapshot) -- flettet inn manuelt
|
||||
for å bevare den grenen og selve typene uendret. Fjernet nå-død
|
||||
`FIELD_MIN_W`-konstant.
|
||||
|
||||
**Verifisert:** `tsc --noEmit` rent, `vitest run` 55/55. Scratch-
|
||||
visning (5 rader, alle 13 spillerpool-felt): alle kolonner synlige
|
||||
uten sidescroll på 1900px, riktig hjelpetekst for spillerpool-modus
|
||||
bevart, redigering + sticky navnekolonne fungerer, lys+mørk, smal
|
||||
skjerm faller korrekt tilbake til sidescroll.
|
||||
|
||||
**Rullet ut:** venter på bekreftelse.
|
||||
|
|
|
|||
|
|
@ -108,26 +108,6 @@ const FIELD_ORDER: FieldKey[] = [
|
|||
"comment",
|
||||
]
|
||||
|
||||
// Per-field column width for the edit table. Compact by default so a wide
|
||||
// screen shows many columns at once; wider for free-text-heavy fields.
|
||||
const FIELD_MIN_W: Partial<Record<FieldKey, string>> = {
|
||||
display_name: "min-w-44",
|
||||
first_name: "min-w-36",
|
||||
last_name: "min-w-36",
|
||||
nickname: "min-w-32",
|
||||
email: "min-w-52",
|
||||
mobile: "min-w-36",
|
||||
handicap_index: "min-w-20",
|
||||
gender: "min-w-28",
|
||||
birth_date: "min-w-36",
|
||||
club: "min-w-44",
|
||||
club_member_number: "min-w-28",
|
||||
country: "min-w-28",
|
||||
team_name: "min-w-40",
|
||||
class_name: "min-w-40",
|
||||
comment: "min-w-56",
|
||||
}
|
||||
|
||||
const GENDER_OPTIONS: { value: string; label: string }[] = [
|
||||
{ value: "", label: "—" },
|
||||
{ value: "female", label: "Kvinne" },
|
||||
|
|
@ -429,6 +409,39 @@ function MapStep({
|
|||
)
|
||||
}
|
||||
|
||||
// Realistisk, innholds-tilpasset kolonnebredde (px), ikke en rundhåndet
|
||||
// min-bredde -- med table-fixed + w-full fordeler nettleseren overskytende
|
||||
// plass proporsjonalt, så alle ~14 felt (spillerpool-modus) får plass uten
|
||||
// sidescroll på en bred skjerm (bruker eksplisitt: "jeg ønsker at tabellen
|
||||
// skal være så bred at jeg slipper å scrolle sidelengs", 2026-08-19).
|
||||
// overflow-x-auto-wrapperen står igjen som fallback for smal skjerm.
|
||||
const COL_W: Partial<Record<FieldKey, number>> = {
|
||||
display_name: 150,
|
||||
first_name: 110,
|
||||
last_name: 110,
|
||||
nickname: 100,
|
||||
email: 190,
|
||||
mobile: 120,
|
||||
handicap_index: 70,
|
||||
gender: 92,
|
||||
birth_date: 130,
|
||||
club: 140,
|
||||
club_member_number: 104,
|
||||
country: 90,
|
||||
team_name: 128,
|
||||
class_name: 128,
|
||||
comment: 180,
|
||||
}
|
||||
|
||||
// Rutenett-cellekontroll (2026-08-19): flat, fyller hele cellen, ingen egen
|
||||
// kant -- rutenett-linjene definerer strukturen, kontrollen lyser kun opp
|
||||
// ved hover/fokus. Samme mønster som tournament-players-table.tsx.
|
||||
const CELL =
|
||||
"h-full w-full border-0 bg-transparent px-2 py-1.5 text-[13px] leading-tight text-foreground outline-none transition-colors hover:bg-accent/40 focus-visible:bg-background focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
||||
const TD = "border-b border-r border-border p-0 align-middle"
|
||||
const TH =
|
||||
"border-b border-r border-border bg-muted px-2 py-2 text-left text-[11px] font-bold uppercase tracking-wide text-muted-foreground"
|
||||
|
||||
function EditTable({
|
||||
mode,
|
||||
rows,
|
||||
|
|
@ -463,19 +476,21 @@ function EditTable({
|
|||
// narrow screens; on a wide screen every column is visible. `relative`
|
||||
// gives absolutely-positioned descendants (sr-only) a clipped block.
|
||||
<div className="relative min-w-0 overflow-x-auto rounded-2xl border border-border">
|
||||
<table className="w-full border-collapse text-left">
|
||||
<thead className="sticky top-0 z-10">
|
||||
<tr className="border-b border-border bg-muted">
|
||||
{editFields.map((field) => (
|
||||
<th
|
||||
key={field}
|
||||
scope="col"
|
||||
className="whitespace-nowrap px-3 py-2.5 text-xs font-extrabold uppercase tracking-wide text-muted-foreground"
|
||||
>
|
||||
<table className="w-full table-fixed border-collapse text-left">
|
||||
<colgroup>
|
||||
{editFields.map((field) => (
|
||||
<col key={field} style={{ width: COL_W[field] ?? 120 }} />
|
||||
))}
|
||||
<col style={{ width: 72 }} />
|
||||
</colgroup>
|
||||
<thead className="sticky top-0 z-20">
|
||||
<tr className="h-9">
|
||||
{editFields.map((field, i) => (
|
||||
<th key={field} scope="col" className={cn(TH, "truncate", i === 0 && "sticky left-0 z-30")}>
|
||||
{FIELD_LABELS[field]}
|
||||
</th>
|
||||
))}
|
||||
<th scope="col" className="px-3 py-2.5 text-right">
|
||||
<th scope="col" className={cn(TH, "border-r-0 text-right")}>
|
||||
<span className="sr-only">Handlinger</span>
|
||||
</th>
|
||||
</tr>
|
||||
|
|
@ -490,19 +505,16 @@ function EditTable({
|
|||
// scrolle til riktig rad uten å måtte utvide selve
|
||||
// props-kontrakten eller endre noe synlig her.
|
||||
data-row-key={row.key}
|
||||
className={cn(
|
||||
"border-b border-border last:border-b-0",
|
||||
rowIndex % 2 === 1 && "bg-muted/30",
|
||||
)}
|
||||
className="group h-9 hover:bg-accent/20"
|
||||
>
|
||||
{editFields.map((field) => (
|
||||
<td key={field} className="px-2 py-1.5 align-middle">
|
||||
{editFields.map((field, i) => (
|
||||
<td key={field} className={cn(TD, i === 0 && "sticky left-0 z-10 bg-card group-hover:bg-accent/20")}>
|
||||
{field === "gender" ? (
|
||||
<select
|
||||
aria-label={`${FIELD_LABELS[field]} for rad ${rowIndex + 1}`}
|
||||
value={row[field] ?? ""}
|
||||
onChange={(e) => onCellChange(row.key, field, e.target.value)}
|
||||
className={cn(CONTROL_BASE, FIELD_MIN_W[field], "w-full px-2 text-sm font-semibold")}
|
||||
className={cn(CELL, "cursor-pointer font-medium")}
|
||||
>
|
||||
{GENDER_OPTIONS.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
|
|
@ -515,7 +527,7 @@ function EditTable({
|
|||
aria-label={`Lag for rad ${rowIndex + 1}`}
|
||||
value={row[field] ?? ""}
|
||||
onChange={(e) => onCellChange(row.key, field, e.target.value)}
|
||||
className={cn(CONTROL_BASE, FIELD_MIN_W[field], "w-full px-2 text-sm font-semibold")}
|
||||
className={cn(CELL, "cursor-pointer font-medium")}
|
||||
>
|
||||
<option value="">—</option>
|
||||
{mode.teams.map((t) => (
|
||||
|
|
@ -529,7 +541,7 @@ function EditTable({
|
|||
aria-label={`Klasse for rad ${rowIndex + 1}`}
|
||||
value={row[field] ?? ""}
|
||||
onChange={(e) => onCellChange(row.key, field, e.target.value)}
|
||||
className={cn(CONTROL_BASE, FIELD_MIN_W[field], "w-full px-2 text-sm font-semibold")}
|
||||
className={cn(CELL, "cursor-pointer font-medium")}
|
||||
>
|
||||
<option value="">—</option>
|
||||
{mode.classes.map((c) => (
|
||||
|
|
@ -544,27 +556,25 @@ function EditTable({
|
|||
value={row[field] ?? ""}
|
||||
inputMode={field === "email" ? "email" : field === "mobile" ? "tel" : "text"}
|
||||
onChange={(e) => onCellChange(row.key, field, e.target.value)}
|
||||
className={cn(CONTROL_BASE, FIELD_MIN_W[field], "w-full px-2.5 text-sm")}
|
||||
className={cn(CELL, field === "handicap_index" && "text-right tabular-nums")}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
<td className="px-2 py-1.5 align-middle">
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onDeleteRow(row.key)}
|
||||
aria-label={`Slett rad ${rowIndex + 1}${row.display_name ? ` for ${row.display_name}` : ""}`}
|
||||
className={cn(
|
||||
"inline-flex h-9 items-center gap-1.5 rounded-lg border border-border px-2.5 text-sm font-bold text-muted-foreground transition-colors",
|
||||
"hover:border-destructive/40 hover:bg-destructive/10 hover:text-destructive active:scale-[0.98]",
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
||||
)}
|
||||
>
|
||||
<Trash2 aria-hidden="true" className="size-4" />
|
||||
Slett
|
||||
</button>
|
||||
</div>
|
||||
<td className={cn(TD, "border-r-0 px-1 text-center")}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onDeleteRow(row.key)}
|
||||
aria-label={`Slett rad ${rowIndex + 1}${row.display_name ? ` for ${row.display_name}` : ""}`}
|
||||
className={cn(
|
||||
"inline-flex h-7 items-center gap-1 rounded-md px-2 text-xs font-bold text-muted-foreground transition-colors",
|
||||
"hover:bg-destructive/10 hover:text-destructive active:scale-[0.98]",
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
|
||||
)}
|
||||
>
|
||||
<Trash2 aria-hidden="true" className="size-3.5" />
|
||||
Slett
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
|
|
|||
Loading…
Reference in a new issue