Mirror the metric sub-page layout on the biomarker detail

Bring the lab biomarker detail in line with the Insights metric pages:

- Lead with the static catalog description directly beneath the heading,
  the explainer-caption position the metric pages use.
- Drop the early big-value + reference-badge block that did not match the
  assessment-card styling.
- Split numeric and qualitative markers: numeric keeps the stat strip and
  chart, qualitative shows only the latest result text (no strip, no chart,
  no range badge). The assessment card now renders for any marker with
  readings and self-suppresses for qualitative-only markers.
- Move the full reading history off the detail page onto a dedicated
  /labs/[biomarkerId]/values route, reached by a top-right list control,
  matching the metric pages' show-all-values affordance.
This commit is contained in:
Marc-André Bombeck committed 2026-06-28 20:37:01 +02:00
1 parent 651b02bc1b
commit ad43eaeff0
9 files changed
+301 -109

No files matched your search

@@ -0,0 +1,75 @@
"use client";
/**
* v1.25.1 — `/labs/[biomarkerId]/values`.
*
* The "show all readings" sub-page for one biomarker, mirroring the metric
* sub-pages' `/insights/values/[type]`. The detail page keeps the
* numbers-first spine (description → stat strip → chart → assessment) and
* links here for the raw, editable reading feed. Owns auth + the back link to
* the biomarker detail, and hands the marker id to `<LabValuesList>`.
*/
import { use, useEffect } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { Loader2 } from "lucide-react";
import { LabValuesList } from "@/components/labs/lab-values-list";
import type { BiomarkerDto } from "@/components/labs/types";
import { SubPageShell } from "@/components/insights/sub-page-shell";
import { BackLink } from "@/components/ui/back-link";
import { useAuth } from "@/hooks/use-auth";
import { useMounted } from "@/hooks/use-mounted";
import { apiGet } from "@/lib/api/api-fetch";
import { useTranslations } from "@/lib/i18n/context";
import { queryKeys } from "@/lib/query-keys";
export default function LabBiomarkerValuesPage({
params,
}: {
params: Promise<{ biomarkerId: string }>;
}) {
const { biomarkerId } = use(params);
const { isAuthenticated, isLoading } = useAuth();
const mounted = useMounted();
const router = useRouter();
const { t } = useTranslations();
useEffect(() => {
if (!isLoading && !isAuthenticated) {
router.push("/auth/login");
}
}, [isLoading, isAuthenticated, router]);
const { data: marker } = useQuery({
queryKey: queryKeys.biomarkerDetail(biomarkerId),
queryFn: () => apiGet<BiomarkerDto>(`/api/biomarkers/${biomarkerId}`),
enabled: isAuthenticated && biomarkerId.length > 0,
});
if (!mounted || isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="text-primary h-8 w-8 animate-spin motion-reduce:animate-none" />
</div>
);
}
const markerName = marker?.name ?? "";
return (
<SubPageShell
title={t("labs.detail.valuesTitle", { marker: markerName })}
description={t("labs.detail.valuesDescription")}
backLink={
<BackLink
href={`/labs/${biomarkerId}`}
label={t("labs.detail.backToMarker", { marker: markerName })}
dataSlot="lab-values-back"
/>
}
>
<LabValuesList biomarkerId={biomarkerId} />
</SubPageShell>
);
}