Untitled
unknown
plain_text
9 months ago
20 kB
10
Indexable
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import {
DocumentIcon,
CalendarIcon,
EyeIcon,
PencilSquareIcon,
MapPinIcon,
TrashIcon,
LockClosedIcon,
} 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 Dialog from '@/components/ui/Dialog/Dialog.vue'
import Button from '@/components/ui/button/Button.vue'
import toast from '@/components/ui/message/toast'
import { useAuthStore } from '@/components/store/authStore'
import {
allDocuments,
currentPage,
itemsPerPage,
totalItems,
isLoading,
fetchAllDocuments,
deleteDocument,
isConfidential,
formatDisplayDate,
formatReferredOffice,
} from './document' //
const isEditDrawerOpen = ref(false)
const isViewDrawerOpen = ref(false)
const documentToView = ref(null)
const documentToEdit = ref(null)
const isTimelineOpen = ref(false)
const selectedDocumentId = ref<number | null>(null)
const selectedDocumentNo = ref<string | null>(null)
// Delete Modal State
const isDeleteModalOpen = ref(false)
const documentToDelete = ref<{ id: number; docNo: string } | null>(null)
// Auth Computeds
const authStore = useAuthStore()
const isGuest = computed(() => authStore.user.value?.role === 'guest')
const isAdmin = computed(() => authStore.user.value?.role === 'admin')
const openEditDrawer = (doc: any) => {
documentToEdit.value = doc
isEditDrawerOpen.value = true
}
const closeEditDrawer = () => {
isEditDrawerOpen.value = false
documentToEdit.value = null
fetchAllDocuments()
}
const openViewDrawer = (doc: any) => {
documentToView.value = doc
isViewDrawerOpen.value = true
}
const closeViewDrawer = () => {
isViewDrawerOpen.value = false
documentToView.value = null
}
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
}
const openDeleteModal = (docId: number, docNo: string) => {
documentToDelete.value = { id: docId, docNo: docNo }
isDeleteModalOpen.value = true
}
const closeDeleteModal = () => {
isDeleteModalOpen.value = false
documentToDelete.value = null
}
const confirmDeleteDocument = async () => {
if (!documentToDelete.value || !isAdmin.value) {
toast.addToast({ title: 'Error', description: 'Unauthorized or missing document details.', type: 'error' })
closeDeleteModal()
return
}
await deleteDocument(documentToDelete.value.id, documentToDelete.value.docNo)
documentToDelete.value = null
}
const handlePageChange = (page: number) => {
currentPage.value = page
}
const handleItemsPerPageChange = (items: number) => {
itemsPerPage.value = items
currentPage.value = 1
}
watch([currentPage, itemsPerPage], () => {
fetchAllDocuments()
})
onMounted(() => {
fetchAllDocuments()
})
</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">
Classification
</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">
<div class="h-6 bg-gray-200 rounded w-20 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 v-if="isAdmin" 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 v-if="isAdmin" 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">
Classification
</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-center">
<span v-if="isConfidential(doc)"
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
<LockClosedIcon class="w-4 h-4 mr-1" />
Confidential
</span>
<span v-else
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
General
</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" />
{{ formatDisplayDate(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 v-if="formatDisplayDate(doc.arta_deadline) !== 'Not Applicable'"
class="w-4 h-4 mr-1 text-gray-400" />
{{ formatDisplayDate(doc.arta_deadline) }}
</span>
<span v-else>—</span>
</td>
<td class="px-4 py-3 text-sm text-gray-700 text-center">
{{ formatReferredOffice(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 v-if="isAdmin" @click="openDeleteModal(doc.incoming_initial_id, doc.document_no)"
class="text-gray-500 hover:text-red-600 p-2 rounded-full hover:bg-gray-100 transition-colors duration-200"
title="Delete Document">
<TrashIcon 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="flex items-center space-x-2 mt-1">
<span v-if="isConfidential(doc)"
class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800">
<LockClosedIcon class="w-3 h-3 mr-1" />
Confidential
</span>
<span v-else
class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">
General
</span>
</div>
</div>
</div>
<span class="text-xs font-semibold text-gray-800">
{{ formatReferredOffice(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-between text-xs text-gray-600 mt-2">
<div v-if="doc.document_deadline" class="flex items-center space-x-1">
<CalendarIcon class="w-4 h-4 text-gray-400" />
<span>Doc Deadline: {{ formatDisplayDate(doc.document_deadline) }}</span>
</div>
<div v-if="doc.arta_deadline" class="flex items-center space-x-1">
<CalendarIcon v-if="formatDisplayDate(doc.arta_deadline) !== 'Not Applicable'"
class="w-4 h-4 text-gray-400" />
<span>ARTA Deadline: {{ formatDisplayDate(doc.arta_deadline) }}</span>
</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 v-if="isAdmin" @click="openDeleteModal(doc.incoming_initial_id, doc.document_no)"
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="Delete Document">
<TrashIcon 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 v-if="documentToEdit" :is-open="isEditDrawerOpen" :document="documentToEdit"
@close="closeEditDrawer" />
<ViewDocumentDrawer :is-open="isViewDrawerOpen" :document="documentToView" @close="closeViewDrawer" />
<DocumentTimelineTracker :is-open="isTimelineOpen" :document-id="selectedDocumentId"
:document-no="selectedDocumentNo" @close="closeTimeline" description="" />
<Dialog :is-open="isDeleteModalOpen" type="modal" title="Confirm Document Deletion" @close="closeDeleteModal"
z-index="z-[10001]" :prevent-close-on-overlay-click="true" description="">
<div class="mt-2">
<div class="flex items-start space-x-3">
<TrashIcon class="w-6 h-6 mt-1 text-red-600 flex-shrink-0" />
<p class="text-sm text-gray-500">
You are about to **permanently delete** document
<span class="font-bold text-red-700">"{{ documentToDelete?.docNo }}"</span>. This action
**cannot be undone**.
<br />
Are you sure you want to proceed?
</p>
</div>
</div>
<div class="mt-6 flex justify-end space-x-3">
<Button @click="closeDeleteModal" variant="danger" class="hover:bg-gray-100">
Cancel
</Button>
<Button @click="confirmDeleteDocument" variant="success" :is-loading="isLoading">
<TrashIcon class="w-5 h-5 mr-1" />
Delete Document
</Button>
</div>
</Dialog>
</div>
</template>Editor is loading...
Leave a Comment