/** * Application JavaScript pour l'interface web 4NK Notariat */ class NotaryApp { constructor() { this.apiUrl = 'http://localhost:8000'; this.currentDocument = null; this.documents = []; 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; } // Update UI const uploadArea = document.getElementById('upload-area'); uploadArea.innerHTML = `
${file.name}

${this.formatFileSize(file.size)}

`; } clearFile() { document.getElementById('file-input').value = ''; document.getElementById('upload-area').innerHTML = `
Glissez-déposez votre document ici

ou cliquez pour sélectionner un fichier

`; // Re-setup event listeners document.getElementById('file-input').addEventListener('change', (e) => { this.handleFileSelect(e.target.files[0]); }); } async uploadDocument() { const fileInput = document.getElementById('file-input'); if (!fileInput) { this.showAlert('Élément de fichier non trouvé', 'error'); return; } const file = fileInput.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...'); // 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 = `
Aucun document trouvé

Commencez par uploader un document

`; return; } const html = this.documents.map(doc => `
${doc.filename || 'Document'}
ID: ${doc.document_id}
${this.getStatusText(doc.status)}
${new Date(doc.created_at).toLocaleDateString()}
`).join(''); container.innerHTML = html; } getStatusClass(status) { const classes = { 'queued': 'bg-warning', 'processing': 'bg-info', 'completed': 'bg-success', 'error': 'bg-danger' }; return classes[status] || 'bg-secondary'; } getStatusText(status) { const texts = { 'queued': 'En attente', 'processing': 'En cours', 'completed': 'Terminé', 'error': 'Erreur' }; return texts[status] || status; } filterDocuments() { const search = document.getElementById('search-documents').value.toLowerCase(); const statusFilter = document.getElementById('filter-status').value; const typeFilter = document.getElementById('filter-type').value; const filtered = this.documents.filter(doc => { const matchesSearch = !search || doc.filename?.toLowerCase().includes(search) || doc.document_id.toLowerCase().includes(search); const matchesStatus = !statusFilter || doc.status === statusFilter; const matchesType = !typeFilter || doc.type === typeFilter; return matchesSearch && matchesStatus && matchesType; }); // Re-render with filtered documents const originalDocuments = this.documents; this.documents = filtered; this.renderDocuments(); this.documents = originalDocuments; } async viewDocument(documentId) { try { const response = await fetch(`${this.apiUrl}/api/notary/document/${documentId}/analysis`); const analysis = await response.json(); this.showAnalysisModal(analysis); } catch (error) { console.error('Erreur chargement analyse:', error); this.showAlert('Erreur lors du chargement de l\'analyse', 'danger'); } } showAnalysisModal(analysis) { const content = document.getElementById('analysis-content'); content.innerHTML = `
Informations Générales
Type détecté: ${analysis.type_detecte}
Confiance classification: ${(analysis.confiance_classification * 100).toFixed(1)}%
Score de vraisemblance: ${(analysis.score_vraisemblance * 100).toFixed(1)}%
Entités Extraites
${this.renderEntities(analysis.entites_extraites)}
Vérifications Externes
${this.renderVerifications(analysis.verifications_externes)}
Avis de Synthèse
${analysis.avis_synthese}
Recommandations
`; const modal = new bootstrap.Modal(document.getElementById('analysisModal')); modal.show(); } renderEntities(entities) { let html = ''; for (const [type, items] of Object.entries(entities)) { if (Array.isArray(items) && items.length > 0) { html += `
${type.charAt(0).toUpperCase() + type.slice(1)}
`; items.forEach(item => { html += `
${JSON.stringify(item, null, 2)}
`; }); } } return html || '

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 += `
${service}: ${result.status} ${result.details ? `
${JSON.stringify(result.details)}` : ''}
`; } return 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) { // Document types chart const typesCtx = document.getElementById('document-types-chart'); if (typesCtx) { 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' } } } }); } } 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'); } // Initialize app when DOM is loaded document.addEventListener('DOMContentLoaded', () => { window.app = new NotaryApp(); });