Untitled
unknown
plain_text
9 months ago
18 kB
15
Indexable
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import {
DocumentIcon,
CalendarIcon,
EyeIcon,
PencilSquareIcon,
MapPinIcon,
} from '@heroicons/vue/24/outline'
import Pagination from '@/components/ui/pagination/Pagination.vue'
import DocumentTimelineTracker from '@/components/Dashboard/TimelineTracker/DocumentTimelineTracker.vue'
import EditdocumentDrawer from '@/components/Dashboard/IncomingDocuments/IncomingDocument(ForEdit)/EditDocumentDrawer.vue'
import ViewDocumentDrawer from '@/components/Dashboard/IncomingDocuments/IncomingDocument(ForView)/ViewDocumentDrawer.vue'
import { useAuthStore } from '@/components/store/authStore'
import apiClient from '@/services/api'
// Component state
const allDocuments = ref([])
const currentPage = ref(1)
const itemsPerPage = ref(10)
const totalItems = ref(0)
const isTimelineOpen = ref(false)
const selectedDocumentId = ref<number | null>(null)
const selectedDocumentNo = ref<string | null>(null)
const isLoading = ref(true)
const isEditDrawerOpen = ref(false)
const isViewDrawerOpen = ref(false)
const documentToView = ref(null) // 🆕 NEW: Document for view drawer
const documentToEdit = ref(null)
const authStore = useAuthStore()
const isGuest = computed(() => authStore.user.value?.role === 'guest')
const formatReferredOffice = (referredOffice: any, doc: any) => {
 if (!doc) return '—'
 const reroutedOffices = parseOffices(doc.rerouted?.office_rerouted)
 if (reroutedOffices.length > 0) {
  // Show Rerouted offices
  return reroutedOffices.join(', ')
 }
 const referredOffices = parseOffices(doc.referred_office)
 const ardMsOffices = ['Admin Division', 'Finance Division', 'Legal Division', 'PMD', 'RSCIG']
 const ardTsOffices = ['CDD', 'ED', 'LPDD', 'SMD', 'OARDTS-Task Force']
 if (referredOffices.includes('ARD MS') && referredOffices.includes('ARD TS')) {
  const oardData = doc.oard || {}
  const finalActionOffices = parseOffices(oardData.final_action_office)
  const hasArdMsSelection = finalActionOffices.some((office) =>
   ardMsOffices.map((o) => o.toUpperCase()).includes(office.toUpperCase()),
  )
  const hasArdTsSelection = finalActionOffices.some((office) =>
   ardTsOffices.map((o) => o.toUpperCase()).includes(office.toUpperCase()),
  )
  if (hasArdMsSelection && !hasArdTsSelection) {
   return 'Waiting for ARD TS'
  }
  // Case 2: ARD TS has selected but ARD MS hasn't
  if (!hasArdMsSelection && hasArdTsSelection) {
   return 'Waiting for ARD MS'
  }
  if (finalActionOffices.length > 0) {
   return finalActionOffices.join(', ')
  }
 }
 const cleanOffices = referredOffices.filter((office) => office && office.trim() !== '')
 return cleanOffices.length > 0 ? cleanOffices.join(', ') : '—'
}
// Helper function to parse office strings/arrays
const parseOffices = (officeData: any): string[] => {
if (!officeData) return []
try {
if (typeof officeData === 'string') {
return JSON.parse(officeData)
}
if (Array.isArray(officeData)) {
return officeData
}
} catch (e) {
console.warn('Error parsing offices:', e)
}
return []
}
// Data fetching
const fetchAllDocuments = async () => {
try {
isLoading.value = true
const response = await apiClient.get('/dms/documents', {
params: {
page: currentPage.value,
per_page: itemsPerPage.value,
sort_by: 'created_at',
sort_order: 'desc',
},
})
allDocuments.value = response.data.data
totalItems.value = response.data.total
currentPage.value = response.data.current_page
} catch (error) {
console.error('Failed to fetch documents:', error)
} finally {
isLoading.value = false
}
}
// Watch for page changes to refetch data
watch([currentPage, itemsPerPage], () => {
fetchAllDocuments()
})
onMounted(() => {
fetchAllDocuments()
})
// Function to open the edit drawer and set the selected document
const openEditDrawer = (doc: any) => {
documentToEdit.value = doc
isEditDrawerOpen.value = true
}
// Function to close the edit drawer and refresh the document list
const closeEditDrawer = () => {
isEditDrawerOpen.value = false
documentToEdit.value = null
fetchAllDocuments()
}
// 🆕 NEW: Function to open the view drawer
const openViewDrawer = (doc: any) => {
documentToView.value = doc
isViewDrawerOpen.value = true
}
// 🆕 NEW: Function to close the view drawer
const closeViewDrawer = () => {
isViewDrawerOpen.value = false
documentToView.value = null
}
// Timeline actions
const openTimeline = (documentId: number, documentNo: string) => {
selectedDocumentId.value = documentId
selectedDocumentNo.value = documentNo
isTimelineOpen.value = true
}
const closeTimeline = () => {
isTimelineOpen.value = false
selectedDocumentId.value = null
selectedDocumentNo.value = null
}
// Pagination logic
const handlePageChange = (page: number) => {
currentPage.value = page
}
const handleItemsPerPageChange = (items: number) => {
itemsPerPage.value = items
currentPage.value = 1
}
</script>
<template>
<div class="bg-white rounded-lg shadow p-4 md:p-6 font-sans">
<div class="flex flex-col sm:flex-row sm:items-center justify-between mb-4 md:mb-6">
<h2 class="text-lg md:text-xl font-medium text-gray-900 mb-2 sm:mb-0">Incoming Documents</h2>
</div>
<div v-if="isLoading">
<div class="hidden lg:block overflow-x-auto rounded-lg">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider rounded-tl-lg"
>
Document No.
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Subject
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Document Deadline
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider"
>
ARTA Deadline
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Referred To
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider rounded-tr-lg"
>
Actions
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100 animate-pulse">
<tr v-for="n in 5" :key="n" class="hover:bg-gray-50 transition-colors">
<td class="px-4 py-3 whitespace-nowrap text-sm text-center">
<div class="h-4 bg-gray-200 rounded w-24 mx-auto"></div>
</td>
<td class="px-4 py-3 text-sm text-center max-w-xs">
<div class="h-4 bg-gray-200 rounded w-32 mx-auto"></div>
</td>
<td class="px-4 py-3 text-sm text-center">
<div class="h-4 bg-gray-200 rounded w-20 mx-auto"></div>
</td>
<td class="px-4 py-3 text-sm text-center">
<div class="h-4 bg-gray-200 rounded w-20 mx-auto"></div>
</td>
<td class="px-4 py-3 text-sm text-center">
<div class="h-4 bg-gray-200 rounded w-16 mx-auto"></div>
</td>
<td class="px-4 py-3 text-sm text-center">
<div class="flex justify-center space-x-2">
<div class="w-8 h-8 bg-gray-200 rounded-full"></div>
<div class="w-8 h-8 bg-gray-200 rounded-full"></div>
<div class="w-8 h-8 bg-gray-200 rounded-full"></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="block lg:hidden space-y-3">
<div
v-for="n in 5"
:key="n"
class="bg-white border border-gray-200 rounded-lg p-4 shadow-sm animate-pulse"
>
<div class="flex items-start justify-between mb-3">
<div class="flex items-center space-x-2 min-w-0 flex-1">
<div class="w-5 h-5 bg-gray-200 rounded-full"></div>
<div class="min-w-0 flex-1">
<div class="h-4 bg-gray-200 rounded w-24 mb-1"></div>
<div class="h-3 bg-gray-200 rounded w-16"></div>
</div>
</div>
<div class="h-4 bg-gray-200 rounded w-12"></div>
</div>
<div class="mb-3">
<div class="h-4 bg-gray-200 rounded w-20 mb-1"></div>
<div class="h-4 bg-gray-200 rounded w-full"></div>
</div>
<div class="flex justify-end space-x-2 pt-3 border-t border-gray-100">
<div class="w-8 h-8 bg-gray-200 rounded-full"></div>
<div class="w-8 h-8 bg-gray-200 rounded-full"></div>
<div class="w-8 h-8 bg-gray-200 rounded-full"></div>
</div>
</div>
</div>
</div>
<div
v-else-if="allDocuments.length === 0"
class="text-center py-12 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50"
>
<DocumentIcon class="mx-auto h-10 w-10 text-gray-400" />
<h3 class="mt-2 text-base font-medium text-gray-900">No Incoming Documents Found</h3>
<p class="mt-1 text-sm text-gray-500">Documents logged into the system will appear here.</p>
</div>
<div v-else>
<div class="hidden lg:block overflow-x-auto rounded-lg">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider rounded-tl-lg"
>
Document No.
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Subject
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Document Deadline
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider"
>
ARTA Deadline
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Referred To
</th>
<th
class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider rounded-tr-lg"
>
Actions
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100">
<tr
v-for="doc in allDocuments"
:key="doc.incoming_initial_id"
class="hover:bg-gray-50 transition-colors"
>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700 text-center">
<span class="inline-flex items-center">
<DocumentIcon class="w-4 h-4 mr-1 text-gray-400" />
{{ doc.document_no }}
</span>
</td>
<td class="px-4 py-3 text-sm text-gray-700 text-center max-w-xs">
<div class="truncate" :title="doc.subject">{{ doc.subject }}</div>
</td>
<td class="px-4 py-3 text-sm text-gray-700 text-center">
<span v-if="doc.document_deadline" class="inline-flex items-center">
<CalendarIcon class="w-4 h-4 mr-1 text-gray-400" />
{{ doc.document_deadline }}
</span>
<span v-else>—</span>
</td>
<td class="px-4 py-3 text-sm text-gray-700 text-center">
<span v-if="doc.arta_deadline" class="inline-flex items-center">
<CalendarIcon class="w-4 h-4 mr-1 text-gray-400" />
{{ doc.arta_deadline }}
</span>
<span v-else>—</span>
</td>
<td class="px-4 py-3 text-sm text-gray-700 text-center">
{{ formatReferredOffice(doc.current_office, doc) }}
</td>
<td class="px-4 py-3 text-sm text-center">
<div class="flex justify-center space-x-2">
<button
@click="openViewDrawer(doc)"
class="text-gray-500 hover:text-blue-600 p-2 rounded-full hover:bg-gray-100 transition-colors duration-200"
title="View"
>
<EyeIcon class="w-5 h-5" />
</button>
<button
:disabled="isGuest"
:class="{
'text-gray-500 hover:text-blue-600 p-2 rounded-full hover:bg-gray-100 transition-colors duration-200':
!isGuest,
'opacity-50 cursor-not-allowed': isGuest,
}"
title="Edit Document"
@click="openEditDrawer(doc)"
>
<PencilSquareIcon class="w-5 h-5" />
</button>
<button
class="text-red-500 hover:text-red-600 p-2 rounded-full hover:bg-gray-100 transition-colors duration-200"
title="Track Location"
@click="openTimeline(doc.incoming_initial_id, doc.document_no)"
>
<MapPinIcon class="w-5 h-5" />
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="block lg:hidden space-y-3">
<div
v-for="doc in allDocuments"
:key="doc.incoming_initial_id"
class="bg-white border border-gray-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow duration-200"
>
<div class="flex items-start justify-between mb-3">
<div class="flex items-center space-x-2 min-w-0 flex-1">
<DocumentIcon class="w-5 h-5 text-gray-400 flex-shrink-0" />
<div class="min-w-0 flex-1">
<div class="text-sm font-semibold text-gray-900">
{{ doc.document_no }}
</div>
<div class="text-xs text-gray-500">Document Number</div>
</div>
</div>
<span class="text-xs font-semibold text-gray-800">
{{ formatReferredOffice(doc.current_office, doc) }}
</span>
</div>
<div class="mb-3">
<div class="text-sm font-medium text-gray-700 mb-1">Subject</div>
<div class="text-sm text-gray-600 leading-relaxed">
{{ doc.subject }}
</div>
</div>
<div class="flex justify-end space-x-2 pt-3 border-t border-gray-100">
<button
@click="openViewDrawer(doc)"
class="flex items-center justify-center text-gray-500 hover:text-blue-600 p-3 rounded-lg hover:bg-blue-50 transition-all duration-200 min-w-[44px]"
title="View Document"
>
<EyeIcon class="w-5 h-5" />
</button>
<button
:disabled="isGuest"
:class="{
'flex items-center justify-center text-gray-500 hover:text-blue-600 p-3 rounded-lg hover:bg-blue-50 transition-all duration-200 min-w-[44px]':
!isGuest,
'opacity-50 cursor-not-allowed': isGuest,
}"
title="Edit Document"
@click="openEditDrawer(doc)"
>
<PencilSquareIcon class="w-5 h-5" />
</button>
<button
class="flex items-center justify-center text-red-500 hover:text-red-600 p-3 rounded-lg hover:bg-red-50 transition-all duration-200 min-w-[44px]"
title="Track Location"
@click="openTimeline(doc.incoming_initial_id, doc.document_no)"
>
<MapPinIcon class="w-5 h-5" />
</button>
</div>
</div>
</div>
<div class="mt-8 border-gray-200 pt-6 overflow-x-auto">
<Pagination
:total-items="totalItems"
:initial-page="currentPage"
:initial-items-per-page="itemsPerPage"
@page-change="handlePageChange"
@items-per-page-change="handleItemsPerPageChange"
/>
</div>
</div>
<EditdocumentDrawer
:is-open="isEditDrawerOpen"
:document="documentToEdit"
@close="closeEditDrawer"
/>
<ViewDocumentDrawer
:is-open="isViewDrawerOpen"
:document="documentToView"
@close="closeViewDrawer"
/>
<DocumentTimelineTracker
:is-open="isTimelineOpen"
:document-no="selectedDocumentNo"
document-type="incoming"
@close="closeTimeline"
/>
</div>
</template>
Editor is loading...
Leave a Comment