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
128 lines
5.8 KiB
Svelte
128 lines
5.8 KiB
Svelte
<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>
|