feat: Ajout aperçu document avant et après upload

- Aperçu immédiat lors de la sélection de fichier
- Aperçu détaillé après upload réussi
- Support images (miniature) et PDF (icône)
- Gestion des instances de graphiques Chart.js
- Correction erreur 'Canvas is already in use'
- Interface utilisateur améliorée avec cartes d'aperçu
- Boutons d'action intégrés (Voir analyse, Fermer)
- Nettoyage automatique des aperçus
This commit is contained in:
ncantu 2025-09-09 07:12:09 +02:00
parent e4b7dc8b58
commit 0acb87c122
2 changed files with 241 additions and 22 deletions

View File

@ -7,6 +7,7 @@ class NotaryApp {
this.apiUrl = 'http://localhost:8000';
this.currentDocument = null;
this.documents = [];
this.charts = {}; // Stockage des instances de graphiques
this.init();
}
@ -110,20 +111,202 @@ class NotaryApp {
return;
}
// Update UI
// Store file for preview
this.selectedFile = file;
// Update UI with preview
this.showFilePreview(file);
}
showFilePreview(file) {
const uploadArea = document.getElementById('upload-area');
uploadArea.innerHTML = `
<i class="fas fa-file fa-3x text-success mb-3"></i>
<h5>${file.name}</h5>
<p class="text-muted">${this.formatFileSize(file.size)}</p>
<button type="button" class="btn btn-outline-danger" onclick="app.clearFile()">
<i class="fas fa-times"></i> Supprimer
</button>
`;
if (file.type.startsWith('image/')) {
// Preview for images
const reader = new FileReader();
reader.onload = (e) => {
uploadArea.innerHTML = `
<div class="file-preview">
<div class="preview-image mb-3">
<img src="${e.target.result}" alt="Aperçu" class="img-thumbnail" style="max-width: 200px; max-height: 200px;">
</div>
<div class="file-info">
<h5>${file.name}</h5>
<p class="text-muted">${this.formatFileSize(file.size)}</p>
<p class="text-muted">Type: ${file.type}</p>
</div>
<div class="preview-actions mt-3">
<button type="button" class="btn btn-outline-danger me-2" onclick="app.clearFile()">
<i class="fas fa-times"></i> Supprimer
</button>
<button type="button" class="btn btn-primary" onclick="app.uploadDocument()">
<i class="fas fa-upload"></i> Uploader
</button>
</div>
</div>
`;
};
reader.readAsDataURL(file);
} else if (file.type === 'application/pdf') {
// Preview for PDF
uploadArea.innerHTML = `
<div class="file-preview">
<div class="preview-pdf mb-3">
<i class="fas fa-file-pdf fa-4x text-danger"></i>
<div class="mt-2">
<small class="text-muted">Aperçu PDF non disponible</small>
</div>
</div>
<div class="file-info">
<h5>${file.name}</h5>
<p class="text-muted">${this.formatFileSize(file.size)}</p>
<p class="text-muted">Type: ${file.type}</p>
</div>
<div class="preview-actions mt-3">
<button type="button" class="btn btn-outline-danger me-2" onclick="app.clearFile()">
<i class="fas fa-times"></i> Supprimer
</button>
<button type="button" class="btn btn-primary" onclick="app.uploadDocument()">
<i class="fas fa-upload"></i> Uploader
</button>
</div>
</div>
`;
} else {
// Generic preview for other files
uploadArea.innerHTML = `
<div class="file-preview">
<div class="preview-generic mb-3">
<i class="fas fa-file fa-4x text-primary"></i>
</div>
<div class="file-info">
<h5>${file.name}</h5>
<p class="text-muted">${this.formatFileSize(file.size)}</p>
<p class="text-muted">Type: ${file.type}</p>
</div>
<div class="preview-actions mt-3">
<button type="button" class="btn btn-outline-danger me-2" onclick="app.clearFile()">
<i class="fas fa-times"></i> Supprimer
</button>
<button type="button" class="btn btn-primary" onclick="app.uploadDocument()">
<i class="fas fa-upload"></i> Uploader
</button>
</div>
</div>
`;
}
}
showDocumentPreview(uploadResult, file) {
// Create a preview modal or section
const previewContainer = document.getElementById('upload-preview');
if (!previewContainer) {
// Create preview container if it doesn't exist
const uploadSection = document.getElementById('upload-section');
const previewDiv = document.createElement('div');
previewDiv.id = 'upload-preview';
previewDiv.className = 'mt-4';
uploadSection.appendChild(previewDiv);
}
const previewContainer = document.getElementById('upload-preview');
let previewContent = '';
if (file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = (e) => {
previewContent = `
<div class="card">
<div class="card-header">
<h6 class="mb-0">
<i class="fas fa-eye me-2"></i>Aperçu du document uploadé
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<img src="${e.target.result}" alt="Aperçu" class="img-fluid rounded">
</div>
<div class="col-md-8">
<h6>Informations du document</h6>
<ul class="list-unstyled">
<li><strong>Nom:</strong> ${file.name}</li>
<li><strong>Taille:</strong> ${this.formatFileSize(file.size)}</li>
<li><strong>Type:</strong> ${file.type}</li>
<li><strong>ID Document:</strong> ${uploadResult.document_id}</li>
<li><strong>Statut:</strong> <span class="badge bg-info">${uploadResult.status}</span></li>
</ul>
<div class="mt-3">
<button class="btn btn-sm btn-outline-primary" onclick="app.viewDocument('${uploadResult.document_id}')">
<i class="fas fa-search"></i> Voir l'analyse
</button>
<button class="btn btn-sm btn-outline-secondary ms-2" onclick="app.hideDocumentPreview()">
<i class="fas fa-times"></i> Fermer
</button>
</div>
</div>
</div>
</div>
</div>
`;
previewContainer.innerHTML = previewContent;
};
reader.readAsDataURL(file);
} else {
previewContent = `
<div class="card">
<div class="card-header">
<h6 class="mb-0">
<i class="fas fa-eye me-2"></i>Aperçu du document uploadé
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-2">
<i class="fas fa-file-pdf fa-4x text-danger"></i>
</div>
<div class="col-md-10">
<h6>Informations du document</h6>
<ul class="list-unstyled">
<li><strong>Nom:</strong> ${file.name}</li>
<li><strong>Taille:</strong> ${this.formatFileSize(file.size)}</li>
<li><strong>Type:</strong> ${file.type}</li>
<li><strong>ID Document:</strong> ${uploadResult.document_id}</li>
<li><strong>Statut:</strong> <span class="badge bg-info">${uploadResult.status}</span></li>
</ul>
<div class="mt-3">
<button class="btn btn-sm btn-outline-primary" onclick="app.viewDocument('${uploadResult.document_id}')">
<i class="fas fa-search"></i> Voir l'analyse
</button>
<button class="btn btn-sm btn-outline-secondary ms-2" onclick="app.hideDocumentPreview()">
<i class="fas fa-times"></i> Fermer
</button>
</div>
</div>
</div>
</div>
</div>
`;
previewContainer.innerHTML = previewContent;
}
}
hideDocumentPreview() {
const previewContainer = document.getElementById('upload-preview');
if (previewContainer) {
previewContainer.innerHTML = '';
}
}
clearFile() {
// Clear selected file
this.selectedFile = null;
// Clear file input
document.getElementById('file-input').value = '';
// Reset upload area
document.getElementById('upload-area').innerHTML = `
<i class="fas fa-cloud-upload-alt fa-3x text-primary mb-3"></i>
<h5>Glissez-déposez votre document ici</h5>
@ -134,6 +317,9 @@ class NotaryApp {
</button>
`;
// Hide document preview
this.hideDocumentPreview();
// Re-setup event listeners
document.getElementById('file-input').addEventListener('change', (e) => {
this.handleFileSelect(e.target.files[0]);
@ -141,14 +327,7 @@ class NotaryApp {
}
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];
const file = this.selectedFile || document.getElementById('file-input')?.files[0];
if (!file) {
this.showAlert('Veuillez sélectionner un fichier', 'warning');
@ -185,6 +364,9 @@ class NotaryApp {
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);
@ -487,10 +669,18 @@ class NotaryApp {
}
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) {
new Chart(typesCtx, {
this.charts.documentTypes = new Chart(typesCtx, {
type: 'doughnut',
data: {
labels: Object.keys(stats.types_documents || {}),
@ -516,6 +706,32 @@ class NotaryApp {
}
});
}
// 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() {
@ -587,7 +803,4 @@ function testConnection() {
app.showAlert('Test de connexion effectué', 'info');
}
// Initialize app when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
window.app = new NotaryApp();
});
// L'application est maintenant initialisée dans index.html

View File

@ -427,5 +427,11 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="app.js"></script>
<script>
// Initialisation de l'application
document.addEventListener('DOMContentLoaded', function() {
window.notaryApp = new NotaryApp();
});
</script>
</body>
</html>