From 8ae498ffb28467e64d3e53a99646db9b112fa9e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 20 Jun 2026 11:59:19 +0200 Subject: [PATCH] feat(labs): surface biomarker delete on the detail page Add a delete affordance with a confirm step to the biomarker detail header, reusing the existing DELETE /api/biomarkers/:id endpoint. On success the catalog and result-list queries invalidate and the view returns to the labs surface. Removes the Settings detour for clearing a stray biomarker. --- src/components/labs/lab-biomarker-detail.tsx | 31 ++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/components/labs/lab-biomarker-detail.tsx b/src/components/labs/lab-biomarker-detail.tsx index 436ec36d3..2b07f364a 100644 --- a/src/components/labs/lab-biomarker-detail.tsx +++ b/src/components/labs/lab-biomarker-detail.tsx @@ -1,15 +1,18 @@ "use client"; import { useState } from "react"; -import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useRouter } from "next/navigation"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { FlaskConical, Pencil, Plus } from "lucide-react"; +import { toast } from "sonner"; +import { DeleteButton } from "@/components/data-list"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { EmptyState } from "@/components/ui/empty-state"; import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; import { Skeleton } from "@/components/ui/skeleton"; -import { apiGet } from "@/lib/api/api-fetch"; +import { apiDelete, apiGet } from "@/lib/api/api-fetch"; import { classifyReferenceRange } from "@/lib/labs/reference-range"; import { formatLabValue } from "@/lib/labs/format-value"; import { useTranslations } from "@/lib/i18n/context"; @@ -34,6 +37,7 @@ import type { */ export function LabBiomarkerDetail({ biomarkerId }: { biomarkerId: string }) { const { t } = useTranslations(); + const router = useRouter(); const queryClient = useQueryClient(); const [addOpen, setAddOpen] = useState(false); // Sticky-footer slot for the add-value sheet (the form portals here). @@ -93,6 +97,21 @@ export function LabBiomarkerDetail({ biomarkerId }: { biomarkerId: string }) { queryClient.invalidateQueries({ queryKey: queryKeys.labResults() }); } + // v1.18.9 (#41/#3) — delete the biomarker directly from its detail page so + // a stray marker is removable without a Settings detour. `onDelete: SetNull` + // keeps the readings and unlinks them. On success, invalidate the catalog + + // result list and return to the labs surface. + const deleteMarker = useMutation({ + mutationFn: () => apiDelete(`/api/biomarkers/${biomarkerId}`), + onSuccess: () => { + toast.success(t("labs.biomarker.deletedToast")); + queryClient.invalidateQueries({ queryKey: queryKeys.biomarkers() }); + queryClient.invalidateQueries({ queryKey: queryKeys.labResults() }); + router.push("/labs"); + }, + onError: () => toast.error(t("labs.biomarker.deleteError")), + }); + if (markerError || listError) { return (

@@ -136,6 +155,14 @@ export function LabBiomarkerDetail({ biomarkerId }: { biomarkerId: string }) { > + deleteMarker.mutate()} + title={t("labs.biomarker.deleteConfirmTitle")} + description={t("labs.biomarker.deleteConfirmDescription")} + confirmLabel={t("labs.biomarker.delete")} + className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9" + iconClassName="h-4 w-4" + />