From 1aac9905e3a526eefec80e588835f7c498489a8e Mon Sep 17 00:00:00 2001 From: Aiman Fakhrullah Mantasan Date: Fri, 30 May 2025 19:14:03 +0800 Subject: [PATCH] Added File Preview, Hiearchy View, Access Type Display --- .../dms/dialogs/DMSAccessRequestDialog.vue | 235 ++-- components/dms/explorer/DMSExplorer.vue | 1248 ++++++++++++----- pages/dms/index.vue | 1 + stores/dms.js | 51 + 4 files changed, 1070 insertions(+), 465 deletions(-) diff --git a/components/dms/dialogs/DMSAccessRequestDialog.vue b/components/dms/dialogs/DMSAccessRequestDialog.vue index 69ba659..a45cc8a 100644 --- a/components/dms/dialogs/DMSAccessRequestDialog.vue +++ b/components/dms/dialogs/DMSAccessRequestDialog.vue @@ -3,7 +3,7 @@ import { ref, computed } from 'vue'; import { useDmsStore } from '~/stores/dms'; const props = defineProps({ - document: { + item: { type: Object, required: true }, @@ -13,63 +13,63 @@ const props = defineProps({ } }); -const emit = defineEmits(['close', 'requestSubmitted']); +const emit = defineEmits(['close', 'submit']); // Store const dmsStore = useDmsStore(); // Form state -const selectedAccessLevel = ref('view'); +const selectedAccessType = ref('view'); +const accessDuration = ref('7 days'); const justification = ref(''); const isSubmitting = ref(false); const formError = ref(''); -// Access level options -const accessLevels = [ +// Access type options +const accessTypes = [ { id: 'view', label: 'View Only', description: 'Can only view the document' }, - { id: 'download', label: 'Download', description: 'Can view and download the document' }, - { id: 'print', label: 'Print', description: 'Can view, download, and print the document' }, - { id: 'edit', label: 'Edit', description: 'Can view, download, print, and edit the document' }, - { id: 'full', label: 'Full Access', description: 'Has complete control over the document' } + { id: 'download', label: 'Download', description: 'Can view and download' }, + { id: 'print', label: 'Print', description: 'Can view and print' }, + { id: 'full', label: 'Full Access', description: 'View, download and print' } +]; + +// Access duration options +const durationOptions = [ + '7 days', + '14 days', + '30 days', + '60 days', + '90 days', + 'Permanent' ]; // Computed properties -const documentTitle = computed(() => { - return props.document?.name || 'Document'; +const itemTitle = computed(() => { + return props.item?.name || 'Document'; }); -const documentIconSvg = computed(() => { - const extension = props.document?.extension?.toLowerCase(); - - if (extension === 'pdf') - return ''; - - if (['doc', 'docx'].includes(extension)) - return ''; - - if (['xls', 'xlsx'].includes(extension)) - return ''; - - if (['ppt', 'pptx'].includes(extension)) - return ''; - - return ''; -}); - -const currentAccessLevel = computed(() => { - // This would be determined from actual permissions in production - return 'None'; +const itemFileName = computed(() => { + if (props.item?.type === 'file') { + return props.item.name; + } + return ''; }); // Methods const closeDialog = () => { + // Reset form + selectedAccessType.value = 'view'; + accessDuration.value = '7 days'; + justification.value = ''; + formError.value = ''; + emit('close'); }; const submitRequest = async () => { // Validate form - if (!selectedAccessLevel.value) { - formError.value = 'Please select an access level'; + if (!selectedAccessType.value) { + formError.value = 'Please select an access type'; return; } @@ -83,12 +83,17 @@ const submitRequest = async () => { try { // Submit the request to the store - const request = await dmsStore.requestAccess(props.document.id, selectedAccessLevel.value); + const request = await dmsStore.requestAccess( + props.item.id, + selectedAccessType.value, + justification.value, + accessDuration.value + ); // Emit success event - emit('requestSubmitted', request); + emit('submit', request); - // Close the dialog + // Close the dialog (this will also reset the form) closeDialog(); } catch (error) { formError.value = 'Failed to submit access request. Please try again.'; @@ -107,79 +112,121 @@ const submitRequest = async () => { size="md" > diff --git a/components/dms/explorer/DMSExplorer.vue b/components/dms/explorer/DMSExplorer.vue index 0f392ed..5f8cb9e 100644 --- a/components/dms/explorer/DMSExplorer.vue +++ b/components/dms/explorer/DMSExplorer.vue @@ -4,6 +4,7 @@ import { useRouter } from 'vue-router'; import DMSDocumentViewer from '~/components/dms/viewers/DMSDocumentViewer.vue'; import DMSUploadDialog from '~/components/dms/dialogs/DMSUploadDialog.vue'; import DMSCreateNewDialog from '~/components/dms/dialogs/DMSCreateNewDialog.vue'; +import DMSAccessRequestDialog from '~/components/dms/dialogs/DMSAccessRequestDialog.vue'; // Props for the explorer const props = defineProps({ @@ -14,6 +15,10 @@ const props = defineProps({ viewMode: { type: String, default: 'list', // list, grid, details + }, + activeDocumentTab: { + type: String, + default: 'all' // all, public, private, personal } }); @@ -29,6 +34,14 @@ const treeExpanded = ref({}); const sortBy = ref('name'); // name, modified, size, type const sortOrder = ref('asc'); // asc, desc const searchQuery = ref(''); +const showAccessRequestDialog = ref(false); +const accessRequestItem = ref(null); + +// Navigation history for back/forward functionality +const navigationHistory = ref([props.initialPath]); +const historyIndex = ref(0); +const canGoBack = computed(() => historyIndex.value > 0); +const canGoForward = computed(() => historyIndex.value < navigationHistory.value.length - 1); // Dialog states const showDocumentViewer = ref(false); @@ -40,10 +53,562 @@ const currentDocument = ref(null); const allItems = ref([]); const router = useRouter(); +// Methods +const loadItems = async () => { + isLoading.value = true; + try { + // Replace with actual API call + // const { data } = await useFetch('/api/dms/items', { + // params: { path: currentPath.value } + // }); + // allItems.value = data.value; + + // Mock data for development + await new Promise(resolve => setTimeout(resolve, 500)); // Simulate API delay + + // Enhanced mock data with examples for each access type + allItems.value = [ + // PUBLIC ACCESS EXAMPLES + { + id: 'public-cabinet1', + name: 'Public Documents', + type: 'cabinet', + parentPath: '/', + description: 'Publicly accessible documents and policies', + tags: ['Public', 'General'], + accessType: 'public', + created: '2023-01-15', + modified: '2023-10-15', + icon: 'unlock' + }, + { + id: 'public-drawer1', + name: 'Government Policies', + type: 'drawer', + parentPath: '/public-cabinet1', + description: 'Public government policies and regulations', + tags: ['Policy', 'Government', 'Public'], + accessType: 'public', + created: '2023-03-01', + modified: '2023-09-30' + }, + { + id: 'public-file1', + name: 'National_Infrastructure_Policy_2023.pdf', + type: 'file', + extension: 'pdf', + size: '2.1MB', + parentPath: '/public-cabinet1/public-drawer1', + description: 'National infrastructure development policy', + tags: ['Infrastructure', 'Policy', 'Public'], + accessType: 'public', + author: 'Ministry of Works', + department: 'Policy Division', + created: '2023-01-10', + modified: '2023-03-15' + }, + { + id: 'public-file2', + name: 'Public_Tender_Guidelines.docx', + type: 'file', + extension: 'docx', + size: '890KB', + parentPath: '/public-cabinet1/public-drawer1', + description: 'Guidelines for public tender processes', + tags: ['Tender', 'Guidelines', 'Public'], + accessType: 'public', + author: 'Procurement Department', + department: 'Finance Division', + created: '2023-02-05', + modified: '2023-08-20' + }, + + // PERSONAL ACCESS EXAMPLES + { + id: 'personal-cabinet1', + name: 'JKR Cawangan Kota Bharu', + type: 'cabinet', + parentPath: '/', + description: 'Documents for Kota Bharu branch staff', + tags: ['Personal', 'Kota Bharu'], + accessType: 'personal', + created: '2023-02-20', + modified: '2023-10-10', + hasAccess: true + }, + { + id: 'personal-drawer1', + name: 'Engineering Projects', + type: 'drawer', + parentPath: '/personal-cabinet1', + description: 'Civil engineering project documents', + tags: ['Engineering', 'Projects', 'Personal'], + accessType: 'personal', + created: '2023-04-01', + modified: '2023-08-15' + }, + { + id: 'personal-file1', + name: 'Bridge_Construction_KB_2023.pdf', + type: 'file', + extension: 'pdf', + size: '5.2MB', + parentPath: '/personal-cabinet1/personal-drawer1', + description: 'Kota Bharu bridge construction project plan', + tags: ['Bridge', 'Construction', 'Kota Bharu'], + accessType: 'personal', + author: 'Ahmad bin Rahman', + department: 'Civil Engineering', + created: '2023-05-15', + modified: '2023-09-10' + }, + { + id: 'personal-file2', + name: 'Staff_Meeting_Minutes_Oct2023.docx', + type: 'file', + extension: 'docx', + size: '156KB', + parentPath: '/personal-cabinet1/personal-drawer1', + description: 'Monthly staff meeting minutes', + tags: ['Meeting', 'Minutes', 'Internal'], + accessType: 'personal', + author: 'Fatimah binti Ali', + department: 'Administration', + created: '2023-10-05', + modified: '2023-10-05' + }, + + // PRIVATE ACCESS EXAMPLES + { + id: 'private-cabinet1', + name: 'JKR Headquarters Confidential', + type: 'cabinet', + parentPath: '/', + description: 'Confidential headquarters documents', + tags: ['Private', 'Confidential', 'HQ'], + accessType: 'private', + created: '2023-03-10', + modified: '2023-10-01', + hasAccess: false, + accessRequestStatus: 'pending', + isLocked: true + }, + { + id: 'private-drawer1', + name: 'Budget & Financial Reports', + type: 'drawer', + parentPath: '/private-cabinet1', + description: 'Confidential financial documents', + tags: ['Financial', 'Budget', 'Confidential'], + accessType: 'private', + created: '2023-06-01', + modified: '2023-09-25', + hasAccess: false, + isLocked: true + }, + { + id: 'private-file1', + name: 'Annual_Budget_2024_CONFIDENTIAL.xlsx', + type: 'file', + extension: 'xlsx', + size: '3.8MB', + parentPath: '/private-cabinet1/private-drawer1', + description: 'Confidential annual budget allocation', + tags: ['Budget', 'Confidential', '2024'], + accessType: 'private', + author: 'Director General', + department: 'Finance', + created: '2023-08-01', + modified: '2023-09-15', + hasAccess: false, + isLocked: true + }, + + // MIXED ACCESS EXAMPLES FOR DEMONSTRATION + { + id: 'folder1', + name: 'IT Department', + type: 'folder', + parentPath: '/personal-cabinet1', + description: 'Information technology department files', + tags: ['IT', 'Technology'], + accessType: 'personal', + created: '2023-04-10', + modified: '2023-09-01' + }, + { + id: 'file3', + name: 'Network_Infrastructure_Plan.pdf', + type: 'file', + extension: 'pdf', + size: '1.9MB', + parentPath: '/personal-cabinet1/folder1', + description: 'Network infrastructure upgrade plan', + tags: ['Network', 'Infrastructure', 'IT'], + accessType: 'personal', + author: 'Omar bin Hassan', + department: 'IT Department', + created: '2023-07-12', + modified: '2023-08-30' + } + ]; + + } catch (error) { + console.error('Error loading items:', error); + } finally { + isLoading.value = false; + } +}; + +const parseFileSize = (size) => { + if (typeof size === 'number') return size; + if (!size || typeof size !== 'string') return 0; + + const units = { 'B': 1, 'KB': 1024, 'MB': 1024*1024, 'GB': 1024*1024*1024 }; + const match = size.match(/^([\d.]+)\s*([A-Z]+)$/i); + + if (match) { + const value = parseFloat(match[1]); + const unit = match[2].toUpperCase(); + return value * (units[unit] || 1); + } + + return parseFloat(size) || 0; +}; + +const selectItem = (item) => { + selectedItem.value = item; + emit('itemSelected', item); +}; + +const goBack = () => { + if (canGoBack.value) { + historyIndex.value--; + currentPath.value = navigationHistory.value[historyIndex.value]; + autoExpandTreeForPath(currentPath.value); + emit('pathChanged', currentPath.value); + } +}; + +const goForward = () => { + if (canGoForward.value) { + historyIndex.value++; + currentPath.value = navigationHistory.value[historyIndex.value]; + autoExpandTreeForPath(currentPath.value); + emit('pathChanged', currentPath.value); + } +}; + +const navigateTo = (path) => { + if (path !== currentPath.value) { + // Remove any forward history if we're navigating to a new path + if (historyIndex.value < navigationHistory.value.length - 1) { + navigationHistory.value = navigationHistory.value.slice(0, historyIndex.value + 1); + } + + // Add new path to history + navigationHistory.value.push(path); + historyIndex.value = navigationHistory.value.length - 1; + + currentPath.value = path; + + // Auto-expand tree to show current path + autoExpandTreeForPath(path); + + emit('pathChanged', path); + } +}; + +// Auto-expand tree items to show the current path +const autoExpandTreeForPath = (path) => { + const segments = path.split('/').filter(Boolean); + segments.forEach((segment, index) => { + const itemId = segments.slice(0, index + 1).join('-'); + if (itemId) { + treeExpanded.value[itemId] = true; + } + }); +}; + +const navigateToItem = (item) => { + // Navigate to the item's location + const newPath = item.parentPath + '/' + item.id; + navigateTo(newPath); + selectItem(item); +}; + +const toggleTreeItem = (itemId) => { + treeExpanded.value[itemId] = !treeExpanded.value[itemId]; +}; + +const changeViewMode = (mode) => { + viewMode.value = mode; + emit('viewModeChanged', mode); +}; + +const setSortBy = (field) => { + if (sortBy.value === field) { + // Toggle sort order if clicking the same field + sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc'; + } else { + // Set new field with ascending order + sortBy.value = field; + sortOrder.value = 'asc'; + } +}; + +const openFileViewer = (file) => { + currentDocument.value = file; + showDocumentViewer.value = true; +}; + +const closeFileViewer = () => { + showDocumentViewer.value = false; + currentDocument.value = null; +}; + +const handleUpload = (fileData) => { + console.log('Uploading file:', fileData); + // Handle file upload logic here + // Add to allItems.value or refresh from API + loadItems(); +}; + +const handleCreateNew = (itemData) => { + console.log('Creating new item:', itemData); + // Handle create new item logic here + // Add to allItems.value or refresh from API + loadItems(); +}; + +const formatFileSize = (size) => { + if (!size) return '0 B'; + + const bytes = parseFileSize(size); + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let i = 0; + let formattedSize = bytes; + + while (formattedSize >= 1024 && i < units.length - 1) { + formattedSize /= 1024; + i++; + } + + return `${formattedSize.toFixed(i === 0 ? 0 : 1)} ${units[i]}`; +}; + +const requestAccess = (item) => { + // In a real app, this would make an API call to request access + console.log('Requesting access for:', item.name); + // For demo purposes, just update the status + item.accessRequestStatus = 'pending'; +}; + +// Get icon for item type +const getItemIcon = (item) => { + if (item.accessType === 'private' && item.isLocked) { + return 'lock'; + } + + if (item.accessType === 'public') { + switch (item.type) { + case 'cabinet': return 'unlock'; + case 'drawer': return 'folder'; + case 'folder': return 'folder'; + case 'file': return getFileIcon(item.extension); + default: return 'folder'; + } + } + + if (item.accessType === 'personal') { + switch (item.type) { + case 'cabinet': return 'user'; + case 'drawer': return 'folder'; + case 'folder': return 'folder'; + case 'file': return getFileIcon(item.extension); + default: return 'folder'; + } + } + + // Default for private access + switch (item.type) { + case 'cabinet': return 'lock'; + case 'drawer': return 'folderLock'; + case 'folder': return 'folderLock'; + case 'file': return 'fileLock'; + default: return 'lock'; + } +}; + +const getFileIcon = (extension) => { + switch (extension?.toLowerCase()) { + case 'pdf': return 'pdf'; + case 'doc': + case 'docx': return 'doc'; + case 'xls': + case 'xlsx': return 'excel'; + case 'ppt': + case 'pptx': return 'powerpoint'; + case 'jpg': + case 'jpeg': + case 'png': + case 'gif': return 'image'; + case 'txt': return 'text'; + default: return 'file'; + } +}; + +// Get SVG path based on icon type +const getSvgIcon = (iconType, size = 24) => { + const icons = { + back: ``, + forward: ``, + home: ``, + search: ``, + x: ``, + upload: ``, + plus: ``, + sortAsc: ``, + sortDesc: ``, + + // Access type specific icons + unlock: ``, + lock: ``, + user: ``, + folder: ``, + folderLock: ``, + + // File type icons + pdf: ``, + doc: ``, + excel: ``, + image: ``, + text: ``, + file: ``, + fileLock: ``, + + // Other icons + tag: ``, + cabinet: ``, + chevronDown: ``, + chevronRight: `` + }; + return icons[iconType] || icons.file; +}; + +const getAccessTypeCount = (accessType) => { + if (accessType === 'all') { + return allItems.value.length; + } + return allItems.value.filter(item => item.accessType === accessType).length; +}; + +// Lifecycle hooks +onMounted(() => { + loadItems(); + // Auto-expand tree for initial path + autoExpandTreeForPath(currentPath.value); +}); + +const getTabClasses = (tab) => { + if (props.activeDocumentTab === tab.id) { + switch (tab.color) { + case 'green': + return 'bg-green-100 text-green-700 dark:bg-green-900/20 dark:text-green-300 shadow-sm'; + case 'blue': + return 'bg-blue-100 text-blue-700 dark:bg-blue-900/20 dark:text-blue-300 shadow-sm'; + case 'red': + return 'bg-red-100 text-red-700 dark:bg-red-900/20 dark:text-red-300 shadow-sm'; + default: + return 'bg-gray-100 text-gray-700 dark:bg-gray-900/20 dark:text-gray-300 shadow-sm'; + } + } + return 'text-gray-600 hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700'; +}; + +const getTabIconClasses = (tab) => { + if (props.activeDocumentTab === tab.id) { + switch (tab.color) { + case 'green': + return 'text-green-600 dark:text-green-400'; + case 'blue': + return 'text-blue-600 dark:text-blue-400'; + case 'red': + return 'text-red-600 dark:text-red-400'; + default: + return 'text-gray-600 dark:text-gray-400'; + } + } + return ''; +}; + +const getTabBadgeClasses = (tab) => { + if (props.activeDocumentTab === tab.id) { + switch (tab.color) { + case 'green': + return 'bg-green-200 text-green-800 dark:bg-green-800 dark:text-green-200'; + case 'blue': + return 'bg-blue-200 text-blue-800 dark:bg-blue-800 dark:text-blue-200'; + case 'red': + return 'bg-red-200 text-red-800 dark:bg-red-800 dark:text-red-200'; + default: + return 'bg-gray-200 text-gray-800 dark:bg-gray-800 dark:text-gray-200'; + } + } + return 'bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-400'; +}; + +// Methods for access control +const checkItemAccess = (item) => { + // Check if item is private and user doesn't have access + if (item.accessType === 'private' && item.hasAccess === false) { + return false; + } + return true; +}; + +const handleItemClick = (item) => { + // Check access before allowing navigation + if (!checkItemAccess(item)) { + // Show access request dialog for private items + accessRequestItem.value = item; + showAccessRequestDialog.value = true; + return; + } + + // Allow normal navigation for accessible items + if (item.type === 'file') { + selectItem(item); + currentDocument.value = item; + showDocumentViewer.value = true; + } else { + navigateToItem(item); + } +}; + +const handleAccessRequestSubmitted = (request) => { + // Handle successful access request + showAccessRequestDialog.value = false; + accessRequestItem.value = null; + + // Could show a success message or update the item status + console.log('Access request submitted:', request); +}; + +const closeAccessRequestDialog = () => { + showAccessRequestDialog.value = false; + accessRequestItem.value = null; +}; + // Computed properties const currentItems = computed(() => { let items = allItems.value.filter(item => item.parentPath === currentPath.value); + // Apply access type filter from parent component + if (props.activeDocumentTab !== 'all') { + items = items.filter(item => item.accessType === props.activeDocumentTab); + } + // Apply search filter if (searchQuery.value.trim()) { const query = searchQuery.value.toLowerCase(); @@ -88,23 +653,51 @@ const currentItems = computed(() => { return items; }); +// Filtered tree items based on access type from parent +const filteredTreeItems = computed(() => { + if (props.activeDocumentTab === 'all') { + return allItems.value; + } + return allItems.value.filter(item => item.accessType === props.activeDocumentTab); +}); + const breadcrumbs = computed(() => { - // Split the path and create breadcrumb items - if (currentPath.value === '/') return [{ name: 'Home', path: '/' }]; + if (currentPath.value === '/') { + return [{ name: 'Home', path: '/' }]; + } - const paths = currentPath.value.split('/').filter(Boolean); - let breadcrumbPath = ''; + // Build breadcrumbs by looking up actual item names + const breadcrumbItems = [{ name: 'Home', path: '/' }]; + const pathSegments = currentPath.value.split('/').filter(Boolean); - return [ - { name: 'Home', path: '/' }, - ...paths.map(segment => { - breadcrumbPath += `/${segment}`; - return { - name: segment.charAt(0).toUpperCase() + segment.slice(1), - path: breadcrumbPath - }; - }) - ]; + let currentBreadcrumbPath = ''; + + pathSegments.forEach((segmentId, index) => { + currentBreadcrumbPath += `/${segmentId}`; + + // Find the item with this ID at the current path level + const item = allItems.value.find(item => + item.id === segmentId && + item.parentPath === (index === 0 ? '/' : breadcrumbItems[index].path) + ); + + if (item) { + breadcrumbItems.push({ + name: item.name, + path: currentBreadcrumbPath, + type: item.type, + accessType: item.accessType + }); + } else { + // Fallback to ID if item not found + breadcrumbItems.push({ + name: segmentId.charAt(0).toUpperCase() + segmentId.slice(1), + path: currentBreadcrumbPath + }); + } + }); + + return breadcrumbItems; }); const parentType = computed(() => { @@ -115,311 +708,32 @@ const parentType = computed(() => { if (pathParts.length === 3) return 'folder'; return 'subfolder'; }); - -// Methods -const loadItems = async () => { - isLoading.value = true; - try { - // Replace with actual API call - // const { data } = await useFetch('/api/dms/items', { - // params: { path: currentPath.value } - // }); - // allItems.value = data.value; - - // Mock data for development - await new Promise(resolve => setTimeout(resolve, 500)); // Simulate API delay - - // Enhanced mock data with more metadata - allItems.value = [ - { - id: 'cabinet1', - name: 'Administrative Cabinet', - type: 'cabinet', - parentPath: '/', - description: 'Contains administrative documents', - tags: ['Administrative', 'Public'], - created: '2023-01-15', - modified: '2023-10-15' - }, - { - id: 'cabinet2', - name: 'Technical Cabinet', - type: 'cabinet', - parentPath: '/', - description: 'Technical specifications and manuals', - tags: ['Technical', 'Confidential'], - created: '2023-02-20', - modified: '2023-10-10' - }, - { - id: 'drawer1', - name: 'Policy Documents', - type: 'drawer', - parentPath: '/cabinet1', - description: 'Company policies and procedures', - tags: ['Policy', 'Important'], - created: '2023-03-01', - modified: '2023-09-30' - }, - { - id: 'drawer2', - name: 'Financial Records', - type: 'drawer', - parentPath: '/cabinet1', - description: 'Financial statements and reports', - tags: ['Financial', 'Confidential'], - created: '2023-03-15', - modified: '2023-10-01' - }, - { - id: 'folder1', - name: 'HR Policies', - type: 'folder', - parentPath: '/cabinet1/drawer1', - description: 'Human resources policies', - tags: ['HR', 'Policy'], - created: '2023-04-01', - modified: '2023-08-15' - }, - { - id: 'folder2', - name: 'IT Policies', - type: 'folder', - parentPath: '/cabinet1/drawer1', - description: 'Information technology policies', - tags: ['IT', 'Technical'], - created: '2023-04-10', - modified: '2023-09-01' - }, - { - id: 'subfolder1', - name: 'Employee Handbook', - type: 'subfolder', - parentPath: '/cabinet1/drawer1/folder1', - description: 'Employee guidelines and procedures', - tags: ['Employee', 'Handbook'], - created: '2023-05-01', - modified: '2023-07-20' - }, - { - id: 'file1', - name: 'Employee_Handbook_2023.pdf', - type: 'file', - parentPath: '/cabinet1/drawer1/folder1/subfolder1', - extension: 'pdf', - size: '2.4 MB', - modified: '2023-10-15', - created: '2023-05-15', - author: 'HR Department', - department: 'Human Resources', - tags: ['Employee', 'Handbook', '2023'], - description: 'Complete employee handbook for 2023', - metadata: { - title: 'Employee Handbook 2023', - subject: 'HR policies and procedures', - category: 'Policy', - confidentiality: 'Internal' - } - }, - { - id: 'file2', - name: 'IT_Security_Policy.docx', - type: 'file', - parentPath: '/cabinet1/drawer1/folder2', - extension: 'docx', - size: '1.8 MB', - modified: '2023-09-20', - created: '2023-06-01', - author: 'IT Department', - department: 'Information Technology', - tags: ['Security', 'Policy', 'IT'], - description: 'Information technology security policies', - metadata: { - title: 'IT Security Policy', - subject: 'Cybersecurity guidelines', - category: 'Technical', - confidentiality: 'Confidential' - } - }, - { - id: 'file3', - name: 'Financial_Report_Q3.xlsx', - type: 'file', - parentPath: '/cabinet1/drawer2', - extension: 'xlsx', - size: '3.2 MB', - modified: '2023-10-01', - created: '2023-09-30', - author: 'Finance Team', - department: 'Finance', - tags: ['Financial', 'Report', 'Q3'], - description: 'Third quarter financial report', - metadata: { - title: 'Q3 Financial Report', - subject: 'Quarterly financial analysis', - category: 'Financial', - confidentiality: 'Confidential' - } - } - ]; - } catch (error) { - console.error('Failed to load items:', error); - // Show error notification - } finally { - isLoading.value = false; - } -}; - -const parseFileSize = (sizeStr) => { - if (!sizeStr || typeof sizeStr !== 'string') return 0; - const match = sizeStr.match(/(\d+\.?\d*)\s*(KB|MB|GB|TB)?/i); - if (!match) return 0; - - const size = parseFloat(match[1]); - const unit = (match[2] || 'B').toUpperCase(); - - const multipliers = { B: 1, KB: 1024, MB: 1024**2, GB: 1024**3, TB: 1024**4 }; - return size * (multipliers[unit] || 1); -}; - -const selectItem = (item) => { - selectedItem.value = item; - emit('itemSelected', item); - - if (item.type === 'file') { - currentDocument.value = item; - } -}; - -const navigateTo = (path) => { - currentPath.value = path; - selectedItem.value = null; - currentDocument.value = null; - emit('pathChanged', path); - loadItems(); -}; - -const navigateToItem = (item) => { - if (item.type === 'file') { - selectItem(item); - return; - } - - let newPath; - if (item.type === 'cabinet') newPath = `/${item.id}`; - else if (item.type === 'drawer') newPath = `/cabinet1/${item.id}`; - else if (item.type === 'folder') newPath = `/cabinet1/drawer1/${item.id}`; - else if (item.type === 'subfolder') newPath = `/cabinet1/drawer1/folder1/${item.id}`; - else newPath = currentPath.value; - - navigateTo(newPath); -}; - -const toggleTreeItem = (path) => { - treeExpanded.value[path] = !treeExpanded.value[path]; -}; - -const changeViewMode = (mode) => { - viewMode.value = mode; - emit('viewModeChanged', mode); -}; - -const setSortBy = (field) => { - if (sortBy.value === field) { - sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc'; - } else { - sortBy.value = field; - sortOrder.value = 'asc'; - } -}; - -const openFileViewer = (document) => { - currentDocument.value = document; - showDocumentViewer.value = true; -}; - -const closeFileViewer = () => { - showDocumentViewer.value = false; - currentDocument.value = null; -}; - -const handleUpload = (fileData) => { - console.log('Uploading file:', fileData); - // Handle file upload logic here - // Add to allItems.value or refresh from API - loadItems(); -}; - -const handleCreateNew = (itemData) => { - console.log('Creating new item:', itemData); - // Handle create new item logic here - // Add to allItems.value or refresh from API - loadItems(); -}; - -// Format file size -const formatSize = (size) => { - return size; -}; - -// Get icon for item type -const getItemIcon = (item) => { - switch (item.type) { - case 'cabinet': return 'cabinet'; - case 'drawer': return 'folder'; - case 'folder': return 'folder'; - case 'subfolder': return 'folder'; - case 'file': - if (item.extension === 'pdf') return 'pdf'; - if (['doc', 'docx'].includes(item.extension)) return 'doc'; - if (['xls', 'xlsx'].includes(item.extension)) return 'sheet'; - return 'file'; - default: return 'folder'; - } -}; - -// Get SVG path based on icon type -const getSvgIcon = (iconType, size = 24) => { - const icons = { - cabinet: ``, - folder: ``, - pdf: ``, - doc: ``, - sheet: ``, - upload: ``, - plus: ``, - search: ``, - sort: ``, - sortAsc: ``, - sortDesc: ``, - home: ``, - back: ``, - forward: ``, - file: ``, - tag: ``, - chevronDown: ``, - chevronRight: `` - }; - return icons[iconType] || icons.file; -}; - -// Lifecycle hooks -onMounted(() => { - loadItems(); -}); diff --git a/pages/dms/index.vue b/pages/dms/index.vue index 09dc6e6..a95416c 100644 --- a/pages/dms/index.vue +++ b/pages/dms/index.vue @@ -194,6 +194,7 @@ onMounted(() => { setTimeout(resolve, 500)); + + // Generate a unique request ID + const requestId = `req${Date.now()}`; + + // Create new access request + const newRequest = { + id: requestId, + userId: 'current-user-id', // Would come from auth store + userName: 'Current User', // Would come from auth store + itemId: itemId, + accessLevel: accessLevel, + justification: justification, + duration: duration, + requestDate: new Date().toISOString().split('T')[0], + status: 'pending' + }; + + // Add to access requests + this.accessRequests.push(newRequest); + + // Update the item's access request status (for mock data) + // In production, this would be handled server-side + const updateItemStatus = (items, id) => { + for (const item of items) { + if (item.id === id) { + item.accessRequestStatus = 'pending'; + return true; + } + } + return false; + }; + + // Try to find and update the item in the mock data arrays + // This is a simplified approach for demo purposes + + return newRequest; + } catch (error) { + console.error('Failed to submit access request:', error); + throw error; + } finally { + this.isLoading = false; + } } } }); \ No newline at end of file