From 888b5c3e408d586cf8e1dde7bbb73431958a33bf Mon Sep 17 00:00:00 2001 From: Lars Date: Sat, 21 Mar 2026 09:51:20 +0100 Subject: [PATCH] fix: [BUG-003] correlations chart shows all weight data with extrapolation Changes: - Show all data points (kcal OR weight, not only both) - Extrapolate missing kcal values at end (use last known value) - Dashed lines (strokeDasharray) for extrapolated values - Solid lines for real measurements - Weight always interpolates gaps (connectNulls=true) Visual distinction: - Solid = Real measurements + gap interpolation - Dashed = Extrapolation at chart end Closes: BUG-003 Co-Authored-By: Claude Opus 4.6 --- frontend/src/pages/NutritionPage.jsx | 54 +++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/frontend/src/pages/NutritionPage.jsx b/frontend/src/pages/NutritionPage.jsx index c22364d..d278cdc 100644 --- a/frontend/src/pages/NutritionPage.jsx +++ b/frontend/src/pages/NutritionPage.jsx @@ -598,26 +598,62 @@ function OverviewCards({ data }) { // ── Chart: Kalorien vs Gewicht ──────────────────────────────────────────────── function CaloriesVsWeight({ data }) { - const filtered = data.filter(d => d.kcal && d.weight) - const withAvg = rollingAvg(filtered.map(d=>({...d,date:dayjs(d.date).format('DD.MM')})), 'kcal') + // BUG-003 fix: Show all weight data, extrapolate kcal if missing + const filtered = data.filter(d => d.kcal || d.weight) if (filtered.length < 3) return (
- Zu wenig gemeinsame Daten (Gewicht + Kalorien am selben Tag nötig) + Zu wenig Daten für diese Auswertung
) + + // Find last real kcal value + const lastKcalIndex = filtered.findLastIndex(d => d.kcal) + const lastKcal = lastKcalIndex >= 0 ? filtered[lastKcalIndex].kcal : null + + // Extrapolate missing kcal values at the end + const withExtrapolated = filtered.map((d, i) => ({ + ...d, + kcal: d.kcal || (i > lastKcalIndex && lastKcal ? lastKcal : null), + isKcalExtrapolated: !d.kcal && i > lastKcalIndex && lastKcal + })) + + // Format dates and calculate rolling average + const formatted = withExtrapolated.map(d => ({ + ...d, + date: dayjs(d.date).format('DD.MM') + })) + const withAvg = rollingAvg(formatted, 'kcal') + + // Split into real and extrapolated segments for dashed lines + const realData = withAvg.map(d => ({ + ...d, + kcal_extrap: d.isKcalExtrapolated ? d.kcal : null, + kcal_avg_extrap: d.isKcalExtrapolated ? d.kcal_avg : null, + kcal: d.isKcalExtrapolated ? null : d.kcal, + kcal_avg: d.isKcalExtrapolated ? null : d.kcal_avg + })) + return ( - + + interval={Math.max(0,Math.floor(realData.length/6)-1)}/> [`${Math.round(v)} ${n==='weight'?'kg':'kcal'}`, n==='kcal_avg'?'Ø 7T Kalorien':n==='weight'?'Gewicht':'Kalorien']}/> - - - + formatter={(v,n)=>[`${Math.round(v)} ${n==='weight'?'kg':'kcal'}`, n.includes('avg')?'Ø 7T Kalorien':n==='weight'?'Gewicht':'Kalorien']}/> + + {/* Real kcal values - solid lines */} + + + + {/* Extrapolated kcal values - dashed lines */} + + + + {/* Weight - always solid */} + )