116 lines
7.5 KiB
Markdown
116 lines
7.5 KiB
Markdown
# Phase 8: Dashboard Widgets Vollimplementierung - Context
|
||
|
||
**Gathered:** 2026-07-01
|
||
**Status:** Ready for planning
|
||
|
||
<domain>
|
||
## Phase Boundary
|
||
|
||
Drei neue Dashboard-Widgets implementieren (Calculator, Favorites-Liste, Einzellink, Stoppuhr) und alle Widget-Grid-Constraints auf sinnvolle, nutzbare Werte setzen. Widget-Registry um neue Typen erweitern. Favoriten brauchen Backend (DB-Tabelle + API + server-seitiges Icon-Discovery). Stoppuhr-Zustand wird persistiert. DomainCheck-Widget wird NICHT implementiert.
|
||
|
||
</domain>
|
||
|
||
<decisions>
|
||
## Implementation Decisions
|
||
|
||
### Grid-Constraints (DASH-11)
|
||
- **D-01:** Kein universeller Einheitswert. Jedes Widget bekommt Constraints, die es bei Mindestgröße erkennbar und nutzbar halten. Neue Widgets (Calculator, Favorites, Einzellink, Stoppuhr) kriegen sinnvolle Werte passend zum Inhalt. Claude legt konkrete minW/minH/defaultW/defaultH fest.
|
||
|
||
### Favoriten-Widget (DASH-09)
|
||
- **D-02:** Backend: neue Prisma-Tabelle `FavoriteLink` mit Feldern: `id`, `userId`, `tenantId`, `widgetId`, `title`, `url`, `iconUrl`, `position`. Eigene Tabelle (nicht JSON-Blob in WidgetInstance.config) — sauber abfragbar und sortierbar.
|
||
- **D-03:** Standard-Ansicht im Widget: **Liste** (Icon + Titel untereinander). Grid-Ansicht (Kacheln nebeneinander) umschaltbar. Bei Grid-Ansicht auf saubere Formatierung achten (kein abgeschnittener Text, Icon + Titel klar lesbar).
|
||
- **D-04:** Favoriten hinzufügen/bearbeiten/löschen **direkt im Widget** im Dashboard-Edit-Modus ('+'-Button + Inline-Formular). Kein separater Einstellungen-Tab.
|
||
- **D-05:** Icon-Discovery: Server-seitig beim Anlegen eines Favoriten. NestJS-API fetcht die Seite, parst `<link rel="apple-touch-icon">`, `<link rel="icon">`, `<link rel="shortcut icon">`, OG-Image aus HTML. Fallback: `/favicon.ico`. Sicherheit: SSRF-Schutz gegen private IPs und localhost (Logik aus `personal-dashboard/src/lib/favorite-icons.ts` portieren). `iconUrl` wird in `FavoriteLink` gespeichert. Client rendert `<img src={iconUrl}>` mit Buchstaben-Fallback.
|
||
|
||
### Einzellink-Widget (neu, kein eigenes Requirement — Teil von DASH-09)
|
||
- **D-06:** Separater WidgetType `link` in der Registry. Zeigt genau einen Link an. Ansicht umschaltbar: Listendesign (Icon + Titel als Zeile) oder Kachel. Teilt Backend-Infrastruktur mit FavoritesWidget (gleiche FavoriteLink-Tabelle, widgetId zur Zuordnung).
|
||
|
||
### Stoppuhr-Widget (DASH-10)
|
||
- **D-07:** Stoppuhr läuft durch — Startzeit (Unix-Timestamp) + Zustand (running/paused/elapsed) wird in `WidgetInstance.config` gespeichert. Beim Laden aus dem gespeicherten Timestamp rekonstruiert. Kein separater API-Endpunkt nötig — nutzt vorhandenes Widget-Config-Update (`PATCH /dashboard/widgets/:id/config`).
|
||
- **D-08:** Funktionen: Start, Stop/Pause, Reset. Rundenzeiten (Lap) optional, Claude entscheidet ob im ersten Schritt oder later.
|
||
|
||
### Calculator-Widget (DASH-08)
|
||
- **D-09:** Grundrechenarten (+, −, ×, ÷), Tastatureingabe unterstützt. Kein Persist-Bedarf (in-memory). Logik aus `personal-dashboard/src/components/CalculatorWidget.tsx` portieren, CSS-Module durch Tailwind ersetzen (wie alle anderen Tessera-Widgets).
|
||
|
||
### Claude's Discretion
|
||
- Konkrete minW/minH/defaultW/defaultH-Werte für neue Widgets
|
||
- Lap-Timer in Stoppuhr: im ersten Schritt oder später
|
||
- Reihenfolge der Kacheln im Favoriten-Grid
|
||
- Keyboard-Shortcuts im Calculator-Widget
|
||
- SSRF-Timeout und max-redirect-Werte für Icon-Discovery (aus personal-dashboard übernehmen oder anpassen)
|
||
|
||
</decisions>
|
||
|
||
<canonical_refs>
|
||
## Canonical References
|
||
|
||
**Downstream agents MUST read these before planning or implementing.**
|
||
|
||
### Bestehendes Dashboard-Code (Tessera)
|
||
- `apps/web/src/components/dashboard/widget-registry.tsx` — WidgetType, WIDGET_CONSTRAINTS, wireXWidget()-Pattern, WidgetDefinition-Interface
|
||
- `apps/web/src/components/dashboard/widgets/clock-widget.tsx` — Referenz-Implementierung für Widget-Komponente (WidgetProps-Interface, Tailwind-Styling)
|
||
- `apps/web/src/components/dashboard/widgets/note-widget.tsx` — Referenz für Widget mit updateWidgetConfig()-Aufruf
|
||
- `apps/web/src/components/dashboard/widget-catalog-modal.tsx` — Wie neue Widget-Typen im Katalog erscheinen
|
||
- `apps/api/src/dashboard/dashboard.controller.ts` — Bestehende Widget-API-Endpunkte
|
||
- `apps/api/src/dashboard/dto/create-widget.dto.ts` — DTO-Pattern für Widget-Requests
|
||
- `apps/api/prisma/schema.prisma` — WidgetInstance-Modell (config: Json), Prisma-Schema für neue FavoriteLink-Tabelle
|
||
|
||
### Referenz-Implementierungen (personal-dashboard)
|
||
- `/home/vicolab/Schreibtisch/personal-dashboard/src/components/CalculatorWidget.tsx` — Vollständige Calculator-Logik (434 Zeilen), zu portieren
|
||
- `/home/vicolab/Schreibtisch/personal-dashboard/src/components/FavoritesWidget.tsx` — Favoriten-UI-Logik (482 Zeilen), zu adaptieren
|
||
- `/home/vicolab/Schreibtisch/personal-dashboard/src/lib/favorite-icons.ts` — Server-seitiges Icon-Discovery mit SSRF-Schutz, zu portieren
|
||
|
||
### Anforderungen
|
||
- `.planning/REQUIREMENTS.md` — DASH-08, DASH-09, DASH-10, DASH-11
|
||
- `.planning/ROADMAP.md` — Phase 8 Goal + Success Criteria
|
||
- `.planning/phases/05-dashboard-calendar/05-CONTEXT.md` — D-01..D-07 (Edit-Modus, Widget-Einstellungen, mehrfach platzierbar) — diese Entscheidungen gelten weiterhin
|
||
|
||
</canonical_refs>
|
||
|
||
<code_context>
|
||
## Existing Code Insights
|
||
|
||
### Reusable Assets
|
||
- `wireXWidget(component)` — Pattern für lazy-loading Widget-Komponenten ohne zirkuläre Abhängigkeiten. Neue Widgets folgen exakt diesem Muster.
|
||
- `updateWidgetConfig(instanceId, config)` in `apps/web/src/lib/dashboard-api.ts` — bestehende Funktion für Widget-Config-Updates, für Stoppuhr-Persistenz direkt verwendbar.
|
||
- `apps/api/src/dashboard/dashboard.service.ts` — bestehende Service-Logik für Widget CRUD, FavoriteLink-Service kann als eigenes Module daneben leben.
|
||
|
||
### Established Patterns
|
||
- Widget-Komponenten: `'use client'` + `WidgetProps` Interface + Tailwind-Styling (keine CSS-Module)
|
||
- Icons im Widget-Registry: Inline SVG als React-Komponente (kein Lucide, kein externe Icon-Library)
|
||
- API-Endpunkte in NestJS: Controller → Service → Prisma, DTOs mit class-validator
|
||
- i18n: `useTranslations()` aus `next-intl` für alle UI-Strings
|
||
|
||
### Integration Points
|
||
- `widget-registry.tsx`: `WidgetType`-Union erweitern (`'calculator' | 'favorites' | 'link' | 'stopwatch'`), `WIDGET_CONSTRAINTS` + `WIDGET_REGISTRY` + je eine `wireXWidget()`-Funktion hinzufügen
|
||
- `apps/api/prisma/schema.prisma`: `FavoriteLink`-Modell hinzufügen + `prisma db push`
|
||
- NestJS-API: neues `FavoritesModule` mit Controller/Service/DTOs für FavoriteLink-CRUD + Icon-Discovery
|
||
|
||
</code_context>
|
||
|
||
<specifics>
|
||
## Specific Ideas
|
||
|
||
- Calculator-Logik aus personal-dashboard portieren (parseDisplay, formatNumber, calculate-Funktionen erhalten — bewährt)
|
||
- Favoriten-Icon-Discovery: SSRF-Schutz aus `favorite-icons.ts` vollständig portieren (private IP-Blöcke, localhost-Check, max-redirects, timeout)
|
||
- Stoppuhr läuft nach Seiten-Reload weiter (Startzeit als ISO-String in config speichern, elapsed time berechnen)
|
||
- Stoppuhr: später ggf. in Browser-Titelzeile anzeigen (deferred)
|
||
- Favoriten-Widget Listenansicht als Standard, Grid optional umschaltbar
|
||
- Einzellink-Widget teilt FavoriteLink-Tabelle (widgetId als FK)
|
||
|
||
</specifics>
|
||
|
||
<deferred>
|
||
## Deferred Ideas
|
||
|
||
- Stoppuhr-Anzeige im Browser-Seitentitel + Taskleiste (spätere Phase)
|
||
- DomainCheck-Widget (explizit ausgeschlossen aus Phase 8)
|
||
- Lap-Timer in Stoppuhr (falls nicht in Phase 8 umgesetzt: separater Task in späterer Phase)
|
||
|
||
</deferred>
|
||
|
||
---
|
||
|
||
*Phase: 8-dashboard-widgets-vollimplementierung*
|
||
*Context gathered: 2026-07-01*
|