(`/api/labs/${encodeURIComponent(id)}`);
+}
+
+export function LabResultRelatedLinks({
+ labResultId,
+ takenAt,
+}: {
+ labResultId: string;
+ takenAt: string | null;
+}) {
+ const qc = useQueryClient();
+ const detail = useQuery({
+ queryKey: queryKeys.labResult(labResultId),
+ queryFn: () => fetchLabResultDetail(labResultId),
+ });
+ // The pickers seed from this read; before it lands they would offer an
+ // empty selection that the first tap could not compute a change from.
+ if (!detail.data) return null;
+ const related = detail.data.related;
+ return (
+
+
+ {
+ // The other ends list the reading too: the document's and the
+ // visit's sheets seed their pickers from their cached reads, and a
+ // condition reaches the reading through the visit.
+ for (const queryKey of [
+ queryKeys.labResult(labResultId),
+ queryKeys.documents(),
+ queryKeys.encounters(),
+ queryKeys.illness(),
+ ]) {
+ void qc.invalidateQueries({ queryKey });
+ }
+ }}
+ />
+
+ );
+}
diff --git a/src/components/labs/types.ts b/src/components/labs/types.ts
index a523a6585..d7a0e2816 100644
--- a/src/components/labs/types.ts
+++ b/src/components/labs/types.ts
@@ -1,3 +1,5 @@
+import type { RelatedLink } from "@/lib/links/related";
+
import type {
ReferenceRangeOrigin,
ReferenceRangeStatus,
@@ -58,6 +60,8 @@ export interface LabResultDto {
/** The single-resource detail DTO — carries the decrypted note. */
export interface LabResultDetailDto extends Omit {
note: string | null;
+ /** v1.43 — the records this reading relates to, resolved on the server. */
+ related?: RelatedLink[];
}
export interface LabResultListResponse {
diff --git a/src/lib/query-keys/labs.ts b/src/lib/query-keys/labs.ts
index ee585e878..aa1a6291b 100644
--- a/src/lib/query-keys/labs.ts
+++ b/src/lib/query-keys/labs.ts
@@ -34,6 +34,11 @@ export const labKeys = {
// mutation's `labResults()` invalidation evict it.
labResultsOverview: () => ["lab-results", "overview"] as const,
+ // One reading's detail (`GET /api/labs/{id}`): the decrypted note and the
+ // records it relates to. Under the `lab-results` prefix so every result
+ // mutation evicts it.
+ labResult: (id: string) => ["lab-results", "detail", id] as const,
+
// `/labs?analyte=` (MCP deep links): the newest reading stored under
// that exact analyte name, one row, to find its marker's page. Its own key
// because the page size differs from every list read; the `lab-results`