import { ref, computed } from 'vue' import { getDoc, listDocs } from 'src/api/erp' import { erpLink } from 'src/composables/useFormatters' /** * Composable for the slide-over detail modal. * Handles state management, data fetching, and navigation for any ERPNext doctype. */ export function useDetailModal () { const modalOpen = ref(false) const modalLoading = ref(false) const modalDoctype = ref('') const modalDocName = ref('') const modalTitle = ref('') const modalDoc = ref(null) const modalComments = ref([]) const modalComms = ref([]) const modalFiles = ref([]) const modalDispatchJobs = ref([]) const modalErpLink = computed(() => erpLink(modalDoctype.value, modalDocName.value)) const modalDocFields = computed(() => { if (!modalDoc.value) return {} const skip = new Set(['name', 'owner', 'creation', 'modified', 'modified_by', 'docstatus', 'idx', 'doctype', '_user_tags', '_comments', '_assign', '_liked_by']) const out = {} for (const [k, v] of Object.entries(modalDoc.value)) { if (skip.has(k) || Array.isArray(v) || v === null || v === '' || typeof v === 'object') continue out[k] = v } return out }) /** * Open the modal for any doctype. * Fetches the document and optional related data (comments, communications, files). */ async function openModal (doctype, name, title) { modalDoctype.value = doctype modalDocName.value = name modalTitle.value = title || name modalDoc.value = null modalComments.value = [] modalComms.value = [] modalFiles.value = [] modalDispatchJobs.value = [] modalOpen.value = true modalLoading.value = true try { const promises = [getDoc(doctype, name)] // Fetch comments for invoices (legacy notes imported as Comments) if (doctype === 'Sales Invoice') { promises.push(listDocs('Comment', { filters: { reference_doctype: 'Sales Invoice', reference_name: name, comment_type: 'Comment' }, fields: ['name', 'content', 'comment_by', 'creation'], limit: 50, orderBy: 'creation desc', })) } // Fetch communications, files, comments, and linked dispatch jobs for Issues if (doctype === 'Issue') { promises.push( listDocs('Communication', { filters: { reference_doctype: 'Issue', reference_name: name }, fields: ['name', 'sender', 'owner', 'content', 'creation', 'communication_type', 'subject'], limit: 50, orderBy: 'creation asc', }).catch(() => []), listDocs('File', { filters: { attached_to_doctype: 'Issue', attached_to_name: name }, fields: ['name', 'file_name', 'file_url', 'file_size'], limit: 20, }).catch(() => []), listDocs('Comment', { filters: { reference_doctype: 'Issue', reference_name: name, comment_type: 'Comment' }, fields: ['name', 'content', 'comment_by', 'creation'], limit: 200, orderBy: 'creation asc', }).catch(() => []), listDocs('Dispatch Job', { filters: { source_issue: name }, fields: ['name', 'subject', 'status', 'assigned_tech', 'scheduled_date', 'job_type', 'priority', 'depends_on', 'duration_h', 'parent_job', 'step_order', 'on_open_webhook', 'on_close_webhook'], limit: 50, orderBy: 'step_order asc, creation asc', }).catch(() => []), ) } const results = await Promise.all(promises) modalDoc.value = results[0] if (doctype === 'Sales Invoice') { modalComments.value = results[1] || [] } else if (doctype === 'Issue') { modalComms.value = results[1] || [] modalFiles.value = results[2] || [] modalComments.value = results[3] || [] modalDispatchJobs.value = results[4] || [] } // Auto-derive title from doc if not provided if (!title) { const d = modalDoc.value modalTitle.value = d.title || d.subject || d.item_name || d.customer_name || name } } catch (e) { console.error('Failed to load', doctype, name, e) } modalLoading.value = false } function closeModal () { modalOpen.value = false } return { modalOpen, modalLoading, modalDoctype, modalDocName, modalTitle, modalDoc, modalComments, modalComms, modalFiles, modalErpLink, modalDocFields, modalDispatchJobs, openModal, closeModal, } }