diff --git a/backend/routers/goals.py b/backend/routers/goals.py index 5f99e0b..c3da8be 100644 --- a/backend/routers/goals.py +++ b/backend/routers/goals.py @@ -699,6 +699,12 @@ def get_goals_grouped(session: dict = Depends(require_auth)): grouped[cat] = [] goal_dict = r2d(goal) + # Keep in sync with GET /goals/list: derive current_value & progress from live data + try: + _update_goal_progress(conn, pid, goal_dict) + except Exception as e: + print(f"[ERROR] Failed to update progress for goal {goal_dict.get('id')}: {e}") + goal_dict['focus_contributions'] = focus_map.get(goal['id'], []) grouped[cat].append(goal_dict) diff --git a/frontend/src/pages/GoalsPage.jsx b/frontend/src/pages/GoalsPage.jsx index fbc7288..64a56d5 100644 --- a/frontend/src/pages/GoalsPage.jsx +++ b/frontend/src/pages/GoalsPage.jsx @@ -55,6 +55,28 @@ const getCategoryForGoalType = (goalType) => { return mapping[goalType] || 'other' } +/** Fortschritt in % von Start zum Ziel; bevorzugt `progress_pct` vom Server, sonst aus Ist-Werten. */ +function getGoalProgressPercent(goal) { + const apiVal = goal?.progress_pct + if (apiVal !== null && apiVal !== undefined && Number.isFinite(Number(apiVal))) { + return Number(apiVal) + } + const s = goal?.start_value != null ? Number(goal.start_value) : NaN + const t = goal?.target_value != null ? Number(goal.target_value) : NaN + const c = + goal?.current_value != null && goal.current_value !== '' + ? Number(goal.current_value) + : NaN + if (!Number.isFinite(s) || !Number.isFinite(t) || !Number.isFinite(c)) return null + const span = Math.abs(t - s) + if (span < 1e-9) { + if (Math.abs(c - s) < 1e-9) return 100 + return null + } + if (t > s) return ((c - s) / (t - s)) * 100 + return ((s - c) / (s - t)) * 100 +} + export default function GoalsPage() { const [goalMode, setGoalMode] = useState(null) const [userFocusWeights, setUserFocusWeights] = useState([]) // v2.0: User's focus area weights @@ -613,6 +635,17 @@ export default function GoalsPage() { {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] + const progressPct = getGoalProgressPercent(goal) + const progressLabel = + progressPct != null + ? (Math.abs(progressPct - Math.round(progressPct)) < 0.05 + ? `${Math.round(progressPct)}` + : `${Math.round(progressPct * 10) / 10}`.replace('.', ',')) + : null + const progressBarWidth = + progressPct != null + ? Math.min(100, Math.max(0, progressPct)) + : 0 return (