Skip to content

feat(ui): Dashboard ↔ Metriken — klickbare Charts + kuratierte „Deine Metriken"-Leiste + Verwandte-Metriken-Block#179

Merged
gerfru merged 4 commits into
mainfrom
claude/dashboard-curated-metrics-phase2-3
Jun 10, 2026
Merged

feat(ui): Dashboard ↔ Metriken — klickbare Charts + kuratierte „Deine Metriken"-Leiste + Verwandte-Metriken-Block#179
gerfru merged 4 commits into
mainfrom
claude/dashboard-curated-metrics-phase2-3

Conversation

@gerfru

@gerfru gerfru commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Kontext

Eigene, berechnete Metriken waren bisher nur unter /metrics (22 Kacheln) sichtbar — am Dashboard liefen fast nur Garmin-Rohwerte. Diese PR macht die Eigen-Metriken am Haupt-Screen sichtbar und vernetzt die Detailseiten. Drilldown-Modell nach Shneiderman („overview first → details on demand"), kuratiert nach Few/Tufte (nicht alle 22).

Umfasst die komplette Feature-Arbeit (Phase 1 war auf dem Vorgänger-Branch, hier mit enthalten, da main sie noch nicht hat).

Phase 1 — Charts klickbar → Detailseite

Block-Link-Pattern: Card-Titel als echter <a> + ganze Card per JS-Maus-Klick (.card[data-href], ignoriert a,button). Mapping bevorzugt die eigene Metrik (Body Battery → body-battery-custom, Stress → stress-score-custom, …). battery-pattern+correlations in die /metrics-Übersicht aufgenommen.

Phase 2 — Kuratierte „Deine Metriken"-Leiste am Dashboard

  • Neue Sektion zwischen Hero und Tabs mit 5 berechneten Metriken, die die Hero NICHT zeigt: Training Monotony, Schlaf-Konsistenz, HRV Recovery, SpO₂ Trend, Running Economy.
  • buildCuratedMetrics() rendert aus bereits geladenem _heroData.ml (/api/ml-insights) + _evidencekein zusätzlicher Request.

Phase 3 — „Verwandte Metriken"-Block auf Detailseiten

  • RELATED-Mapping je Metrik → 2–3 verwandte Metriken, gerendert nach dem KPI-Block. Macht u. a. battery-pattern/correlations natürlich auffindbar.

Design / Secure / Styling

  • Secure (CSP): keine Inline-Handler, keine neuen Endpunkte/CSP-Änderung; innerHTML nur mit eigenen Werten + eigenem Evidence-Katalog, alle Interpolationen via esc().
  • Styling (DRY): geteilte .metric-overview-* aus dem metrics_overview-Inline-<style> in die globale style.css verschoben (jetzt von Dashboard + Übersicht genutzt); neue .related-*-Klassen nur über Design-Tokens → kein make tailwind-build.
  • A11y (BFSG/WCAG 2.1 AA): echte <a>-Links, semantische <section>/<nav> mit aria-labelledby, globaler :focus-visible. Canvas-a11y unverändert (makeChart/buildChartDataTable).

Tests / Verifikation

  • 262/262 Vitest grün, node --check grün.
  • Pre-commit grün: biome (JS lint+format), mypy api, gitleaks, detect-secrets.
  • Deployed via make dashboard: Container healthy, /health{"status":"ok"}, ausgelieferte Assets enthalten die Änderungen.

🤖 Generated with Claude Code

claude and others added 4 commits June 10, 2026 06:45
- Chart-Cards am Dashboard zu Detailseiten verlinken (Block-Link: Titel als
  echter <a> + ganze Card per Maus-Klick), bevorzugt eigene Custom-Metriken
  statt Garmin-Rohwerte (body-battery-custom, stress-score-custom,
  sleep-score-custom, hrv-status-custom)
- Kein Canvas-Overlay → Chart.js-Hover-Tooltips bleiben erhalten
- battery-pattern + correlations in /metrics-Übersicht aufgenommen
  (waren funktionsfähig, nur nicht gelistet — keine toten Routen)
- Briefing-Doc docs/dashboard-metrics-redesign.md (Faktenlage, UX-Recherche,
  3-Phasen-Plan)

https://claude.ai/code/session_012tUMV4UZ5AB6mLNjA85LRr
…k (Phase 2+3)

Phase 2 — kuratierte Eigen-Metriken am Dashboard sichtbar machen:
- Neue Sektion „Deine Metriken" zwischen Hero und Tabs mit 5 berechneten
  Metriken, die die Hero NICHT zeigt (Training Monotony, Schlaf-Konsistenz,
  HRV Recovery, SpO₂ Trend, Running Economy) — kuratiert statt alle 22 (Few/Tufte).
- buildCuratedMetrics() in dashboard-hero.js rendert aus bereits geladenem
  _heroData.ml (/api/ml-insights) + _evidence → KEIN zusätzlicher Request.
  Verdrahtet in loadMlInsights() und nach loadEvidence().

Phase 3 — Detailseiten vernetzen:
- „Verwandte Metriken"-Block (RELATED-Mapping) am Ende jeder Detailseite,
  gerendert in metrics.js nach dem KPI-Block. Macht u. a. battery-pattern/
  correlations natürlich auffindbar.

Styling (DRY): geteilte .metric-overview-*/.metrics-section-heading aus dem
metrics_overview-Inline-<style> in die globale style.css verschoben (jetzt von
Dashboard + Übersicht genutzt); neue .related-*-Klassen nur über Design-Tokens
→ kein make tailwind-build nötig.

Secure/A11y: keine Inline-Handler, keine neuen Endpunkte/CSP-Änderung, innerHTML
nur mit eigenen Werten + eigenem Evidence-Katalog (alles via esc()); echte
<a>-Links, semantische <section>/<nav> mit aria-labelledby, globaler :focus-visible.

Tests: 262/262 Vitest grün, node --check grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… pool)

Die DB-Funktionen save_ml_feedback/get_ml_feedback wurden bisher nur über
gemockte Endpoint-Tests (Route-Layer-Patch) + E2E berührt — ihre Bodies liefen
nie im Unit-Test (Cover 44%, Z. 15-16/29-35 ungetestet). Drei direkte Unit-Tests
via vorhandenem _pool_mock-Helper:
- save: Upsert-SQL (ON CONFLICT) + Bind-Params (user_id/model/helpful)
- get: Row→dict-Mapping über beide FEEDBACK_MODELS
- get: leeres dict bei keinen Rows

→ src/db/ml_feedback.py 100% (Stmts 9/9). 102 Tests in test_db.py grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JS (Vitest, 12-Modul-Gate jetzt aggregat Branch 100 %, vorher 95.07):
- metrics-activity/sleep/energy/ml/readiness: Null-Daten-Fixtures decken die
  Chart `?? null`-Mappings, `|| []`-History-Fallbacks, leeren Sub-Branches und
  Recommendation-Mittelbänder ab (rein verhaltensbasiert — echte Daten mit Lücken).
- epilepsy: DOM-Guard- + Delegations-Defensiv-Branches (fehlende Elemente,
  delBtn-null, pendingDeleteId==null) via Element-Removal-Muster.
- dashboard-status: die zwei unerreichbaren Defensiv-False-Branches
  (if (_mlPollTimer)/if (_syncPollTimer) — Poll läuft nur via gesetztem Timer)
  mit /* v8 ignore */ + Begründung annotiert statt Hohl-Test.

Python (full-suite je Service):
- pool.py 94→100 %: create_pool-Fehlerpfad (loggt nur Exception-Typ, nie die DSN).
- logging_config.py 94→100 % in ALLEN drei Services (api/sync/ml): _release()
  APP_VERSION-Override + "unknown"-Fallback.

Verifikation: JS 284 grün (Branch 100), API 438 / Sync 194 / ML 300 Python grün, ruff sauber.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gerfru gerfru merged commit f460117 into main Jun 10, 2026
12 checks passed
@gerfru gerfru deleted the claude/dashboard-curated-metrics-phase2-3 branch June 10, 2026 11:15
gerfru added a commit that referenced this pull request Jun 12, 2026
… Metriken"-Leiste + Verwandte-Metriken-Block (#179)

* Dashboard: Charts klickbar → Metrik-Detailseite (Phase 1)

- Chart-Cards am Dashboard zu Detailseiten verlinken (Block-Link: Titel als
  echter <a> + ganze Card per Maus-Klick), bevorzugt eigene Custom-Metriken
  statt Garmin-Rohwerte (body-battery-custom, stress-score-custom,
  sleep-score-custom, hrv-status-custom)
- Kein Canvas-Overlay → Chart.js-Hover-Tooltips bleiben erhalten
- battery-pattern + correlations in /metrics-Übersicht aufgenommen
  (waren funktionsfähig, nur nicht gelistet — keine toten Routen)
- Briefing-Doc docs/dashboard-metrics-redesign.md (Faktenlage, UX-Recherche,
  3-Phasen-Plan)

https://claude.ai/code/session_012tUMV4UZ5AB6mLNjA85LRr

* feat(ui): Dashboard „Deine Metriken"-Leiste + Verwandte-Metriken-Block (Phase 2+3)

Phase 2 — kuratierte Eigen-Metriken am Dashboard sichtbar machen:
- Neue Sektion „Deine Metriken" zwischen Hero und Tabs mit 5 berechneten
  Metriken, die die Hero NICHT zeigt (Training Monotony, Schlaf-Konsistenz,
  HRV Recovery, SpO₂ Trend, Running Economy) — kuratiert statt alle 22 (Few/Tufte).
- buildCuratedMetrics() in dashboard-hero.js rendert aus bereits geladenem
  _heroData.ml (/api/ml-insights) + _evidence → KEIN zusätzlicher Request.
  Verdrahtet in loadMlInsights() und nach loadEvidence().

Phase 3 — Detailseiten vernetzen:
- „Verwandte Metriken"-Block (RELATED-Mapping) am Ende jeder Detailseite,
  gerendert in metrics.js nach dem KPI-Block. Macht u. a. battery-pattern/
  correlations natürlich auffindbar.

Styling (DRY): geteilte .metric-overview-*/.metrics-section-heading aus dem
metrics_overview-Inline-<style> in die globale style.css verschoben (jetzt von
Dashboard + Übersicht genutzt); neue .related-*-Klassen nur über Design-Tokens
→ kein make tailwind-build nötig.

Secure/A11y: keine Inline-Handler, keine neuen Endpunkte/CSP-Änderung, innerHTML
nur mit eigenen Werten + eigenem Evidence-Katalog (alles via esc()); echte
<a>-Links, semantische <section>/<nav> mit aria-labelledby, globaler :focus-visible.

Tests: 262/262 Vitest grün, node --check grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(db): ml_feedback.py 44% → 100% — Unit-Tests für save/get (mocked pool)

Die DB-Funktionen save_ml_feedback/get_ml_feedback wurden bisher nur über
gemockte Endpoint-Tests (Route-Layer-Patch) + E2E berührt — ihre Bodies liefen
nie im Unit-Test (Cover 44%, Z. 15-16/29-35 ungetestet). Drei direkte Unit-Tests
via vorhandenem _pool_mock-Helper:
- save: Upsert-SQL (ON CONFLICT) + Bind-Params (user_id/model/helpful)
- get: Row→dict-Mapping über beide FEEDBACK_MODELS
- get: leeres dict bei keinen Rows

→ src/db/ml_feedback.py 100% (Stmts 9/9). 102 Tests in test_db.py grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: JS branch coverage → 100% (gate) + pool/logging_config → 100%

JS (Vitest, 12-Modul-Gate jetzt aggregat Branch 100 %, vorher 95.07):
- metrics-activity/sleep/energy/ml/readiness: Null-Daten-Fixtures decken die
  Chart `?? null`-Mappings, `|| []`-History-Fallbacks, leeren Sub-Branches und
  Recommendation-Mittelbänder ab (rein verhaltensbasiert — echte Daten mit Lücken).
- epilepsy: DOM-Guard- + Delegations-Defensiv-Branches (fehlende Elemente,
  delBtn-null, pendingDeleteId==null) via Element-Removal-Muster.
- dashboard-status: die zwei unerreichbaren Defensiv-False-Branches
  (if (_mlPollTimer)/if (_syncPollTimer) — Poll läuft nur via gesetztem Timer)
  mit /* v8 ignore */ + Begründung annotiert statt Hohl-Test.

Python (full-suite je Service):
- pool.py 94→100 %: create_pool-Fehlerpfad (loggt nur Exception-Typ, nie die DSN).
- logging_config.py 94→100 % in ALLEN drei Services (api/sync/ml): _release()
  APP_VERSION-Override + "unknown"-Fallback.

Verifikation: JS 284 grün (Branch 100), API 438 / Sync 194 / ML 300 Python grün, ruff sauber.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants