mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
feat(insights): link the overview Trends cards to their detail pages
The overview charted each trend but never offered a way into the metric's detail page — the steps page in particular exists and is used, yet Insights never linked to it. Give every Trends-row chart a routed detail target and a discreet "view details" link below the caption, so a metric the row surfaces (steps included, when the briefing flags it) drills into /insights/<slug>.
This commit is contained in:
1 parent
71b29f5b6f
commit
34fbfe1c67
10 files changed
+83
-7
No files matched your search
+2
-1
@@ -2029,7 +2029,8 @@
|
||||
"distance": "Geh- und Laufstrecke der letzten 30 Tage.",
|
||||
"vo2_max": "VO₂max der letzten 30 Tage.",
|
||||
"body_temp": "Körpertemperatur der letzten 30 Tage."
|
||||
}
|
||||
},
|
||||
"viewDetail": "{metric} im Detail"
|
||||
},
|
||||
"vo2Max": {
|
||||
"statLatest": "Aktuell",
|
||||
|
||||
+2
-1
@@ -2029,7 +2029,8 @@
|
||||
"distance": "Walking and running distance over the last 30 days.",
|
||||
"vo2_max": "VO₂ max over the last 30 days.",
|
||||
"body_temp": "Body temperature over the last 30 days."
|
||||
}
|
||||
},
|
||||
"viewDetail": "View {metric} details"
|
||||
},
|
||||
"vo2Max": {
|
||||
"statLatest": "Latest",
|
||||
|
||||
+2
-1
@@ -2029,7 +2029,8 @@
|
||||
"distance": "Distancia caminando y corriendo de los últimos 30 días.",
|
||||
"vo2_max": "VO₂ máx de los últimos 30 días.",
|
||||
"body_temp": "Temperatura corporal de los últimos 30 días."
|
||||
}
|
||||
},
|
||||
"viewDetail": "Ver detalles de {metric}"
|
||||
},
|
||||
"vo2Max": {
|
||||
"statLatest": "Actual",
|
||||
|
||||
+2
-1
@@ -2029,7 +2029,8 @@
|
||||
"distance": "Distance de marche et de course des 30 derniers jours.",
|
||||
"vo2_max": "VO₂ max des 30 derniers jours.",
|
||||
"body_temp": "Température corporelle des 30 derniers jours."
|
||||
}
|
||||
},
|
||||
"viewDetail": "Voir le détail de {metric}"
|
||||
},
|
||||
"vo2Max": {
|
||||
"statLatest": "Actuel",
|
||||
|
||||
+2
-1
@@ -2029,7 +2029,8 @@
|
||||
"distance": "Distanza a piedi e di corsa degli ultimi 30 giorni.",
|
||||
"vo2_max": "VO₂ max degli ultimi 30 giorni.",
|
||||
"body_temp": "Temperatura corporea degli ultimi 30 giorni."
|
||||
}
|
||||
},
|
||||
"viewDetail": "Vedi i dettagli di {metric}"
|
||||
},
|
||||
"vo2Max": {
|
||||
"statLatest": "Attuale",
|
||||
|
||||
+2
-1
@@ -2029,7 +2029,8 @@
|
||||
"distance": "Dystans marszu i biegu z ostatnich 30 dni.",
|
||||
"vo2_max": "VO₂ max z ostatnich 30 dni.",
|
||||
"body_temp": "Temperatura ciała z ostatnich 30 dni."
|
||||
}
|
||||
},
|
||||
"viewDetail": "Zobacz szczegóły: {metric}"
|
||||
},
|
||||
"vo2Max": {
|
||||
"statLatest": "Aktualne",
|
||||
|
||||
@@ -284,4 +284,22 @@ describe("<TrendsRow>", () => {
|
||||
expect(html).toContain("Moderate confidence");
|
||||
expect(html).toContain("Low confidence");
|
||||
});
|
||||
|
||||
// ── v1.18.6 — detail-page cross-links (the Insights→steps gap) ──────
|
||||
it("links each metric card to its detail page", () => {
|
||||
const html = render(<TrendsRow />);
|
||||
// The default BP / weight / mood triple each link to their sub-page.
|
||||
expect(html).toContain('data-slot="trends-row-detail-link"');
|
||||
expect(html).toContain('href="/insights/blood-pressure"');
|
||||
expect(html).toContain('href="/insights/weight"');
|
||||
expect(html).toContain('href="/insights/mood"');
|
||||
});
|
||||
|
||||
it("offers the steps detail link when the briefing flags steps", () => {
|
||||
// Steps has a routed sub-page but the overview never linked to it; a
|
||||
// briefing finding on steps now charts it AND drills into /insights/steps.
|
||||
const html = render(<TrendsRow briefing={briefing(["steps"])} />);
|
||||
expect(html).toMatch(/data-metric="steps"/);
|
||||
expect(html).toContain('href="/insights/steps"');
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { TrendingUp } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { ArrowRight, TrendingUp } from "lucide-react";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { ChartSkeleton } from "@/components/charts/chart-skeleton";
|
||||
@@ -301,6 +302,22 @@ export function TrendsRow({
|
||||
text={t(config.captionKey)}
|
||||
/>
|
||||
)}
|
||||
{/* v1.18.6 — drill-in link to the metric's detail page. The
|
||||
overview charted these metrics but never offered a way INTO
|
||||
their detail pages — notably the steps page, which exists and
|
||||
is used but was unreachable from Insights. The link sits below
|
||||
the caption so the chart keeps its own interactions. */}
|
||||
{config.detailHref ? (
|
||||
<Link
|
||||
href={config.detailHref}
|
||||
data-slot="trends-row-detail-link"
|
||||
data-metric={config.metric}
|
||||
className="text-muted-foreground hover:text-foreground mt-auto inline-flex items-center gap-1 self-start text-xs font-medium transition-colors"
|
||||
>
|
||||
{t("insights.trendsRow.viewDetail", { metric: title })}
|
||||
<ArrowRight className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -151,4 +151,17 @@ describe("selectTrendCharts", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("every chart-bearing sourceMetric carries a routed detailHref", () => {
|
||||
// v1.18.6 — the overview Trends row drills into the metric's detail page.
|
||||
// Each chartable slot must resolve to a `/insights/<slug>` target so the
|
||||
// cross-link never lands on a non-route — steps in particular.
|
||||
for (const [metric, config] of Object.entries(TREND_CHART_CONFIG)) {
|
||||
if (!config) continue;
|
||||
expect(config.detailHref, `${metric} detailHref`).toMatch(
|
||||
/^\/insights\//,
|
||||
);
|
||||
}
|
||||
expect(TREND_CHART_CONFIG.steps?.detailHref).toBe("/insights/steps");
|
||||
});
|
||||
});
|
||||
@@ -74,6 +74,15 @@ export interface TrendChartConfig {
|
||||
* for additive metrics, which fall back to {@link captionKey}.
|
||||
*/
|
||||
annotationKey?: TrendAnnotationKey;
|
||||
/**
|
||||
* Routed detail page for the metric (`/insights/<slug>`), when one exists.
|
||||
* The Trends-row card links to it so the overview can drill into the metric
|
||||
* — notably the one cross-link to `/insights/steps`, which the overview
|
||||
* otherwise never offered despite the steps page existing and being used.
|
||||
* Omitted for slots with no dedicated page (mood routes through its own
|
||||
* surface; metabolic re-frames live on their composite pages).
|
||||
*/
|
||||
detailHref?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,6 +109,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
titleKey: "charts.bloodPressure",
|
||||
captionKey: "insights.trendsRow.caption.bp",
|
||||
annotationKey: "bp",
|
||||
detailHref: "/insights/blood-pressure",
|
||||
},
|
||||
weight: {
|
||||
metric: "weight",
|
||||
@@ -110,6 +120,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
titleKey: "charts.weight",
|
||||
captionKey: "insights.trendsRow.caption.weight",
|
||||
annotationKey: "weight",
|
||||
detailHref: "/insights/weight",
|
||||
},
|
||||
pulse: {
|
||||
metric: "pulse",
|
||||
@@ -120,6 +131,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "bpm",
|
||||
titleKey: "charts.pulse",
|
||||
captionKey: "insights.trendsRow.caption.pulse",
|
||||
detailHref: "/insights/pulse",
|
||||
},
|
||||
mood: {
|
||||
metric: "mood",
|
||||
@@ -129,6 +141,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
titleKey: "charts.mood",
|
||||
captionKey: "insights.trendsRow.caption.mood",
|
||||
annotationKey: "mood",
|
||||
detailHref: "/insights/mood",
|
||||
},
|
||||
sleep: {
|
||||
metric: "sleep",
|
||||
@@ -139,6 +152,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "h",
|
||||
titleKey: "charts.sleep",
|
||||
captionKey: "insights.trendsRow.caption.sleep",
|
||||
detailHref: "/insights/sleep",
|
||||
},
|
||||
steps: {
|
||||
metric: "steps",
|
||||
@@ -149,6 +163,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "steps",
|
||||
titleKey: "charts.steps",
|
||||
captionKey: "insights.trendsRow.caption.steps",
|
||||
detailHref: "/insights/steps",
|
||||
},
|
||||
hrv: {
|
||||
metric: "hrv",
|
||||
@@ -159,6 +174,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "ms",
|
||||
titleKey: "charts.hrv",
|
||||
captionKey: "insights.trendsRow.caption.hrv",
|
||||
detailHref: "/insights/hrv",
|
||||
},
|
||||
resting_hr: {
|
||||
metric: "resting_hr",
|
||||
@@ -169,6 +185,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "bpm",
|
||||
titleKey: "charts.restingHeartRate",
|
||||
captionKey: "insights.trendsRow.caption.resting_hr",
|
||||
detailHref: "/insights/resting-pulse",
|
||||
},
|
||||
active_energy: {
|
||||
metric: "active_energy",
|
||||
@@ -179,6 +196,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "kcal",
|
||||
titleKey: "charts.activeEnergy",
|
||||
captionKey: "insights.trendsRow.caption.active_energy",
|
||||
detailHref: "/insights/active-energy",
|
||||
},
|
||||
flights: {
|
||||
metric: "flights",
|
||||
@@ -189,6 +207,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "flights",
|
||||
titleKey: "charts.flights",
|
||||
captionKey: "insights.trendsRow.caption.flights",
|
||||
detailHref: "/insights/flights-climbed",
|
||||
},
|
||||
distance: {
|
||||
metric: "distance",
|
||||
@@ -199,6 +218,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "km",
|
||||
titleKey: "charts.distance",
|
||||
captionKey: "insights.trendsRow.caption.distance",
|
||||
detailHref: "/insights/walking-distance",
|
||||
},
|
||||
vo2_max: {
|
||||
metric: "vo2_max",
|
||||
@@ -209,6 +229,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "mL/kg·min",
|
||||
titleKey: "charts.vo2Max",
|
||||
captionKey: "insights.trendsRow.caption.vo2_max",
|
||||
detailHref: "/insights/cardio-fitness",
|
||||
},
|
||||
body_temp: {
|
||||
metric: "body_temp",
|
||||
@@ -219,6 +240,7 @@ export const TREND_CHART_CONFIG: Record<
|
||||
yAxisUnit: "°C",
|
||||
titleKey: "charts.bodyTemperature",
|
||||
captionKey: "insights.trendsRow.caption.body_temp",
|
||||
detailHref: "/insights/body-temperature",
|
||||
},
|
||||
// Adherence-context findings — no single plottable trend series in
|
||||
// this row. A finding on one of these is skipped during selection so
|
||||
|
||||
Reference in new issue
Block a user