stammtisch-hersbruck/frontend/src/routes/dashboard/+page.svelte
Daniel Michelberger 83a1693ef1 refactor: Schnellzugriff raus, Event-Kacheln komplett verlinkt
Die Sidebar fuehrt ohnehin ueberall hin, der Schnellzugriff war eine
zweite Navigation ohne Mehrwert.

Die Kacheln unter "Heute und demnaechst" sind jetzt vollflaechig
verlinkt statt ueber einen Button darin -- dasselbe Muster wie in der
Event- und der Trailliste.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Ah9PvkL2xB7YkuadnfHRz
2026-09-03 12:40:52 +02:00

128 lines
5.8 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts">
import { auth } from '$lib/stores/pocketbase.svelte'
import { getTeamContext } from '$lib/stores/teams.svelte'
import { getEventContext } from '$lib/stores/events.svelte'
import { getRiderContext } from '$lib/stores/riders.svelte'
import { getTimeContext } from '$lib/stores/times.svelte'
import { getRunContext } from '$lib/stores/runs.svelte'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { formatDateRange } from '$lib/time'
import { Calendar, Users, Clock, MapPin } from 'lucide-svelte'
const teams = getTeamContext()
const events = getEventContext()
const runs = getRunContext()
const riders = getRiderContext()
const times = getTimeContext()
// „Aktiv" heißt jetzt: findet heute statt. Für die Startseite ist das
// zusammen mit dem Anstehenden interessant.
const upNext = $derived([...events.active, ...events.upcoming].slice(0, 4))
const stats = $derived({
totalEvents: events.scoped.length,
activeEvents: events.active.length,
totalRiders: riders.scoped.length,
running: times.running.length,
finished: times.finished.length,
})
</script>
<svelte:head>
<title>Dashboard Zeitnahme</title>
</svelte:head>
<div class="space-y-8">
<div>
<h1 class="text-3xl font-bold tracking-tight">Willkommen, {auth.user?.name || auth.user?.email}!</h1>
{#if teams.active}
<p class="mt-2 text-muted-foreground">
Team <span class="font-medium text-foreground">{teams.active.name}</span> · {stats.totalEvents} Events, {stats.totalRiders} Fahrer
</p>
{:else}
<p class="mt-2 text-muted-foreground">Kein Team aktiv. <a href="/dashboard/settings/team" class="underline">Team verwalten</a>.</p>
{/if}
</div>
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<Card>
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium text-muted-foreground">Events</CardTitle>
<Calendar class="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div class="text-3xl font-bold">{stats.totalEvents}</div>
<p class="text-xs text-muted-foreground mt-1">{stats.activeEvents} heute</p>
</CardContent>
</Card>
<Card>
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium text-muted-foreground">Fahrer</CardTitle>
<Users class="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div class="text-3xl font-bold">{stats.totalRiders}</div>
<p class="text-xs text-muted-foreground mt-1">registriert</p>
</CardContent>
</Card>
<Card>
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle class="text-sm font-medium text-muted-foreground">Laufende Zeiten</CardTitle>
<Clock class="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div class="text-3xl font-bold">{stats.running}</div>
<p class="text-xs text-muted-foreground mt-1">{stats.finished} abgeschlossen</p>
</CardContent>
</Card>
</div>
<div>
<h2 class="text-xl font-semibold mb-4">Heute und demnächst</h2>
{#if upNext.length === 0}
<Card>
<CardContent class="py-12 text-center">
<Calendar class="h-10 w-10 text-muted-foreground mx-auto mb-3" />
<p class="font-medium">Nichts geplant</p>
<p class="text-sm text-muted-foreground mt-1">
Trag einem Event ein Datum ein, dann steht es hier.
</p>
<Button href="/dashboard/events" variant="outline" class="mt-4">Events ansehen</Button>
</CardContent>
</Card>
{:else}
<div class="grid gap-3 sm:grid-cols-2">
{#each upNext as event}
{@const eventRuns = runs.getByEvent(event.id).length}
<Card class="hover:border-primary transition-colors">
<a href="/dashboard/events/{event.id}" class="block">
<CardHeader>
<div class="flex items-start justify-between gap-2">
<CardTitle>{event.name || event.description || 'Unbenannt'}</CardTitle>
{#if event.starts}
<Badge variant="secondary" class="shrink-0">
{formatDateRange(event.starts, event.ends)}
</Badge>
{/if}
</div>
{#if event.location}
<CardDescription class="flex items-center gap-1">
<MapPin class="h-3 w-3" />
{event.location}
</CardDescription>
{/if}
</CardHeader>
<CardContent>
<p class="text-sm text-muted-foreground">{eventRuns} Run{eventRuns === 1 ? '' : 's'}</p>
</CardContent>
</a>
</Card>
{/each}
</div>
{/if}
</div>
</div>