/** * Application JavaScript pour l'interface web 4NK Notariat */ class NotaryApp { constructor() { this.apiUrl = 'http://localhost:8000'; this.currentDocument = null; this.documents = []; this.charts = {}; // Stockage des instances de graphiques this.init(); } init() { this.setupEventListeners(); this.loadDocuments(); this.loadStats(); this.checkSystemStatus(); } setupEventListeners() { // Navigation document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); this.showSection(link.dataset.section); }); }); // Upload form document.getElementById('upload-form').addEventListener('submit', (e) => { e.preventDefault(); this.uploadDocument(); }); // File input document.getElementById('file-input').addEventListener('change', (e) => { this.handleFileSelect(e.target.files[0]); }); // Drag and drop const uploadArea = document.getElementById('upload-area'); uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('dragover'); }); uploadArea.addEventListener('dragleave', () => { uploadArea.classList.remove('dragover'); }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('dragover'); this.handleFileSelect(e.dataTransfer.files[0]); }); // Search and filters document.getElementById('search-documents').addEventListener('input', () => { this.filterDocuments(); }); document.getElementById('filter-status').addEventListener('change', () => { this.filterDocuments(); }); document.getElementById('filter-type').addEventListener('change', () => { this.filterDocuments(); }); } showSection(sectionName) { // Hide all sections document.querySelectorAll('.content-section').forEach(section => { section.style.display = 'none'; }); // Remove active class from nav links document.querySelectorAll('.nav-link').forEach(link => { link.classList.remove('active'); }); // Show selected section document.getElementById(`${sectionName}-section`).style.display = 'block'; // Add active class to nav link document.querySelector(`[data-section="${sectionName}"]`).classList.add('active'); // Load section-specific data if (sectionName === 'documents') { this.loadDocuments(); } else if (sectionName === 'stats') { this.loadStats(); } } handleFileSelect(file) { if (!file) return; // Validate file type const allowedTypes = [ 'application/pdf', 'image/jpeg', 'image/png', 'image/tiff', 'image/heic' ]; if (!allowedTypes.includes(file.type)) { this.showAlert('Type de fichier non supporté', 'danger'); return; } // Store file for preview this.selectedFile = file; // Update UI with preview this.showFilePreview(file); } showFilePreview(file) { const uploadArea = document.getElementById('upload-area'); if (file.type.startsWith('image/')) { // Preview for images const reader = new FileReader(); reader.onload = (e) => { uploadArea.innerHTML = `
${this.formatFileSize(file.size)}
Type: ${file.type}
${this.formatFileSize(file.size)}
Type: ${file.type}
${this.formatFileSize(file.size)}
Type: ${file.type}
${this.formatFileSize(file.size)}
Type: ${file.type}
ou cliquez pour sélectionner un fichier
`; // Hide document preview this.hideDocumentPreview(); // Re-setup event listeners document.getElementById('file-input').addEventListener('change', (e) => { this.handleFileSelect(e.target.files[0]); }); } async uploadDocument() { const file = this.selectedFile || document.getElementById('file-input')?.files[0]; if (!file) { this.showAlert('Veuillez sélectionner un fichier', 'warning'); return; } const formData = new FormData(); formData.append('file', file); formData.append('id_dossier', document.getElementById('id-dossier').value); formData.append('etude_id', document.getElementById('etude-id').value); formData.append('utilisateur_id', document.getElementById('utilisateur-id').value); formData.append('source', 'upload'); const typeDocument = document.getElementById('type-document').value; if (typeDocument) { formData.append('type_document_attendu', typeDocument); } try { this.showProgress(true); this.updateProgress(0, 'Envoi du document...'); const response = await fetch(`${this.apiUrl}/api/notary/upload`, { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`Erreur HTTP: ${response.status}`); } const result = await response.json(); this.currentDocument = result; this.updateProgress(25, 'Document reçu, traitement en cours...'); // Show document preview after successful upload this.showDocumentPreview(result, file); // Poll for status updates this.pollDocumentStatus(result.document_id); } catch (error) { console.error('Erreur upload:', error); this.showAlert(`Erreur lors de l'upload: ${error.message}`, 'danger'); this.showProgress(false); } } async pollDocumentStatus(documentId) { const maxAttempts = 60; // 5 minutes max let attempts = 0; const poll = async () => { try { const response = await fetch(`${this.apiUrl}/api/notary/document/${documentId}/status`); const status = await response.json(); this.updateProgress( status.progress || 0, status.current_step || 'Traitement en cours...' ); if (status.status === 'completed') { this.updateProgress(100, 'Traitement terminé!'); setTimeout(() => { this.showProgress(false); this.loadDocuments(); this.showAlert('Document traité avec succès!', 'success'); this.showSection('documents'); }, 1000); } else if (status.status === 'error') { this.showProgress(false); this.showAlert('Erreur lors du traitement', 'danger'); } else if (attempts < maxAttempts) { attempts++; setTimeout(poll, 5000); // Poll every 5 seconds } else { this.showProgress(false); this.showAlert('Timeout du traitement', 'warning'); } } catch (error) { console.error('Erreur polling:', error); this.showProgress(false); this.showAlert('Erreur de communication', 'danger'); } }; poll(); } showProgress(show) { const container = document.querySelector('.progress-container'); container.style.display = show ? 'block' : 'none'; } updateProgress(percent, text) { const progressBar = document.querySelector('.progress-bar'); const progressText = document.getElementById('progress-text'); progressBar.style.width = `${percent}%`; progressText.textContent = text; } async loadDocuments() { try { const response = await fetch(`${this.apiUrl}/api/notary/documents`); const data = await response.json(); this.documents = data.documents || []; this.renderDocuments(); } catch (error) { console.error('Erreur chargement documents:', error); this.showAlert('Erreur lors du chargement des documents', 'danger'); } } renderDocuments() { const container = document.getElementById('documents-list'); if (this.documents.length === 0) { container.innerHTML = `Commencez par uploader un document
Aucune entité extraite
'; } renderVerifications(verifications) { let html = ''; for (const [service, result] of Object.entries(verifications)) { const statusClass = result.status === 'verified' ? 'success' : result.status === 'error' ? 'error' : 'warning'; html += `Aucune vérification effectuée
'; } async loadStats() { try { const response = await fetch(`${this.apiUrl}/api/notary/stats`); const stats = await response.json(); document.getElementById('total-documents').textContent = stats.documents_traites || 0; document.getElementById('processing-documents').textContent = stats.documents_en_cours || 0; document.getElementById('success-rate').textContent = `${((stats.taux_reussite || 0) * 100).toFixed(1)}%`; document.getElementById('avg-time').textContent = `${stats.temps_moyen_traitement || 0}s`; this.renderCharts(stats); } catch (error) { console.error('Erreur chargement stats:', error); } } renderCharts(stats) { // Détruire les graphiques existants if (this.charts.documentTypes) { this.charts.documentTypes.destroy(); } if (this.charts.timeline) { this.charts.timeline.destroy(); } // Document types chart const typesCtx = document.getElementById('document-types-chart'); if (typesCtx) { this.charts.documentTypes = new Chart(typesCtx, { type: 'doughnut', data: { labels: Object.keys(stats.types_documents || {}), datasets: [{ data: Object.values(stats.types_documents || {}), backgroundColor: [ '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40' ] }] }, options: { responsive: true, plugins: { legend: { position: 'bottom' } } } }); } // Timeline chart const timelineCtx = document.getElementById('timeline-chart'); if (timelineCtx) { this.charts.timeline = new Chart(timelineCtx, { type: 'line', data: { labels: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun'], datasets: [{ label: 'Documents traités', data: [12, 19, 3, 5, 2, 3], borderColor: '#007bff', backgroundColor: 'rgba(0, 123, 255, 0.1)', tension: 0.4 }] }, options: { responsive: true, scales: { y: { beginAtZero: true } } } }); } } async checkSystemStatus() { try { // Check API const response = await fetch(`${this.apiUrl}/api/health`); const health = await response.json(); document.getElementById('api-status').textContent = 'Connecté'; document.getElementById('api-status').className = 'badge bg-success'; // Check LLM (simplified) document.getElementById('llm-status').textContent = 'Disponible'; document.getElementById('llm-status').className = 'badge bg-success'; // Check external APIs (simplified) document.getElementById('external-apis-status').textContent = 'OK'; document.getElementById('external-apis-status').className = 'badge bg-success'; } catch (error) { console.error('Erreur vérification statut:', error); document.getElementById('api-status').textContent = 'Erreur'; document.getElementById('api-status').className = 'badge bg-danger'; } } showAlert(message, type = 'info') { const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type} alert-dismissible fade show`; alertDiv.innerHTML = ` ${message} `; // Insert at the top of main content const mainContent = document.querySelector('.main-content'); mainContent.insertBefore(alertDiv, mainContent.firstChild); // Auto-dismiss after 5 seconds setTimeout(() => { if (alertDiv.parentNode) { alertDiv.remove(); } }, 5000); } 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]; } async downloadDocument(documentId) { // Implementation for document download this.showAlert('Fonctionnalité de téléchargement en cours de développement', 'info'); } downloadReport() { // Implementation for report download this.showAlert('Fonctionnalité de téléchargement de rapport en cours de développement', 'info'); } } // Global functions function testConnection() { app.checkSystemStatus(); app.showAlert('Test de connexion effectué', 'info'); } // L'application est maintenant initialisée dans index.html