import { useState, useEffect } from 'react' import { Target, Plus, Pencil, Trash2, TrendingUp, Calendar } from 'lucide-react' import { api } from '../utils/api' import dayjs from 'dayjs' import 'dayjs/locale/de' dayjs.locale('de') // Goal Mode Definitions const GOAL_MODES = [ { id: 'weight_loss', icon: '📉', label: 'Gewichtsreduktion', description: 'Kaloriendefizit, Fettabbau', color: '#D85A30' }, { id: 'strength', icon: '💪', label: 'Kraftaufbau', description: 'Muskelwachstum, progressive Belastung', color: '#378ADD' }, { id: 'endurance', icon: '🏃', label: 'Ausdauer', description: 'VO2Max, aerobe Kapazität', color: '#1D9E75' }, { id: 'recomposition', icon: '⚖️', label: 'Körperkomposition', description: 'Gleichzeitig Fett ab- & Muskeln aufbauen', color: '#7B68EE' }, { id: 'health', icon: '❤️', label: 'Allgemeine Gesundheit', description: 'Ausgewogen, präventiv', color: '#E67E22' } ] export default function GoalsPage() { const [goalMode, setGoalMode] = useState(null) const [focusAreas, setFocusAreas] = useState(null) const [focusEditing, setFocusEditing] = useState(false) const [focusTemp, setFocusTemp] = useState({ weight_loss_pct: 0, muscle_gain_pct: 0, strength_pct: 0, endurance_pct: 0, flexibility_pct: 0, health_pct: 0 }) const [goals, setGoals] = useState([]) const [goalTypes, setGoalTypes] = useState([]) // Dynamic from DB (Phase 1.5) const [goalTypesMap, setGoalTypesMap] = useState({}) // For quick lookup const [showGoalForm, setShowGoalForm] = useState(false) const [editingGoal, setEditingGoal] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [toast, setToast] = useState(null) // Form state const [formData, setFormData] = useState({ goal_type: 'weight', is_primary: false, target_value: '', unit: 'kg', target_date: '', name: '', description: '' }) useEffect(() => { loadData() }, []) const loadData = async () => { setLoading(true) setError(null) try { const [modeData, goalsData, typesData, focusData] = await Promise.all([ api.getGoalMode(), api.listGoals(), api.listGoalTypeDefinitions(), // Phase 1.5: Load from DB api.getFocusAreas() // v2.0: Load focus areas ]) setGoalMode(modeData.goal_mode) setGoals(goalsData) setFocusAreas(focusData) setFocusTemp(focusData) // Initialize temp state // Convert types array to map for quick lookup const typesMap = {} if (typesData && Array.isArray(typesData)) { typesData.forEach(type => { typesMap[type.type_key] = { label: type.label_de, unit: type.unit, icon: type.icon || '📊', category: type.category, is_system: type.is_system } }) } setGoalTypes(typesData || []) setGoalTypesMap(typesMap) } catch (err) { console.error('Failed to load goals:', err) setError(`Fehler beim Laden: ${err.message || err.toString()}`) } finally { setLoading(false) } } const showToast = (message, duration = 2000) => { setToast(message) setTimeout(() => setToast(null), duration) } const handleGoalModeChange = async (newMode) => { try { await api.updateGoalMode(newMode) setGoalMode(newMode) showToast('✓ Trainingsmodus aktualisiert') } catch (err) { console.error('Failed to update goal mode:', err) setError('Fehler beim Aktualisieren des Trainingsmodus') } } const handleCreateGoal = () => { if (goalTypes.length === 0) { setError('Keine Goal Types verfügbar. Bitte Admin kontaktieren.') return } setEditingGoal(null) const firstType = goalTypes[0].type_key setFormData({ goal_type: firstType, is_primary: goals.length === 0, // First goal is primary by default target_value: '', unit: goalTypesMap[firstType]?.unit || 'kg', target_date: '', name: '', description: '' }) setShowGoalForm(true) } const handleEditGoal = (goal) => { setEditingGoal(goal.id) setFormData({ goal_type: goal.goal_type, is_primary: goal.is_primary, target_value: goal.target_value, unit: goal.unit, target_date: goal.target_date || '', name: goal.name || '', description: goal.description || '' }) setShowGoalForm(true) } const handleGoalTypeChange = (type) => { setFormData(f => ({ ...f, goal_type: type, unit: goalTypesMap[type]?.unit || 'unit' })) } const handleSaveGoal = async () => { if (!formData.target_value) { setError('Bitte Zielwert eingeben') return } try { const data = { goal_type: formData.goal_type, is_primary: formData.is_primary, target_value: parseFloat(formData.target_value), unit: formData.unit, target_date: formData.target_date || null, name: formData.name || null, description: formData.description || null } console.log('[DEBUG] Saving goal:', { editingGoal, data }) if (editingGoal) { await api.updateGoal(editingGoal, data) showToast('✓ Ziel aktualisiert') } else { await api.createGoal(data) showToast('✓ Ziel erstellt') } await loadData() setShowGoalForm(false) setEditingGoal(null) } catch (err) { console.error('Failed to save goal:', err) setError(err.message || 'Fehler beim Speichern') } } const handleDeleteGoal = async (goalId) => { if (!confirm('Ziel wirklich löschen?')) return try { await api.deleteGoal(goalId) showToast('✓ Ziel gelöscht') await loadData() } catch (err) { console.error('Failed to delete goal:', err) setError('Fehler beim Löschen') } } const getProgressColor = (progress) => { if (progress >= 100) return 'var(--accent)' if (progress >= 75) return '#1D9E75' if (progress >= 50) return '#378ADD' if (progress >= 25) return '#E67E22' return '#D85A30' } if (loading) { return (
) } return (

Ziele

{error && (

{error}

)} {toast && (
{toast}
)} {/* Focus Areas (v2.0) */}

🎯 Fokus-Bereiche

{!focusEditing && focusAreas && ( )}

Setze relative Gewichte für deine Trainingsziele. Das System berechnet automatisch die Prozentanteile. {focusAreas && !focusAreas.custom && ( ℹ️ Aktuell abgeleitet aus Trainingsmodus "{goalMode}" - klicke "Anpassen" für individuelle Gewichtung )}

{focusEditing ? ( <> {/* Sliders */}
{[ { key: 'weight_loss_pct', label: 'Fettabbau', icon: '📉', color: '#D85A30' }, { key: 'muscle_gain_pct', label: 'Muskelaufbau', icon: '💪', color: '#378ADD' }, { key: 'strength_pct', label: 'Kraftsteigerung', icon: '🏋️', color: '#7B68EE' }, { key: 'endurance_pct', label: 'Ausdauer', icon: '🏃', color: '#1D9E75' }, { 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 return (
{area.icon} {area.label}
{weight} → {actualPercent}%
setFocusTemp(f => ({ ...f, [area.key]: parseInt(e.target.value) * 10 }))} style={{ width: '100%', height: 8, borderRadius: 4, background: `linear-gradient(to right, ${area.color} 0%, ${area.color} ${weight * 10}%, var(--border) ${weight * 10}%, var(--border) 100%)`, outline: 'none', cursor: 'pointer' }} />
) })}
{/* Weight Total Display */}
Gewichtung gesamt: {Object.values(focusTemp).reduce((a, b) => a + b, 0) / 10}
💡 Die Prozentanteile werden automatisch berechnet
{/* Action Buttons */}
) : focusAreas && ( /* Display Mode */
{[ { key: 'weight_loss_pct', label: 'Fettabbau', icon: '📉', color: '#D85A30' }, { key: 'muscle_gain_pct', label: 'Muskelaufbau', icon: '💪', color: '#378ADD' }, { key: 'strength_pct', label: 'Kraftsteigerung', icon: '🏋️', color: '#7B68EE' }, { key: 'endurance_pct', label: 'Ausdauer', icon: '🏃', color: '#1D9E75' }, { key: 'flexibility_pct', label: 'Beweglichkeit', icon: '🤸', color: '#E67E22' }, { key: 'health_pct', label: 'Gesundheit', icon: '❤️', color: '#F59E0B' } ].filter(area => focusAreas[area.key] > 0).map(area => (
{area.icon}
{area.label}
{focusAreas[area.key]}%
))}
)}
{/* Tactical Goals List */}

🎯 Konkrete Ziele

{goals.length === 0 ? (

Noch keine Ziele definiert

) : (
{goals.map(goal => { const typeInfo = goalTypesMap[goal.goal_type] || { label: goal.goal_type, unit: '', icon: '📊' } return (
{typeInfo.icon} {goal.name || typeInfo.label} {goal.is_primary && ( PRIMÄR )} {goal.status === 'active' ? 'AKTIV' : goal.status?.toUpperCase()}
Start:{' '} {goal.start_value} {goal.unit}
Aktuell:{' '} {goal.current_value || '—'} {goal.unit}
Ziel:{' '} {goal.target_value} {goal.unit}
{goal.target_date && (
{dayjs(goal.target_date).format('DD.MM.YYYY')}
)}
{goal.progress_pct !== null && (
Fortschritt {goal.progress_pct}%
{goal.on_track !== null && (
{goal.on_track ? ( ✓ Ziel voraussichtlich erreichbar bis {dayjs(goal.target_date).format('DD.MM.YYYY')} ) : ( ⚠ Prognose: {goal.projection_date ? dayjs(goal.projection_date).format('DD.MM.YYYY') : 'Offen'} {goal.target_date && ' (später als geplant)'} )}
)}
)}
) })}
)}
{/* Goal Form Modal */} {showGoalForm && (
{editingGoal ? 'Ziel bearbeiten' : 'Neues Ziel'}
{error && (
{error}
)} {/* Zieltyp */}
{/* Warning for incomplete goal types */} {['bp', 'strength', 'flexibility'].includes(formData.goal_type) && (
⚠️ Dieser Zieltyp ist aktuell eingeschränkt: {formData.goal_type === 'bp' && ' Blutdruck benötigt 2 Werte (geplant für v2.0)'} {formData.goal_type === 'strength' && ' Keine Datenquelle vorhanden (geplant für v2.0)'} {formData.goal_type === 'flexibility' && ' Keine Datenquelle vorhanden (geplant für v2.0)'}
)}
{/* Name */}
setFormData(f => ({ ...f, name: e.target.value }))} placeholder="z.B. Sommerfigur 2026" />
{/* Zielwert */}
🎯 Zielwert
setFormData(f => ({ ...f, target_value: e.target.value }))} placeholder="Zielwert eingeben" />
{formData.unit}
{/* Zieldatum */}
setFormData(f => ({ ...f, target_date: e.target.value }))} />
{/* Beschreibung */}