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 <noreply@anthropic.com>
This commit is contained in:
parent
d1675dcc80
commit
888b5c3e40
|
|
@ -598,26 +598,62 @@ function OverviewCards({ data }) {
|
||||||
|
|
||||||
// ── Chart: Kalorien vs Gewicht ────────────────────────────────────────────────
|
// ── Chart: Kalorien vs Gewicht ────────────────────────────────────────────────
|
||||||
function CaloriesVsWeight({ data }) {
|
function CaloriesVsWeight({ data }) {
|
||||||
const filtered = data.filter(d => d.kcal && d.weight)
|
// BUG-003 fix: Show all weight data, extrapolate kcal if missing
|
||||||
const withAvg = rollingAvg(filtered.map(d=>({...d,date:dayjs(d.date).format('DD.MM')})), 'kcal')
|
const filtered = data.filter(d => d.kcal || d.weight)
|
||||||
if (filtered.length < 3) return (
|
if (filtered.length < 3) return (
|
||||||
<div style={{padding:20,textAlign:'center',color:'var(--text3)',fontSize:13}}>
|
<div style={{padding:20,textAlign:'center',color:'var(--text3)',fontSize:13}}>
|
||||||
Zu wenig gemeinsame Daten (Gewicht + Kalorien am selben Tag nötig)
|
Zu wenig Daten für diese Auswertung
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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 (
|
return (
|
||||||
<ResponsiveContainer width="100%" height={220}>
|
<ResponsiveContainer width="100%" height={220}>
|
||||||
<LineChart data={withAvg} margin={{top:4,right:8,bottom:0,left:-16}}>
|
<LineChart data={realData} margin={{top:4,right:8,bottom:0,left:-16}}>
|
||||||
<CartesianGrid stroke="var(--border)" strokeDasharray="3 3"/>
|
<CartesianGrid stroke="var(--border)" strokeDasharray="3 3"/>
|
||||||
<XAxis dataKey="date" tick={{fontSize:9,fill:'var(--text3)'}} tickLine={false}
|
<XAxis dataKey="date" tick={{fontSize:9,fill:'var(--text3)'}} tickLine={false}
|
||||||
interval={Math.max(0,Math.floor(withAvg.length/6)-1)}/>
|
interval={Math.max(0,Math.floor(realData.length/6)-1)}/>
|
||||||
<YAxis yAxisId="kcal" tick={{fontSize:9,fill:'var(--text3)'}} tickLine={false} domain={['auto','auto']}/>
|
<YAxis yAxisId="kcal" tick={{fontSize:9,fill:'var(--text3)'}} tickLine={false} domain={['auto','auto']}/>
|
||||||
<YAxis yAxisId="weight" orientation="right" tick={{fontSize:9,fill:'var(--text3)'}} tickLine={false} domain={['auto','auto']}/>
|
<YAxis yAxisId="weight" orientation="right" tick={{fontSize:9,fill:'var(--text3)'}} tickLine={false} domain={['auto','auto']}/>
|
||||||
<Tooltip contentStyle={{background:'var(--surface)',border:'1px solid var(--border)',borderRadius:8,fontSize:11}}
|
<Tooltip contentStyle={{background:'var(--surface)',border:'1px solid var(--border)',borderRadius:8,fontSize:11}}
|
||||||
formatter={(v,n)=>[`${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']}/>
|
||||||
<Line yAxisId="kcal" type="monotone" dataKey="kcal" stroke="#EF9F2744" strokeWidth={1} dot={false}/>
|
|
||||||
<Line yAxisId="kcal" type="monotone" dataKey="kcal_avg" stroke="#EF9F27" strokeWidth={2} dot={false} name="kcal_avg"/>
|
{/* Real kcal values - solid lines */}
|
||||||
<Line yAxisId="weight" type="monotone" dataKey="weight" stroke="#378ADD" strokeWidth={2} dot={{r:3,fill:'#378ADD'}} name="weight"/>
|
<Line yAxisId="kcal" type="monotone" dataKey="kcal" stroke="#EF9F2744" strokeWidth={1} dot={false} connectNulls={false}/>
|
||||||
|
<Line yAxisId="kcal" type="monotone" dataKey="kcal_avg" stroke="#EF9F27" strokeWidth={2} dot={false} name="kcal_avg" connectNulls={false}/>
|
||||||
|
|
||||||
|
{/* Extrapolated kcal values - dashed lines */}
|
||||||
|
<Line yAxisId="kcal" type="monotone" dataKey="kcal_extrap" stroke="#EF9F2744" strokeWidth={1} strokeDasharray="3 3" dot={false} connectNulls={false}/>
|
||||||
|
<Line yAxisId="kcal" type="monotone" dataKey="kcal_avg_extrap" stroke="#EF9F27" strokeWidth={2} strokeDasharray="3 3" dot={false} connectNulls={false}/>
|
||||||
|
|
||||||
|
{/* Weight - always solid */}
|
||||||
|
<Line yAxisId="weight" type="monotone" dataKey="weight" stroke="#378ADD" strokeWidth={2} dot={{r:3,fill:'#378ADD'}} name="weight" connectNulls={true}/>
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user