import { ref, computed, watch, watchEffect, type ComputedRef } from "vue"; import { useQuery, useMutation } from "@vue/apollo-composable"; import { DocumentsQueryDocument, CreateWorkspaceDocumentDocument, DeleteWorkspaceDocumentDocument, } from "~~/graphql/generated"; import { formatDocumentScope } from "~/composables/useWorkspaceDocuments"; import type { ClientTimelineItem } from "~/composables/useTimeline"; export type DocumentSortMode = "updatedAt" | "title" | "owner"; export type WorkspaceDocument = { id: string; title: string; type: "Regulation" | "Playbook" | "Policy" | "Template"; owner: string; scope: string; updatedAt: string; summary: string; body: string; }; function safeTrim(value: unknown) { return String(value ?? "").trim(); } export function useDocuments(opts: { apolloAuthReady: ComputedRef }) { const { result: documentsResult, refetch: refetchDocuments } = useQuery( DocumentsQueryDocument, null, { enabled: opts.apolloAuthReady }, ); const { mutate: doCreateWorkspaceDocument } = useMutation(CreateWorkspaceDocumentDocument, { refetchQueries: [{ query: DocumentsQueryDocument }], }); const { mutate: doDeleteWorkspaceDocument } = useMutation(DeleteWorkspaceDocumentDocument, { refetchQueries: [{ query: DocumentsQueryDocument }], }); const documents = ref([]); const documentSearch = ref(""); const documentSortMode = ref("updatedAt"); const selectedDocumentId = ref(documents.value[0]?.id ?? ""); const documentDeletingId = ref(""); const documentSortOptions: Array<{ value: DocumentSortMode; label: string }> = [ { value: "updatedAt", label: "Updated" }, { value: "title", label: "Title" }, { value: "owner", label: "Owner" }, ]; watch(() => documentsResult.value?.documents, (v) => { if (v) documents.value = v as WorkspaceDocument[]; }, { immediate: true }); const filteredDocuments = computed(() => { const query = documentSearch.value.trim().toLowerCase(); const list = documents.value .filter((item) => { if (!query) return true; const haystack = [item.title, item.summary, item.owner, formatDocumentScope(item.scope), item.body].join(" ").toLowerCase(); return haystack.includes(query); }) .sort((a, b) => { if (documentSortMode.value === "title") return a.title.localeCompare(b.title); if (documentSortMode.value === "owner") return a.owner.localeCompare(b.owner); return b.updatedAt.localeCompare(a.updatedAt); }); return list; }); watchEffect(() => { if (!filteredDocuments.value.length) { selectedDocumentId.value = ""; return; } if (!filteredDocuments.value.some((item) => item.id === selectedDocumentId.value)) { const first = filteredDocuments.value[0]; if (first) selectedDocumentId.value = first.id; } }); const selectedDocument = computed(() => documents.value.find((item) => item.id === selectedDocumentId.value)); function updateSelectedDocumentBody(value: string) { if (!selectedDocument.value) return; selectedDocument.value.body = value; } function openDocumentsTab(opts2: { setTab: (tab: string) => void; syncPath: (push: boolean) => void }, push = false) { opts2.setTab("documents"); if (!selectedDocumentId.value && filteredDocuments.value.length) { const first = filteredDocuments.value[0]; if (first) selectedDocumentId.value = first.id; } opts2.syncPath(push); } async function deleteWorkspaceDocumentById( documentIdInput: string, clientTimelineItems: { value: ClientTimelineItem[] }, ) { const documentId = safeTrim(documentIdInput); if (!documentId) return; if (documentDeletingId.value === documentId) return; const target = documents.value.find((doc) => doc.id === documentId); const targetLabel = safeTrim(target?.title) || "this document"; if (process.client && !window.confirm(`Delete ${targetLabel}?`)) return; documentDeletingId.value = documentId; try { await doDeleteWorkspaceDocument({ id: documentId }); documents.value = documents.value.filter((doc) => doc.id !== documentId); clientTimelineItems.value = clientTimelineItems.value.filter((item) => { const isDocumentEntry = String(item.contentType).toLowerCase() === "document"; if (!isDocumentEntry) return true; return item.contentId !== documentId && item.document?.id !== documentId; }); if (selectedDocumentId.value === documentId) { selectedDocumentId.value = ""; } } finally { if (documentDeletingId.value === documentId) { documentDeletingId.value = ""; } } } async function createCommDocument( threadContact: { id: string; contact: string } | undefined, draftText: string, commDocumentForm: { value: { title: string } }, authDisplayName: string, additionalCallbacks: { buildScope: (contactId: string, contactName: string) => string; onSuccess: (created: WorkspaceDocument | null) => void; }, ) { if (!threadContact) return false; const summary = draftText.trim(); if (!summary) return false; const title = safeTrim(commDocumentForm.value.title) || buildCommDocumentTitle(summary, threadContact.contact); const scope = additionalCallbacks.buildScope(threadContact.id, threadContact.contact); const body = summary; try { const res = await doCreateWorkspaceDocument({ input: { title, owner: authDisplayName, scope, summary, body, }, }); const created = res?.data?.createWorkspaceDocument; if (created) { documents.value = [created as WorkspaceDocument, ...documents.value.filter((doc) => doc.id !== created.id)]; selectedDocumentId.value = created.id; } else { selectedDocumentId.value = ""; } additionalCallbacks.onSuccess((created as WorkspaceDocument) ?? null); return true; } catch { return false; } } function buildCommDocumentTitle(text: string, contact: string) { const cleaned = text.replace(/\s+/g, " ").trim(); if (cleaned) { const sentence = cleaned.split(/[.!?\n]/)[0]?.trim() ?? ""; if (sentence) return sentence.slice(0, 120); } return `Документ для ${contact}`; } return { documents, documentSearch, documentSortMode, selectedDocumentId, documentDeletingId, documentSortOptions, selectedDocument, filteredDocuments, updateSelectedDocumentBody, createCommDocument, buildCommDocumentTitle, deleteWorkspaceDocumentById, openDocumentsTab, refetchDocuments, }; }