fix(labs): mark un-linked lab groups and bridge biomarker detail to source reports

- Lab groups not backfilled to a catalog biomarker now carry a muted
  'not linked to a biomarker yet' affordance in both list and card views, so
  an inert row no longer reads as a broken link next to clickable neighbours.
- The biomarker detail page links to the document vault filtered to
  lab-result reports (gated on the documents module), giving a path from a
  value to its source scans. A per-value document relation remains a
  data-model follow-up.
This commit is contained in:
Marc-André Bombeck committed 2026-07-09 00:02:56 +02:00
1 parent e5ca125dfb
commit 1c57da7d44
8 files changed
+52 -7

No files matched your search

+2 -1
View File
@@ -7383,7 +7383,8 @@
"pdfNeedsAnthropic": "PDF-Scannen benötigt einen Claude-Bildanbieter; lade stattdessen ein Foto hoch."
},
"loadMoreReadings": "Weitere Werte laden",
"documentsLink": "Befund-Dokumente"
"documentsLink": "Befund-Dokumente",
"notLinkedYet": "Noch keinem Biomarker zugeordnet"
},
"modules": {
"cycle": {
+2 -1
View File
@@ -7383,7 +7383,8 @@
"pdfNeedsAnthropic": "PDF scanning needs a Claude vision provider; upload a photo instead."
},
"loadMoreReadings": "Load more readings",
"documentsLink": "Lab report documents"
"documentsLink": "Lab report documents",
"notLinkedYet": "Not linked to a biomarker yet"
},
"modules": {
"cycle": {
+2 -1
View File
@@ -7383,7 +7383,8 @@
"pdfNeedsAnthropic": "El escaneo de PDF necesita un proveedor de visión de Claude; sube una foto en su lugar."
},
"loadMoreReadings": "Cargar más mediciones",
"documentsLink": "Documentos de resultados"
"documentsLink": "Documentos de resultados",
"notLinkedYet": "Aún no vinculado a un biomarcador"
},
"modules": {
"cycle": {
+2 -1
View File
@@ -7383,7 +7383,8 @@
"pdfNeedsAnthropic": "Le scan PDF nécessite un fournisseur de vision Claude ; importez plutôt une photo."
},
"loadMoreReadings": "Charger plus de mesures",
"documentsLink": "Documents de résultats"
"documentsLink": "Documents de résultats",
"notLinkedYet": "Pas encore lié à un biomarqueur"
},
"modules": {
"cycle": {
+2 -1
View File
@@ -7383,7 +7383,8 @@
"pdfNeedsAnthropic": "La scansione PDF richiede un provider di visione Claude; carica invece una foto."
},
"loadMoreReadings": "Carica altre misurazioni",
"documentsLink": "Documenti dei referti"
"documentsLink": "Documenti dei referti",
"notLinkedYet": "Non ancora collegato a un biomarcatore"
},
"modules": {
"cycle": {
+2 -1
View File
@@ -7383,7 +7383,8 @@
"pdfNeedsAnthropic": "Skanowanie PDF wymaga dostawcy wizji Claude; prześlij zamiast tego zdjęcie."
},
"loadMoreReadings": "Wczytaj więcej pomiarów",
"documentsLink": "Dokumenty wyników badań"
"documentsLink": "Dokumenty wyników badań",
"notLinkedYet": "Jeszcze niepowiązane z biomarkerem"
},
"modules": {
"cycle": {
+26 -1
View File
@@ -9,9 +9,17 @@ import {
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import { FlaskConical, ListOrdered, Pencil, Plus } from "lucide-react";
import {
ArrowRight,
FlaskConical,
FolderOpen,
ListOrdered,
Pencil,
Plus,
} from "lucide-react";
import { toast } from "sonner";
import { useAuth } from "@/hooks/use-auth";
import { DeleteButton } from "@/components/data-list";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
@@ -84,6 +92,7 @@ const READINGS_PAGE_SIZE = 200;
* it. Mirrors the measurement-detail layout.
*/
export function LabBiomarkerDetail({ biomarkerId }: { biomarkerId: string }) {
const { user } = useAuth();
const { t } = useTranslations();
const router = useRouter();
const queryClient = useQueryClient();
@@ -367,6 +376,22 @@ export function LabBiomarkerDetail({ biomarkerId }: { biomarkerId: string }) {
catalog-less marker still carries a description. */}
<p className="text-foreground text-sm leading-relaxed">{description}</p>
{/* Bridge to the source report PDFs. There is no per-value document
relation in the data model yet, so this links to the vault filtered
to lab-result documents rather than the exact scan behind one value.
Only when the documents module is enabled. */}
{user?.modules?.inboundDocuments ? (
<Link
href="/documents?kind=LAB_RESULT"
data-slot="lab-biomarker-documents-link"
className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex items-center gap-1.5 rounded-md text-sm transition-colors focus-visible:ring-[3px] focus-visible:outline-none"
>
<FolderOpen className="size-4" aria-hidden="true" />
{t("labs.documentsLink")}
<ArrowRight className="size-3.5" aria-hidden="true" />
</Link>
) : null}
{isLoading ? (
<Card>
<CardContent>
+14
View File
@@ -230,11 +230,17 @@ export function LabList({ onAddFirst }: { onAddFirst?: () => void } = {}) {
<ChevronRight className="text-muted-foreground h-4 w-4 shrink-0" />
</Link>
) : (
// Un-linked group (not backfilled to a catalog biomarker):
// render inert but say so, so it doesn't read as a broken
// link next to its clickable neighbours.
<div className="flex min-w-0 flex-1 items-center gap-3">
{inner}
<LabTrendSparkline
values={group.readings.map((r) => r.value)}
/>
<span className="text-muted-foreground shrink-0 text-xs italic">
{t("labs.notLinkedYet")}
</span>
</div>
)}
</div>
@@ -319,6 +325,14 @@ export function LabList({ onAddFirst }: { onAddFirst?: () => void } = {}) {
values={group.readings.map((r) => r.value)}
/>
</div>
{/* Un-linked group: the header renders no link, so name the
reason rather than leaving a card that silently does
nothing on tap. */}
{!group.biomarkerId ? (
<p className="text-muted-foreground text-xs italic">
{t("labs.notLinkedYet")}
</p>
) : null}
</CardContent>
</Card>
</li>