Files
HealthLog/src/components/documents/document-link-picker.tsx
T
Marc-André Bombeck cc190c4710 refactor(documents): one DocumentPickerSheet behind both vault pickers
Linking documents to an illness episode and choosing documents for a
share link carried the same sheet as two copies: the same endpoint and
query key, the same debounced search, skeleton, error and empty states
and toggle rows. DocumentPickerSheet owns that body; each picker keeps
what a toggle means (an immediate link write, or a capped local choice
posted with the share link).
2026-10-20 13:50:40 +02:00

72 lines
2.3 KiB
TypeScript

"use client";
/**
* "Verknüpfen" — link existing vault documents to an illness episode from
* the episode's detail page. The shared `DocumentPickerSheet` lists and
* searches the vault; toggling a row fires the bulk link/unlink action for
* that single id (idempotent by contract, no-op-success on already-in-state
* rows) and refreshes every document read through the `["documents"]`
* prefix.
*/
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { showErrorToast } from "@/lib/errors/error-toast";
import { apiPost } from "@/lib/api/api-fetch";
import { useTranslations } from "@/lib/i18n/context";
import { invalidateKeys, queryKeys } from "@/lib/query-keys";
import type { DocumentBulkResultDto } from "@/lib/validations/inbound-documents";
import { DocumentPickerSheet } from "./document-picker-sheet";
export function DocumentLinkPicker({
episodeId,
open,
onOpenChange,
}: {
episodeId: string;
open: boolean;
onOpenChange: (open: boolean) => void;
}) {
const { t } = useTranslations();
const queryClient = useQueryClient();
const toggle = useMutation({
mutationFn: (input: { documentId: string; linked: boolean }) =>
apiPost<{ results: DocumentBulkResultDto[] }>(
"/api/documents/inbound/bulk",
{
ids: [input.documentId],
action: input.linked ? "unlinkEpisode" : "linkEpisode",
episodeId,
},
),
onSuccess: (data) => {
if (data.results.some((r) => !r.ok)) {
showErrorToast(t("documents.bulk.failed"));
}
},
onError: (failure) =>
showErrorToast(t("documents.bulk.failed"), { cause: failure }),
onSettled: () => {
void invalidateKeys(queryClient, [queryKeys.documents()]);
},
});
return (
<DocumentPickerSheet
open={open}
onOpenChange={onOpenChange}
title={t("documents.linkPicker.title")}
description={t("documents.linkPicker.description")}
searchPlaceholder={t("documents.linkPicker.searchPlaceholder")}
emptyLabel={t("documents.linkPicker.empty")}
isPicked={(doc) =>
doc.conditionLinks.some((l) => l.episodeId === episodeId)
}
isDisabled={() => toggle.isPending}
onToggle={(doc, _title, linked) =>
toggle.mutate({ documentId: doc.id, linked })
}
/>
);
}