feat: enhance KPI tiles with contextual hints and improve chart legends
- Added contextual hints to KPI tiles in the nutrition interpretation to provide users with actionable insights regarding protein intake and weight assessment. - Updated the KpiTilesOverview component to display these hints, improving user understanding of nutrition metrics. - Introduced a new KcalVsWeightLegend component to clarify chart data representation, enhancing the overall user experience in the history visualization.
This commit is contained in:
parent
31fbf33031
commit
fc816da335
|
|
@ -103,6 +103,7 @@ def build_nutrition_history_kpi_tiles(
|
|||
"sublabel": "Referenzgewicht fehlt",
|
||||
"status": "warn",
|
||||
"verdict": _verdict("warn"),
|
||||
"hint": "Ohne aktuelles Körpergewicht lässt sich das Protein-Ziel (g/kg) nicht bewerten.",
|
||||
"hoverTop": "Protein-Ziel nicht berechenbar",
|
||||
"hoverBody": "Für 1,6–2,2 g/kg wird ein aktuelles Körpergewicht benötigt.",
|
||||
"keys": ["protein_adequacy"],
|
||||
|
|
@ -119,6 +120,10 @@ def build_nutrition_history_kpi_tiles(
|
|||
"sublabel": f"Unterversorgung: {avg_protein}g/Tag (Ziel {pt_low}–{pt_high}g)",
|
||||
"status": "bad",
|
||||
"verdict": _verdict("bad"),
|
||||
"hint": (
|
||||
f"Es fehlen rund {miss} g Protein pro Tag – bei Kaloriendefizit "
|
||||
"steigt das Risiko für Muskelerhalt."
|
||||
),
|
||||
"hoverTop": f"Unterversorgung: {avg_protein}g/Tag (Ziel {pt_low}–{pt_high}g)",
|
||||
"hoverBody": (
|
||||
f"1,6–2,2g/kg KG. Fehlend: ~{miss}g täglich. "
|
||||
|
|
@ -153,6 +158,10 @@ def build_nutrition_history_kpi_tiles(
|
|||
"sublabel": f"Protein-Anteil niedrig: {prot_pct}% der Kalorien",
|
||||
"status": "warn",
|
||||
"verdict": _verdict("warn"),
|
||||
"hint": (
|
||||
f"Viele Kalorien kommen aus KH/Fett; Proteinanteil oft sinnvoll bei 25–35 % "
|
||||
f"(aktuell P {prot_pct} % / KH {kh_pct} % / F {fat_pct} %)."
|
||||
),
|
||||
"hoverTop": f"Protein-Anteil niedrig: {prot_pct}% der Kalorien",
|
||||
"hoverBody": (
|
||||
f"Empfehlung oft 25–35%. Aktuell: {prot_pct}% P / {kh_pct}% KH / {fat_pct}% F"
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export function buildKpiTileTitleString(t) {
|
|||
* - `value` (ReactNode) — Hauptwert
|
||||
* - `status` — für Farbstreifen: `good` | `warn` | `bad`
|
||||
* - optional: `icon`, `sublabel`, `verdict`, `valueColor`, `hoverTop`, `hoverBody`, `keys`
|
||||
* - optional: `hint` — Kurz-Hinweis/Warnung direkt auf der Kachel (z. B. Ernährung bei warn/bad)
|
||||
*/
|
||||
export default function KpiTilesOverview({
|
||||
tiles,
|
||||
|
|
@ -82,6 +83,7 @@ export default function KpiTilesOverview({
|
|||
{tiles.map(t => {
|
||||
const accent = getStatusColor(t.status)
|
||||
const tip = buildKpiTileTitleString(t)
|
||||
const cardHint = t.hint ? String(t.hint) : null
|
||||
return (
|
||||
<div
|
||||
key={t.key}
|
||||
|
|
@ -119,6 +121,23 @@ export default function KpiTilesOverview({
|
|||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{cardHint ? (
|
||||
<div
|
||||
className="kpi-tiles-card__hint"
|
||||
style={{
|
||||
marginTop: 10,
|
||||
padding: '8px 10px',
|
||||
borderRadius: 8,
|
||||
fontSize: 10,
|
||||
lineHeight: 1.45,
|
||||
color: 'var(--text2)',
|
||||
background: 'var(--surface2)',
|
||||
borderLeft: `3px solid ${accent}`,
|
||||
}}
|
||||
>
|
||||
{cardHint}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -700,6 +700,70 @@ function BodySection({ profile, insights, onRequest, loadingSlug, filterActiveSl
|
|||
)
|
||||
}
|
||||
|
||||
/** Legende unter dem Chart: Linien + ggf. TDEE-Referenz (gestrichelt). */
|
||||
function KcalVsWeightLegend({ showTdee }) {
|
||||
const line = (color) => ({
|
||||
display: 'inline-block',
|
||||
width: 22,
|
||||
height: 3,
|
||||
background: color,
|
||||
borderRadius: 1,
|
||||
verticalAlign: 'middle',
|
||||
marginRight: 6,
|
||||
})
|
||||
return (
|
||||
<div
|
||||
className="kcal-vs-weight-legend"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: '12px 18px',
|
||||
marginTop: 10,
|
||||
fontSize: 10,
|
||||
color: 'var(--text2)',
|
||||
lineHeight: 1.35,
|
||||
}}
|
||||
>
|
||||
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
|
||||
<span style={line('#EA580C')} />
|
||||
Ø Kalorien (7-Tage-Mittel)
|
||||
</span>
|
||||
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: 9,
|
||||
height: 9,
|
||||
borderRadius: '50%',
|
||||
background: '#2563EB',
|
||||
marginRight: 6,
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
/>
|
||||
Gewicht (kg)
|
||||
</span>
|
||||
{showTdee ? (
|
||||
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: 22,
|
||||
height: 0,
|
||||
verticalAlign: 'middle',
|
||||
marginRight: 6,
|
||||
borderTop: '2px dashed #EA580C',
|
||||
opacity: 0.95,
|
||||
}}
|
||||
/>
|
||||
TDEE-Referenz (geschätzt)
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Kalorien (Ø 7T) vs. Gewicht — Daten aus Layer-2b-Bundle (nutrition_metrics / TDEE wie Data Layer). */
|
||||
function KcalVsWeightChart({ vizKcalWeight, corrData: corrRows, profile, cutoffDate, allTime }) {
|
||||
if (vizKcalWeight?.points?.length >= 5) {
|
||||
|
|
@ -716,7 +780,7 @@ function KcalVsWeightChart({ vizKcalWeight, corrData: corrRows, profile, cutoffD
|
|||
Kalorien (Ø 7 Tage) vs. Gewicht
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 8 }}>
|
||||
Gleitender 7-Tage-Mittelwert der Kalorien vs. tägliches Gewicht (gemeinsame Tage). Orange: kcal · Blau: Gewicht.
|
||||
Nur Tage mit Kalorien- und Gewichtsdaten. Linke Achse: kcal (Ø 7 Tage), rechte Achse: kg.
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height={200}>
|
||||
<LineChart data={kcalVsW} margin={{ top: 4, right: 8, bottom: 0, left: -16 }}>
|
||||
|
|
@ -735,9 +799,10 @@ function KcalVsWeightChart({ vizKcalWeight, corrData: corrRows, profile, cutoffD
|
|||
<Line yAxisId="weight" type="monotone" dataKey="weight" stroke="#2563EB" strokeWidth={2.5} dot={{ r: 2, fill: '#2563EB' }} name="weight" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
<div style={{ fontSize: 10, color: 'var(--text3)', textAlign: 'center', marginTop: 6 }}>
|
||||
<KcalVsWeightLegend showTdee={tdeeLabel != null} />
|
||||
<div style={{ fontSize: 10, color: 'var(--text3)', textAlign: 'center', marginTop: 8 }}>
|
||||
{tdeeLabel != null
|
||||
? `Referenz TDEE ~${tdeeLabel} kcal (Data Layer, gestrichelt) · ${n} gemeinsame Tage`
|
||||
? `TDEE ~${tdeeLabel} kcal · ${n} gemeinsame Tage`
|
||||
: `Keine TDEE-Referenz (Gewicht/Demografie) · ${n} gemeinsame Tage`}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -765,7 +830,7 @@ function KcalVsWeightChart({ vizKcalWeight, corrData: corrRows, profile, cutoffD
|
|||
Kalorien (Ø 7 Tage) vs. Gewicht
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: 'var(--text3)', lineHeight: 1.45, marginBottom: 8 }}>
|
||||
Gleitender 7-Tage-Mittelwert der Kalorien vs. tägliches Gewicht (gemeinsame Tage). Orange: kcal · Blau: Gewicht.
|
||||
Nur Tage mit Kalorien- und Gewichtsdaten. Linke Achse: kcal (Ø 7 Tage), rechte Achse: kg.
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height={200}>
|
||||
<LineChart data={kcalVsW} margin={{ top: 4, right: 8, bottom: 0, left: -16 }}>
|
||||
|
|
@ -782,8 +847,9 @@ function KcalVsWeightChart({ vizKcalWeight, corrData: corrRows, profile, cutoffD
|
|||
<Line yAxisId="weight" type="monotone" dataKey="weight" stroke="#2563EB" strokeWidth={2.5} dot={{ r: 2, fill: '#2563EB' }} name="weight" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
<div style={{ fontSize: 10, color: 'var(--text3)', textAlign: 'center', marginTop: 6 }}>
|
||||
Referenz TDEE ~{tdee} kcal (Fallback Mifflin ×1,4) · {raw.length} gemeinsame Tage
|
||||
<KcalVsWeightLegend showTdee />
|
||||
<div style={{ fontSize: 10, color: 'var(--text3)', textAlign: 'center', marginTop: 8 }}>
|
||||
TDEE ~{tdee} kcal (Fallback Mifflin ×1,4) · {raw.length} gemeinsame Tage
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user