diff --git a/backend/data_layer/fitness_viz.py b/backend/data_layer/fitness_viz.py index 9c8ae03..fbf5446 100644 --- a/backend/data_layer/fitness_viz.py +++ b/backend/data_layer/fitness_viz.py @@ -59,6 +59,15 @@ def _last_activity_date(profile_id: str) -> Optional[str]: return _iso(row["d"]) +def get_activity_last_updated_iso(profile_id: str) -> Optional[str]: + """ + Leichtgewicht: letztes activity_log.date — identisch zu ``last_updated`` im Fitness-Viz-Bundle. + + Für History-Header o. Ä. ohne vollständige Aktivitätsliste (Phase A, Issue-53-Pfad). + """ + return _last_activity_date(profile_id) + + def get_fitness_dashboard_viz_bundle(profile_id: str, days: int) -> Dict[str, Any]: """ Bundle für Fitness-Übersicht: KPI-Kacheln + eingebettete Chart-Payloads (Chart.js-Format). diff --git a/backend/routers/charts.py b/backend/routers/charts.py index eed8e92..fd42033 100644 --- a/backend/routers/charts.py +++ b/backend/routers/charts.py @@ -33,7 +33,7 @@ from data_layer.body_metrics import ( ) from data_layer.body_viz import get_body_history_viz_bundle from data_layer.nutrition_viz import get_nutrition_history_viz_bundle -from data_layer.fitness_viz import get_fitness_dashboard_viz_bundle +from data_layer.fitness_viz import get_fitness_dashboard_viz_bundle, get_activity_last_updated_iso from data_layer.recovery_viz import get_recovery_dashboard_viz_bundle from data_layer.history_overview_viz import get_history_overview_viz_bundle from data_layer.recovery_chart_payloads import ( @@ -319,6 +319,17 @@ def get_fitness_dashboard_viz( return serialize_dates(bundle) +@router.get("/activity-last-updated") +def get_activity_last_updated(session: dict = Depends(require_auth)) -> Dict: + """ + Minimal-Metadatum: letztes Trainingsdatum — gleiche Quelle wie ``last_updated`` im Fitness-Viz-Bundle. + + Vermeidet Massen-Ladevorgänge (z. B. listActivity) nur für Datumsanzeige im Verlauf. + """ + pid = session["profile_id"] + return {"last_activity_date": get_activity_last_updated_iso(pid)} + + @router.get("/recovery-dashboard-viz") def get_recovery_dashboard_viz( days: int = Query( diff --git a/backend/version.py b/backend/version.py index bb05fce..2e03bfe 100644 --- a/backend/version.py +++ b/backend/version.py @@ -7,8 +7,8 @@ Semantic Versioning: MAJOR.MINOR.PATCH - PATCH: Bugfix, kleine Änderung, Refactor """ -APP_VERSION = "0.9q" -BUILD_DATE = "2026-04-11" +APP_VERSION = "0.9r" +BUILD_DATE = "2026-04-20" DB_SCHEMA_VERSION = "20260409c" # 048/049 vitals_baseline.source csv + SAVEPOINT Import MODULE_VERSIONS = { @@ -36,6 +36,14 @@ MODULE_VERSIONS = { } CHANGELOG = [ + { + "version": "0.9r", + "date": "2026-04-20", + "changes": [ + "History Phase A: GET /api/charts/activity-last-updated (data_layer fitness_viz, gleiche Quelle wie last_updated im Fitness-Bundle)", + "History: entfernt toten Initial-Load listWeight/listCaliper/listCirc/listNutrition/listActivity(25k); Profil/Insights/Prompts + Activity-Datum", + ], + }, { "version": "0.9q", "date": "2026-04-11", diff --git a/frontend/src/pages/History.jsx b/frontend/src/pages/History.jsx index b0eb75a..fff5c14 100644 --- a/frontend/src/pages/History.jsx +++ b/frontend/src/pages/History.jsx @@ -1196,14 +1196,12 @@ function NutritionSection({ profile, insights, onRequest, loadingSlug, filterAct } // ── Activity Section — nur Layer-2b-Bundle (+ KI-Insights), keine parallelen Client-Charts ─ -function ActivitySection({ activities, insights, onRequest, loadingSlug, filterActiveSlugs, globalQualityLevel }) { +function ActivitySection({ activityLastDate, insights, onRequest, loadingSlug, filterActiveSlugs, globalQualityLevel }) { const [period, setPeriod] = useState(30) - const actList = activities || [] - const hasList = actList.length > 0 return (
- +

Fitness und Erholung aus den Data-Layer-Bundles (Issue 53). Zeitraum-Buttons steuern beide Bereiche gleichzeitig. @@ -1215,7 +1213,7 @@ function ActivitySection({ activities, insights, onRequest, loadingSlug, filterA

- {hasList && globalQualityLevel && globalQualityLevel !== 'all' && ( + {activityLastDate && globalQualityLevel && globalQualityLevel !== 'all' && (
Promise.all([ - api.listWeight(365), api.listCaliper(), api.listCirc(), - api.listNutrition(90), api.listActivity(25_000), - api.latestInsights(), api.getProfile(), + api.latestInsights(), + api.getProfile(), api.listPrompts(), - ]).then(([w,ca,ci,n,a,ins,p,pr])=>{ - setWeights(w); setCalipers(ca); setCircs(ci) - setNutrition(n); setActivities(a) - setInsights(Array.isArray(ins)?ins:[]); setProfile(p) - setPrompts(Array.isArray(pr)?pr:[]) + api.getActivityLastUpdated(), + ]).then(([ins, p, pr, actMeta]) => { + setInsights(Array.isArray(ins) ? ins : []) + setProfile(p) + setPrompts(Array.isArray(pr) ? pr : []) + setActivityLastDate(actMeta?.last_activity_date ?? null) setLoading(false) }) @@ -1820,7 +1814,7 @@ export default function History() { {tab==='overview' && } {tab==='body' && } {tab==='nutrition' && } - {tab==='activity' && } + {tab==='activity' && } {tab==='photos' && }
diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js index 505e354..80fba9c 100644 --- a/frontend/src/utils/api.js +++ b/frontend/src/utils/api.js @@ -644,6 +644,8 @@ export const api = { /** Layer 2b: Erholung — KPI, Insights, Charts R1–R5 (recovery_metrics) */ getRecoveryDashboardViz: (days=28) => req(`/charts/recovery-dashboard-viz?days=${days}`), getHistoryOverviewViz: (days=30) => req(`/charts/history-overview-viz?days=${days}`), + /** Minimal: letztes activity_log.date — wie fitness-dashboard-viz.last_updated */ + getActivityLastUpdated: () => req('/charts/activity-last-updated'), getWeightEnergyCorrelationChart: (maxLag=14) => req(`/charts/weight-energy-correlation?max_lag=${maxLag}`), getLbmProteinCorrelationChart: (maxLag=14) => req(`/charts/lbm-protein-correlation?max_lag=${maxLag}`), getLoadVitalsCorrelationChart: (maxLag=14) => req(`/charts/load-vitals-correlation?max_lag=${maxLag}`),