# Dashboard Widgets – Designprinzipien (Extraktion) **Status:** Analyse / Arbeitspapier **Stand:** 2026-07-04 **Geltungsbereich:** Konfigurierbare Übersicht (Widget-Katalog, Layout, Entitlements, Frontend-Registry) — keine Chart-/Metrik-Berechnung **Serie:** Designprinzipien für Produktfamilie · Dokument 7 von n **Vorgänger:** [UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md](./UNIVERSAL_IMPORT_DESIGN_PRINCIPLES.md) **Kernkomponenten:** | Bereich | Pfade | |---------|-------| | Katalog (SSoT) | `backend/widget_catalog.py` | | Layout-Schema | `backend/dashboard_layout_schema.py` | | Config-Validierung | `backend/dashboard_widget_config.py` | | Entitlements | `backend/dashboard_widget_entitlements.py`, `widget_feature_requirements_db.py` | | Produkt-Standard | `backend/system_dashboard_product_default.py` | | HTTP | `backend/routers/app_dashboard.py` | | Frontend-Registry | `frontend/src/widgetSystem/dashboardWidgetRegistry.jsx` | | Registrierung | `frontend/src/widgetSystem/registerDashboardWidgets.js` | | Layout-Editor | `frontend/src/pages/DashboardConfigurePage.jsx` | | Fehler-Isolation | `frontend/src/widgetSystem/WidgetErrorBoundary.jsx` | | Leitfaden | `DASHBOARD_WIDGETS_AGENT_GUIDE.md` | | Registry-Meta | [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) | --- ## Modul **Dashboard Widgets** Erweiterbares System für **konfigurierbare Startübersicht**: Backend-Katalog definiert erlaubte Widget-IDs; Nutzer speichern Reihenfolge, Ein/Aus und optionale `config` pro Profil; Frontend rendert über eine lokale Komponenten-Registry. --- ## Fachliche Verantwortung Das Modul übernimmt: 1. **Widget-Katalog** — IDs, Titel, Beschreibung, optionale Feature-Anforderung (`requires_feature`). 2. **Layout-Persistenz** — `profiles.dashboard_layout` (JSON v1: `{ version, widgets[] }`). 3. **Validierung** — Erlaubte IDs, keine Duplikate, max. 32 Widgets, mindestens eines aktiv. 4. **Pro-Widget-Config** — Whitelist pro Widget-ID; Normalisierung beim Speichern. 5. **Standard-Layouts** — Code-Fallback (`DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS`), Admin-Override (`system_config`), Lab-Template (`DEFAULT_LAB_WIDGET_IDS`). 6. **Entitlements** — `allowed` im Katalog; Layout bereinigt bei fehlender Berechtigung. 7. **Frontend-Rendering** — Registry mappt Katalog-ID → React-Komponente + Props aus `layoutEntry.config`. 8. **Nutzer-Konfigurator** — „Übersicht anpassen“ (Sortieren, Toggle, Config-Editoren). Es übernimmt **nicht**: - Berechnung von KPIs, Charts, Scores (→ Data Layer + Chart-Endpoints, siehe [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md)) - Tier-/Subscription-Logik in Widgets (→ Feature System, siehe [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md)) - Prompt-/KI-Ausführung (Widget zeigt nur UI; Pipeline läuft über eigene API) ### Datenfluss (Happy Path) ``` WIDGET_CATALOG (Backend) → GET /api/app/widgets/catalog (+ allowed via check_feature_access) → GET /api/app/dashboard-layout → coalesce_effective_layout (Profil oder Standard) → merge_missing_catalog_widgets (neue IDs anhängen) → apply_entitlements_to_layout_dict → Frontend: ensureDashboardWidgetsRegistered() → WidgetRenderer: enabled widgets → mapProps(layoutEntry.config) → Component → PUT /api/app/dashboard-layout (Pydantic + Entitlements + speichern) ``` ### Layout-Eintrag (Struktur) | Feld | Bedeutung | |------|-----------| | `id` | Muss in `WIDGET_CATALOG` existieren | | `enabled` | Sichtbar auf der Übersicht | | `config` | Optional; nur für whitelisted Widgets mit Inhalt erlaubt | --- ## Administrierte vs. code-definierte Konfiguration | Konfiguration | Speicherort | Wer pflegt? | |---------------|-------------|-------------| | Widget-IDs, Metadaten, Default-Aktivierung | `widget_catalog.py` | Entwickler | | Produkt-Standard-Layout (live) | `system_config.dashboard_product_default` | Admin | | Produkt-Standard (Fallback) | `DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS` | Entwickler | | Lab-/Editor-Standard | `DEFAULT_LAB_WIDGET_IDS` | Entwickler | | Nutzer-Layout | `profiles.dashboard_layout` | Nutzer | | Feature-Gate (Katalog) | `requires_feature` pro Eintrag | Entwickler | | Feature-Gate (Override) | `widget_feature_requirements` + Marker | Admin | | Config-Schema pro Widget | `dashboard_widget_config.py` | Entwickler | | React-Komponente | `registerDashboardWidgets.js` | Entwickler | --- ## Designprinzipien ### 1. Backend-Katalog als Single Source of Truth für IDs | | | |---|---| | **Prinzip** | `WIDGET_CATALOG` ist die einzige autoritative Liste erlaubter Widget-IDs; `ALLOWED_WIDGET_IDS` wird daraus abgeleitet — nicht manuell duplizieren. | | **Begründung** | Layout-Validator, API und Default-Layouts bleiben synchron; unbekannte IDs werden beim PUT abgewiesen. | | **Quelle** | `widget_catalog.py`; Agent-Guide §4 | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Frontend-Registry ist zweite manuelle Bindung (kein Build-Time-Gate). | ### 2. Dual Registry: Backend-Kanon + Frontend-Komponentenbindung | | | |---|---| | **Prinzip** | Jede Katalog-ID braucht einen Eintrag in `registerDashboardWidget({ id, Component, mapProps })`; idempotent via `ensureDashboardWidgetsRegistered()`. | | **Begründung** | React-Komponenten können nicht im Python-Katalog leben; explizite Zuordnung hält Bundle tree-shakeable. | | **Quelle** | `registerDashboardWidgets.js`, `dashboardWidgetRegistry.jsx` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Fehlende Registrierung → Laufzeit „Unbekanntes Widget“, kein CI-Fail. | ### 3. Layout als versioniertes Profil-JSON | | | |---|---| | **Prinzip** | Nutzer-Layout in `profiles.dashboard_layout`; Schema `version: 1`, Liste `{ id, enabled, config? }`. | | **Begründung** | Pro Profil anpassbar; Reset auf NULL → System-Standard. | | **Quelle** | `DashboardLayoutPayload`, `app_dashboard.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Nur v1; Schema-Evolution braucht Migrationspfad. | ### 4. Validierung an der API-Grenze (Pydantic) | | | |---|---| | **Prinzip** | Jeder GET/PUT-Pfad normalisiert über `DashboardLayoutPayload`: Duplikat-IDs, unbekannte IDs, leeres Layout (kein enabled) → Fehler. | | **Begründung** | Keine korrupten Layouts in der DB; Frontend kann auf gültige Struktur vertrauen. | | **Quelle** | `dashboard_layout_schema.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Ungültiges gespeichertes Layout → Fallback auf Standard (`coalesce_effective_layout`). | ### 5. Config nur für explizit whitelisted Widgets | | | |---|---| | **Prinzip** | `WIDGETS_ALLOWING_CONFIG`: Widgets **ohne** Eintrag dürfen nur leere `config` haben; sonst Validierungsfehler. | | **Begründung** | Verhindert unkontrollierte JSON-Blobs und stille Ignorierung unbekannter Keys. | | **Quelle** | `dashboard_widget_config.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Pro Widget heterogene Schemas (chart_days vs. KPI-Tiles vs. show_*-Booleans). | ### 6. Strikte Config-Keys (Whitelist, Normalisierung) | | | |---|---| | **Prinzip** | Unbekannte Keys in `config` werden abgelehnt; bekannte Keys typgeprüft und normalisiert (z. B. `chart_days` 7–90, KPI max. 9 Kacheln). | | **Begründung** | Vorhersagbares Verhalten; Editor und Backend stimmen überein. | | **Quelle** | `_validate_chart_days_only`, `_validate_kpi_board_config`, History-Viz-Defaults | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Frontend-Normalizer (`bodyChartDays.js`, `*VizConfig.js`) teils parallel — Abweichungsrisiko. | ### 7. Config-Größenlimit | | | |---|---| | **Prinzip** | `MAX_WIDGET_CONFIG_JSON_BYTES` (3072) — keine großen Blobs in Layout-JSON. | | **Begründung** | DB-Spalte und API-Payload bleiben schlank; Config = Präferenzen, nicht Datenspeicher. | | **Quelle** | `dashboard_widget_config.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | — | ### 8. Katalog-Erweiterung ohne Layout-Reset | | | |---|---| | **Prinzip** | `merge_missing_catalog_widgets` hängt neue Katalog-IDs ans bestehende Layout an (`enabled: false`). | | **Begründung** | Nutzer müssen nach Deploy nicht resetten; „Übersicht anpassen“ zeigt neue Optionen. | | **Quelle** | `dashboard_layout_schema.py`; Agent-Guide | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Reihenfolge neuer Widgets immer am Ende. | ### 9. Mehrere Standard-Layouts (Produkt vs. Lab vs. Admin) | | | |---|---| | **Prinzip** | **Produkt:** `get_product_default_base_dict` (DB-Override oder `DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS`). **Lab:** `lab_default_layout_dict` für Editor/Reset. **Nutzer:** eigenes JSON oder NULL. | | **Begründung** | Onboarding-Default getrennt von Entwickler-/Lab-Template; Admin kann Produkt-Standard ohne Deploy ändern. | | **Quelle** | `system_dashboard_product_default.py`, `widget_catalog.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Feldname `lab_default_layout` historisch irreführend (Servertemplate, nicht nur Lab). | ### 10. Entitlements zentral, Widgets konsumieren nur `allowed` | | | |---|---| | **Prinzip** | Sichtbarkeit über `check_feature_access` in `widget_id_allowed`; Katalog liefert `allowed` pro Zeile. Widgets/React duplizieren **keine** Tier-Logik. | | **Begründung** | Eine Wahrheit für „darf angezeigt werden“; spätere Feature-Cluster ohne Widget-Refactor. | | **Quelle** | `dashboard_widget_entitlements.py`; Agent-Guide §0 | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Inhalts-Endpoints (Charts, KI) brauchen **eigenes** Feature-Gate (Defense in Depth). | ### 11. Layout-Persistenz bereinigt nicht erlaubte Widgets | | | |---|---| | **Prinzip** | `apply_entitlements_to_layout_dict`: bei fehlender Berechtigung `enabled: false`; mindestens `welcome` bleibt aktiv. GET und PUT wenden an. | | **Begründung** | Keine „gespeichert aber nie sichtbar“-Zombies; Downgrade/Tier-Wechsel degradieren gracefully. | | **Quelle** | `dashboard_widget_entitlements.py`, `app_dashboard.py` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Policy ist deaktivieren, nicht entfernen — IDs bleiben im JSON. | ### 12. DB-Override für Widget-Feature-Anforderungen | | | |---|---| | **Prinzip** | Katalog-`requires_feature` ist Default; Admin kann per `dashboard_widget_requirement_custom` + `widget_feature_requirements` überschreiben (AND-Semantik). | | **Begründung** | Runtime-Anpassung ohne Code-Deploy; Marker-Zeile trennt Custom von Fallback. | | **Quelle** | `widget_feature_requirements_db.py`, Migration 041 | | **Tragfähigkeit** | **mittel–hoch** | | **Einschränkung** | Zwei Quellen (Code + DB) — Dokumentation und Admin-UI nötig. | ### 13. mapProps: Layout-Config → Komponenten-Props | | | |---|---| | **Prinzip** | Registry-Eintrag mappt `ctx.layoutEntry.config` auf typisierte Props (`chartDays`, `kpiConfig`, `bodyHistoryVizConfig`, …). | | **Begründung** | Widget-Komponenten bleiben layout-agnostisch; Normalisierung an einer Stelle pro ID. | | **Quelle** | `registerDashboardWidgets.js` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Teilweise Normalisierung in Widget statt in `mapProps` (inkonsistent, aber dokumentiert). | ### 14. Refresh-Koordination über Context | | | |---|---| | **Prinzip** | `refreshTick` + `requestRefresh()` im Render-Context; Widgets laden Daten bei Tick-Änderung neu; Aktionen (z. B. Schnelleingabe) rufen `requestRefresh`. | | **Begründung** | Kein globales State-Monster; gezielte Invalidierung nach Capture. | | **Quelle** | `dashboardWidgetRegistry.jsx`, Widget-Implementierungen | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Kein feingranulares Cache pro Widget. | ### 15. Fehler-Isolation pro Widget | | | |---|---| | **Prinzip** | `WidgetErrorBoundary` um jede Instanz — Render-Fehler crashen nicht die ganze Übersicht. | | **Begründung** | Robuste PWA; ein defektes Chart blockiert nicht Gewicht-Eingabe. | | **Quelle** | `WidgetErrorBoundary.jsx` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Kein automatisches Retry/Reporting. | ### 16. Konfigurator filtert nach `allowed` | | | |---|---| | **Prinzip** | `DashboardConfigurePage` blendet Widgets mit `allowed === false` aus der bearbeitbaren Liste aus. | | **Begründung** | Nutzer sehen keine Optionen, die sie nicht nutzen dürfen (Agent-Guide A2). | | **Quelle** | `DashboardConfigurePage.jsx` | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Bereits gespeicherte disabled Einträge können im JSON verbleiben. | ### 17. Widgets konsumieren Data Layer, duplizieren keine Logik | | | |---|---| | **Prinzip** | Chart-/KPI-Widgets rufen Chart-Endpoints bzw. API-Fassaden auf; Berechnungen leben in `data_layer/`, nicht in Widget-JS. | | **Begründung** | Gleiche Zahlen wie Verlauf, KI-Platzhalter und Export. | | **Quelle** | Layer-2b `*_history_viz`-Widgets; [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Legacy-Widgets unter `dashboard-widgets-legacy/` teils ältere Fetch-Pfade. | ### 18. Dedizierte Config-Editoren für komplexe Widgets | | | |---|---| | **Prinzip** | Einfache `chart_days`: Set `CHART_DAYS_WIDGET_IDS` im Layout-Editor; komplexe Config: eigene Editor-Komponenten (`KpiBoardConfigEditor`, `*VizConfigEditor`). | | **Begründung** | UX skaliert mit Config-Komplexität; Backend-Schema und Editor bleiben parallel pflegbar. | | **Quelle** | `widgetSystem/*ConfigEditor.jsx`, Agent-Guide §3.4 | | **Tragfähigkeit** | **hoch** | | **Einschränkung** | Jedes neue komplexe Widget = Editor + Validator + Tests. | --- ## Nicht übernehmen 1. **Tier-Logik in React-Widgets** — nur `allowed` aus API; keine hardcodierten Plan-Namen. 2. **`ALLOWED_WIDGET_IDS` manuell pflegen** — immer aus Katalog ableiten. 3. **Config ohne Backend-Whitelist** — stille Ignorierung unbekannter Keys in Widgets. 4. **Nur UI-Gating ohne API-Absicherung** — Chart-/KI-/Export-Endpoints weiterhin `check_feature_access` (403). 5. **Frontend-Registry vergessen** — Katalog-Eintrag ohne `registerDashboardWidget` → Laufzeitfehler statt Build-Fail. 6. **Große Daten in `config`** — Layout ist Präferenzspeicher, kein Blob-Store (>3072 Bytes). 7. **Doppelte Widget-IDs im Layout** — Validator verbietet; Editor muss dasselbe erzwingen. 8. **Neue Katalog-IDs ohne `merge_missing_catalog_widgets`-Pfad** — Nutzer-Layouts veralten unsichtbar. 9. **Kompletter Katalog nur in DB** — schwer testbar; Code-Katalog + optionale Feature-Overrides ist das Muster. 10. **Evidence-Pflicht à la Placeholder-Registry** — 22 Metadatenfelder pro Widget wären Overkill; Tiefe an Risiko anpassen ([REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md)). 11. **Ein Default für alles** — Produkt-Onboarding, Lab-Template und Admin-Override haben unterschiedliche Zwecke; nicht vermischen. 12. **Fehlender Cross-Check Backend ↔ Frontend IDs** — empfohener Test/Gate fehlt im Ist-Stand; nicht als „optional“ ignorieren. 13. **Berechnungslogik im Widget** — KPIs/Scores gehören in Data Layer, nicht in `useEffect`-Mathe. 14. **Entitlements beim Speichern ablehnen statt deaktivieren** — Mitai wählt deaktivieren; Policy bewusst festlegen und dokumentieren. --- ## Modul-Inventar (Ist-Stand) ``` backend/ ├── widget_catalog.py # WIDGET_CATALOG, DEFAULT_*_IDS ├── dashboard_layout_schema.py # Pydantic, merge_missing, defaults ├── dashboard_widget_config.py # WIDGETS_ALLOWING_CONFIG, Validatoren ├── dashboard_widget_entitlements.py # allowed, layout cleanup ├── widget_feature_requirements_db.py # Admin-Override ├── system_dashboard_product_default.py └── routers/app_dashboard.py frontend/src/ ├── widgetSystem/ │ ├── dashboardWidgetRegistry.jsx │ ├── registerDashboardWidgets.js │ ├── layoutEditor.js │ ├── bodyChartDays.js, *VizConfig.js │ └── *ConfigEditor.jsx ├── components/dashboard-widgets/ # Produkt-Widgets ├── components/dashboard-widgets-legacy/ # ältere Kern-Widgets └── pages/DashboardConfigurePage.jsx DB: ├── profiles.dashboard_layout ├── system_config.dashboard_product_default ├── dashboard_widget_requirement_custom └── widget_feature_requirements ``` **Katalog-Umfang:** ~24 Widget-IDs (Stand `widget_catalog.py`); ~13 mit konfigurierbarer `config`. --- ## Verwandte Dokumentation - Agent-Guide (normativ): [DASHBOARD_WIDGETS_AGENT_GUIDE.md](../../technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md) - Registry-Meta: [REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md](./REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md) - Feature-Gates: [FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md](./FEATURE_ENTITLEMENT_DESIGN_PRINCIPLES.md) - Datenberechnung: [DATA_LAYER_DESIGN_PRINCIPLES.md](./DATA_LAYER_DESIGN_PRINCIPLES.md) - Architektur §9: `.claude/rules/ARCHITECTURE.md` --- ## Geplante Folgedokumente (Serie) | # | Modul | Status | |---|-------|--------| | 1–6 | … | ✅ | | 7 | Dashboard Widgets | ✅ dieses Dokument | | 8 | Navigation / IA | ✅ | | 9 | Migration & Deploy | ✅ |