Update LemonSpace Manifest to v2.1, enabling all 9 image models in OpenRouter with server-side tier enforcement. Enhance dashboard functionality with a bundled snapshot query and localStorage caching for improved performance and analytics. Introduce credits activity chart and optimize canvas graph queries for better data handling.
This commit is contained in:
@@ -10,93 +10,123 @@ UI-Komponenten für die Startseite nach dem Login.
|
||||
|-------|-------|
|
||||
| `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.tsx` — Server Component, rendert Dashboard-Layout mit den Komponenten oben.
|
||||
`app/dashboard/page-client.tsx` — Client Component, rendert Dashboard mit gebündelter Snapshot-Query.
|
||||
|
||||
**Layout-Struktur:**
|
||||
```
|
||||
Dashboard
|
||||
├── Header (User-Avatar, Name)
|
||||
├── Quick Actions (Create Canvas, Search)
|
||||
├── Credit Overview (Balance, Usage Bars)
|
||||
├── Recent Transactions (List)
|
||||
└── Canvas Grid (Canvas Cards)
|
||||
├── 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:<userId>`
|
||||
- **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
|
||||
|
||||
Alle Daten kommen aus Convex-Queries via `useAuthQuery` (aus `hooks/use-auth-query.ts`):
|
||||
| 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` |
|
||||
|
||||
| Komponente | Query |
|
||||
|-----------|-------|
|
||||
| `canvas-card.tsx` | `api.canvases.list` |
|
||||
| `credit-overview.tsx` | `api.credits.getBalance`, `api.credits.getUsageStats` |
|
||||
| `recent-transactions.tsx` | `api.credits.getRecentTransactions` |
|
||||
> 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 — alles via Convex-Subscriptions (reaktiv)
|
||||
- `useAuthQuery` statt `useQuery` verwenden, um Auth-Races zu vermeiden (skippt Query bis Token bereit ist)
|
||||
- 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 md:grid-cols-2 lg:grid-cols-3`)
|
||||
- Leere Zustände (keine Canvases, keine Transaktionen) mit nutzerfreundlichen Platzhaltern zeigen
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Canvas Cards
|
||||
|
||||
Jede Karte zeigt:
|
||||
- Canvas-Thumbnail (wenn verfügbar)
|
||||
- Canvas-Name (truncate)
|
||||
- Letzte Änderungszeit (relative Formatierung)
|
||||
- Klick → Öffnen im Canvas-Editor
|
||||
- Menü → Umbenennen, Löschen
|
||||
|
||||
### Credit Overview
|
||||
|
||||
Zeigt:
|
||||
- Aktueller Credit-Balance (z.B. "350 Credits")
|
||||
- Verbrauchs-Balken (Progress Bar) für aktuellen Monat
|
||||
- Monats-Details (vom `api.credits.getUsageStats`)
|
||||
- Link zum Upgrade im Abo-Settings
|
||||
|
||||
### Recent Transactions
|
||||
|
||||
Liste der letzten Credit-Transaktionen mit:
|
||||
- Transaktions-Typ (Subscription, Top-Up, Usage)
|
||||
- Betrag (+/-)
|
||||
- Beschreibung
|
||||
- Datum (relative Formatierung)
|
||||
- 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**: `useAuthQuery` verhindert Fehler beim Initialisierungs-Flash
|
||||
- **Empty States**: Wenn keine Canvases/Transaktionen vorhanden sind, informative Platzhalter zeigen
|
||||
- **Offline**: Connection-Banner anzeigen, wenn Convex nicht erreichbar
|
||||
- **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
|
||||
|
||||
- Canvas-Thumbnails werden über `api.canvases.list` geladen (nur wenn vorhanden)
|
||||
- Transaktions-Listen sind paginiert oder limitiert (z.B. letzte 20 Transaktionen)
|
||||
- Kein lokaler State für Canvas-Daten — alles über real-time Convex-Subscriptions
|
||||
- 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)
|
||||
|
||||
Reference in New Issue
Block a user