mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 22:32:04 +00:00
fix(links): offer the whole vault when linking a document to a record
The visit and vaccination forms asked the document list for a page of 200 while the route accepts at most 100. The refusal read as an empty list, so the picker said "No documents yet." to anyone with a full vault, and the only way a scan reached a dose was the seven-day suggestion offered at upload. The visit form's condition picker had the same problem against the episode list. The document pickers now walk the vault page by page at the route's own limit, so any document can be linked from a vaccination or a visit, and one document can sit on as many records as it covers. Documents dated within seven days of the record stay on top under their own heading, the rest follow by month and are searchable. A failed read now shows an error with a retry instead of passing for an empty list. Refs #1024, #1005
This commit is contained in:
1 parent
8f889cf942
commit
f46ed99def
17 files changed
+443
-85
No files matched your search
@@ -5,6 +5,11 @@
|
||||
* transcribed from. The shared {@link EntityLinkPicker} — an inline summary
|
||||
* (removable chips + an add button) over a searchable, month-grouped sheet.
|
||||
*
|
||||
* The whole vault is offered, not only the pages dated near the dose: one
|
||||
* childhood record commonly covers a dozen doses given years apart, and it
|
||||
* belongs on every one of them. The pages dated within the upload
|
||||
* suggestion's window of this dose sit on top as suggestions.
|
||||
*
|
||||
* **The gate blanks the block, it does not post-filter it.** When the
|
||||
* `inboundDocuments` module is off, this renders nothing — no heading, no empty
|
||||
* list — because an empty picker for a switched-off module advertises a feature
|
||||
@@ -13,75 +18,43 @@
|
||||
* Nothing here can block a save: the list starts empty and stays valid empty,
|
||||
* and linking is optional, capped and idempotent behind the link facade.
|
||||
*/
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { FolderOpen } from "lucide-react";
|
||||
|
||||
import { apiGet } from "@/lib/api/api-fetch";
|
||||
import { useFormatters, useTranslations } from "@/lib/i18n/context";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import type { InboundDocumentDto } from "@/lib/validations/inbound-documents";
|
||||
import {
|
||||
EntityLinkPicker,
|
||||
type EntityLinkOption,
|
||||
} from "@/components/links/entity-link-picker";
|
||||
|
||||
const PICKER_FETCH_LIMIT = 200;
|
||||
|
||||
interface DocumentListPage {
|
||||
documents: InboundDocumentDto[];
|
||||
}
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { EntityLinkPicker } from "@/components/links/entity-link-picker";
|
||||
import { useVaultDocumentOptions } from "@/components/links/vault-document-options";
|
||||
|
||||
export function VaccinationDocumentPicker({
|
||||
enabled,
|
||||
anchor,
|
||||
documentIds,
|
||||
onChange,
|
||||
}: {
|
||||
/** The `inboundDocuments` module flag — false blanks the block entirely. */
|
||||
enabled: boolean;
|
||||
/** The dose's own date (ISO), for the suggestions on top. */
|
||||
anchor: string | null;
|
||||
documentIds: string[];
|
||||
onChange: (documentIds: string[]) => void;
|
||||
}) {
|
||||
const { t } = useTranslations();
|
||||
const format = useFormatters();
|
||||
|
||||
const documents = useQuery({
|
||||
queryKey: queryKeys.inboundDocumentPicker("vaccination-form"),
|
||||
enabled,
|
||||
queryFn: () =>
|
||||
apiGet<DocumentListPage>(
|
||||
`/api/documents/inbound?sort=documentDate&order=desc&limit=${PICKER_FETCH_LIMIT}`,
|
||||
),
|
||||
});
|
||||
const vault = useVaultDocumentOptions({ enabled, anchor });
|
||||
|
||||
if (!enabled) return null;
|
||||
|
||||
const options: EntityLinkOption[] = (documents.data?.documents ?? []).map(
|
||||
(doc) => {
|
||||
const date = doc.documentDate ?? doc.reportDate ?? doc.createdAt;
|
||||
return {
|
||||
id: doc.id,
|
||||
label: doc.title ?? doc.filename ?? doc.id,
|
||||
dateLabel: date ? format.date(date) : null,
|
||||
group: date
|
||||
? {
|
||||
key: date.slice(0, 7),
|
||||
label: `${format.monthShort(date)} ${date.slice(0, 4)}`,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="border-t pt-4" data-slot="vaccination-document-picker">
|
||||
<EntityLinkPicker
|
||||
icon={FolderOpen}
|
||||
title={t("vaccinations.form.linkDocuments")}
|
||||
slot="vaccination-document"
|
||||
pending={documents.isPending}
|
||||
pending={vault.pending}
|
||||
error={vault.error}
|
||||
errorLabel={t("links.picker.loadError")}
|
||||
onRetry={vault.retry}
|
||||
selected={documentIds}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
options={vault.options}
|
||||
searchPlaceholder={t("links.picker.searchPlaceholder")}
|
||||
emptyLabel={t("vaccinations.form.linkNothingToOffer")}
|
||||
/>
|
||||
|
||||
@@ -305,6 +305,7 @@ export function VaccinationForm({
|
||||
|
||||
<VaccinationDocumentPicker
|
||||
enabled={user?.modules?.inboundDocuments === true}
|
||||
anchor={anchor}
|
||||
documentIds={draft.documentIds}
|
||||
onChange={(documentIds) => patch({ documentIds })}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user