Files
HealthLog/src/lib/query-keys/documents.ts
T
Marc-André Bombeck be9c79974f feat(documents): a document can say which visit it belongs to
Every arm here is the same shape as the one that already exists for
conditions, because the vault's filing model is settled and a second shape
would be the thing to explain rather than the thing to use: encounterIds on
upload and on PATCH with replace-set semantics, linkEncounter / unlinkEncounter
beside the episode pair in the bulk action, and an encounterId list filter.

Both directions ride ONE table. The visit's sheet writes encounter_document_links
from its end and the document reads it from the other, declared as a second
direction in the link service rather than a fourth table. A visit's kind
crosses the wire as the enum constant rather than a rendered name: unlike a
condition label, which is the person's own words, a kind is one of eight closed
values whose human name the reader's own bundle owns.

The suggestion is offered where the document is being looked at, resolved by
the shared rule rather than by this surface: one candidate pre-selects with an
undo, two offer a picker with nothing pre-selected, none offers nothing at all.
It resolves with no AI provider configured, and it renders nothing once the
document already names a visit.

The bulk verb has a caller — the vault's selection bar files a whole selection
against a visit, mirroring the condition verb and hiding itself on an account
with no visits. Nothing here is one-ended.

OpenAPI regenerated in the same commit.
2026-08-08 13:41:42 +02:00

78 lines
3.6 KiB
TypeScript

/**
* Query keys — the Dokumente vault: the filtered timeline list (keyset-
* paginated), a single document's detail, and the usage/limits read the
* upload path pre-flights against. Part of the centralized factory;
* aggregated in `./index.ts`. Any document write (upload / edit / link /
* delete / restore / bulk) invalidates the `["documents"]` prefix so the
* timeline, an open detail sheet, and the quota bar repaint in lockstep.
*/
import type { InboundDocumentKindValue } from "@/lib/validations/inbound-documents";
/**
* The parameter set that distinguishes one vault timeline view from another —
* mirrors the URL facets (`?q&kind&episode&year`) one-to-one so a deep link
* and an in-page filter change resolve to the same cache bucket. `undefined`
* fields are dropped by TanStack's stable hash, so an all-defaults view and
* an explicit `{ q: undefined, … }` share a key. Sort is pinned server-side
* (filing date, newest first) and is deliberately NOT part of the key.
*/
export interface DocumentVaultFilters {
q?: string;
/** Type facet — OR inside the facet, sorted before keying by the caller. */
kinds?: InboundDocumentKindValue[];
/** Only documents linked to this illness/condition episode. */
episodeId?: string;
/** Only documents linked to this visit. Arrives by deep link from it. */
encounterId?: string;
/** Only documents whose filing date falls in this calendar year. */
year?: number;
}
export const documentKeys = {
documents: () => ["documents"] as const,
inboundDocuments: () => ["documents", "inbound"] as const,
/**
* The parameterised vault list (keyset-paginated via `useInfiniteQuery`).
* The literal `"list"` segment keeps it distinct from a single-document key
* (`inboundDocument(id)`); cuid ids never collide with it. Stays under the
* `["documents", "inbound"]` prefix so prefix invalidation still reaches it.
*/
inboundDocumentList: (filters: DocumentVaultFilters) =>
["documents", "inbound", "list", filters] as const,
inboundDocument: (id: string) => ["documents", "inbound", id] as const,
/**
* The illness-episode detail's compact document preview (single page,
* small limit). Deliberately NOT `inboundDocumentList({ episodeId })` —
* that key belongs to the vault's `useInfiniteQuery` and a plain query
* under the same key would poison the cache with a different shape.
*/
inboundDocumentEpisodePreview: (episodeId: string) =>
["documents", "inbound", "episode-preview", episodeId] as const,
/**
* The link-existing-documents picker (searchable single page). Own key
* for the same shape-collision reason as the episode preview.
*/
inboundDocumentPicker: (q: string) =>
["documents", "inbound", "picker", q] as const,
/**
* Storage usage + effective limits (`GET /api/documents/inbound/usage`).
* Invalidated through the `["documents"]` prefix after every upload /
* delete so the quota bar tracks reality.
*/
inboundDocumentUsage: () => ["documents", "inbound", "usage"] as const,
/**
* Document-scoped AI capability probe
* (`GET /api/documents/inbound/capability`). Distinct from the labs
* `ocrCapability` key because it resolves over the DOCUMENT provider order
* (local-first) and carries the per-egress class the vault notice reads.
*/
inboundDocumentAiCapability: () =>
["documents", "inbound", "ai-capability"] as const,
/**
* The per-user "read documents automatically with AI" opt-in
* (`GET/PATCH /api/auth/me/documents-auto-ai-read`). Drives the AI-settings
* toggle; a flip invalidates the document AI capability probe.
*/
documentsAutoAiRead: () => ["documents", "auto-ai-read"] as const,
};