Untitled
unknown
plain_text
10 months ago
16 kB
16
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 { 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)
// State for managing the edit drawer
const isEditDrawerOpen = ref(false)
const documentToEdit = ref(null)
// Auth store and permissions
const authStore = useAuthStore()
const isGuest = computed(() => authStore.user.value?.role === 'guest')
// š§ NEW: Helper function to format referred office array
const formatReferredOffice = (referredOffice: any) => {
if (!referredOffice) return 'ā'
// If it's already a string and looks clean, return it
if (typeof referredOffice === 'string' && !referredOffice.includes('[') && !referredOffice.includes('"')) {
return referredOffice
}
let offices = []
try {
// If it's a string that looks like JSON, parse it
if (typeof referredOffice === 'string') {
if (referredOffice.startsWith('[') && referredOffice.endsWith(']')) {
offices = JSON.parse(referredOffice)
} else {
// Handle comma-separated values
offices = referredOffice.split(',').map(item => item.trim())
}
} else if (Array.isArray(referredOffice)) {
offices = referredOffice
}
// Filter out empty values and join with commas
const cleanOffices = offices.filter(office => office && office.trim() !== '')
return cleanOffices.length > 0 ? cleanOffices.join(', ') : 'ā'
} catch (error) {
console.warn('Error parsing referred office:', error)
return referredOffice || 'ā'
}
}
// 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()
}
// 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>
<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>
<!-- š§ FIXED: Use the helper function to format referred office -->
<td class="px-4 py-3 text-sm text-gray-700 text-center">
{{ formatReferredOffice(doc.current_office) }}
</td>
<td class="px-4 py-3 text-sm text-center">
<div class="flex justify-center space-x-2">
<button
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>
<!-- š§ FIXED: Use the helper function for mobile view too -->
<span class="text-xs font-semibold text-gray-800">
{{ formatReferredOffice(doc.referred_office) }}
</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
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"
/>
<DocumentTimelineTracker
:is-open="isTimelineOpen"
:document-no="selectedDocumentNo"
document-type="incoming"
@close="closeTimeline"
/>
</div>
</template>Editor is loading...
Leave a Comment