/** * File Upload Handler for Legal Dashboard * Manages document uploads, OCR processing, and real-time progress */ class FileUploadHandler { constructor() { this.uploadEndpoint = '/api/ocr/upload'; this.processEndpoint = '/api/ocr/process'; this.maxFileSize = 10 * 1024 * 1024; // 10MB this.allowedTypes = ['application/pdf', 'image/jpeg', 'image/png', 'image/tiff']; this.currentUpload = null; this.uploadQueue = []; this.initializeEventListeners(); } initializeEventListeners() { // File input change const fileInput = document.getElementById('documentUpload'); if (fileInput) { fileInput.addEventListener('change', (e) => this.handleFileSelection(e)); } // Upload button const uploadBtn = document.getElementById('uploadButton'); if (uploadBtn) { uploadBtn.addEventListener('click', () => this.startUpload()); } // Drag and drop const dropZone = document.getElementById('uploadDropZone'); if (dropZone) { dropZone.addEventListener('dragover', (e) => this.handleDragOver(e)); dropZone.addEventListener('drop', (e) => this.handleDrop(e)); dropZone.addEventListener('dragleave', (e) => this.handleDragLeave(e)); } } handleFileSelection(event) { const files = event.target.files; if (files.length > 0) { this.validateAndQueueFiles(files); } } handleDragOver(event) { event.preventDefault(); event.currentTarget.classList.add('drag-over'); } handleDragLeave(event) { event.preventDefault(); event.currentTarget.classList.remove('drag-over'); } handleDrop(event) { event.preventDefault(); event.currentTarget.classList.remove('drag-over'); const files = event.dataTransfer.files; if (files.length > 0) { this.validateAndQueueFiles(files); } } validateAndQueueFiles(files) { const validFiles = []; const errors = []; for (let file of files) { // Check file size if (file.size > this.maxFileSize) { errors.push(`${file.name}: File too large (max 10MB)`); continue; } // Check file type if (!this.allowedTypes.includes(file.type)) { errors.push(`${file.name}: Unsupported file type`); continue; } validFiles.push(file); } // Show errors if any if (errors.length > 0) { this.showErrors(errors); } // Queue valid files if (validFiles.length > 0) { this.uploadQueue.push(...validFiles); this.updateUploadQueue(); } } updateUploadQueue() { const queueContainer = document.getElementById('uploadQueue'); if (!queueContainer) return; if (this.uploadQueue.length === 0) { queueContainer.innerHTML = '

هیچ فایلی برای آپلود انتخاب نشده

'; return; } const queueHTML = this.uploadQueue.map((file, index) => `
${file.name} ${this.formatFileSize(file.size)}
`).join(''); queueContainer.innerHTML = queueHTML; } removeFromQueue(index) { this.uploadQueue.splice(index, 1); this.updateUploadQueue(); } async startUpload() { if (this.uploadQueue.length === 0) { this.showToast('لطفاً فایلی برای آپلود انتخاب کنید', 'warning'); return; } if (this.currentUpload) { this.showToast('آپلود در حال انجام است، لطفاً صبر کنید', 'warning'); return; } this.currentUpload = true; this.showUploadProgress(); try { for (let i = 0; i < this.uploadQueue.length; i++) { const file = this.uploadQueue[i]; await this.uploadFile(file, i + 1, this.uploadQueue.length); } this.showToast('تمام فایل‌ها با موفقیت آپلود شدند', 'success'); this.refreshDocumentsList(); } catch (error) { this.showToast(`خطا در آپلود: ${error.message}`, 'error'); } finally { this.currentUpload = false; this.hideUploadProgress(); this.uploadQueue = []; this.updateUploadQueue(); } } async uploadFile(file, current, total) { return new Promise((resolve, reject) => { const formData = new FormData(); formData.append('file', file); formData.append('filename', file.name); const xhr = new XMLHttpRequest(); // Progress tracking xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { const percentComplete = (e.loaded / e.total) * 100; this.updateProgress(percentComplete, current, total); } }); // Response handling xhr.addEventListener('load', () => { if (xhr.status === 200) { try { const response = JSON.parse(xhr.responseText); this.handleUploadSuccess(response, file); resolve(response); } catch (error) { reject(new Error('Invalid response format')); } } else { reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`)); } }); xhr.addEventListener('error', () => { reject(new Error('Network error during upload')); }); xhr.addEventListener('abort', () => { reject(new Error('Upload cancelled')); }); // Start upload xhr.open('POST', this.uploadEndpoint); xhr.send(formData); }); } handleUploadSuccess(response, file) { // Update dashboard stats this.updateDashboardStats(); // Show success message this.showToast(`${file.name} با موفقیت آپلود شد`, 'success'); // Process OCR if needed if (response.document_id) { this.processOCR(response.document_id, file.name); } } async processOCR(documentId, fileName) { try { const response = await fetch(this.processEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ document_id: documentId, filename: fileName }) }); if (!response.ok) { throw new Error(`OCR processing failed: ${response.status}`); } const result = await response.json(); this.showOCRResult(result, fileName); } catch (error) { this.showToast(`خطا در پردازش OCR: ${error.message}`, 'error'); } } showOCRResult(result, fileName) { const ocrResultsContainer = document.getElementById('ocrResults'); if (!ocrResultsContainer) return; const resultHTML = `

نتایج OCR - ${fileName}

کیفیت: ${(result.quality || 0).toFixed(2)}%

متن استخراج شده:

${result.text || 'متنی استخراج نشد'}
`; ocrResultsContainer.insertAdjacentHTML('afterbegin', resultHTML); } updateProgress(percent, current, total) { const progressBar = document.getElementById('uploadProgressBar'); const progressText = document.getElementById('uploadProgressText'); if (progressBar) { progressBar.style.width = `${percent}%`; } if (progressText) { progressText.textContent = `آپلود فایل ${current} از ${total} (${Math.round(percent)}%)`; } } showUploadProgress() { const progressContainer = document.getElementById('uploadProgress'); if (progressContainer) { progressContainer.style.display = 'block'; } } hideUploadProgress() { const progressContainer = document.getElementById('uploadProgress'); if (progressContainer) { progressContainer.style.display = 'none'; } } updateDashboardStats() { // Trigger dashboard stats refresh if (typeof loadDashboardStats === 'function') { loadDashboardStats(); } } refreshDocumentsList() { // Trigger documents list refresh if (typeof loadDocumentsList === 'function') { loadDocumentsList(); } } showErrors(errors) { const errorMessage = errors.join('\n'); this.showToast(errorMessage, 'error'); } showToast(message, type = 'info') { if (typeof showToast === 'function') { showToast(message, type); } else { console.log(`${type.toUpperCase()}: ${message}`); } } formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } } // Initialize file upload handler const fileUploadHandler = new FileUploadHandler();