// Documents Page Functionality let currentPage = 1; const itemsPerPage = 10; let searchQuery = ''; let allDocuments = []; // Initialize documents page document.addEventListener('DOMContentLoaded', () => { loadDocuments(); setupEventListeners(); }); // Setup event listeners function setupEventListeners() { // File input change const fileInput = document.getElementById('file-input'); if (fileInput) { fileInput.addEventListener('change', handleFileSelect); } // Drag and drop const uploadArea = document.getElementById('upload-area'); if (uploadArea) { uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('!border-primary', '!bg-primary/5'); }); uploadArea.addEventListener('dragleave', () => { uploadArea.classList.remove('!border-primary', '!bg-primary/5'); }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('!border-primary', '!bg-primary/5'); const files = e.dataTransfer.files; handleFiles(files); }); } // Search input const searchInput = document.getElementById('search-input'); if (searchInput) { let debounceTimer; searchInput.addEventListener('input', (e) => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { searchQuery = e.target.value.toLowerCase(); currentPage = 1; loadDocuments(); }, 300); }); } } // Handle file select from input function handleFileSelect(e) { const files = e.target.files; handleFiles(files); } // Handle file upload async function handleFiles(files) { if (files.length === 0) return; const allowedTypes = ['pdf', 'csv', 'xlsx', 'xls', 'png', 'jpg', 'jpeg']; const maxSize = 10 * 1024 * 1024; // 10MB for (const file of files) { const ext = file.name.split('.').pop().toLowerCase(); if (!allowedTypes.includes(ext)) { showNotification('error', `${file.name}: Unsupported file type. Only PDF, CSV, XLS, XLSX, PNG, JPG allowed.`); continue; } if (file.size > maxSize) { showNotification('error', `${file.name}: File size exceeds 10MB limit.`); continue; } await uploadFile(file); } // Reset file input const fileInput = document.getElementById('file-input'); if (fileInput) fileInput.value = ''; // Reload documents list loadDocuments(); } // Upload file to server async function uploadFile(file) { const formData = new FormData(); formData.append('file', file); try { const response = await fetch('/api/documents/', { method: 'POST', body: formData }); const result = await response.json(); if (response.ok) { showNotification('success', `${file.name} uploaded successfully!`); } else { showNotification('error', result.error || 'Upload failed'); } } catch (error) { console.error('Upload error:', error); showNotification('error', 'An error occurred during upload'); } } // Load documents from API async function loadDocuments() { try { const params = new URLSearchParams({ page: currentPage, per_page: itemsPerPage }); if (searchQuery) { params.append('search', searchQuery); } const data = await apiCall(`/api/documents/?${params.toString()}`); allDocuments = data.documents; displayDocuments(data.documents); updatePagination(data.pagination); } catch (error) { console.error('Error loading documents:', error); document.getElementById('documents-list').innerHTML = ` Failed to load documents. Please try again. `; } } // Display documents in table function displayDocuments(documents) { const tbody = document.getElementById('documents-list'); if (documents.length === 0) { tbody.innerHTML = ` No documents found. Upload your first document! `; return; } tbody.innerHTML = documents.map(doc => { const statusConfig = getStatusConfig(doc.status); const fileIcon = getFileIcon(doc.file_type); return `
${fileIcon.icon}
${escapeHtml(doc.original_filename)} ${formatFileSize(doc.file_size)}
${formatDate(doc.created_at)} ${doc.document_category || 'Other'} ${statusConfig.hasIcon ? `${statusConfig.icon}` : ''} ${doc.status}
${['PNG', 'JPG', 'JPEG', 'PDF'].includes(doc.file_type.toUpperCase()) ? `` : '' }
`; }).join(''); } // Get status configuration function getStatusConfig(status) { const configs = { uploaded: { className: 'bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-400', icon: 'upload', hasIcon: true }, processing: { className: 'bg-purple-100 dark:bg-purple-500/20 text-purple-700 dark:text-purple-400 animate-pulse', icon: 'sync', hasIcon: true }, analyzed: { className: 'bg-green-100 dark:bg-green-500/20 text-green-700 dark:text-green-400', icon: 'verified', hasIcon: true }, error: { className: 'bg-red-100 dark:bg-red-500/20 text-red-700 dark:text-red-400', icon: 'error', hasIcon: true } }; return configs[status] || configs.uploaded; } // Get file icon function getFileIcon(fileType) { const icons = { pdf: { icon: 'picture_as_pdf', color: 'text-red-500' }, csv: { icon: 'table_view', color: 'text-green-500' }, xlsx: { icon: 'table_view', color: 'text-green-600' }, xls: { icon: 'table_view', color: 'text-green-600' }, png: { icon: 'image', color: 'text-blue-500' }, jpg: { icon: 'image', color: 'text-blue-500' }, jpeg: { icon: 'image', color: 'text-blue-500' } }; return icons[fileType?.toLowerCase()] || { icon: 'description', color: 'text-gray-500' }; } // Update pagination function updatePagination(pagination) { const { page, pages, total, per_page } = pagination; // Update count display const start = (page - 1) * per_page + 1; const end = Math.min(page * per_page, total); document.getElementById('page-start').textContent = total > 0 ? start : 0; document.getElementById('page-end').textContent = end; document.getElementById('total-count').textContent = total; // Update pagination buttons const paginationDiv = document.getElementById('pagination'); if (pages <= 1) { paginationDiv.innerHTML = ''; return; } let buttons = ''; // Previous button buttons += ` `; // Page numbers const maxButtons = 5; let startPage = Math.max(1, page - Math.floor(maxButtons / 2)); let endPage = Math.min(pages, startPage + maxButtons - 1); if (endPage - startPage < maxButtons - 1) { startPage = Math.max(1, endPage - maxButtons + 1); } for (let i = startPage; i <= endPage; i++) { buttons += ` `; } // Next button buttons += ` `; paginationDiv.innerHTML = buttons; } // Change page function changePage(page) { currentPage = page; loadDocuments(); } // View document (preview in modal) function viewDocument(id, fileType, filename) { const modalHtml = `

${escapeHtml(filename)}

${fileType.toUpperCase() === 'PDF' ? `` : `${escapeHtml(filename)}` }
`; document.body.insertAdjacentHTML('beforeend', modalHtml); } // Close preview modal function closePreviewModal(event) { if (!event || event.target.id === 'document-preview-modal' || !event.target.closest) { const modal = document.getElementById('document-preview-modal'); if (modal) { modal.remove(); } } } // Download document async function downloadDocument(id) { try { const response = await fetch(`/api/documents/${id}/download`); if (!response.ok) { throw new Error('Download failed'); } const blob = await response.blob(); const contentDisposition = response.headers.get('Content-Disposition'); const filename = contentDisposition ? contentDisposition.split('filename=')[1].replace(/"/g, '') : `document_${id}`; const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); showNotification('success', 'Document downloaded successfully!'); } catch (error) { console.error('Download error:', error); showNotification('error', 'Failed to download document'); } } // Delete document async function deleteDocument(id) { const confirmMsg = getCurrentLanguage() === 'ro' ? 'Ești sigur că vrei să ștergi acest document? Această acțiune nu poate fi anulată.' : 'Are you sure you want to delete this document? This action cannot be undone.'; if (!confirm(confirmMsg)) { return; } try { const response = await fetch(`/api/documents/${id}`, { method: 'DELETE' }); const result = await response.json(); if (response.ok) { showNotification('success', 'Document deleted successfully!'); loadDocuments(); } else { showNotification('error', result.error || 'Failed to delete document'); } } catch (error) { console.error('Delete error:', error); showNotification('error', 'An error occurred while deleting'); } } // Format file size function 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 Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; } // Format date function formatDate(dateString) { const date = new Date(dateString); const now = new Date(); const diff = now - date; const days = Math.floor(diff / (1000 * 60 * 60 * 24)); if (days === 0) { const hours = Math.floor(diff / (1000 * 60 * 60)); if (hours === 0) { const minutes = Math.floor(diff / (1000 * 60)); return minutes <= 1 ? 'Just now' : `${minutes}m ago`; } return `${hours}h ago`; } else if (days === 1) { return 'Yesterday'; } else if (days < 7) { return `${days}d ago`; } else { return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); } } // Escape HTML to prevent XSS function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Show notification function showNotification(type, message) { // Create notification element const notification = document.createElement('div'); notification.className = `fixed top-4 right-4 z-50 px-4 py-3 rounded-lg shadow-lg flex items-center gap-3 animate-slideIn ${ type === 'success' ? 'bg-green-500 text-white' : 'bg-red-500 text-white' }`; notification.innerHTML = ` ${type === 'success' ? 'check_circle' : 'error'} ${escapeHtml(message)} `; document.body.appendChild(notification); // Remove after 3 seconds setTimeout(() => { notification.classList.add('animate-slideOut'); setTimeout(() => { document.body.removeChild(notification); }, 300); }, 3000); }