From dbdc99a4a153f77712924b82a8f05f9723051446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Wed, 3 Jun 2026 08:55:05 +0200 Subject: [PATCH] fix(insights): return values sub-page back-link to the originating metric The show-all-readings link now carries the originating metric page as a sanitised from param, so a weight to show-all drill-in returns to weight rather than the Insights overview. The param is constrained to an internal /insights/ path so a crafted value cannot redirect off-site. --- src/app/insights/values/[type]/page.tsx | 23 ++++++++++-- src/components/insights/sub-page-shell.tsx | 12 +++++- .../__tests__/values-back-link.test.ts | 37 +++++++++++++++++++ src/lib/insights/values-back-link.ts | 25 +++++++++++++ 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 src/lib/insights/__tests__/values-back-link.test.ts create mode 100644 src/lib/insights/values-back-link.ts diff --git a/src/app/insights/values/[type]/page.tsx b/src/app/insights/values/[type]/page.tsx index fd1ff6556..8586df1a4 100644 --- a/src/app/insights/values/[type]/page.tsx +++ b/src/app/insights/values/[type]/page.tsx @@ -2,10 +2,14 @@ import { use } from "react"; import Link from "next/link"; -import { notFound } from "next/navigation"; +import { notFound, useSearchParams } from "next/navigation"; import { ArrowLeft } from "lucide-react"; import { useTranslations } from "@/lib/i18n/context"; +import { + INSIGHTS_OVERVIEW, + resolveValuesBackHref, +} from "@/lib/insights/values-back-link"; import { measurementTypeEnum } from "@/lib/validations/measurement"; import { MEASUREMENT_TYPE_LABEL_KEYS } from "@/components/measurements/measurement-list-meta"; import { Button } from "@/components/ui/button"; @@ -33,6 +37,7 @@ export default function InsightsMetricValuesPage({ }) { const { t } = useTranslations(); const { type } = use(params); + const searchParams = useSearchParams(); // Reject any segment that is not a real MeasurementType so a typo'd // deep-link 404s instead of mounting a blank list. @@ -43,6 +48,16 @@ export default function InsightsMetricValuesPage({ const labelKey = MEASUREMENT_TYPE_LABEL_KEYS[type]; const metricLabel = labelKey ? t(labelKey) : type; + // v1.10.2 — return to the originating metric page when the link carried a + // `from` param (set by `` on the "show all readings" link), + // so a `weight → show all values` drill-in lands back on `weight` rather + // than the Insights overview. `resolveValuesBackHref` sanitises the value to + // an internal `/insights/` path so a crafted `?from=` can never become + // an off-site or protocol-relative target; anything else falls back to the + // overview. + const backHref = resolveValuesBackHref(searchParams.get("from")); + const backToOrigin = backHref !== INSIGHTS_OVERVIEW; + return ( - +