diff --git a/backend/data_layer/nutrition_interpretation.py b/backend/data_layer/nutrition_interpretation.py index 304f189..74e0283 100644 --- a/backend/data_layer/nutrition_interpretation.py +++ b/backend/data_layer/nutrition_interpretation.py @@ -174,7 +174,7 @@ def build_macro_donut_from_averages(navg: Dict[str, Any]) -> Optional[List[Dict[ if tot <= 0: return None return [ - {"name": "Protein", "value": round(pkcal / tot * 100), "color": "#059669", "grams": round(p, 1)}, - {"name": "KH", "value": round(ckcal / tot * 100), "color": "#EA580C", "grams": round(c, 1)}, - {"name": "Fett", "value": round(fkcal / tot * 100), "color": "#2563EB", "grams": round(f, 1)}, + {"name": "Protein", "value": round(pkcal / tot * 100), "color": "#4a8f72", "grams": round(p, 1)}, + {"name": "KH", "value": round(ckcal / tot * 100), "color": "#c17d45", "grams": round(c, 1)}, + {"name": "Fett", "value": round(fkcal / tot * 100), "color": "#6e8eb8", "grams": round(f, 1)}, ] diff --git a/backend/data_layer/nutrition_metrics.py b/backend/data_layer/nutrition_metrics.py index 9389d48..33c844a 100644 --- a/backend/data_layer/nutrition_metrics.py +++ b/backend/data_layer/nutrition_metrics.py @@ -668,19 +668,19 @@ def get_weekly_macro_distribution_chart_data(profile_id: str, weeks: int) -> Dic { "label": "Protein (%)", "data": protein_pcts, - "backgroundColor": "#1D9E75", + "backgroundColor": "#4a8f72", "stack": "macro", }, { "label": "Kohlenhydrate (%)", "data": carbs_pcts, - "backgroundColor": "#F59E0B", + "backgroundColor": "#c17d45", "stack": "macro", }, { "label": "Fett (%)", "data": fat_pcts, - "backgroundColor": "#EF4444", + "backgroundColor": "#6e8eb8", "stack": "macro", }, ], diff --git a/frontend/src/app.css b/frontend/src/app.css index c6d23c7..9cccb52 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -368,6 +368,35 @@ body { font-family: var(--font); background: var(--bg); color: var(--text1); -we min-width: 0; } +/* Einheitliche Chart-Höhe (Donut-Bereich ≈ E3-Balken) */ +.nutrition-macro-pair__chart-wrap { + width: 100%; + min-height: 260px; +} + +.nutrition-macro-pair__donut-inner { + display: flex; + flex-direction: column; + gap: 12px; + width: 100%; +} + +.nutrition-macro-pair__donut-chart { + width: 100%; + min-height: 260px; +} + +.nutrition-macro-pair__legend { + width: 100%; + padding-top: 2px; +} + +.nutrition-macro-pair .card.nutrition-macro-pair__donut, +.nutrition-macro-pair .card.nutrition-macro-pair__weekly { + display: flex; + flex-direction: column; +} + .history-page__title { margin-bottom: 12px; } diff --git a/frontend/src/components/NutritionCharts.jsx b/frontend/src/components/NutritionCharts.jsx index 9d8d312..acec389 100644 --- a/frontend/src/components/NutritionCharts.jsx +++ b/frontend/src/components/NutritionCharts.jsx @@ -5,6 +5,7 @@ import { ComposedChart, ReferenceArea, } from 'recharts' import { api } from '../utils/api' +import { MACRO_CHART, NUTRITION_MACRO_CHART_BLOCK_PX } from '../utils/macroChartTheme' import dayjs from 'dayjs' const fmtDate = d => dayjs(d).format('DD.MM') @@ -172,26 +173,28 @@ export function WeeklyMacroDistributionPanel({ macroWeeklyData, loading, error } Anteil der Kalorien aus jedem Makronährstoff pro Kalenderwoche (100 % gestapelt). Gut vergleichbar mit der Donut-Übersicht links. - - - - - - [`${v}%`, name]} - /> - - - - - - +
+ + + + + + [`${v}%`, name]} + /> + + + + + + +
Ø Verteilung: P {meta.avg_protein_pct}% · KH {meta.avg_carbs_pct}% · F {meta.avg_fat_pct}% · Variabilität (CV): P{' '} {meta.protein_cv}% · KH {meta.carbs_cv}% · F {meta.fat_cv}% diff --git a/frontend/src/pages/History.jsx b/frontend/src/pages/History.jsx index b13ca4c..2587293 100644 --- a/frontend/src/pages/History.jsx +++ b/frontend/src/pages/History.jsx @@ -11,6 +11,7 @@ import { api } from '../utils/api' import { photoMonthKey, photoSortKey, formatPhotoCaption } from '../utils/photoDisplay' import { getBfCategory } from '../utils/calc' import { getStatusColor, getStatusBg } from '../utils/interpret' +import { MACRO_CHART, macroFillByName, NUTRITION_MACRO_CHART_BLOCK_PX } from '../utils/macroChartTheme' import Markdown from '../utils/Markdown' import TrainingTypeDistribution from '../components/TrainingTypeDistribution' import NutritionCharts, { WeeklyMacroDistributionPanel } from '../components/NutritionCharts' @@ -909,18 +910,18 @@ function NutritionSection({ profile, insights, onRequest, loadingSlug, filterAct {ptLow > 0 && ( - + )} [`${v}g`, name]} /> - - - + + +
- Protein (oben) - KH - Fett + Protein (unten) + Fett (Mitte) + KH (oben)
@@ -929,35 +930,52 @@ function NutritionSection({ profile, insights, onRequest, loadingSlug, filterAct
Ø Makro-Quote ({n} Tage)
-
- {pieData.length > 0 ? ( - <> - - - {pieData.map((e, i) => )} - - [`${v}%`, name]} /> - -
- {pieData.map(p => ( -
-
+ {pieData.length > 0 ? ( +
+
+ + + + {pieData.map((e, i) => ( + + ))} + + [`${v}%`, name]} /> + + +
+
+ {pieData.map(p => { + const fill = macroFillByName(p.name) + return ( +
+
{p.name}
-
{p.value}%
+
{p.value}%
{p.grams != null ? `${p.grams}g` : '—'}
- ))} -
- Ø {avgKcal} kcal/Tag · Anteil der Makro-Kalorien am Tagesumsatz -
+ ) + })} +
+ Ø {avgKcal} kcal/Tag · Anteil der Makro-Kalorien am Tagesumsatz
- - ) : ( -
Keine Makro-Mittelwerte im Zeitraum.
- )} -
+
+
+ ) : ( +
Keine Makro-Mittelwerte im Zeitraum.
+ )}
diff --git a/frontend/src/pages/NutritionPage.jsx b/frontend/src/pages/NutritionPage.jsx index fc6c1ac..09ed0ba 100644 --- a/frontend/src/pages/NutritionPage.jsx +++ b/frontend/src/pages/NutritionPage.jsx @@ -5,6 +5,7 @@ import { ResponsiveContainer, CartesianGrid, Legend, ReferenceLine, ScatterChart, Scatter } from 'recharts' import { api as nutritionApi } from '../utils/api' +import { MACRO_CHART } from '../utils/macroChartTheme' import dayjs from 'dayjs' import isoWeek from 'dayjs/plugin/isoWeek' dayjs.extend(isoWeek) @@ -709,9 +710,9 @@ function WeeklyMacros({ weekly }) { [`${Math.round(v)} g`, n]}/> - - - + + + ) diff --git a/frontend/src/utils/macroChartTheme.js b/frontend/src/utils/macroChartTheme.js new file mode 100644 index 0000000..03ab9ad --- /dev/null +++ b/frontend/src/utils/macroChartTheme.js @@ -0,0 +1,21 @@ +/** + * Einheitliche Makro-Farben für Verlauf (Balken, Donut, E3). + * Reihenfolge gestapelter Balken (Recharts, unten zuerst): Protein → Fett → Kohlenhydrate. + */ +export const MACRO_CHART = { + protein: '#4a8f72', + fat: '#6e8eb8', + carbs: '#c17d45', +} + +/** Einheitliche Höhe Donut-Bereich / E3-Balken (Verlauf) */ +export const NUTRITION_MACRO_CHART_BLOCK_PX = 260 + +/** Farbe nach Segment-Name (Protein / KH / Fett / englische Keys). */ +export function macroFillByName(name) { + const n = String(name || '').toLowerCase() + if (n.includes('protein') || n === 'p') return MACRO_CHART.protein + if (n.includes('fett') || n.includes('fat')) return MACRO_CHART.fat + if (n.includes('kh') || n.includes('kohlenhydrat') || n.includes('carb')) return MACRO_CHART.carbs + return MACRO_CHART.carbs +}