fix: focus areas slider NaN values and validation
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s

Fixed multiple issues with relative weight sliders:
1. Sanitize focusData on load (ensure all 6 fields are numeric)
2. Sync focusTemp when clicking "Anpassen" button
3. Robust sum calculation filtering only *_pct fields
4. Convert NaN/undefined to 0 in all calculations
5. Safe Number() coercion before normalization

Fixes errors:
- "Gewichtung gesamt: NaN"
- "Input should be a valid integer, input: null"
- Prozent always showing 0%

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-27 12:20:01 +01:00
parent 92cc309489
commit 2f51b26418

View File

@ -92,8 +92,21 @@ export default function GoalsPage() {
])
setGoalMode(modeData.goal_mode)
setGoals(goalsData)
setFocusAreas(focusData)
setFocusTemp(focusData) // Initialize temp state
// Ensure all focus fields are present and numeric
const sanitizedFocus = {
weight_loss_pct: focusData?.weight_loss_pct ?? 0,
muscle_gain_pct: focusData?.muscle_gain_pct ?? 0,
strength_pct: focusData?.strength_pct ?? 0,
endurance_pct: focusData?.endurance_pct ?? 0,
flexibility_pct: focusData?.flexibility_pct ?? 0,
health_pct: focusData?.health_pct ?? 0,
custom: focusData?.custom,
updated_at: focusData?.updated_at
}
setFocusAreas(sanitizedFocus)
setFocusTemp(sanitizedFocus)
// Convert types array to map for quick lookup
const typesMap = {}
@ -278,7 +291,10 @@ export default function GoalsPage() {
{!focusEditing && focusAreas && (
<button
className="btn-secondary"
onClick={() => setFocusEditing(true)}
onClick={() => {
setFocusTemp(focusAreas) // Sync temp state before editing
setFocusEditing(true)
}}
style={{ padding: '6px 12px' }}
>
<Pencil size={14} /> Anpassen
@ -306,9 +322,12 @@ export default function GoalsPage() {
{ key: 'flexibility_pct', label: 'Beweglichkeit', icon: '🤸', color: '#E67E22' },
{ key: 'health_pct', label: 'Gesundheit', icon: '❤️', color: '#F59E0B' }
].map(area => {
const weight = Math.round(focusTemp[area.key] / 10)
const sum = Object.values(focusTemp).reduce((a, b) => a + b, 0)
const actualPercent = sum > 0 ? Math.round(focusTemp[area.key] / sum * 100) : 0
const rawValue = Number(focusTemp[area.key]) || 0
const weight = Math.round(rawValue / 10)
const sum = Object.entries(focusTemp)
.filter(([k]) => k.endsWith('_pct'))
.reduce((acc, [k, v]) => acc + (Number(v) || 0), 0)
const actualPercent = sum > 0 ? Math.round(rawValue / sum * 100) : 0
return (
<div key={area.key}>
@ -361,7 +380,12 @@ export default function GoalsPage() {
Gewichtung gesamt:
</span>
<span style={{ fontSize: 18, fontWeight: 600, color: 'var(--text1)' }}>
{Object.values(focusTemp).reduce((a, b) => a + b, 0) / 10}
{(() => {
const total = Object.entries(focusTemp)
.filter(([k]) => k.endsWith('_pct'))
.reduce((acc, [k, v]) => acc + (Number(v) || 0), 0)
return (total / 10).toFixed(1)
})()}
</span>
</div>
<div style={{ fontSize: 12, marginTop: 4, color: 'var(--text3)' }}>
@ -374,18 +398,25 @@ export default function GoalsPage() {
<button
className="btn-primary"
onClick={async () => {
const sum = Object.values(focusTemp).reduce((a, b) => a + b, 0)
// Calculate sum (filter out NaN/undefined)
const sum = Object.entries(focusTemp)
.filter(([k]) => k.endsWith('_pct'))
.reduce((acc, [k, v]) => acc + (Number(v) || 0), 0)
if (sum === 0) {
if (sum === 0 || isNaN(sum)) {
setError('Mindestens ein Bereich muss gewichtet sein')
return
}
// Normalize to percentages
const normalized = {}
Object.keys(focusTemp).forEach(key => {
normalized[key] = Math.round(focusTemp[key] / sum * 100)
})
// Normalize to percentages (ensure no NaN values)
const normalized = {
weight_loss_pct: Math.round((Number(focusTemp.weight_loss_pct) || 0) / sum * 100),
muscle_gain_pct: Math.round((Number(focusTemp.muscle_gain_pct) || 0) / sum * 100),
strength_pct: Math.round((Number(focusTemp.strength_pct) || 0) / sum * 100),
endurance_pct: Math.round((Number(focusTemp.endurance_pct) || 0) / sum * 100),
flexibility_pct: Math.round((Number(focusTemp.flexibility_pct) || 0) / sum * 100),
health_pct: Math.round((Number(focusTemp.health_pct) || 0) / sum * 100)
}
// Ensure sum is exactly 100 (adjust largest value if needed due to rounding)
const normalizedSum = Object.values(normalized).reduce((a, b) => a + b, 0)