import { computed, ref } from 'vue' import { fetchApprovalExpenseClaims, fetchArchivedExpenseClaims, fetchExpenseClaims } from '../services/reimbursements.js' import { DOCUMENT_VIEWED_KEYS_CHANGE_EVENT, countNewDocuments, readViewedDocumentKeys, resolveDocumentNewKey } from '../utils/documentCenterNewState.js' import { mapExpenseClaimToRequest } from './useRequests.js' const SOURCE_PRIORITY = { owned: 1, approval: 2, archive: 3 } const documentRows = ref([]) const viewedDocumentKeys = ref(readViewedDocumentKeys()) const loading = ref(false) const INBOX_CACHE_TTL_MS = 30000 let refreshTimer = null let refreshPromise = null let lastRefreshAt = 0 let viewedKeysListenerAttached = false function normalizeClaimText(...values) { for (const value of values) { const normalized = String(value || '').trim() if (normalized) { return normalized } } return '' } function buildDocumentInboxRow(claim, source) { const request = mapExpenseClaimToRequest(claim) const claimId = normalizeClaimText(request.claimId, request.id, claim?.id, claim?.claim_id) const documentNo = normalizeClaimText(request.documentNo, request.claimNo, request.id, claim?.claim_no) const documentKey = normalizeClaimText(claimId, documentNo) return documentKey ? { source, claimId: claimId || documentKey, documentNo, documentKey: `${source}:${documentKey}` } : null } function sourcePriority(row) { return SOURCE_PRIORITY[row?.source] || 0 } function mergeNonArchivedRows(rows) { const rowMap = new Map() rows.filter(Boolean).forEach((row) => { const key = normalizeClaimText(row.claimId, row.documentNo, row.documentKey) if (!key) { return } const current = rowMap.get(key) if (!current || sourcePriority(row) >= sourcePriority(current)) { rowMap.set(key, row) } }) return Array.from(rowMap.values()) } function uniqueRowsByNewKey(rows) { const seenKeys = new Set() return rows.filter((row) => { const key = resolveDocumentNewKey(row) if (!key || seenKeys.has(key)) { return false } seenKeys.add(key) return true }) } function mapClaimsToRows(claims, source) { return Array.isArray(claims) ? claims.map((claim) => buildDocumentInboxRow(claim, source)).filter(Boolean) : [] } export function buildDocumentInboxRows({ ownedClaims = [], approvalClaims = [], archivedClaims = [] } = {}) { const ownedRows = mapClaimsToRows(ownedClaims, 'owned') const approvalRows = mapClaimsToRows(approvalClaims, 'approval') const archiveRows = mapClaimsToRows(archivedClaims, 'archive') return uniqueRowsByNewKey([ ...mergeNonArchivedRows([...ownedRows, ...approvalRows]), ...archiveRows ]) } function refreshViewedDocumentKeys() { viewedDocumentKeys.value = readViewedDocumentKeys() } function attachViewedKeysListener() { if (typeof window === 'undefined' || viewedKeysListenerAttached) { return } window.addEventListener(DOCUMENT_VIEWED_KEYS_CHANGE_EVENT, refreshViewedDocumentKeys) viewedKeysListenerAttached = true } async function readClaimList(fetcher) { const result = await fetcher() return Array.isArray(result) ? result : [] } export function useDocumentCenterInbox() { attachViewedKeysListener() const unreadCount = computed(() => countNewDocuments(documentRows.value, viewedDocumentKeys.value)) const hasUnread = computed(() => unreadCount.value > 0) async function refreshDocumentInbox(options = {}) { const force = Boolean(options.force) const now = Date.now() if (refreshPromise) { return refreshPromise } if (!force && lastRefreshAt && now - lastRefreshAt < INBOX_CACHE_TTL_MS) { refreshViewedDocumentKeys() return documentRows.value } loading.value = true refreshPromise = (async () => { const [ownedResult, approvalResult, archiveResult] = await Promise.allSettled([ readClaimList(fetchExpenseClaims), readClaimList(fetchApprovalExpenseClaims), readClaimList(fetchArchivedExpenseClaims) ]) documentRows.value = buildDocumentInboxRows({ ownedClaims: ownedResult.status === 'fulfilled' ? ownedResult.value : [], approvalClaims: approvalResult.status === 'fulfilled' ? approvalResult.value : [], archivedClaims: archiveResult.status === 'fulfilled' ? archiveResult.value : [] }) lastRefreshAt = Date.now() refreshViewedDocumentKeys() return documentRows.value })() try { return await refreshPromise } finally { loading.value = false refreshPromise = null } } function startDocumentInboxPolling(intervalMs = 120000) { stopDocumentInboxPolling() if (typeof window === 'undefined') { return } refreshTimer = window.setInterval(() => { void refreshDocumentInbox() }, intervalMs) } function stopDocumentInboxPolling() { if (refreshTimer && typeof window !== 'undefined') { window.clearInterval(refreshTimer) refreshTimer = null } } return { hasUnread, loading, refreshDocumentInbox, startDocumentInboxPolling, stopDocumentInboxPolling, unreadCount } }