15 KiB
Boilerplate-Abgleich — M20 Dashboard / Reporting
| Feld | Wert |
|---|---|
| Modul-ID | M20 |
| Name | Dashboard / Reporting (Geschäftsführer-Dashboard) |
| Milestones | M147 (Layout + Widget-Engine), M148 (7 Rollen-Standard-Layouts), M149 (Drill-Down + Custom-Widget-Konfig), M150 (Export + Report-Versand) |
| Pflichtenheft-Block | Block 6 · Modul 20 (_bmad-output/pflichtenheft/blocks/block6.md §§20.1–20.10) |
| Detail-Spec | _bmad-output/specs/M20-dashboard.md |
| Reuse-Level | medium |
Kurz-Verdict
Die Boilerplate liefert ein solides Fundament für ein fixes Stat-Dashboard, aber keine konfigurierbare Widget-/Layout-Engine, wie M20 sie verlangt. Konkret vorhanden und stark nutzbar:
- Ein funktionierendes Tenant-Dashboard-Route-Pattern mit Permission-Gate, Period-Filter, Server-Timing-Metrics und i18n.
- Ein Dashboard-Modul-Registry-Pattern (
dashboard-registry) mit Feature-Flag-/Plan-Gating — ideale Vorlage für den permissions-aware Widget-Katalog (Block C/F). - Ein Stat-/Drill-Down-Pattern (
Stat-DTO mitpath/entity.slug,DashboardStats-Komponente mit<Link>) — direkte Vorlage für die Kern-Anforderung Drill-Down (§20.4). - Eine vollständige Export-Infrastruktur (
packages/exports: PDF via@react-pdf/renderer, CSV, XLSX, S3-Sinks) für M150. - Ein Realtime-/SSE-Fundament (
packages/realtime) mit Reconnect/Backoff für Live-Updates (§20.7). - Recharts + Chart-UI-Primitive für Visualisierungen.
Was komplett fehlt: das Datenmodell (Dashboard, DashboardLayout, WidgetInstance), die Drag-&-Drop-Grid-Engine (kein react-grid-layout; nur @dnd-kit als Baustein vorhanden), die Per-Widget-Caching-/Aggregat-Schicht, der konkrete Widget-Katalog (Sales/Projekt/HR/… aus den Modulen) und die HiveGrid-Layer-Split-Packages (ui-hivegrid, gefüllte view-models, Subframe-Adapter).
Wichtiger Hinweis zur „packages/metrics + Widget-Engine"-Annahme aus dem Auftrag:
packages/metricsist Prometheus/prom-client (Ops-Observability: Histogramme, AI-Cost-Counter, Metrics-Token-Auth) — NICHT eine Business-KPI- oder Widget-Engine. DasWidget-Modell incore.prismaist ein generisches Legacy-UI-Widget (name/appearance/metadataals Strings) und deckt das M20-Widget-Modell nicht ab.
Übernehmbar aus Boilerplate
Pfade beziehen sich auf
/Users/raffaelsheikh/Documents/GitHub/boilerplate. Der Boilerplate-Code lebt in der Legacy-ToolCTRL-Struktur (apps/web/app/...,~/utils/...), nicht im HiveGrid-Ziel-Layer-Split. Übernahme bedeutet daher fast überall „als-vorlage" + Umbau inapp/routes+packages/view-models+packages/ui-hivegrid.
| Boilerplate-Pfad | Was | Wie nutzbar |
|---|---|---|
apps/web/app/routes/app.$tenant/dashboard.tsx |
Tenant-Dashboard-Route: Permission-Gate (app.dashboard.view), Period-Filter (PeriodHelper), Server-Timing-Metrics, i18n-Title, Empty/Permission-Handling, Modul-Gating-Verdrahtung |
als-vorlage (Loader-/VM-Struktur übernehmen, in HiveGrid-Layer-Split überführen) |
apps/web/app/services/dashboard-registry/registry.server.ts |
Dashboard-Modul-Registry als Code-Konstante (id, titleKey, descriptionKey, featureFlagKey, planFeatureKey), Object.freeze-Pattern |
als-vorlage — direkte Blaupause für WidgetCatalog (Code-Registry, Block A/C) |
apps/web/app/services/dashboard-registry/gating.server.ts |
Gating-Engine: pro Modul Feature-Flag + Plan-/Entitlement-Check → allowed + Diagnostic + Redirect |
als-vorlage — Vorlage für permissions-aware Widget-Sichtbarkeit (§20.8 / Block F); Permission-Check ersetzt/ergänzt Plan-Check |
apps/web/app/application/dtos/stats/Stat.ts (+ StatChange.ts) |
Stat-DTO mit name, stat, previousStat, change, changeType, path, entity.slug |
anpassen — path/entity.slug sind die Drill-Down-Hooks (§20.4); ins VM überführen |
apps/web/app/components/ui/stats/DashboardStats.tsx |
Stat-Grid-Komponente, rendert klickbare Stat-Kacheln via <Link to={item.path}> |
als-vorlage — Drill-Down-Verlinkung; nach packages/ui-hivegrid portieren (VM-only, kein Loader-Zugriff) |
apps/web/app/utils/services/appDashboardService.ts |
Aggregat-Service: zählt Entity-Rows seit gte, berechnet change%/changeType, baut Stat[] |
als-vorlage — Muster für serverseitige Aggregat-Berechnung (§20.7.1); pro HiveGrid-Widget eigener Builder |
apps/web/app/utils/app/DashboardUtils.ts (getStatChangePercentage, getStatChangeType) |
Reine Change-Berechnungs-Helfer | 1:1 (pure functions, in VM-Builder nutzbar) |
packages/exports/src/pdf/ + packages/exports/src/index.ts |
PDF-Export (@react-pdf/renderer), Streaming, S3-Multipart-Sink; audit-pdf.ts als konkretes PDF-Beispiel |
anpassen — Dashboard-PDF-Snapshot (M150) |
packages/exports/src/csv/ + packages/exports/src/xlsx/ |
CSV-/XLSX-Export mit Streaming + Spreadsheet-Safety (spreadsheet-safety.ts), format-caps.ts, limits.ts |
anpassen — Widget-CSV-Export (M150); ersetzt json2csv (nicht in BP) |
packages/realtime/src/server.ts (serializeRealtimeEvent, SSE) |
SSE-Event-Serialisierung (id/event/data + retry), Sanitizing | anpassen — Live-Update-Push-Kanal (§20.7.3); neue Event-Typen für Dashboard nötig |
packages/realtime/src/client.ts |
SSE-Subscriber mit Reconnect/Backoff, Status-Diagnostics, EventSource-Abstraktion | anpassen — Client-Side Live-Update-Empfang inkl. „Live-Updates pausieren"-Opt-Out (§20.7.4) |
packages/realtime/src/events.ts |
Event-Schema-Registry (REALTIME_EVENT_NAMES) + Validierung |
anpassen — derzeit nur Notification-Events; Dashboard-Events (deal.won, lead.received, defect.accepted) ergänzen |
apps/web/app/components/ui/chart.tsx |
shadcn-Chart-Wrapper um Recharts (ChartConfig, Theming, Locale-Formatters) | 1:1 / anpassen — Visualisierungs-Primitive für Trend-/Conversion-Widgets (§20.1) |
apps/web/app/components/analytics/AnalyticsOverview.tsx (+ AnalyticsOverviewChart.tsx) |
Echtes Chart-Dashboard-Pattern: MetricCards + lazy-geladener Chart, klickbare Metriken via <Link> |
als-vorlage — Muster für klickbare KPI-Karten + Lazy-Loading (§20.7.5) |
packages/view-models/src/index.ts |
VM-Foundation: VMState, ActionCallbacks, SidebarRegistryItem, RegistryGate (permission/flag), I18nLabel |
1:1 — Pflicht-Basis für alle M20-VMs (AGENTS.md M013/M014) |
packages/feature-flags/ |
Feature-Flag-Querschnitt (M006) für Dashboard- und Widget-Toggles | 1:1 |
packages/entitlements/ |
Plan-/Entitlement-Checks (im gating.server.ts bereits verdrahtet) |
1:1 (für general.tenant_health-Widget + Gating) |
packages/db/src/client.server.ts (getReadDbClient/getExtendedDbClient) |
Read-/Tenant-scoped Prisma-Clients, Outbox/Audit-Middleware | 1:1 — read-heavy Dashboard-Queries |
packages/db/src/kpi.server.ts |
Admin-Plattform-KPIs (Tenant-Counts, Active-User via log.groupBy) |
als-vorlage (nur Plattform-Admin-Scope; Muster für Count-/GroupBy-Aggregate, nicht der Tenant-Widget-Katalog) |
infra/grafana/business-kpi.json |
Vorgefertigtes Grafana-Business-KPI-Dashboard | Referenz — Ops-Sicht, kein In-App-Widget; als Inspiration für KPI-Auswahl |
Vorhandene Daten-Modelle (Prisma)
Verifiziert in
/Users/raffaelsheikh/Documents/GitHub/boilerplate/prisma/schema/.
| Modell | Pfad | Relevanz für M20 |
|---|---|---|
Widget |
prisma/schema/core.prisma:267 |
Deckt M20 NICHT ab. Generisches Legacy-UI-Widget: id, tenantId?, name, appearance (String), metadata (String), @@unique([tenantId, name]). Keine Layout-/Instanz-/Config-/Position-Struktur. Reader: packages/db/src/widgets.server.ts (findLegacyWidgetById). Allenfalls als Namens-Squatter zu beachten (Kollision mit neuem WidgetInstance). |
MetricLog |
prisma/schema/core.prisma:180 |
Request-Performance-Log (route/duration/userId/tenantId) — Ops-Metrik, nicht Business-Aggregat. |
AnalyticsSettings, AnalyticsUniqueVisitor, AnalyticsPageView, AnalyticsEvent |
prisma/schema/analytics.prisma |
First-Party-Web-Analytics (Visitor/PageView/Event, UTM, Geo, Device), portal-zentriert. Quelle für ein evtl. „Web-Traffic"-Widget, aber kein CRM-/Sales-/HR-Aggregat. |
FeatureFlag (+ Filter) |
prisma/schema/featureFlags.prisma |
Für Dashboard-/Widget-Feature-Gating (M006). |
CollabDoc |
prisma/schema/collabDocs.prisma |
Yjs-Realtime-Docs — nicht Dashboard-relevant (nur falls geteilte Layout-Editing-Sessions gewünscht; Phase-1 nicht gefordert). |
Fazit Daten-Layer: Die vier M20-Kernmodelle (Dashboard, DashboardLayout, WidgetInstance; WidgetCatalog als Code-Registry) existieren nicht und müssen neu gebaut werden. Tenant-/User-Relationen und das Tenant-Scoping-Muster sind vorhanden und wiederverwendbar.
Lücken / komplett neu zu bauen
- Datenmodell M20 — neue Prisma-Modelle
Dashboard,DashboardLayout,WidgetInstance(Spec Block A). Forward-Only-Migration mit Inverse-SQL (DoD). Achtung:Widgetist als Modellname belegt →WidgetInstance+WidgetCatalogKeyverwenden. - WidgetCatalog (Code-Registry) — die konkrete Liste aus Block C (
sales.*,projects.*,tasks.*,activity.*,comms.*,hr.*,ai.*,general.*) mit Default-Config-Schema, Render-Komponente, Datenquelle + Quellen-Modul-Permission. Dasdashboard-registry-Pattern ist die Vorlage, der Inhalt ist komplett neu und modul-abhängig (M3/M4/M26/M1/M9/M12/M14/M18). - Drag-&-Drop-Grid-Engine —
react-grid-layoutist NICHT in den Deps (entgegen PLAN-Hinweis); nur@dnd-kit/core+@dnd-kit/sortablevorhanden. Entweder Grid-Library hinzufügen oder Grid auf@dnd-kitselbst bauen (Resize ist mit dnd-kit nicht out-of-the-box). Layout-Persistenz (layout_dataJSON) neu. - Per-Widget-Aggregat-/Caching-Schicht — Cache-Key
tenant_id + widget_key + config_hash + user_id, TTL 60 s, Invalidierung (§20.7). Kein generischer Widget-Cache vorhanden;appDashboardServicezählt direkt ohne Cache. Pre-Calc-Aggregat-Tables + Cron (§20.7.2) neu —apps/worker+ BullMQ als Fundament vorhanden. - 7 Rollen-Default-Layouts als deklaratives Branchen-Template (M148, §20.5, LP-3/AV#3) — Seed-Daten beim Tenant-Anlegen. Seed-Infra (
packages/db/src/seed.server.ts,entitlements-seed,permissions-seed) vorhanden, die Layout-Templates selbst neu. - Widget-Datenquellen — jeder Widget-Builder hängt an einem anderen Modul (Sales-Pipeline-KPIs, Forecast, Vertriebler-Performance, Projekt-Auslastung/Plantafel, offene Mängel, HR „wer ist heute da", ungelesene Mails, Anrufe heute, KI-Vorschläge). Diese Quell-Module sind im Boilerplate noch nicht als HiveGrid-Module gebaut → harte Abhängigkeit zu M01/M03/M04/M09/M12/M14/M18/M26.
- Drill-Down-Filter-Ableitung (§20.4, Kern-Anforderung) — aus Widget-Config den vorgefilterten Listen-Link bauen, permissions-aware.
Stat.path/entity.slugist der Hook, die Filter-Serialisierung je Widget neu. - Dashboard-Permissions (
dashboard.read/customize/create/share/admin+ Bundles, Block F) — neue Permission-Keys. Boilerplate hat nurapp.dashboard.view/admin.dashboard.view(packages/db/src/permissions-seed.server.ts). Permission-Definition gehört nachpackages/entitlements/src/permissions.ts(Integration-Contract §10). - Dashboard-Live-Events —
packages/realtimekennt nur Notification-Events; Dashboard-Events (deal.won,lead.received,defect.accepted) + Server-Push-Verdrahtung + „Live-Updates pausieren"-Opt-Out neu. - HiveGrid-Layer-Split —
packages/ui-hivegrid, gefülltepackages/view-models/src/dashboard/*.vm.ts, Subframe-Adapter existieren noch nicht. Der Boilerplate-Dashboard-Code liegt monolithisch inapps/webmituseLoaderData/~/db-Nähe und verletzt die HiveGrid-Anti-Patterns → bei Übernahme zwingend umbauen. - Sidebar-/Shell-Wiring —
packages/ui-hivegrid/src/app-shell/sidebar.config.ts(Integration-Contract §2) existiert nicht; Boilerplate nutzt eine andere Shell. Sidebar-Eintrag „Dashboard" (Gruppe ARBEITEN) neu zu registrieren.SidebarRegistryItem-Typ auspackages/view-modelsist die Basis. - Mobile-First Monteur-Dashboard (LP-2, §20.5.7) — Mobile-Tab-Bar-Eintrag (M169) + reduziertes Layout neu.
- Klartext-States — Empty/Loading/Error pro Widget aus M002-Catalog + M003-i18n. Boilerplate-Dashboard hat i18n-Keys (
app.dashboard.*), die konkreten Widget-Texte (§20.10) neu. - Report-Versand-Trigger (M150) — manuelle Generierung via vorhandene
exports-Infra; geplante Email-Reports sind explizit Phase 2 (§20.9.2, Out-of-Scope).
Empfohlenes Vorgehen
Phase 0 — Layer-Fundament & Übernahme der Patterns (vor M147):
packages/view-models-Foundation (VMState,ActionCallbacks,SidebarRegistryItem) ist bereits da → 1:1 nutzen,dashboard-VM-Ordner anlegen.Stat-DTO +getStatChangePercentage/getStatChangeType(pure) nachpackages/view-models/app/domainportieren (1:1).DashboardStats- undAnalyticsOverview-Komponente als Vorlage nachpackages/ui-hivegridübernehmen (VM-only, Drill-Down via<Link>beibehalten).dashboard-registry-Pattern (registry.server.ts+gating.server.ts) als Blaupause kopieren → daraus denWidgetCatalog+ permissions-aware Gating formen (Permission-Check statt Plan-Check).
M147 — Layout + Widget-Engine:
5. Neue Prisma-Modelle Dashboard/DashboardLayout/WidgetInstance + Migration. Tenant-Scoping-Muster aus packages/db nutzen.
6. Grid-Engine: Entscheidung react-grid-layout hinzufügen oder auf vorhandenem @dnd-kit bauen (Resize-Aufwand einplanen).
7. Per-Widget-Aggregat-Builder nach Muster appDashboardService (serverseitig, im Loader → VM). Caching-Schicht (TTL 60 s, Cache-Key per Spec) neu; getReadDbClient() für read-heavy Queries.
8. Recharts/chart.tsx-Primitive für Trend-/Conversion-Widgets 1:1 übernehmen.
9. Realtime: packages/realtime Server/Client übernehmen, Dashboard-Event-Typen ergänzen, Lazy-Loading (lazy-Chart wie AnalyticsOverviewChart).
M148 — 7 Rollen-Default-Layouts:
10. Deklarative Layout-Templates als Seed-Daten (Seed-Infra aus packages/db/src/seed.server.ts vorhanden); permissions-aware Widget-Filter aus dem Gating-Pattern.
M149 — Drill-Down + Custom-Konfig:
11. Drill-Down-Filter-Ableitung je Widget (Stat.path/entity.slug als Hook), permissions-aware. Widget-Picker mit Catalog-Gating (ausgegraut bei fehlender Permission).
M150 — Export:
12. packages/exports (PDF @react-pdf/renderer + CSV/XLSX) direkt anbinden: Dashboard-PDF-Snapshot + Widget-CSV. Geplante Email-Reports bleiben Phase 2.
Querschnitt durchgehend: Permission-Keys in packages/entitlements, Feature-Flags (M006), i18n (M003), Audit (M001 auto) für Layout-Änderungen an shared Dashboards (AV#6), Sidebar-Eintrag (§2), Smoke-Test + Hot-Swap-Drill + Visual-Regression (DoD D).