mitai-jinkendo/.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md
Lars ddc87ba5ae
All checks were successful
Deploy Development / deploy (push) Successful in 56s
Build Test / pytest-backend (push) Successful in 5s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 17s
feat: remove deprecated demo route and enhance dashboard widget registration
- Removed outdated visualization demo route and fixed demo layout in the frontend.
- Updated widget registration logic in `frontend/src/widgetSystem/registerDashboardWidgets.js` to ensure proper integration of core widgets.
- Adjusted documentation in `.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md` and comments in `backend/widget_catalog.py` to reflect changes.
- Added new dashboard widgets for activity and body overview, enhancing user experience and data visualization capabilities.
- Bumped application version to reflect these changes.
2026-04-23 15:24:13 +02:00

11 KiB
Raw Blame History

Dashboard-Lab-Widgets Anleitung für Coding-Agenten

Ziel: Ein neues Dashboard-Widget end-to-end korrekt einbinden (Backend-Katalog, Validierung, API-Layout, Frontend-Registrierung, optional Lab-Editor für config).
Kontext: Dashboard-Lab unter geschützten Endpoints GET/PUT /api/app/... (siehe backend/routers/app_dashboard.py). Layout liegt pro Profil in profiles.dashboard_layout (JSON).


0. Architekturanforderung: Subscription / Feature-System vs. Widget-Katalog

0.1 Ist-Stand (Verifikation)

  • Bereits vorhanden: Membership- und Feature-Modell (features, tier_limits, user_feature_restrictions, check_feature_access in backend/auth.py). Siehe .claude/docs/architecture/FEATURE_ENFORCEMENT.md.
  • Umgesetzt: GET /api/app/widgets/catalog liefert pro Widget allowed (aus requires_feature im Katalog + check_feature_access). GET/PUT /api/app/dashboard-layout wendet apply_entitlements_to_layout_dict an (nicht erlaubte Einträge: enabled: false; Standard-Layout ebenfalls bereinigt). Implementierung: backend/dashboard_widget_entitlements.py.
  • Optional pro Katalogzeile: requires_feature (features.id) in widget_catalog.py; fehlt der Key → Widget für alle authentifizierten Nutzer katalog-sichtbar (ohne zusätzliches Feature-Gate).

0.2 Soll: eine Wahrheit für „darf angezeigt werden“

  • Komplexität (Module aus, Cluster, Stufen: z.B. Ernährung an, aber bestimmte Auswertungen nur in höherem Tier) gehört in die Feature-/Subscription-Schicht (inkl. späterer Feature-Cluster), nicht in einzelne React-Widgets.
  • Widgets sollen das Ergebnis nur abrufen (z.B. allowed / sichtbar im Katalog), nicht die Tier-Logik duplizieren.

0.3 Bindende Anforderungen (wenn Feature-Gating umgesetzt wird)

Anforderung Beschreibung
A1 Zentrale Auflösung Backend ermittelt pro Profil (effektiver Tier + Restrictions), welche Widget-IDs erlaubt sind idealerweise in einer Stelle (Erweiterung des Katalog-Endpoints oder dedizierter Entitlements-Teil der Response). Intern: check_feature_access und später ggf. Mapping Widget-ID → Feature-ID(n) / Cluster.
A2 Nutzer-Konfigurator Im Dashboard-Lab (und jedem späteren Layout-Konfigurator): Widgets ohne Berechtigung nicht anbieten (ausgeblendet oder gar nicht in der Liste). Alle erlaubten Widgets bleiben wie heute wählbar.
A3 Layout-Persistenz PUT /api/app/dashboard-layout: Layout darf keine nicht erlaubten Widgets dauerhaft speichern entweder ablehnen (422) oder beim Speichern entfernen/deaktivieren (Policy festlegen und dokumentieren). Verhindert „gespeichert, aber nie sichtbar“-Zombies.
A4 API-/Datenschutz Sichtbarkeit im UI reicht nicht: Endpoints, die Inhalte für gated Widgets liefern (Charts, KI, …), müssen weiterhin wie heute eigenständig über Features abgesichert sein (check_feature_access, 403).

0.4 Katalog-Erweiterung (Vorbereitung ohne feste Tier-Namen)

  • Tiers bleiben in der DB konfigurierbar; im Code keine Annahme „free vs. pro“.
  • Pro Widget-Eintrag (oder separater Mapping-Layer) kann später required_feature_id (ein Key aus features.id) oder ein Cluster-Key ergänzt werden, der auf eine oder mehrere check_feature_access-Abfragen abgebildet wird Details bei Implementierung festlegen.
  • Neue Widget-Doku: Wenn ein Widget an ein Feature hängt, in widget_catalog-description und in dieser Anleitung vermerken.

Verweis: Verbindliche Regel auf Projektebene: .claude/rules/ARCHITECTURE.md § 9.


1. Datenfluss (kurz)

  1. backend/widget_catalog.py WIDGET_CATALOG: erlaubte Widget-IDs, Reihenfolge, Titel/Beschreibung für API und Default-Layout.
  2. backend/dashboard_layout_schema.py DashboardLayoutPayload: jede Zeile hat id, enabled, optional config. IDs müssen in ALLOWED_WIDGET_IDS sein (aus dem Katalog abgeleitet).
  3. backend/dashboard_widget_config.py validate_widget_entry_config: nur Widgets in WIDGETS_ALLOWING_CONFIG dürfen nicht-leere config haben; Keys werden streng validiert (unbekannte Keys → Fehler).
  4. Frontend ensureDashboardWidgetsRegistered() in frontend/src/widgetSystem/registerDashboardWidgets.js: verbindet jede Katalog-ID mit einer React-Komponente und mappt ctx.layoutEntry.config auf Props.
  5. Dashboard-Lab-UI frontend/src/pages/DashboardLabPage.jsx: Umsortieren, Ein/Aus, Speichern; zusätzliche UI nur nötig, wenn das Widget konfigurierbare Felder braucht.

2. Checkliste: neues Widget ohne Konfiguration

Schritt Datei Aktion
A backend/widget_catalog.py Neuen Eintrag { "id", "title", "description" } in WIDGET_CATALOG einfügen (Reihenfolge = Default-Reihenfolge im Layout). Optional "requires_feature": "<features.id>" für Tarif-Gating (dashboard_widget_entitlements).
B backend/widget_catalog.py Optional: ID zu DEFAULT_LAB_WIDGET_IDS hinzufügen, wenn es im Standard-Lab aktiv sein soll.
C frontend/src/components/dashboard-widgets/MyWidget.jsx (oder Legacy-Widget unter dashboard-widgets-legacy/) React-Komponente implementieren; typischerweise refreshTick aus mapProps nutzen, um Daten neu zu laden.
D frontend/src/widgetSystem/registerDashboardWidgets.js import + registerDashboardWidget({ id, Component, mapProps }) id exakt wie im Katalog.
E backend/tests/test_widget_catalog.py Läuft implizit mit; bei Strukturänderungen Katalog-Tests beachten.
F backend/version.py MODULE_VERSIONS["app_dashboard"] MINOR erhöhen und kurz kommentieren.
G Build/Tests pytest (z.B. tests/test_dashboard_layout_schema.py, test_widget_catalog.py); npm run build im frontend.

Nicht nötig: WIDGETS_ALLOWING_CONFIG oder validate_widget_entry_config-Zweig, solange config immer {} bleibt.

Wichtig: Widget-IDs im Frontend-Registry ohne Registrierung führen im UI zu „Unbekanntes Widget“ (dashboardWidgetRegistry.jsx).


3. Checkliste: Widget mit konfigurierbaren Einstellungen (config)

3.1 Backend

  1. WIDGETS_ALLOWING_CONFIG in backend/dashboard_widget_config.py um die neue widget_id ergänzen.
  2. In validate_widget_entry_config einen eigenen Zweig oder Aufruf einer Hilfsfunktion hinzufügen (siehe bestehende Muster unten).
  3. MAX_WIDGET_CONFIG_JSON_BYTES (3072): keine großen Blobs in config.
  4. Regeln konsistent halten:
    • Unbekannte Keys ablehnen (wie bei kpi_board, quick_capture, chart_days-only).
    • Leeres Objekt {} erlauben, wenn alle Keys optional sind (Validator entscheidet).

Referenz-Muster im Code:

Muster Verwendung Implementierung
Nur chart_days (790) Chart-Kacheln _validate_chart_days_only(raw, label="...")
KPI-Kacheln tiles-Liste, max. 9 _validate_kpi_board_config
Booleans + Mindestens eines true Schnelleingabe-Sichtbarkeit _validate_quick_capture_config

Neue komplexe Config: eigene _validate_my_widget_config schreiben, Keys als frozenset whitelisten, Typen prüfen, sinnvoll normalisieren/abrunden.

  1. Tests in backend/tests/test_dashboard_widget_config.py: Happy-Path, ein ungültiger Wert, unbekannter Key, ggf. Größe/Limits.

3.2 Katalog-Beschreibung

In widget_catalog.py bei description die konfigurierbaren Keys kurz nennen (hilft Admin/API-Nutzern). Einheitliche Benennung mit dem Backend (z.B. chart_days 790).

3.3 Frontend: Props aus Layout

registerDashboardWidget erhält mapProps(ctx):

  • ctx.layoutEntry: { id, enabled, config? } hierher kommt die gespeicherte Konfiguration.
  • ctx.refreshTick / ctx.requestRefresh(): Datenaktualisierung nach Aktionen.

Typische Zuordnung:

mapProps: (ctx) => ({
  refreshTick: ctx.refreshTick,
  myOption: ctx.layoutEntry?.config?.my_option,
})

Abgleich mit Chart-Zeitraum: Für chart_days existiert frontend/src/widgetSystem/bodyChartDays.js (BODY_CHART_DAYS_MIN/MAX, normalizeBodyChartDays). Entweder in mapProps normalisieren (wie body_overview) oder rohen Wert durchreichen und in der Widget-Komponente normalisieren (wie nutrition_detail_charts / TrendKcalWeightWidget) beides ist im Projekt vertreten; wichtig ist Konsistenz mit der Backend-Grenze 790.

3.4 Dashboard-Lab-Editor (DashboardLabPage.jsx)

Ohne UI-Änderung bleibt config beim Nutzer {} konfigurierbare Widgets brauchen Editor-Controls:

  • Einfaches Zahlfeld chart_days: Eintrag in CHART_DAYS_WIDGET_IDS (Set oben in der Datei) + bestehendes Label/aria-label-Pattern für die Zeitraum-Zeile erweitern (siehe body_overview, nutrition_detail_charts).
  • Strukturierte Config (Listen, mehrere Booleans): Eigenes Editor-Komponenten-File nach Vorbild KpiBoardConfigEditor.jsx / QuickCaptureConfigEditor.jsx einbinden und setLayout + normalizeLayoutForEditor wie bei den bestehenden Blöcken verwenden.

Nach Speichern ruft die Seite api.putAppDashboardLayout(layout) auf; das Backend validiert über DashboardLayoutPayloadvalidate_widget_entry_config.


4. Grenzen und Fehlerbilder

Thema Detail
Erlaubte IDs Nur IDs aus WIDGET_CATALOG. ALLOWED_WIDGET_IDS wird daraus abgeleitet nicht manuell duplizieren.
Doppelte IDs Im Layout sind keine doppelten widget.id erlaubt (DashboardLayoutPayload).
Max. Widgets widgets max. 32 Einträge (DashboardLayoutPayload).
Config verboten Widget nicht in WIDGETS_ALLOWING_CONFIG → jede nicht-leere config → Validierungsfehler beim Speichern.
Frontend ≠ Katalog Komponente registriert, ID fehlt im Katalog → PUT schlägt fehl.
Katalog ohne Registry GET Layout ok, Render zeigt „Unbekanntes Widget“.

5. API zum Prüfen

  • GET /api/app/widgets/catalog Katalog inkl. allowed je Widget (Auth + X-Profile-Id wie andere App-Endpoints).
  • GET /api/app/dashboard-layout layout (effektiv, bereinigt), custom, product_default_layout (Übersichts-Standard), lab_default_layout (Dashboard-Lab-Standard).
  • PUT /api/app/dashboard-layout Body { "version": 1, "widgets": [ ... ] } (unerlaubte Widgets werden auf enabled: false gesetzt).

6. Nach getaner Arbeit

  • pytest für dashboard_widget_config und widget_catalog / dashboard_layout_schema.
  • npm run build.
  • MODULE_VERSIONS["app_dashboard"] in backend/version.py anheben.

7. Verwandte Dateien (Referenz)

Zweck Pfad
Katalog backend/widget_catalog.py
Config-Validierung backend/dashboard_widget_config.py
Layout-Pydantic backend/dashboard_layout_schema.py
HTTP backend/routers/app_dashboard.py
Registry + Render frontend/src/widgetSystem/dashboardWidgetRegistry.jsx
Dashboard-Widget-Registrierung frontend/src/widgetSystem/registerDashboardWidgets.js
Lab-UI frontend/src/pages/DashboardLabPage.jsx