Untitled

 avatar
unknown
plain_text
9 months ago
20 kB
12
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