From 22c5f695c9251818a09990bedae5e2164f2aa9b5 Mon Sep 17 00:00:00 2001 From: Lars Date: Sun, 19 Apr 2026 21:37:12 +0200 Subject: [PATCH] refactor: update fitness dashboard integration and terminology - Changed the fitness overview path from `/activity` to `/history` and updated related navigation labels to reflect this change. - Refactored the `FitnessDashboardOverview` component to accept external period control and conditionally display the period selector. - Integrated the `FitnessDashboardOverview` into the `History` page, enhancing the user experience with consistent terminology and layout. - Removed the fitness overview from the `ActivityPage` to streamline the interface and focus on activity data collection. --- .../issues/issue-fitness-dashboard-layer2b.md | 6 +-- .../components/FitnessDashboardOverview.jsx | 49 +++++++++++++------ frontend/src/config/captureNav.js | 2 +- frontend/src/pages/ActivityPage.jsx | 8 +-- frontend/src/pages/History.jsx | 30 +++++++++--- 5 files changed, 60 insertions(+), 35 deletions(-) diff --git a/docs/issues/issue-fitness-dashboard-layer2b.md b/docs/issues/issue-fitness-dashboard-layer2b.md index 57dbb3f..d18ce8f 100644 --- a/docs/issues/issue-fitness-dashboard-layer2b.md +++ b/docs/issues/issue-fitness-dashboard-layer2b.md @@ -8,7 +8,7 @@ ## Ziel -- Eine **Fitness-Übersicht** auf `/activity` (Capture-Hub: „Fitness“), die **keine parallelen Berechnungen** im Client führt. +- Eine **Fitness-Übersicht** auf **`/history`** (Tab Fitness), analog Körper/Ernährung — **keine parallelen Berechnungen** im Client für Layer 2b. - **Single Source of Truth:** `data_layer/activity_metrics` (und Scores/Focus wie bei den Platzhaltern), identische Chart-Payloads wie die bestehenden Chart-Endpunkte A1/A2. --- @@ -35,8 +35,8 @@ |-------------|------| | API-Client | `getFitnessDashboardViz(days)` in `frontend/src/utils/api.js` | | Darstellung | `frontend/src/components/FitnessDashboardOverview.jsx` | -| Einbindung | `frontend/src/pages/ActivityPage.jsx` (oben, vor Tabs) | -| Navigation Capture | `frontend/src/config/captureNav.js` – Label **Fitness**, Route `/activity` | +| Einbindung | `frontend/src/pages/History.jsx` → `ActivitySection` (gemeinsamer `PeriodSelector` wie die Liste darunter) | +| Erfassung | `/activity` bleibt reine Erfassung; Capture-Hub-Label **Aktivität** | --- diff --git a/frontend/src/components/FitnessDashboardOverview.jsx b/frontend/src/components/FitnessDashboardOverview.jsx index 227479f..b321d87 100644 --- a/frontend/src/components/FitnessDashboardOverview.jsx +++ b/frontend/src/components/FitnessDashboardOverview.jsx @@ -22,9 +22,20 @@ const PERIODS = [ /** * Layer 2b: Kennzahlen und Charts nur aus GET /api/charts/fitness-dashboard-viz (activity_metrics). + * + * @param {number} [period] – gesteuert von außen (z. B. Verlauf `PeriodSelector`); mit `onPeriodChange` koppeln. + * @param {(n: number) => void} [onPeriodChange] + * @param {boolean} [hidePeriodSelector] – eigenes Zeitraum-Dropdown ausblenden (wenn die Seite oben schon einen Zeitraum wählt). */ -export default function FitnessDashboardOverview() { - const [period, setPeriod] = useState(28) +export default function FitnessDashboardOverview({ + period: periodProp, + onPeriodChange, + hidePeriodSelector = false, +}) { + const [internalPeriod, setInternalPeriod] = useState(28) + const controlled = periodProp !== undefined && typeof onPeriodChange === 'function' + const period = controlled ? periodProp : internalPeriod + const setPeriod = controlled ? onPeriodChange : setInternalPeriod const [viz, setViz] = useState(null) const [loading, setLoading] = useState(true) const [err, setErr] = useState(null) @@ -104,25 +115,31 @@ export default function FitnessDashboardOverview() { const wUsed = viz.training_volume_weeks_used const dTyp = viz.training_type_dist_days_used + const showPeriodDropdown = !hidePeriodSelector && !controlled + return (
Fitness-Übersicht - + Zeitraum + + + ) : null}

diff --git a/frontend/src/config/captureNav.js b/frontend/src/config/captureNav.js index 64314da..29bab26 100644 --- a/frontend/src/config/captureNav.js +++ b/frontend/src/config/captureNav.js @@ -56,7 +56,7 @@ export const CAPTURE_HUB_TILES = [ }, { icon: '🏋️', - label: 'Fitness', + label: 'Aktivität', sub: 'Training manuell oder Apple Health importieren', to: '/activity', color: '#D4537E', diff --git a/frontend/src/pages/ActivityPage.jsx b/frontend/src/pages/ActivityPage.jsx index 85d05ab..45a872c 100644 --- a/frontend/src/pages/ActivityPage.jsx +++ b/frontend/src/pages/ActivityPage.jsx @@ -3,7 +3,6 @@ import { Upload, Pencil, Trash2, Check, X, CheckCircle } from 'lucide-react' import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts' import { api } from '../utils/api' import UsageBadge from '../components/UsageBadge' -import FitnessDashboardOverview from '../components/FitnessDashboardOverview' import TrainingTypeSelect from '../components/TrainingTypeSelect' import BulkCategorize from '../components/BulkCategorize' import dayjs from 'dayjs' @@ -913,12 +912,7 @@ export default function ActivityPage() { return (

-

Fitness

-

- Auswertung (Data-Layer) und Erfassung an einem Ort. -

- - +

Aktivität

diff --git a/frontend/src/pages/History.jsx b/frontend/src/pages/History.jsx index 2940aec..be6c619 100644 --- a/frontend/src/pages/History.jsx +++ b/frontend/src/pages/History.jsx @@ -14,6 +14,7 @@ import { getStatusColor, getStatusBg } from '../utils/interpret' import { MACRO_CHART, macroFillByName, NUTRITION_MACRO_CHART_BLOCK_PX } from '../utils/macroChartTheme' import Markdown from '../utils/Markdown' import TrainingTypeDistribution from '../components/TrainingTypeDistribution' +import FitnessDashboardOverview from '../components/FitnessDashboardOverview' import NutritionCharts, { WeeklyMacroDistributionPanel } from '../components/NutritionCharts' import RecoveryCharts from '../components/RecoveryCharts' import KpiTilesOverview from '../components/KpiTilesOverview' @@ -1097,16 +1098,14 @@ function NutritionSection({ profile, insights, onRequest, loadingSlug, filterAct ) } -// ── Activity Section ────────────────────────────────────────────────────────── +// ── Activity Section — Layer 2b Fitness-Bundle wie Körper/Ernährung auf /history ─ function ActivitySection({ activities, insights, onRequest, loadingSlug, filterActiveSlugs, globalQualityLevel }) { const [period, setPeriod] = useState(30) - if (!activities?.length) return ( - - ) const cutoff = dayjs().subtract(period,'day').format('YYYY-MM-DD') // Issue #31: Backend already filters by global quality level - only filter by period here - const filtA = activities.filter(d => period === 9999 || d.date >= cutoff) + const actList = activities || [] + const filtA = actList.filter(d => period === 9999 || d.date >= cutoff) const byDate={} filtA.forEach(a=>{ byDate[a.date]=(byDate[a.date]||0)+(a.kcal_active||0) }) @@ -1130,13 +1129,24 @@ function ActivitySection({ activities, insights, onRequest, loadingSlug, filterA value:consistency+'%' }] + const hasList = actList.length > 0 + return (
- + +

+ Fitness-Kennzahlen und Diagramme (Layer 2b) kommen aus dem Aktivitäts-Data-Layer — dieselbe Quelle wie die + KI-Platzhalter. Zeitraum gilt auch für die Liste unten. +

+ + + {!hasList ? ( + + ) : null} {/* Issue #31: Show active global quality filter */} - {globalQualityLevel && globalQualityLevel !== 'all' && ( + {hasList && globalQualityLevel && globalQualityLevel !== 'all' && (
)} + {!hasList ? null : ( + <>
{[['Trainings',filtA.length,'var(--text1)'],['Kcal',totalKcal,'#EF9F27'], ['Stunden',Math.round(totalMin/60*10)/10,'#378ADD'], @@ -1186,7 +1198,7 @@ function ActivitySection({ activities, insights, onRequest, loadingSlug, filterA
{type}
{count}×
-
+
))}
@@ -1199,6 +1211,8 @@ function ActivitySection({ activities, insights, onRequest, loadingSlug, filterA {actRules.map((item,i)=>)}
+ + )}
) }