## Ergebnis - Basis geprüft: `79e46c1825f78525f68011f71defd67d1425177c` (`79e46c1`), Worktree sauber. - Keine Dateien erstellt oder geändert. - Bestehendes Designsystem und lokales ECharts reichen aus; keine neue Bibliothek erforderlich. ## Wesentliche Befunde 1. **Der Datenkatalog ist bereits vollständig:** 41 Nährstoffe in fünf Gruppen (`macro: 5`, `factor: 4`, `vitamin: 13`, `mineral: 14`, `other: 5`) in `scripts/health/dashboard_v5/nutrition_contract.py:57-99`. 2. **Die API liefert aber nicht alle 41 Nährstoffe pro Antwort.** - Tagesdetail materialisiert nur tatsächlich gefundene oder supplementierte Schlüssel: `scripts/health/dashboard_v5/read_api.py:1777-1878`. - Zeitraumsmittel werden nur bei vorhandenen dokumentierten Werten erzeugt: `scripts/health/dashboard_v5/read_api.py:1669-1713`. - Dadurch kann die UI fehlende Nährstoffe nicht dauerhaft als „nicht übermittelt/unbekannt“ anzeigen. 3. **Die UI blendet fehlende Nährstoffe standardmäßig aus.** - `microSection()` zeigt zunächst nur `documented_value`, `documented_zero` und `estimated`; erst „Alle anzeigen“ macht weitere Zeilen sichtbar: `scripts/health/assets/health-assets/dashboard-v5-nutrition.js:36`. - Das widerspricht „alle relevanten Nährstoffe immer sichtbar“. - Die Gruppen `macro`, `factor` und `other` besitzen zudem keine einheitliche vollständige Übersicht. 4. **Es gibt noch kein Nährstoff-Detailpanel.** - Vorhanden ist nur ein Lebensmittel-Dialog: `dashboard-v5-nutrition.js:38`. - Nährstoffquellen erscheinen aktuell als einzelne Inline-`
`: `dashboard-v5-nutrition.js:24-31`. 5. **Referenzmetadaten sind bereits sicher vorhanden, aber praktisch nicht dargestellt.** - DGE/EFSA, Quellenstand und allgemeines Erwachsenenprofil: `scripts/health/dashboard_v5/reference_contract.py:32-42`. - Sie werden derzeit nur an vorhandene Zeitraumsmittel gehängt: `read_api.py:1696-1713`. - Die UI prüft `reference_context.available`, dieses Feld existiert im Vertrag nicht; deshalb erscheint stets „Keine belastbare Orientierung hinterlegt“: `dashboard-v5-nutrition.js:35` gegenüber `reference_contract.py:32-42`. 6. **1/7/30 ist nicht als lokale Nährstoffperiode umgesetzt.** - `loadDay()` verwendet den globalen Range-Zustand: `dashboard-v5-nutrition.js:41`. - Es gibt keinen expliziten Nutrition-Zustand `1 | 7 | 30` und keine entsprechende URL-Wiederherstellung. 7. **390 px, 44 px und Reduced Motion sind teilweise vorbereitet.** - Nutrition-Touchhöhen: `dashboard-v5.css:557`, `659-680`, `756`. - Mobile Reflow: `dashboard-v5.css:771-781`, `1178-1192`. - Globale Reduced-Motion-Regel: `dashboard-v5.css:280-282`, `1015-1017`. - ECharts schaltet Animation bereits ab: `dashboard-v5-nutrition.js:33`, `43`. - Der vorhandene 390px-Test misst aber nur die **Höhe** sichtbarer Buttons/Summaries, nicht Breite, Inputs, Selects oder das Detailpanel: `tests/browser/dashboard_v5_sprint6h_a.spec.js:87-98`. ## Minimaler Implementierungsplan ### 1. API-Vertrag vollständig materialisieren **Dateien** - `scripts/health/dashboard_v5/read_api.py:1568-1743` - `scripts/health/dashboard_v5/read_api.py:1764-1880` - Tests: `tests/test_dashboard_v5_sprint6g_nutrition.py:61-134` **Änderung** - Eine kleine gemeinsame Hilfsfunktion einführen, die für jeden Eintrag aus `NUTRIENT_CONTRACTS` stets ein Objekt erzeugt: - `key`, `label`, `group`, `unit` - `food_value`, `yazio_value`, `enrichment_value` - `supplement_value`, `total_value` - `data_status` - `status_counts`, `raw_units`, `conversion` - `contract_version`, `null_contract_version` - `reference_context` - Fehlt ein Wert, bleiben alle Werte `null`; Status wird `not_reported` oder bei widersprüchlichen Quelldaten `unknown` – niemals `0`. - `_nutrition_day_detail()` gibt damit immer exakt die 41 freigegebenen Schlüssel zurück. - `_nutrition_days()` erzeugt `summary.averages` ebenfalls für alle 41 Schlüssel: - Mittelwert/Median nullable - `documented_days: 0` - `present_days: 0` - korrektes `expected_days` - Referenzkontext immer vorhanden. - Keine neuen Endpunkte und keine Schemaänderung; vorhandene, gebundene `from/to`-API weiterverwenden. **Akzeptanz** - Tagesantwort und Zeitraumantwort besitzen immer dieselbe Schlüsselmenge wie `NUTRIENT_CONTRACTS`. - `null`, `not_reported`, `unknown` und `documented_zero` bleiben unterscheidbar. - Verbotene Rohfelder und IDs bleiben ausgeschlossen. ### 2. Expliziten 1/7/30-Zustand ergänzen **Dateien** - `scripts/health/dashboard_v5/render.py:209` - `scripts/health/assets/health-assets/dashboard-v5-nutrition.js:18-22` - `dashboard-v5-nutrition.js:40-46` **Änderung** - In der Nutrition-Tagesansicht eine kompakte Segmentsteuerung „1 Tag / 7 Tage / 30 Tage“ ergänzen. - URL-Zustand als `nutrition_period=1|7|30`; ungültige Werte fallen auf `1`. - `writeArea()`, Initial Load und `popstate` erhalten den Wert. - `loadDay()` berechnet den Zeitraum immer **einschließlich des gewählten Tages** und ruft weiter den bestehenden Endpoint auf: - 1: `from = to = nutrition_date` - 7: `from = nutrition_date - 6 Tage` - 30: `from = nutrition_date - 29 Tage` - Die globale Range bleibt für die separate Verlaufsansicht zuständig; Tages-Nährstoffübersicht und globale Charts werden nicht mehr unabsichtlich gekoppelt. ### 3. Eine vollständige Nährstoffübersicht statt versteckter Teilmengen **Datei** - `scripts/health/assets/health-assets/dashboard-v5-nutrition.js:35-40` **Änderung** - `factorModule()` und beide `microSection()`-Teillisten durch einen Renderer `renderNutrientOverview(day, period)` ersetzen. - Alle 41 Nährstoffe werden immer als Zeilen gerendert; keine „Alle anzeigen“-Ausblendung mehr. - Vorhandene Makro-Hero-/ECharts-Darstellung und Mahlzeiten bleiben bestehen. - Gruppierung in bestehender Vertragsreihenfolge: 1. Energie und Makronährstoffe 2. Ernährungsfaktoren 3. Vitamine 4. Mineralstoffe 5. Weitere Nährstoffe - Sortierauswahl: - „Nach Gruppe“ - „Name A–Z“ - „Datenstatus“ - Sortierung ändert nur Reihenfolge, nie Sichtbarkeit. - Jede Zeile zeigt: - Bezeichnung und Einheit - Tageswert oder „unbekannt/nicht übermittelt“ - Zeitraumsmittel oder „unbekannt“ - `n/erwartete Tage` - Status. - `documented_zero` erscheint ausdrücklich als dokumentierte `0`; alle anderen fehlenden Zustände ohne numerischen Ersatz. ### 4. Zugängliches Nährstoff-Detailpanel **Dateien** - `scripts/health/assets/health-assets/dashboard-v5-nutrition.js:24-31`, `35-40` - `scripts/health/assets/health-assets/dashboard-v5.css:742-781`, `1141-1153`, `1178-1192` **Änderung** - Jede Nährstoffzeile wird ein mindestens 44×44 px großes Button-Ziel. - Ein wiederverwendbares `` analog zum bestehenden Lebensmittel-Dialog öffnen. - Panelinhalt: - Nährstoffname, Gruppe, Einheit und Datenstatus - Tageswert - Mittelwert für gewählte 1/7/30 Tage - dokumentierte/erwartete Tage - YAZIO-Wert - lokale Lebensmittelschätzung - Supplementwert - ausdrücklich kombinierte Summe - Einheiten-/Konvertierungsvertrag - **Quellenstand:** Referenzquelle und `reference_context.version` - **Referenzgruppe/Profil:** `reference_context.profile` - `display_rule` und Hinweis, dass kein persönlicher Zielbereich bzw. keine Mangel-/Supplementempfehlung abgeleitet wird. - Fokus nach `showModal()` auf Überschrift/Schließen; Escape schließt; Fokus kehrt zur auslösenden Zeile zurück. - Keine URLs oder Historieneinträge für reine Panelöffnung erforderlich. ### 5. CSS für 390 px, 44×44 und Reduced Motion **Datei** - `scripts/health/assets/health-assets/dashboard-v5.css:742-781` - `dashboard-v5.css:1141-1153` - `dashboard-v5.css:1186-1192` - `dashboard-v5.css:1212-1214` **Änderung** - Neue Übersicht als Grid/Table-artige Liste mit `min-width: 0`, `overflow-wrap: anywhere`. - Desktop: Name, Tageswert, Zeitraum, Abdeckung, Status. - Unter 560 px: gestapelte Zeile, ohne horizontales Scrollen. - Perioden- und Sortiercontrols sowie jede Zeile und alle Dialogcontrols mindestens `44px` hoch **und breit**. - Detaildialog bei 390 px: `width: min(..., calc(100vw - 16px))`, begrenzte Höhe, internes vertikales Scrollen, keine horizontale Überbreite. - Bestehende globale Reduced-Motion-Regel beibehalten; neue Panel-/Zeilen-Transitions zusätzlich explizit deaktivieren. - ECharts-Konfiguration unverändert weiterverwenden und `animation: false`, Dauer `0` bei Reduced Motion beibehalten. ### 6. Tests – höchstens sechs Browser-Szenarien **Bestehende Zieldatei** - `tests/browser/dashboard_v5_sprint6j_c.spec.js:7-51` Die vorhandene Datei hat bereits genau sechs Szenarien. Keine siebte Browser-Story anlegen, sondern erweitern: 1. **Ernährungstag:** Makros, ECharts, Mahlzeitengruppen und exakt 41 sichtbare Nährstoffzeilen. 2. **Nährstoffübersicht:** Unknown ≠ 0, Sortierung, 1/7/30 mit korrekter Abdeckung; Detailpanel zeigt Quellenstand und Referenzgruppe; Reduced Motion deaktiviert ECharts-Animation. 3. **Mapping Master/Detail:** bestehend lassen. 4. **Akte/Dokumente:** bestehend lassen. 5. **Kalender:** bestehend lassen. 6. **390×844:** Nutrition-Übersicht und geöffnetes Nährstoffpanel einbeziehen; kein Overflow; alle sichtbaren `button`, `summary`, `input`, `select`, Links und Dialogcontrols jeweils mindestens 44×44 px; Fokus/Escape-Rückkehr prüfen. **API-/Unit-Regressions** - In `tests/test_dashboard_v5_sprint6g_nutrition.py:61-134` ergänzen: - exakt 41 Schlüssel bei vollständigem und unvollständigem Tag - fehlende Werte `null`, nie implizit `0` - alle fünf Gruppen vorhanden - alle 41 Mittelwertobjekte bei 1/7/30 - Referenzquelle, Version und Profil strukturell vorhanden - keine neuen verbotenen Felder. **Runner** - `tests/browser/run_v5_isolated_matrix.sh:47-106` um eine fokussierte Gruppe `6j-c` ergänzen, die ausschließlich `dashboard_v5_sprint6j_c.spec.js` ausführt. - Der aktuelle Runner führt diese bereits versionierte Sechs-Szenarien-Datei noch nicht aus; sein Default endet bei älteren Gruppen: `run_v5_isolated_matrix.sh:107-121`. ## Verifikation nach Umsetzung ```bash python -m pytest tests/test_dashboard_v5_sprint6g_nutrition.py -q HEALTH_DASHBOARD_BROWSER_GROUP=6j-c tests/browser/run_v5_isolated_matrix.sh git diff --check ``` Zusätzlich im Browser-Szenario: keine Console Errors, keine externen Requests, nur vorhandenes lokales ECharts und keine Chart.js-/Bibliotheksneueinführung. **Probleme bei der Analyse:** keine technischen Blocker; lediglich mehrere große JS-/HTML-Bereiche sind stark auf einzelne Zeilen komprimiert, daher beziehen sich einige präzise Hinweise auf Funktionszeilen statt kleinere Zeilenintervalle.