import { useState, useEffect } from 'react' import { Pencil, Trash2, Check, X, BookOpen } from 'lucide-react' import { useNavigate } from 'react-router-dom' import { api } from '../utils/api' import { calcBodyFat, getBfCategory, METHOD_POINTS } from '../utils/calc' import { CALIPER_POINTS, CALIPER_METHODS } from '../utils/guideData' import dayjs from 'dayjs' function emptyForm() { return { date: dayjs().format('YYYY-MM-DD'), sf_method:'jackson3', notes:'', sf_chest:'', sf_axilla:'', sf_triceps:'', sf_subscap:'', sf_suprailiac:'', sf_abdomen:'', sf_thigh:'', sf_calf_med:'', sf_lowerback:'', sf_biceps:'' } } function CaliperForm({ form, setForm, profile, onSave, onCancel, saveLabel='Speichern' }) { const sex = profile?.sex||'m' const age = profile?.dob ? Math.floor((Date.now()-new Date(profile.dob))/(365.25*24*3600*1000)) : 30 const weight = form.weight || 80 const sfPoints = METHOD_POINTS[form.sf_method]?.[sex] || [] const sfVals = {} sfPoints.forEach(k=>{ const v=form[`sf_${k}`]; if(v!==''&&v!=null) sfVals[k]=parseFloat(v) }) const bfPct = Object.keys(sfVals).length===sfPoints.length&&sfPoints.length>0 ? Math.round(calcBodyFat(form.sf_method, sfVals, sex, age)*10)/10 : null const bfCat = bfPct ? getBfCategory(bfPct, sex) : null const set = (k,v) => setForm(f=>({...f,[k]:v})) return (
set('date',e.target.value)}/>
Rechte Körperseite · Falte 1 cm abheben · Caliper 2 Sek. warten · 3× messen, Mittelwert
{sfPoints.map(k=>{ const p = CALIPER_POINTS[k] return p ? (
set(`sf_${k}`,e.target.value)}/> mm
) : null })} {bfPct!==null && (
{bfPct}%
{bfCat?.label} · {CALIPER_METHODS[form.sf_method]?.label}
)}
set('notes',e.target.value)}/>
{onCancel && }
) } export default function CaliperScreen() { const [entries, setEntries] = useState([]) const [profile, setProfile] = useState(null) const [form, setForm] = useState(emptyForm()) const [editing, setEditing] = useState(null) const [saved, setSaved] = useState(false) const nav = useNavigate() const load = () => Promise.all([api.listCaliper(), api.getProfile()]) .then(([e,p])=>{ setEntries(e); setProfile(p) }) useEffect(()=>{ load() },[]) const buildPayload = (f, bfPct, sex) => { const weight = profile?.weight || null const payload = { date: f.date, sf_method: f.sf_method, notes: f.notes } Object.entries(f).forEach(([k,v])=>{ if(k.startsWith('sf_')&&v!==''&&v!=null) payload[k]=parseFloat(v) }) if(bfPct!=null) { payload.body_fat_pct = bfPct // get latest weight from profile or skip lean/fat } return payload } const handleSave = async (bfPct, sex) => { const payload = buildPayload(form, bfPct, sex) await api.upsertCaliper(payload) setSaved(true); await load() setTimeout(()=>setSaved(false),2000) setForm(emptyForm()) } const handleUpdate = async (bfPct, sex) => { const payload = buildPayload(editing, bfPct, sex) await api.updateCaliper(editing.id, payload) setEditing(null); await load() } const handleDelete = async (id) => { if(!confirm('Eintrag löschen?')) return await api.deleteCaliper(id); await load() } return (

Caliper

Neue Messung
Verlauf ({entries.length})
{entries.length===0 &&

Noch keine Caliper-Messungen.

} {entries.map((e,i)=>{ const prev = entries[i+1] const bfCat = e.body_fat_pct ? getBfCategory(e.body_fat_pct, profile?.sex||'m') : null const isEd = editing?.id===e.id return (
{isEd ? ( setEditing(null)} saveLabel="Änderungen speichern"/> ) : (
{dayjs(e.date).format('DD. MMMM YYYY')}
{e.body_fat_pct && (
{e.body_fat_pct}% {bfCat && {bfCat.label}} {prev?.body_fat_pct && {e.body_fat_pct}
)}
{e.sf_method} · {CALIPER_METHODS[e.sf_method]?.label}
{e.notes &&

"{e.notes}"

}
)}
) })}
) }