# components/dashboard/ — Dashboard UI-Komponenten für die Startseite nach dem Login. --- ## Dateien | Datei | Zweck | |-------|-------| | `canvas-card.tsx` | Karte für einen Canvas in der Übersicht (Navigation, Umbenennen, Löschen mit Confirm-Dialog) | | `credit-overview.tsx` | Monatsverbrauch und verfügbare Credits als Balken-Visualisierung | | `credits-activity-chart.tsx` | Credits-Aktivitäts-Chart (Recharts AreaChart) mit Verbrauch, Aktivität und verfügbaren Credits | | `recent-transactions.tsx` | Liste der letzten Credit-Transaktionen | --- ## Layout-Seite `app/dashboard/page-client.tsx` — Client Component, rendert Dashboard mit gebündelter Snapshot-Query. **Layout-Struktur:** ``` Dashboard ├── Header (Logo, Suche, User-Menü mit Theme-Wechsel) ├── Begrüßung (Name, Untertitel) ├── Credit-Übersicht (Balance, Usage Bars) ├── Arbeitsbereiche (Canvas Grid) ├── Credits Verlauf + Transaktionen (zwei-spaltig) │ ├── CreditsActivityChart (Recharts AreaChart) │ └── Recent Transactions (List) ``` --- ## Dashboard Snapshot Architecture **Früher:** Separate Convex-Queries für Balance, Subscription, UsageStats, Transactions, Canvases → 5+ gleichzeitige Queries. **Jetzt:** Gebündelte Snapshot-Query (`api.dashboard.getSnapshot`) + localStorage-Cache. ### Datenfluss ``` page-client.tsx └── useDashboardSnapshot(userId) ├── Convex Query: api.dashboard.getSnapshot (gebündelt) │ → Balance + Subscription + UsageStats + Transactions + Canvases ├── localStorage Cache: readDashboardSnapshotCache (12h TTL) │ → Sofortige Anzeige aus Cache während Query lädt └── Automatisches Cache-Write bei neuen Query-Daten ``` ### Snapshot Cache (`lib/dashboard-snapshot-cache.ts`) - **Key:** `lemonspace.dashboard:snapshot:v1:` - **TTL:** 12 Stunden (DEFAULT_TTL_MS) - **Version:** `CACHE_VERSION = 1` — Version-Bumps invalidieren automatisch - **Cache-Invalidierung:** Bei Logout wird der Cache des vorherigen Users gelöscht (via `sessionStorage("ls-last-dashboard-user")`) - Defensiv: Alle Storage-Zugriffe sind try-catch-gewrappt (Quota-Fehler, Disabled Storage etc.) ### Snapshot Query (`convex/dashboard.ts`) - `getSnapshot` — Gebündelte Query, lädt alle Dashboard-Daten in einem Convex-Call - Nutzt `optionalAuth` → Default-Werte bei fehlender Session (kein Error) - Transaktions-Priorisierung via `prioritizeRecentCreditTransactions` (Usage > Reservation > Refund > Topup > Subscription) --- ## Datenquellen | Komponente | Datenquelle | |-----------|-------------| | `credit-overview.tsx` | `dashboardSnapshot.balance`, `dashboardSnapshot.subscription`, `dashboardSnapshot.usageStats` | | `credits-activity-chart.tsx` | `dashboardSnapshot.balance`, `dashboardSnapshot.recentTransactions` | | `recent-transactions.tsx` | `dashboardSnapshot.recentTransactions` | | `canvas-card.tsx` | `dashboardSnapshot.canvases` | > Alle Daten kommen aus dem gebündelten Snapshot (`useDashboardSnapshot`). Keine separaten Queries mehr. ## Mutations | Komponente | Mutation | |-----------|----------| | `canvas-card.tsx` | `api.canvases.update`, `api.canvases.remove` | | `page-client.tsx` | `api.canvases.create` | --- ## Credits Activity Chart Recharts-basiertes AreaChart (via ShadCN `ChartContainer`) mit drei Datenseries: - **Usage** (gefüllt): Tatsächlicher Credit-Verbrauch pro Tag (committed usage-Transactions) - **Activity** (gefüllt): Usage + aktive Reservationen pro Tag - **Available** (Linie): Aktueller verfügbarer Credit-Stand als Referenzlinie **Daten-Processing:** `lib/credits-activity.ts` - `buildCreditsActivitySeries()` — Gruppiert Transactions nach Tag, aggregiert Usage/Activity - `calculateUsageActivityDomain()` — Berechnet Y-Achsen-Domain mit Headroom - `prioritizeRecentCreditTransactions()` — Sortiert Transactions nach Typ-Priorität **Chart Config:** Teal für Usage, Accent-Foreground für Activity, Muted-Foreground für Available. --- ## Konventionen - Kein lokaler State für Server-Daten — Snapshot kommt aus `useDashboardSnapshot` - Dashboard zeigt sofort Cache-Daten an, während Live-Query lädt (`source: "cache" | "live" | "none"`) - Canvas-Thumbnails sind optional (`thumbnail?: Id<"_storage">`) — Fallback-State immer behandeln - Responsive Grid-Layout für Canvas-Cards (`grid-cols-1 sm:grid-cols-3`) - Leere Zustände (keine Canvases, keine Transaktionen, keine Chart-Daten) mit nutzerfreundlichen Platzhaltern zeigen - Chart-Komponente nutzt ShadCN `ChartContainer` (`components/ui/chart.tsx`) + Recharts 3.8 --- ## Error Handling - **Auth-Race:** `useDashboardSnapshot` nutzt `useAuthQuery` intern → skippt bis Auth bereit - **Empty States:** Wenn keine Canvases/Transaktionen vorhanden sind, informative Platzhalter zeigen - **Cache-Fehler:** localStorage-Zugriffe sind defensiv gewrappt (quota, disabled storage) - **Offline:** Snapshot-Cache bietet Fallback bei fehlender Verbindung --- ## Performance - Gebündelte Snapshot-Query statt 5+ separater Queries → weniger WebSocket-Last - localStorage-Cache mit 12h TTL → sofortige Anzeige bei wiederholtem Besuch - Canvas-Thumbnails werden über den Snapshot geladen (nur wenn vorhanden) - Transaktions-Liste ist auf 20 Einträge limitiert (priorisiert nach Typ)