import { defineStore } from 'pinia'; export const useDmsStore = defineStore('dms', { state: () => ({ // Navigation state currentPath: '/', pathHistory: ['/'], historyIndex: 0, // User information currentUser: { id: 'user1', name: 'Aiman Fakhrullah', email: 'aiman@example.com', role: 'engineer', // engineer, admin, manager, etc. department: 'JKR Bahagian Kejuruteraan Awam Cawangan Kota Bharu' }, // Cabinet access types cabinetAccessTypes: [ { id: 'public', name: 'Public Access', icon: 'check-circle', color: 'green' }, { id: 'personal', name: 'Personal Access', icon: 'circle-check', color: 'orange' }, { id: 'private', name: 'Private Access', icon: 'lock', color: 'red' } ], // Access request statuses accessRequestStatuses: [ { id: 'pending', name: 'Pending', color: 'orange' }, { id: 'approved', name: 'Approved', color: 'green' }, { id: 'rejected', name: 'Rejected', color: 'red' } ], // Items state - in production this would be loaded from API cabinets: [ { id: 'public-cabinet', name: 'Public Cabinet', type: 'cabinet', accessType: 'public', children: [ { id: 'public-cabinet-1', name: 'Public Cabinet', type: 'cabinet', accessType: 'public', parentId: 'public-cabinet' } ] }, { id: 'my-cabinets', name: 'My Cabinets', type: 'cabinet-group', accessType: 'personal', children: [ { id: 'jkr-tebedu', name: 'JKR Cawangan Tebedu, Sarawak', type: 'cabinet', accessType: 'personal', parentId: 'my-cabinets', hasAccess: true }, { id: 'jkr-kota-bharu', name: 'JKR Cawangan Kota Bharu, Kelantan', type: 'cabinet', accessType: 'personal', parentId: 'my-cabinets', hasAccess: true, children: [ { id: 'jkr-kewangan-kb', name: 'JKR Bahagian Kewangan Cawangan Kota Bharu', type: 'cabinet', accessType: 'personal', parentId: 'jkr-kota-bharu', hasAccess: true }, { id: 'jkr-kejuruteraan-kb', name: 'JKR Bahagian Kejuruteraan Awam Cawangan Kota Bharu', type: 'cabinet', accessType: 'personal', parentId: 'jkr-kota-bharu', hasAccess: true, children: [ { id: 'pembinaan-jambatan-kb', name: 'Pembinaan Jambatan Kota Bharu', type: 'cabinet', accessType: 'personal', parentId: 'jkr-kejuruteraan-kb', hasAccess: true, children: [ { id: 'kewangan-tag', name: 'Kewangan', type: 'tag', color: 'purple', parentId: 'pembinaan-jambatan-kb' }, { id: 'kejuruteraan-awam-tag', name: 'Kejuruteraan Awam', type: 'tag', color: 'purple', parentId: 'pembinaan-jambatan-kb' }, { id: 'teknologi-maklumat-tag', name: 'Teknologi Maklumat', type: 'tag', color: 'purple', parentId: 'pembinaan-jambatan-kb' } ] }, { id: 'projek-jalan-raya-kb', name: 'Projek Jalan Raya Kota Bharu', type: 'cabinet', accessType: 'personal', parentId: 'jkr-kejuruteraan-kb', hasAccess: true } ] }, { id: 'jkr-teknologi-kb', name: 'JKR Bahagian Teknologi Maklumat Cawangan Kota Bharu', type: 'cabinet', accessType: 'personal', parentId: 'jkr-kota-bharu', hasAccess: true } ] }, { id: 'jkr-ipoh', name: 'JKR Cawangan Ipoh, Perak', type: 'cabinet', accessType: 'personal', parentId: 'my-cabinets', hasAccess: true } ] }, { id: 'private-cabinets', name: 'Private Cabinets', type: 'cabinet-group', accessType: 'private', children: [ { id: 'jkr-batu-kawan', name: 'JKR Cawangan Batu Kawan, Penang', type: 'cabinet', accessType: 'private', parentId: 'private-cabinets', hasAccess: false, accessRequestStatus: 'pending' }, { id: 'jkr-kuala-terengganu', name: 'JKR Cawangan Kuala Terengganu, Terengganu', type: 'cabinet', accessType: 'private', parentId: 'private-cabinets', hasAccess: false, accessRequestStatus: 'pending' }, { id: 'jkr-arkitek', name: 'JKR Cawangan Arkitek', type: 'cabinet', accessType: 'private', parentId: 'private-cabinets', hasAccess: false, accessRequestStatus: 'pending' }, { id: 'jkr-putrajaya', name: 'JKR Cawangan Putrajaya', type: 'cabinet', accessType: 'private', parentId: 'private-cabinets', hasAccess: false, accessRequestStatus: 'rejected', isLocked: true } ] } ], // Files within cabinets items: [ { id: 'file1', name: 'Pembangunan_Sistem_IT_2021.pdf', type: 'file', extension: 'pdf', size: '4MB', modified: '2021-05-20', cabinetId: 'jkr-kota-bharu', accessType: 'personal', status: 'locked', info: { title: 'Projek Pembangunan Sistem IT', subject: 'Dokumen spesifikasi sistem', state: 'Kelantan', date: '2021-05-20', user: 'Mohd Faizal bin Abdullah', storeDate: '2021-05-25' } }, { id: 'file2', name: 'Projek_Jalan_Raya_Kota_Bharu.pdf', type: 'file', extension: 'pdf', size: '5MB', modified: '2021-06-15', cabinetId: 'jkr-kejuruteraan-kb', accessType: 'personal', status: 'unlocked' }, { id: 'file3', name: 'Anggaran_Kos_Projek_MRT3.xlsx', type: 'file', extension: 'xlsx', size: '3MB', modified: '2021-07-10', cabinetId: 'pembinaan-jambatan-kb', accessType: 'personal', status: 'locked' }, { id: 'file4', name: 'EIA_Empangan_Nenggiri.pdf', type: 'file', extension: 'pdf', size: '15MB', modified: '2021-04-18', cabinetId: 'jkr-ipoh', accessType: 'personal', status: 'locked' }, { id: 'file5', name: 'Borang_Maklumabalas.xlsx', type: 'file', extension: 'xlsx', size: '34.1 KB', modified: '2025-01-27', cabinetId: 'public-cabinet-1', accessType: 'public', createdBy: 'aimantasan', status: 'unlocked', info: { authors: 'aimantasan', lastSavedBy: 'aimantasan', dateAccessed: '2025-05-28', dateModified: '2025-01-27', contentCreated: '2025-01-27' } } ], // Access requests accessRequests: [ { id: 'req1', userId: 'user1', userName: 'Aiman Fakhrullah', cabinetId: 'jkr-batu-kawan', cabinetName: 'JKR Cawangan Batu Kawan, Penang', requestDate: '2023-06-15', status: 'pending', reason: 'Need access for project collaboration' }, { id: 'req2', userId: 'user1', userName: 'Aiman Fakhrullah', cabinetId: 'jkr-kuala-terengganu', cabinetName: 'JKR Cawangan Kuala Terengganu, Terengganu', requestDate: '2023-06-16', status: 'pending', reason: 'Required for cross-department coordination' }, { id: 'req3', userId: 'user1', userName: 'Aiman Fakhrullah', cabinetId: 'jkr-arkitek', cabinetName: 'JKR Cawangan Arkitek', requestDate: '2023-06-17', status: 'pending', reason: 'Need architectural plans for current project' }, { id: 'req4', userId: 'user1', userName: 'Aiman Fakhrullah', cabinetId: 'jkr-putrajaya', cabinetName: 'JKR Cawangan Putrajaya', requestDate: '2023-06-01', status: 'rejected', reason: 'Need access to headquarters documents', rejectionReason: 'Access restricted to headquarters staff only' } ], // Selected item selectedItem: null, // View settings viewMode: 'list', // list, grid, details sortBy: 'name', sortDirection: 'asc', // User permissions - would be loaded from auth service userPermissions: { canCreate: true, canEdit: true, canDelete: true, canManageAccess: true }, // Flags isLoading: false, showFileViewer: false, viewerDocument: null, searchQuery: '', searchResults: [], // Settings integration systemSettings: { upload: { allowedFileTypes: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'jpg', 'png'], blockedFileTypes: ['exe', 'bat', 'cmd'], maxFileSize: 100 * 1024 * 1024, // 100MB in bytes quotas: { perUser: 5000 * 1024 * 1024, // 5GB perGroup: 50000 * 1024 * 1024, // 50GB perProject: 100000 * 1024 * 1024 // 100GB } }, metadata: { customFields: [ { name: 'Department', type: 'dropdown', required: true, options: ['HR', 'Finance', 'IT', 'Legal'] }, { name: 'Priority', type: 'select', required: false, options: ['Low', 'Medium', 'High', 'Critical'] }, { name: 'Project Code', type: 'text', required: true }, { name: 'Review Date', type: 'date', required: false } ], predefinedTags: ['urgent', 'confidential', 'public', 'draft', 'final'], userGeneratedTags: true, tagSuggestions: true }, documents: { versionControl: { enabled: true, maxVersions: 10, autoVersioning: true }, namingConventions: { autoGenerate: true, pattern: '{department}_{title}_{date}' } }, access: { rbacEnabled: true, permissions: { view: true, edit: true, delete: false, download: true, share: true } } }, // Enhanced user permissions with detailed tracking userQuotas: { used: 1500 * 1024 * 1024, // 1.5GB used limit: 5000 * 1024 * 1024, // 5GB limit documents: 450, maxDocuments: 1000 }, // Document metadata templates metadataTemplates: { standard: { title: '', description: '', department: '', author: '', tags: [], priority: 'Medium', reviewDate: null, projectCode: '' }, contract: { title: '', description: '', department: 'Legal', contractType: '', vendor: '', startDate: null, endDate: null, value: '', tags: ['contract'], priority: 'High' }, report: { title: '', description: '', department: '', reportType: '', period: '', author: '', tags: ['report'], priority: 'Medium' } }, // Version tracking documentVersions: {}, // Enhanced search with metadata advancedSearchFilters: { fileType: [], department: [], tags: [], dateRange: { start: null, end: null }, author: [], priority: [], hasVersions: false } }), getters: { // Get items at the current cabinet currentCabinetItems: (state) => { const currentCabinetId = state.currentPath.split('/').filter(Boolean).pop(); return state.items.filter(item => item.cabinetId === currentCabinetId); }, // Get breadcrumbs for current path breadcrumbs: (state) => { if (state.currentPath === '/') return [{ name: 'Home', path: '/' }]; const paths = state.currentPath.split('/').filter(Boolean); let breadcrumbPath = ''; return [ { name: 'Home', path: '/' }, ...paths.map(segment => { breadcrumbPath += `/${segment}`; // Find the actual cabinet name const findCabinetName = (cabinets, id) => { for (const cabinet of cabinets) { if (cabinet.id === id) return cabinet.name; if (cabinet.children) { const name = findCabinetName(cabinet.children, id); if (name) return name; } } return null; }; const name = findCabinetName(state.cabinets, segment) || segment; return { name: name, path: breadcrumbPath }; }) ]; }, // Get public cabinets publicCabinets: (state) => { return state.cabinets.filter(cabinet => cabinet.accessType === 'public'); }, // Get personal cabinets (ones the user has access to) personalCabinets: (state) => { return state.cabinets.find(cabinet => cabinet.id === 'my-cabinets') || null; }, // Get private cabinets privateCabinets: (state) => { return state.cabinets.find(cabinet => cabinet.id === 'private-cabinets') || null; }, // Check if we can navigate back canGoBack: (state) => { return state.historyIndex > 0; }, // Check if we can navigate forward canGoForward: (state) => { return state.historyIndex < state.pathHistory.length - 1; }, // Filter items by access level publicDocuments: (state) => { return state.items.filter(item => item.accessType === 'public'); }, personalDocuments: (state) => { return state.items.filter(item => item.accessType === 'personal'); }, privateDocuments: (state) => { return state.items.filter(item => item.accessType === 'private'); }, // Get pending access requests pendingAccessRequests: (state) => { return state.accessRequests.filter(req => req.status === 'pending'); } }, actions: { // Navigation actions navigateTo(path) { // Add to history if it's a new path if (path !== this.currentPath) { // If we navigated back and then to a new path, truncate the forward history if (this.historyIndex < this.pathHistory.length - 1) { this.pathHistory = this.pathHistory.slice(0, this.historyIndex + 1); } this.pathHistory.push(path); this.historyIndex = this.pathHistory.length - 1; } this.currentPath = path; this.selectedItem = null; }, navigateBack() { if (this.canGoBack) { this.historyIndex--; this.currentPath = this.pathHistory[this.historyIndex]; this.selectedItem = null; } }, navigateForward() { if (this.canGoForward) { this.historyIndex++; this.currentPath = this.pathHistory[this.historyIndex]; this.selectedItem = null; } }, selectItem(item) { this.selectedItem = item; }, // View actions setViewMode(mode) { this.viewMode = mode; }, setSortBy(field) { if (this.sortBy === field) { // Toggle direction if clicking the same field this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc'; } else { this.sortBy = field; this.sortDirection = 'asc'; } }, // File viewer actions openFileViewer(document) { this.viewerDocument = document; this.showFileViewer = true; }, closeFileViewer() { this.showFileViewer = false; this.viewerDocument = null; }, // Enhanced file validation based on settings validateFile(file) { const errors = []; const settings = this.systemSettings.upload; // Check file type const fileExtension = file.name.split('.').pop().toLowerCase(); if (!settings.allowedFileTypes.includes(fileExtension)) { errors.push(`File type .${fileExtension} is not allowed`); } if (settings.blockedFileTypes.includes(fileExtension)) { errors.push(`File type .${fileExtension} is blocked`); } // Check file size if (file.size > settings.maxFileSize) { const maxSizeMB = settings.maxFileSize / (1024 * 1024); errors.push(`File size exceeds limit of ${maxSizeMB}MB`); } // Check user quota if (this.userQuotas.used + file.size > this.userQuotas.limit) { errors.push('Upload would exceed your storage quota'); } return { isValid: errors.length === 0, errors }; }, // Enhanced metadata validation validateMetadata(metadata, template = 'standard') { const errors = []; const templateConfig = this.metadataTemplates[template]; const customFields = this.systemSettings.metadata.customFields; // Validate required custom fields customFields.forEach(field => { if (field.required && (!metadata[field.name] || metadata[field.name].toString().trim() === '')) { errors.push(`${field.name} is required`); } // Validate field options for dropdown/select types if (field.options && metadata[field.name] && !field.options.includes(metadata[field.name])) { errors.push(`Invalid value for ${field.name}`); } }); // Validate document naming convention if (this.systemSettings.documents.namingConventions.autoGenerate) { const pattern = this.systemSettings.documents.namingConventions.pattern; const requiredFields = pattern.match(/{(\w+)}/g)?.map(match => match.slice(1, -1)) || []; requiredFields.forEach(field => { if (!metadata[field]) { errors.push(`${field} is required for auto-generated naming`); } }); } return { isValid: errors.length === 0, errors }; }, // Generate document name based on naming convention generateDocumentName(metadata, originalFileName) { if (!this.systemSettings.documents.namingConventions.autoGenerate) { return originalFileName; } let pattern = this.systemSettings.documents.namingConventions.pattern; const date = new Date().toISOString().split('T')[0]; // Replace placeholders pattern = pattern.replace(/{department}/g, metadata.department || 'unknown'); pattern = pattern.replace(/{title}/g, metadata.title || 'document'); pattern = pattern.replace(/{date}/g, date); pattern = pattern.replace(/{author}/g, metadata.author || 'unknown'); pattern = pattern.replace(/{projectCode}/g, metadata.projectCode || ''); // Get file extension const extension = originalFileName.split('.').pop(); return `${pattern}.${extension}`; }, // Enhanced upload with metadata and validation async uploadFileWithMetadata(file, metadata, currentPath) { // Validate file const fileValidation = this.validateFile(file); if (!fileValidation.isValid) { throw new Error(`File validation failed: ${fileValidation.errors.join(', ')}`); } // Validate metadata const metadataValidation = this.validateMetadata(metadata); if (!metadataValidation.isValid) { throw new Error(`Metadata validation failed: ${metadataValidation.errors.join(', ')}`); } this.isLoading = true; try { // Generate document name const documentName = this.generateDocumentName(metadata, file.name); // Create document object const newDocument = { id: `doc_${Date.now()}`, name: documentName, originalName: file.name, type: 'file', extension: file.name.split('.').pop().toLowerCase(), size: this.formatFileSize(file.size), modified: new Date().toLocaleDateString(), author: metadata.author || 'Current User', department: metadata.department, description: metadata.description, tags: metadata.tags || [], priority: metadata.priority, projectCode: metadata.projectCode, reviewDate: metadata.reviewDate, version: 1, hasAccess: true, accessType: metadata.accessType || 'private', metadata: { ...metadata } }; // Add to items (simulate upload) this.items.push(newDocument); // Update user quota this.userQuotas.used += file.size; this.userQuotas.documents += 1; // Initialize version tracking if enabled if (this.systemSettings.documents.versionControl.enabled) { this.documentVersions[newDocument.id] = [{ version: 1, uploadDate: new Date().toISOString(), author: metadata.author, changes: 'Initial upload', fileSize: file.size }]; } return newDocument; } catch (error) { console.error('Upload failed:', error); throw error; } finally { this.isLoading = false; } }, // Version control actions async createNewVersion(documentId, file, metadata) { if (!this.systemSettings.documents.versionControl.enabled) { throw new Error('Version control is not enabled'); } const document = this.items.find(item => item.id === documentId); if (!document) { throw new Error('Document not found'); } // Check version limit const versions = this.documentVersions[documentId] || []; if (versions.length >= this.systemSettings.documents.versionControl.maxVersions) { // Remove oldest version versions.shift(); } // Create new version const newVersion = { version: document.version + 1, uploadDate: new Date().toISOString(), author: metadata.author, changes: metadata.changes || 'Updated document', fileSize: file.size }; // Update document document.version = newVersion.version; document.modified = new Date().toLocaleDateString(); document.size = this.formatFileSize(file.size); // Add version to tracking versions.push(newVersion); this.documentVersions[documentId] = versions; return newVersion; }, // Get document versions getDocumentVersions(documentId) { return this.documentVersions[documentId] || []; }, // Enhanced search with metadata async advancedSearch(query, filters = {}) { this.isLoading = true; this.searchQuery = query; Object.assign(this.advancedSearchFilters, filters); try { // Mock API delay await new Promise(resolve => setTimeout(resolve, 300)); let results = this.items.filter(item => item.type === 'file'); // Text search if (query) { const searchLower = query.toLowerCase(); results = results.filter(item => item.name.toLowerCase().includes(searchLower) || item.description?.toLowerCase().includes(searchLower) || item.department?.toLowerCase().includes(searchLower) || item.author?.toLowerCase().includes(searchLower) || item.projectCode?.toLowerCase().includes(searchLower) || item.tags?.some(tag => tag.toLowerCase().includes(searchLower)) ); } // Filter by file type if (filters.fileType && filters.fileType.length > 0) { results = results.filter(item => filters.fileType.includes(item.extension)); } // Filter by department if (filters.department && filters.department.length > 0) { results = results.filter(item => filters.department.includes(item.department)); } // Filter by tags if (filters.tags && filters.tags.length > 0) { results = results.filter(item => item.tags?.some(tag => filters.tags.includes(tag)) ); } // Filter by priority if (filters.priority && filters.priority.length > 0) { results = results.filter(item => filters.priority.includes(item.priority)); } // Filter by date range if (filters.dateRange?.start || filters.dateRange?.end) { results = results.filter(item => { const itemDate = new Date(item.modified); const startDate = filters.dateRange.start ? new Date(filters.dateRange.start) : null; const endDate = filters.dateRange.end ? new Date(filters.dateRange.end) : null; if (startDate && itemDate < startDate) return false; if (endDate && itemDate > endDate) return false; return true; }); } // Filter by version existence if (filters.hasVersions) { results = results.filter(item => this.documentVersions[item.id]?.length > 1); } this.searchResults = results; return results; } catch (error) { console.error('Advanced search failed:', error); throw error; } finally { this.isLoading = false; } }, // Tag suggestions getTagSuggestions(input) { if (!this.systemSettings.metadata.tagSuggestions) return []; const allTags = [ ...this.systemSettings.metadata.predefinedTags, ...new Set(this.items.flatMap(item => item.tags || [])) ]; return allTags.filter(tag => tag.toLowerCase().includes(input.toLowerCase()) ).slice(0, 10); }, // Update system settings updateSystemSettings(category, settings) { if (this.systemSettings[category]) { Object.assign(this.systemSettings[category], settings); } }, // Format file size helper formatFileSize(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }, // Cabinet access actions async requestCabinetAccess(cabinetId, reason) { this.isLoading = true; try { // Find the cabinet let cabinetName = ''; const findCabinet = (cabinets, id) => { for (const cabinet of cabinets) { if (cabinet.id === id) { cabinetName = cabinet.name; return cabinet; } if (cabinet.children) { const result = findCabinet(cabinet.children, id); if (result) return result; } } return null; }; const cabinet = findCabinet(this.cabinets, cabinetId); if (!cabinet) throw new Error('Cabinet not found'); // Mock API delay await new Promise(resolve => setTimeout(resolve, 500)); // Create new access request const newRequest = { id: `req${Date.now()}`, userId: this.currentUser.id, userName: this.currentUser.name, cabinetId: cabinetId, cabinetName: cabinetName, requestDate: new Date().toISOString().split('T')[0], status: 'pending', reason: reason }; // Add to access requests this.accessRequests.push(newRequest); // Update cabinet request status cabinet.accessRequestStatus = 'pending'; return newRequest; } catch (error) { console.error('Failed to request access:', error); throw error; } finally { this.isLoading = false; } }, async approveAccessRequest(requestId) { this.isLoading = true; try { // Mock API delay await new Promise(resolve => setTimeout(resolve, 500)); // Find the request const request = this.accessRequests.find(req => req.id === requestId); if (!request) throw new Error('Request not found'); // Update request status request.status = 'approved'; // Find the cabinet and update access const updateCabinetAccess = (cabinets, id) => { for (const cabinet of cabinets) { if (cabinet.id === id) { cabinet.hasAccess = true; cabinet.accessRequestStatus = 'approved'; return true; } if (cabinet.children) { if (updateCabinetAccess(cabinet.children, id)) return true; } } return false; }; updateCabinetAccess(this.cabinets, request.cabinetId); return request; } catch (error) { console.error('Failed to approve access request:', error); throw error; } finally { this.isLoading = false; } }, async rejectAccessRequest(requestId, reason) { this.isLoading = true; try { // Mock API delay await new Promise(resolve => setTimeout(resolve, 500)); // Find the request const request = this.accessRequests.find(req => req.id === requestId); if (!request) throw new Error('Request not found'); // Update request status request.status = 'rejected'; request.rejectionReason = reason; // Find the cabinet and update access const updateCabinetAccess = (cabinets, id) => { for (const cabinet of cabinets) { if (cabinet.id === id) { cabinet.hasAccess = false; cabinet.accessRequestStatus = 'rejected'; return true; } if (cabinet.children) { if (updateCabinetAccess(cabinet.children, id)) return true; } } return false; }; updateCabinetAccess(this.cabinets, request.cabinetId); return request; } catch (error) { console.error('Failed to reject access request:', error); throw error; } finally { this.isLoading = false; } }, // CRUD operations async createItem(item) { this.isLoading = true; try { // Mock API delay await new Promise(resolve => setTimeout(resolve, 500)); // Get current cabinet id const currentCabinetId = this.currentPath.split('/').filter(Boolean).pop(); // Generate a unique ID const newItem = { ...item, id: `${item.type}${Date.now()}`, cabinetId: currentCabinetId, modified: new Date().toISOString().split('T')[0], createdBy: this.currentUser.name }; // Add to items this.items.push(newItem); return newItem; } catch (error) { console.error('Failed to create item:', error); throw error; } finally { this.isLoading = false; } }, async updateItem(id, updates) { this.isLoading = true; try { // Mock API delay await new Promise(resolve => setTimeout(resolve, 500)); // Find the item const itemIndex = this.items.findIndex(item => item.id === id); if (itemIndex === -1) throw new Error('Item not found'); // Update the item const updatedItem = { ...this.items[itemIndex], ...updates, modified: new Date().toISOString().split('T')[0] }; this.items.splice(itemIndex, 1, updatedItem); return updatedItem; } catch (error) { console.error('Failed to update item:', error); throw error; } finally { this.isLoading = false; } }, async deleteItem(id) { this.isLoading = true; try { // Mock API delay await new Promise(resolve => setTimeout(resolve, 500)); // Find the item const itemIndex = this.items.findIndex(item => item.id === id); if (itemIndex === -1) throw new Error('Item not found'); // Remove the item const deletedItem = this.items.splice(itemIndex, 1)[0]; return deletedItem; } catch (error) { console.error('Failed to delete item:', error); throw error; } finally { this.isLoading = false; } }, // Search functionality async searchDocuments(query) { this.isLoading = true; this.searchQuery = query; try { // Mock API delay await new Promise(resolve => setTimeout(resolve, 500)); // Simple search implementation - in production would be more sophisticated const results = this.items.filter(item => item.type === 'file' && (item.name.toLowerCase().includes(query.toLowerCase()) || (item.info?.title && item.info.title.toLowerCase().includes(query.toLowerCase())) || (item.info?.subject && item.info.subject.toLowerCase().includes(query.toLowerCase()))) ); this.searchResults = results; return results; } catch (error) { console.error('Failed to search documents:', error); throw error; } finally { this.isLoading = false; } }, clearSearch() { this.searchQuery = ''; this.searchResults = []; }, // Access request functionality async requestAccess(itemId, accessLevel, justification, duration = '7 days') { this.isLoading = true; try { // Mock API delay await new Promise(resolve => 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; } } } });