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
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 |
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:
- Widget-Katalog — IDs, Titel, Beschreibung, optionale Feature-Anforderung (
requires_feature).
- Layout-Persistenz —
profiles.dashboard_layout (JSON v1: { version, widgets[] }).
- Validierung — Erlaubte IDs, keine Duplikate, max. 32 Widgets, mindestens eines aktiv.
- Pro-Widget-Config — Whitelist pro Widget-ID; Normalisierung beim Speichern.
- Standard-Layouts — Code-Fallback (
DEFAULT_PRODUCT_DASHBOARD_WIDGET_IDS), Admin-Override (system_config), Lab-Template (DEFAULT_LAB_WIDGET_IDS).
- Entitlements —
allowed im Katalog; Layout bereinigt bei fehlender Berechtigung.
- Frontend-Rendering — Registry mappt Katalog-ID → React-Komponente + Props aus
layoutEntry.config.
- Nutzer-Konfigurator — „Übersicht anpassen“ (Sortieren, Toggle, Config-Editoren).
Es übernimmt nicht:
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 |
| 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
-
Tier-Logik in React-Widgets — nur allowed aus API; keine hardcodierten Plan-Namen.
-
ALLOWED_WIDGET_IDS manuell pflegen — immer aus Katalog ableiten.
-
Config ohne Backend-Whitelist — stille Ignorierung unbekannter Keys in Widgets.
-
Nur UI-Gating ohne API-Absicherung — Chart-/KI-/Export-Endpoints weiterhin check_feature_access (403).
-
Frontend-Registry vergessen — Katalog-Eintrag ohne registerDashboardWidget → Laufzeitfehler statt Build-Fail.
-
Große Daten in config — Layout ist Präferenzspeicher, kein Blob-Store (>3072 Bytes).
-
Doppelte Widget-IDs im Layout — Validator verbietet; Editor muss dasselbe erzwingen.
-
Neue Katalog-IDs ohne merge_missing_catalog_widgets-Pfad — Nutzer-Layouts veralten unsichtbar.
-
Kompletter Katalog nur in DB — schwer testbar; Code-Katalog + optionale Feature-Overrides ist das Muster.
-
Evidence-Pflicht à la Placeholder-Registry — 22 Metadatenfelder pro Widget wären Overkill; Tiefe an Risiko anpassen (REGISTRY_PLUGIN_DESIGN_PRINCIPLES.md).
-
Ein Default für alles — Produkt-Onboarding, Lab-Template und Admin-Override haben unterschiedliche Zwecke; nicht vermischen.
-
Fehlender Cross-Check Backend ↔ Frontend IDs — empfohener Test/Gate fehlt im Ist-Stand; nicht als „optional“ ignorieren.
-
Berechnungslogik im Widget — KPIs/Scores gehören in Data Layer, nicht in useEffect-Mathe.
-
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
Geplante Folgedokumente (Serie)
| # |
Modul |
Status |
| 1–6 |
… |
✅ |
| 7 |
Dashboard Widgets |
✅ dieses Dokument |
| 8 |
Navigation / IA |
✅ |
| 9 |
Migration & Deploy |
✅ |