import { useState } from 'react' import { ChevronRight, ChevronLeft, 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 { CIRCUMFERENCE_POINTS, CALIPER_POINTS, CALIPER_METHODS } from '../utils/guideData' import dayjs from 'dayjs' // ── Circumference Wizard ────────────────────────────────────────────────────── function CircumWizard({ onDone, onCancel }) { const [step, setStep] = useState(0) const [date, setDate] = useState(dayjs().format('YYYY-MM-DD')) const [values, setValues] = useState({}) const [saving, setSaving] = useState(false) const points = CIRCUMFERENCE_POINTS const current = points[step] const totalSteps = points.length const handleNext = () => { if (step < totalSteps - 1) setStep(s => s + 1) else handleSave() } const handleSave = async () => { setSaving(true) try { const payload = { date } Object.entries(values).forEach(([k,v]) => { if(v) payload[k] = parseFloat(v) }) await api.upsertCirc(payload) onDone() } finally { setSaving(false) } } const progress = ((step + 1) / totalSteps) * 100 return (
{/* Header */}
Schritt {step+1} von {totalSteps}
{/* Datum (nur erster Schritt) */} {step === 0 && (
setDate(e.target.value)}/>
)} {/* Messpunkt */}
{/* Punkt-Indikator */}
{step+1}
{current.label}
Umfang messen
{/* Anleitung */}
{[ ['📍 Wo', current.where], ['🧍 Haltung', current.posture], ['📏 Maßband', current.how], ['💡 Tipp', current.tip], ].map(([label, text]) => (
{label}
{text}
))}
{/* Eingabe */}
setValues(v => ({...v, [current.id]: e.target.value}))} onKeyDown={e => e.key==='Enter' && handleNext()} autoFocus /> cm
{values[current.id] && (
✓ {values[current.id]} cm erfasst
)}
{/* Navigation */}
{/* Übersicht bereits erfasster Werte */} {Object.keys(values).length > 0 && (
Bisher erfasst:
{points.slice(0, step+1).map(p => values[p.id] ? ( setStep(points.indexOf(p))} style={{fontSize:12,background:'var(--accent-light)',color:'var(--accent-dark)', padding:'2px 8px',borderRadius:6,cursor:'pointer'}}> {p.label}: {values[p.id]} ) : null)}
)}
) } // ── Caliper Wizard ──────────────────────────────────────────────────────────── function CaliperWizard({ onDone, onCancel, profile }) { const [method, setMethod] = useState('jackson3') const [step, setStep] = useState(-1) // -1 = method select const [date, setDate] = useState(dayjs().format('YYYY-MM-DD')) const [values, setValues] = useState({}) const [saving, setSaving] = useState(false) const sex = profile?.sex || 'm' const age = profile?.dob ? Math.floor((Date.now()-new Date(profile.dob))/(365.25*24*3600*1000)) : 30 const sfPoints = METHOD_POINTS[method]?.[sex] || [] const current = step >= 0 ? CALIPER_POINTS[sfPoints[step]] : null const totalSteps = sfPoints.length // Live BF calculation const sfVals = {} sfPoints.forEach(k => { const v=values[`sf_${k}`]; if(v) sfVals[k]=parseFloat(v) }) const bfPct = Object.keys(sfVals).length === sfPoints.length && sfPoints.length > 0 ? Math.round(calcBodyFat(method, sfVals, sex, age)*10)/10 : null const bfCat = bfPct ? getBfCategory(bfPct, sex) : null const handleNext = () => { if (step < totalSteps - 1) setStep(s => s+1) else handleSave() } const handleSave = async () => { setSaving(true) try { const payload = { date, sf_method: method } Object.entries(values).forEach(([k,v]) => { if(v) payload[k]=parseFloat(v) }) if (bfPct) { payload.body_fat_pct = bfPct if (profile?.weight || values.weight) { const w = parseFloat(profile?.weight || values.weight) payload.lean_mass = Math.round(w*(1-bfPct/100)*10)/10 payload.fat_mass = Math.round(w*(bfPct/100)*10)/10 } } await api.upsertCaliper(payload) onDone() } finally { setSaving(false) } } const progress = step >= 0 ? ((step+1)/totalSteps)*100 : 0 // Method selection screen if (step === -1) { return (

Caliper-Methode

setDate(e.target.value)}/>
{Object.entries(CALIPER_METHODS).map(([k,m]) => ( ))}
Immer rechte Körperseite · Falte 1 cm abheben · Caliper 2 Sek. · 3× messen, Mittelwert
) } return (
{/* Header */}
Punkt {step+1} von {totalSteps} · {CALIPER_METHODS[method]?.label}
{/* Live BF Preview */} {bfPct && (
{bfPct}%
{bfCat &&
{bfCat.label}
}
Körperfett (live)
)} {/* Messpunkt */} {current && (
{step+1}
{current.label}
Hautfalte messen
{[ ['📍 Wo', current.where], ['🧍 Haltung', current.posture], ['🔧 Technik', current.how], ['💡 Tipp', current.tip], ].map(([label, text]) => (
{label}
{text}
))}
setValues(v => ({...v, [`sf_${sfPoints[step]}`]: e.target.value}))} onKeyDown={e => e.key==='Enter' && handleNext()} autoFocus /> mm
{values[`sf_${sfPoints[step]}`] && (
✓ {values[`sf_${sfPoints[step]}`]} mm erfasst
)}
)} {/* Navigation */}
{/* Übersicht */} {Object.keys(values).length > 0 && (
Bisher erfasst:
{sfPoints.slice(0,step+1).map((k,idx) => values[`sf_${k}`] ? ( setStep(idx)} style={{fontSize:12,background:'#D85A3022',color:'#D85A30', padding:'2px 8px',borderRadius:6,cursor:'pointer'}}> {CALIPER_POINTS[k]?.label}: {values[`sf_${k}`]}mm ) : null)}
)}
) } // ── Main Wizard Page ────────────────────────────────────────────────────────── export default function MeasureWizard() { const [mode, setMode] = useState(null) // null | 'circum' | 'caliper' const [done, setDone] = useState(false) const [profile, setProfile] = useState(null) const nav = useNavigate() useState(() => { api.getProfile().then(setProfile) }) if (done) { return (

Gespeichert!

Deine Messung wurde erfolgreich gespeichert.

) } if (mode === 'circum') return (
setDone(true)} onCancel={()=>setMode(null)}/>
) if (mode === 'caliper') return (
setDone(true)} onCancel={()=>setMode(null)} profile={profile}/>
) return (

Assistent

Der Assistent führt dich Schritt für Schritt durch die Messung – mit Anleitung für jeden Messpunkt.

💡 Für schnelle Einzeleingaben oder Bearbeitung bestehender Werte nutze die direkten Screens unter Gewicht, Umfänge und Caliper.
) }