Kairo-Jinkendo/docs/reference/design-principles/mitai/DASHBOARD_WIDGETS_DESIGN_PRINCIPLES.md

364 lines
17 KiB
Markdown
Raw Normal View History

# 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 | ✅ |