import { useCallback, useState, useEffect, useRef } from 'react' import { useDropzone } from 'react-dropzone' import { Box, Typography, Paper, CircularProgress, Alert, Button, Chip, LinearProgress, Card, CardContent, List, ListItem, ListItemText, ListItemIcon, Divider } from '@mui/material' import { CloudUpload, CheckCircle, Error, HourglassEmpty, Visibility, Description, Image, PictureAsPdf } from '@mui/icons-material' import { useAppDispatch, useAppSelector } from '../store' import { uploadDocument, removeDocument, addDocuments, setCurrentDocument } from '../store/documentSlice' import { Layout } from '../components/Layout' import { FilePreview } from '../components/FilePreview' import { getTestFilesList, loadTestFile, filterSupportedFiles } from '../services/testFilesApi' import type { Document } from '../types' export default function UploadView() { const dispatch = useAppDispatch() const { documents, error, extractionById } = useAppSelector((state) => state.document) const [previewDocument, setPreviewDocument] = useState(null) const [bootstrapped, setBootstrapped] = useState(false) const [bootstrapInProgress, setBootstrapInProgress] = useState(false) const bootstrapTriggered = useRef(false) const [isProcessing, setIsProcessing] = useState(false) const [processedCount, setProcessedCount] = useState(0) const [totalFiles, setTotalFiles] = useState(0) const onDrop = useCallback( async (acceptedFiles: File[]) => { setIsProcessing(true) setTotalFiles(acceptedFiles.length) setProcessedCount(0) // Traitement en parallèle de tous les fichiers const uploadPromises = acceptedFiles.map(async (file) => { try { const doc = await dispatch(uploadDocument(file)).unwrap() // Déclencher l'extraction immédiatement if (!extractionById[doc.id]) { const { extractDocument } = await import('../store/documentSlice') dispatch(extractDocument(doc.id)) } setProcessedCount(prev => prev + 1) return doc } catch (error) { console.error(`Erreur lors du traitement de ${file.name}:`, error) setProcessedCount(prev => prev + 1) return null } }) // Attendre que tous les fichiers soient traités await Promise.all(uploadPromises) setIsProcessing(false) }, [dispatch, extractionById] ) const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, accept: { 'application/pdf': ['.pdf'], 'image/*': ['.png', '.jpg', '.jpeg', '.tiff'], }, multiple: true, }) const getStatusIcon = (status: string) => { switch (status) { case 'completed': return case 'error': return case 'processing': return default: return } } const getStatusColor = (status: string) => { switch (status) { case 'completed': return 'success' case 'error': return 'error' case 'processing': return 'warning' default: return 'default' } } // Bootstrap: charger automatiquement les fichiers de test et les traiter en parallèle useEffect(() => { if (bootstrapped || bootstrapInProgress || bootstrapTriggered.current || !import.meta.env.DEV) return const load = async () => { bootstrapTriggered.current = true setBootstrapInProgress(true) console.log('🔄 [BOOTSTRAP] Chargement automatique des fichiers de test...') try { // Récupérer la liste des fichiers disponibles const testFiles = await getTestFilesList() console.log('📁 [BOOTSTRAP] Fichiers trouvés:', testFiles.map(f => f.name)) // Filtrer les fichiers supportés const supportedFiles = filterSupportedFiles(testFiles) console.log('✅ [BOOTSTRAP] Fichiers supportés:', supportedFiles.map(f => f.name)) if (supportedFiles.length === 0) { console.log('⚠️ [BOOTSTRAP] Aucun fichier de test supporté trouvé') setBootstrapped(true) return } // Démarrer le traitement en parallèle setIsProcessing(true) setTotalFiles(supportedFiles.length) setProcessedCount(0) // Traitement en parallèle de tous les fichiers de test const loadPromises = supportedFiles.map(async (fileInfo) => { try { console.log(`📄 [BOOTSTRAP] Chargement de ${fileInfo.name}...`) const file = await loadTestFile(fileInfo.name) if (file) { // Simuler upload local const previewUrl = URL.createObjectURL(file) const document: Document = { id: `boot-${fileInfo.name}-${Date.now()}`, name: fileInfo.name, mimeType: fileInfo.type || 'application/octet-stream', functionalType: undefined, size: fileInfo.size, uploadDate: new Date(), status: 'completed', previewUrl, } // Ajouter le document au store dispatch(addDocuments([document])) setProcessedCount(prev => prev + 1) console.log(`✅ [BOOTSTRAP] ${fileInfo.name} chargé (extraction gérée par Layout)`) return document } } catch (error) { console.warn(`❌ [BOOTSTRAP] Erreur lors du chargement de ${fileInfo.name}:`, error) setProcessedCount(prev => prev + 1) return null } }) // Attendre que tous les fichiers soient chargés const results = await Promise.all(loadPromises) const successfulDocs = results.filter(doc => doc !== null) if (successfulDocs.length > 0) { console.log(`🎉 [BOOTSTRAP] ${successfulDocs.length} fichiers chargés avec succès`) // Définir le premier document comme document courant const firstDoc = successfulDocs[0] if (firstDoc) { dispatch(setCurrentDocument(firstDoc)) } } setIsProcessing(false) setBootstrapped(true) } catch (error) { console.error('❌ [BOOTSTRAP] Erreur lors du chargement des fichiers de test:', error) setIsProcessing(false) setBootstrapped(true) } finally { setBootstrapInProgress(false) } } load() }, [dispatch, bootstrapped]) const getFileIcon = (mimeType: string) => { if (mimeType.includes('pdf')) return if (mimeType.includes('image')) return return } const progressPercentage = totalFiles > 0 ? (processedCount / totalFiles) * 100 : 0 return ( Analyse de documents 4NK IA {/* Barre de progression globale */} {isProcessing && ( Traitement en cours... {processedCount} fichier{processedCount > 1 ? 's' : ''} sur {totalFiles} traité{totalFiles > 1 ? 's' : ''} )} {/* Zone de drop */} {isDragActive ? 'Déposez les fichiers ici...' : 'Glissez-déposez vos documents ou cliquez pour sélectionner'} Formats acceptés: PDF, PNG, JPG, JPEG, TIFF {error && ( {error} )} {/* Liste des documents */} {documents.length > 0 && ( Documents analysés ({documents.length}) {documents.map((doc, index) => (
{getFileIcon(doc.mimeType)} {getStatusIcon(doc.status)} {doc.name} } secondary={ {doc.mimeType} • {(doc.size / 1024 / 1024).toFixed(2)} MB } /> {index < documents.length - 1 && }
))}
)} {/* Aperçu du document */} {previewDocument && ( setPreviewDocument(null)} /> )}
) }