diff --git a/frontend/src/app.css b/frontend/src/app.css index dca4688..a87ca12 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -145,3 +145,18 @@ cursor: default; } } + +@layer components { + /* + * Startnummern wie am Trikot: schmal, fett, ohne Zierrat. Impact ist auf + * Windows und macOS da, Haettenschweiler und Arial Narrow Bold springen + * ein; am Ende steht eine beliebige serifenlose Schrift, dann sieht die + * Nummer eben normal aus statt falsch. + */ + .font-startnumber { + font-family: Impact, Haettenschweiler, 'Arial Narrow Bold', 'Franklin Gothic Bold', sans-serif; + font-weight: 700; + font-stretch: condensed; + letter-spacing: 0.02em; + } +} diff --git a/frontend/src/lib/components/RunningTimesToast.svelte b/frontend/src/lib/components/RunningTimesToast.svelte index 40c0ff5..8dfad7e 100644 --- a/frontend/src/lib/components/RunningTimesToast.svelte +++ b/frontend/src/lib/components/RunningTimesToast.svelte @@ -5,11 +5,12 @@ * zwei Startern die Trails oder die Fahrerliste an, und die laufende Uhr * bleibt trotzdem im Blick — samt Stopp ohne Umweg über die Stage-Seite. * - * Bei mehreren Zeiten zeigt die Leiste eingeklappt nur die Zahl und die - * längste Zeit; ausgeklappt alle. Bei genau einer entfällt das Klappen. + * Aufgeklappt ist der Normalfall: Wer an der Strecke steht, will die + * laufenden Zeiten sehen und nicht erst danach fragen. Einklappen geht, + * sobald es mehr als eine ist. Bei genau einer entfällt das Klappen. * - * Gestoppt wird auch von hier aus nicht direkt: Der Knopf öffnet den - * Zeit-Dialog mit Fahrer und Stage vorbelegt. + * Gestoppt wird auch von hier aus nicht direkt: Ein Klick auf die Zeile + * öffnet den Zeit-Dialog mit Fahrer und Stage vorbelegt. */ import { getTimeContext } from '$lib/stores/times.svelte' import { riderName, getRiderContext } from '$lib/stores/riders.svelte' @@ -29,7 +30,7 @@ const running = $derived(times.allRunning(clock.now)) const single = $derived(running.length === 1) - let expanded = $state(false) + let expanded = $state(true) // Vorbelegung für den Zeit-Dialog let timeDialog = $state(false) @@ -48,7 +49,8 @@ } // Eine einzelne Zeit steht immer offen da; bei mehreren entscheidet der - // Nutzer. Fällt die Anzahl wieder auf eine, ist der Zustand egal. + // Nutzer — voreingestellt ist offen. Fällt die Anzahl wieder auf eine, + // ist der Zustand egal. const open = $derived(single || expanded) /** Die Startnummer gilt je Event, deshalb der Umweg über die Stage. */ @@ -61,12 +63,6 @@ return participants.riderLabel(eventId, riderId) } - function stagePath(stageId: string | undefined) { - const stage = stageId ? stages.getById(stageId) : undefined - if (!stage?.event) return null - return `/dashboard/events/${stage.event}/stages/${stage.id}` - } - function stageLabel(stageId: string | undefined) { const stage = stageId ? stages.getById(stageId) : undefined return stage?.description || 'Stage' @@ -113,32 +109,38 @@ {#if open} diff --git a/frontend/src/lib/components/TimeDialog.svelte b/frontend/src/lib/components/TimeDialog.svelte index 0ade8b7..c3bcf2b 100644 --- a/frontend/src/lib/components/TimeDialog.svelte +++ b/frontend/src/lib/components/TimeDialog.svelte @@ -5,6 +5,12 @@ * beim Starten und Stoppen nie ein Zweifel bleibt, wen und was man * gerade erwischt. Start und Stopp passieren ausschließlich hier: weder * Tabellen noch die Toast-Leiste lösen sie direkt aus. + * + * Bedient wird das an der Strecke, im Stehen, mit einer Hand am Lenker + * und einem Daumen am Telefon. Danach ist alles hier bemessen: Der Fahrer + * wird aus Kacheln gewählt statt aus einer Auswahlliste — die Startnummer + * gross wie am Trikot, der Name darunter —, und Starten und Stoppen sind + * flächige Knöpfe und keine Zeile im Fussbereich. */ import { getTimeContext, type TimeStatus } from '$lib/stores/times.svelte' import { riderName, getRiderContext } from '$lib/stores/riders.svelte' @@ -15,7 +21,7 @@ import { Label } from '@/components/ui/label' import * as Dialog from '@/components/ui/dialog' import * as Select from '@/components/ui/select' - import { AlertTriangle, Play, Square } from 'lucide-svelte' + import { AlertTriangle, Flag, Play, Square } from 'lucide-svelte' let { open = $bindable(false), @@ -65,9 +71,6 @@ ) const eventStages = $derived(eventId ? stages.getByEvent(eventId) : []) - const riderLabel = $derived( - selectedRider ? participants.riderLabel(eventId, selectedRider) : 'Fahrer wählen…', - ) const stageLabel = $derived( selectedStage ? stages.getById(selectedStage)?.description ?? 'Stage wählen…' : 'Stage wählen…', ) @@ -134,19 +137,45 @@
- - - {riderLabel} - - {#each eventRiders as r (r.id)} - - {participants.riderLabel(eventId, r.id)} - - {/each} - - + + {#if eventRiders.length === 0}

Für dieses Event gibt es noch keine Fahrer.

+ {:else} + +
+ {#each eventRiders as r (r.id)} + {@const number = participants.numberOf(eventId, r.id)} + {@const chosen = selectedRider === r.id} + + {/each} +
{/if}
@@ -202,22 +231,50 @@
{:else} - - - {#if active} - - - {:else} - - {/if} + + {:else} + + {/if} + + + {/if}