From caebc37da0eb1b7e8a41739faceab70c0374fb82 Mon Sep 17 00:00:00 2001 From: Lars Date: Fri, 27 Mar 2026 12:33:17 +0100 Subject: [PATCH] feat: goal categories UI - complete rebuild MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completed frontend for multi-dimensional goal priorities. **UI Changes:** - Category-grouped goal display with color-coded headers - Each category shows: icon, name, description, goal count - Priority stars (⭐⭐⭐/⭐⭐/⭐) replace "PRIMÄR" badge - Goals have category-colored left border - Form fields: Category dropdown + Priority selector - Removed "Gewichtung gesamt" display (useless UX) **Categories:** - 📉 Körper (body): Gewicht, Körperfett, Muskelmasse - 🏋️ Training: Kraft, Frequenz, Performance - 🍎 Ernährung: Kalorien, Makros, Essgewohnheiten - 😴 Erholung: Schlaf, Regeneration, Ruhetage - ❤️ Gesundheit: Vitalwerte, Blutdruck, HRV - 📌 Sonstiges: Weitere Ziele **Priority Levels:** - ⭐⭐⭐ Hoch (1) - ⭐⭐ Mittel (2) - ⭐ Niedrig (3) **Implementation:** - Load groupedGoals via api.listGoalsGrouped() - GOAL_CATEGORIES + PRIORITY_LEVELS constants - handleEditGoal/handleSaveGoal/handleCreateGoal extended - Backward compatible (is_primary still exists) Next: Test migration + UI, then update Dashboard to show top-1 per category Co-Authored-By: Claude Opus 4.6 --- frontend/src/pages/GoalsPage.jsx | 430 ++++++++++++++++--------------- 1 file changed, 221 insertions(+), 209 deletions(-) diff --git a/frontend/src/pages/GoalsPage.jsx b/frontend/src/pages/GoalsPage.jsx index d65925c..96d4d4e 100644 --- a/frontend/src/pages/GoalsPage.jsx +++ b/frontend/src/pages/GoalsPage.jsx @@ -5,44 +5,22 @@ 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' - } -] +// Goal Categories +const GOAL_CATEGORIES = { + body: { label: 'Körper', icon: '📉', color: '#D85A30', description: 'Gewicht, Körperfett, Muskelmasse' }, + training: { label: 'Training', icon: '🏋️', color: '#378ADD', description: 'Kraft, Frequenz, Performance' }, + nutrition: { label: 'Ernährung', icon: '🍎', color: '#1D9E75', description: 'Kalorien, Makros, Essgewohnheiten' }, + recovery: { label: 'Erholung', icon: '😴', color: '#7B68EE', description: 'Schlaf, Regeneration, Ruhetage' }, + health: { label: 'Gesundheit', icon: '❤️', color: '#E67E22', description: 'Vitalwerte, Blutdruck, HRV' }, + other: { label: 'Sonstiges', icon: '📌', color: '#94A3B8', description: 'Weitere Ziele' } +} + +// Priority Levels +const PRIORITY_LEVELS = { + 1: { label: 'Hoch', stars: '⭐⭐⭐', color: 'var(--accent)' }, + 2: { label: 'Mittel', stars: '⭐⭐', color: '#94A3B8' }, + 3: { label: 'Niedrig', stars: '⭐', color: '#CBD5E1' } +} export default function GoalsPage() { const [goalMode, setGoalMode] = useState(null) @@ -56,7 +34,8 @@ export default function GoalsPage() { flexibility_pct: 0, health_pct: 0 }) - const [goals, setGoals] = useState([]) + const [goals, setGoals] = useState([]) // Kept for backward compat + const [groupedGoals, setGroupedGoals] = useState({}) // Category-grouped goals const [goalTypes, setGoalTypes] = useState([]) // Dynamic from DB (Phase 1.5) const [goalTypesMap, setGoalTypesMap] = useState({}) // For quick lookup const [showGoalForm, setShowGoalForm] = useState(false) @@ -69,6 +48,8 @@ export default function GoalsPage() { const [formData, setFormData] = useState({ goal_type: 'weight', is_primary: false, + category: 'body', + priority: 2, target_value: '', unit: 'kg', target_date: '', @@ -84,14 +65,16 @@ export default function GoalsPage() { setLoading(true) setError(null) try { - const [modeData, goalsData, typesData, focusData] = await Promise.all([ + const [modeData, goalsData, groupedData, typesData, focusData] = await Promise.all([ api.getGoalMode(), api.listGoals(), + api.listGoalsGrouped(), // v2.1: Load grouped by category api.listGoalTypeDefinitions(), // Phase 1.5: Load from DB api.getFocusAreas() // v2.0: Load focus areas ]) setGoalMode(modeData.goal_mode) setGoals(goalsData) + setGroupedGoals(groupedData) // Ensure all focus fields are present and numeric const sanitizedFocus = { @@ -158,6 +141,8 @@ export default function GoalsPage() { setFormData({ goal_type: firstType, is_primary: goals.length === 0, // First goal is primary by default + category: 'body', + priority: 2, target_value: '', unit: goalTypesMap[firstType]?.unit || 'kg', target_date: '', @@ -172,6 +157,8 @@ export default function GoalsPage() { setFormData({ goal_type: goal.goal_type, is_primary: goal.is_primary, + category: goal.category || 'other', + priority: goal.priority || 2, target_value: goal.target_value, unit: goal.unit, target_date: goal.target_date || '', @@ -199,6 +186,8 @@ export default function GoalsPage() { const data = { goal_type: formData.goal_type, is_primary: formData.is_primary, + category: formData.category, + priority: formData.priority, target_value: parseFloat(formData.target_value), unit: formData.unit, target_date: formData.target_date || null, @@ -367,32 +356,6 @@ export default function GoalsPage() { })} - {/* Weight Total Display */} -
-
- - Gewichtung gesamt: - - - {(() => { - const total = Object.entries(focusTemp) - .filter(([k]) => k.endsWith('_pct')) - .reduce((acc, [k, v]) => acc + (Number(v) || 0), 0) - return (total / 10).toFixed(1) - })()} - -
-
- 💡 Die Prozentanteile werden automatisch berechnet -
-
- {/* Action Buttons */}
- {goals.length === 0 ? ( + {Object.keys(groupedGoals).length === 0 ? (

Noch keine Ziele definiert

@@ -500,128 +463,150 @@ export default function GoalsPage() {
) : ( -
- {goals.map(goal => { - const typeInfo = goalTypesMap[goal.goal_type] || { label: goal.goal_type, unit: '', icon: '📊' } +
+ {Object.entries(GOAL_CATEGORIES).map(([catKey, catInfo]) => { + const categoryGoals = groupedGoals[catKey] || [] + if (categoryGoals.length === 0) return null + return ( -
-
+
+ {/* Category Header */} +
+ {catInfo.icon}
-
- {typeInfo.icon} - - {goal.name || typeInfo.label} - - {goal.is_primary && ( - - PRIMÄR - - )} - - {goal.status === 'active' ? 'AKTIV' : goal.status?.toUpperCase()} - -
+

{catInfo.label}

+

{catInfo.description}

+
+ + {categoryGoals.length} {categoryGoals.length === 1 ? 'Ziel' : 'Ziele'} + +
-
-
- 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')} -
- )} -
+ {/* Goals in Category */} +
+ {categoryGoals.map(goal => { + const typeInfo = goalTypesMap[goal.goal_type] || { label_de: goal.goal_type, unit: '', icon: '📊' } + const priorityInfo = PRIORITY_LEVELS[goal.priority] || PRIORITY_LEVELS[2] - {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')} + return ( +
+
+
+
+ {typeInfo.icon} + + {goal.name || typeInfo.label_de} - ) : ( - - ⚠ Prognose: {goal.projection_date ? dayjs(goal.projection_date).format('DD.MM.YYYY') : 'Offen'} - {goal.target_date && ' (später als geplant)'} + + {priorityInfo.stars} + + {goal.status === 'active' ? 'AKTIV' : goal.status?.toUpperCase() || 'AKTIV'} + +
+ +
+
+ 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}% +
+
+
+
+
)}
- )} -
- )} -
-
- - -
+
+ + +
+
+
+ ) + })}
) @@ -817,34 +802,61 @@ export default function GoalsPage() { />
- {/* Primärziel */} -
- + {/* Category & Priority */} +
+ 📂 Kategorisierung +
+ +
+ {/* Category */} +
+ + +
+ + {/* Priority */} +
+ + +
{/* Buttons */}