From ea2ce8697ed20f97c84bd70f7663a90d85cd97fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 5 Jun 2026 12:20:54 +0200 Subject: [PATCH] insights: lead every metric subpage with the stat strip above the chart Render the Min/Max/Median/Mittelwert stat strip directly above the chart on the canonical spine (intro -> stats -> chart -> target -> assessment), the numbers-first detail-screen convention. Wire the series headings on each subpage: blood pressure shows the full four-stat grid for both systolic and diastolic as labelled blocks, single-series pages pass the metric name and icon. Leave a marker for chart-brush-reactive stats as a follow-up. --- .../__tests__/assessment-spine-order.test.ts | 11 +++--- src/app/insights/blood-pressure/page.tsx | 4 ++- src/app/insights/pulse/page.tsx | 9 ++++- src/app/insights/weight/page.tsx | 9 ++++- src/components/insights/sub-page-shell.tsx | 36 ++++++++++++------- 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/app/insights/__tests__/assessment-spine-order.test.ts b/src/app/insights/__tests__/assessment-spine-order.test.ts index d73973512..acde8bac2 100644 --- a/src/app/insights/__tests__/assessment-spine-order.test.ts +++ b/src/app/insights/__tests__/assessment-spine-order.test.ts @@ -5,10 +5,13 @@ import { join } from "node:path"; /** * v1.12.4 — canonical metric-detail spine guard. * - * The unified subpage spine is: intro → chart → target card → assessment → - * stat strip. The assessment is the LAST content block the page hands to - * `` as `children`; the shell appends the Min/Max/Median/Mean - * stat strip below it. The generic `HealthKitMetricPage` scaffold enforces + * v1.12.6 — the unified subpage spine is: intro → stat strip → chart → + * target card → assessment. The stat strip moved ABOVE the chart (the + * shell renders the `statStrip` prop before `children`); the assessment + * stays the LAST content block the page hands to `` as + * `children`. This source-order guard is unaffected by the render-position + * move because `statStrip` is a prop on the opening tag and the assessment + * is still the final child element. The generic `HealthKitMetricPage` scaffold enforces * this for the ~29 HealthKit pages structurally; the bespoke pages each * hand-write their body, so a future edit can silently slot a card after the * assessment (as `mood` and `medications` did before). diff --git a/src/app/insights/blood-pressure/page.tsx b/src/app/insights/blood-pressure/page.tsx index 875cdb12b..e6369be8e 100644 --- a/src/app/insights/blood-pressure/page.tsx +++ b/src/app/insights/blood-pressure/page.tsx @@ -1,7 +1,7 @@ "use client"; import Link from "next/link"; -import { HeartPulse } from "lucide-react"; +import { ArrowDownRight, ArrowUpRight, HeartPulse } from "lucide-react"; import { useAuth } from "@/hooks/use-auth"; import { useInsightsAnalytics } from "@/hooks/use-insights-analytics"; @@ -112,12 +112,14 @@ export default function InsightsBlutdruckPage() { unit="mmHg" fractionDigits={0} seriesLabel={t("charts.systolic")} + icon={ArrowUpRight} /> } diff --git a/src/app/insights/pulse/page.tsx b/src/app/insights/pulse/page.tsx index 5ee3b6531..a6ce89b51 100644 --- a/src/app/insights/pulse/page.tsx +++ b/src/app/insights/pulse/page.tsx @@ -113,7 +113,14 @@ export default function InsightsPulsPage() { title={t("insights.pulseSectionTitle")} description={t("insights.subPage.pulsDescription")} explainerMetric="pulse" - statStrip={} + statStrip={ + + } diversityNudge={ } + statStrip={ + + } diversityNudge={ `; the strip + * v1.12.6 — the Min / Max / Median / Mittelwert stat strip. It now leads + * the canonical spine, directly ABOVE the chart, so every metric subpage + * reads the same: intro → stat strip → chart → target → assessment. + * Numbers-first matches how Apple Health / Withings / Oura lead a detail + * screen. Sub-pages pass ``; the strip * self-gates (renders nothing without data), so the empty-state and * brand-new-metric paths stay clean. Omitted on the empty-state branch. */ @@ -255,17 +256,26 @@ export function SubPageShell({

{description}

) : null} - {/* v1.12.4 — the canonical spine body: intro (header, above) → - chart → target card → assessment, all rendered by the page as - `children`. */} - {children} - {/* v1.12.4 — the Min / Max / Median / Mittelwert stat strip closes - the spine, below the assessment. Self-gating (renders nothing + {/* v1.12.6 — the Min / Max / Median / Mittelwert stat strip LEADS + the spine, directly above the chart. Self-gating (renders nothing without data), so the spacing rhythm holds on the empty-state and - brand-new-metric paths. The diversity nudge rides the heading row; - the headline tile and the "Letzte Messung" card are retired so the - subpage is identical by construction across every metric. */} + brand-new-metric paths. Numbers-first is the Apple-Health / + Withings / Oura detail-screen convention. + + TODO(v1.12.7): chart-brush-reactive stats. When the user brushes / + selects a window in the chart below, recompute the stat strip's + Min/Max/Median/Mean for the visible domain. Deferred: the shared + `` (~1900 LOC, also driving the dashboard) has no + Recharts `` or lifted domain-change callback today, and its + range state lives internally; wiring a stable brush + lifting the + selected domain into this shell so both the chart and the strip read + it is a larger change than this release's scope warrants without + destabilising the chart. */} {statStrip} + {/* v1.12.6 — the canonical spine body: intro (header) → stat strip + (above) → chart → target card → assessment. The page renders + chart → target → assessment as `children`. */} + {children} {/* v1.8.5 W4b — "show all readings" entry at the foot, linking to the dedicated per-metric values subpage. v1.8.6 — normalised to the `h-10` secondary-button height so it