import { useState, useEffect } from 'react' import { Link } from 'react-router-dom' import dayjs from 'dayjs' import { api } from '../../utils/api' import { getBfCategory } from '../../utils/calc' import { useProfile } from '../../context/ProfileContext' import { KPI_KCAL_WINDOW_DEFAULT } from '../../widgetSystem/bodyChartDays' const MAX_KPI = 9 function formatRefVal(row) { if (row.value_numeric != null && row.value_numeric !== '') { const n = Number(row.value_numeric) return Number.isFinite(n) ? String(n) : String(row.value_numeric) } return row.value_text != null ? String(row.value_text) : '–' } /** * KPIs: Referenzwerte (Layer-1-Summary) + Körperfett % + Ø Kalorien (Fenster konfigurierbar) — max. 9 Kacheln. */ export default function PilotKpiBoard({ refreshTick = 0, kcalWindowDays = KPI_KCAL_WINDOW_DEFAULT }) { const { activeProfile } = useProfile() const sex = activeProfile?.sex || 'm' const [refs, setRefs] = useState([]) const [bf, setBf] = useState(null) const [avgKcal, setAvgKcal] = useState(null) const [loading, setLoading] = useState(true) const [err, setErr] = useState(null) useEffect(() => { let cancelled = false ;(async () => { try { setLoading(true) const nutrLimit = Math.min(2000, Math.max(60, kcalWindowDays * 5)) const [summary, calipers, nutrition] = await Promise.all([ api.listProfileReferenceValuesSummary().catch(() => ({ tiles: [] })), api.listCaliper(3).catch(() => []), api.listNutrition(nutrLimit).catch(() => []), ]) if (cancelled) return const tiles = Array.isArray(summary?.tiles) ? summary.tiles.filter((t) => t?.latest) : [] const latestCal = Array.isArray(calipers) && calipers[0]?.body_fat_pct != null ? calipers[0] : null const recentNutr = (nutrition || []).filter( (n) => n.date >= dayjs().subtract(kcalWindowDays, 'day').format('YYYY-MM-DD'), }) const kcal = recentNutr.length > 0 ? Math.round(recentNutr.reduce((s, n) => s + (n.kcal || 0), 0) / recentNutr.length) : null const wantBf = !!latestCal?.body_fat_pct const wantKcal = kcal != null && kcal > 0 const extra = (wantBf ? 1 : 0) + (wantKcal ? 1 : 0) const refCap = Math.max(0, MAX_KPI - extra) setRefs(tiles.slice(0, refCap)) setBf( wantBf ? { pct: latestCal.body_fat_pct, cat: getBfCategory(latestCal.body_fat_pct, sex), date: latestCal.date, } : null, ) setAvgKcal(wantKcal ? kcal : null) setErr(null) } catch (e) { if (!cancelled) { setErr(e.message || 'KPIs konnten nicht geladen werden') setRefs([]) } } finally { if (!cancelled) setLoading(false) } })() return () => { cancelled = true } }, [refreshTick, sex, kcalWindowDays]) if (loading) { return (
) } if (err) { return (
{err}
) } const tiles = [] refs.forEach((tile) => { const l = tile.latest tiles.push(
{tile.type_label}
{formatRefVal(l)} {l.unit ? ( {l.unit} ) : null}
Ref.wert
, ) }) if (bf) { tiles.push(
Körperfett
{bf.pct}%
{bf.cat?.label || 'Caliper'}
, ) } if (avgKcal != null) { tiles.push(
Ø Kalorien ({kcalWindowDays}T)
{avgKcal} kcal
Ernährung
, ) } if (tiles.length === 0) { return (
Kennzahlen

Noch keine Daten.{' '} Referenzwerte ,{' '} Caliper ,{' '} Ernährung .

) } return (
Kennzahlen

Referenzwerte (bis {MAX_KPI} gesamt inkl. KF% und Ø-Kalorien). Fehlende Typen werden automatisch weggelassen.

{tiles}
) }