import { useEffect, useState } from 'react' import { useFileStore } from '@/stores/fileStore' import { api } from '@/services/api' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Checkbox } from '@/components/ui/checkbox' import { FileUpload } from './FileUpload' import { DeleteConfirmDialog } from './DeleteConfirmDialog' import { PDFPreviewModal } from './PDFPreviewModal' import { CollectionVerifier } from './CollectionVerifier' import { ChunkViewerModal } from './ChunkViewerModal' import { ChunkingConfigModalLandingAI, type LandingAIConfig } from './ChunkingConfigModalLandingAI' import { Upload, Download, Trash2, Search, FileText, Eye, MessageSquare, Scissors } from 'lucide-react' interface DashboardProps { onProcessingChange?: (isProcessing: boolean) => void } export function Dashboard({ onProcessingChange }: DashboardProps = {}) { const { selectedTema, files, setFiles, loading, setLoading, selectedFiles, toggleFileSelection, selectAllFiles, clearSelection } = useFileStore() const [searchTerm, setSearchTerm] = useState('') const [uploadDialogOpen, setUploadDialogOpen] = useState(false) const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) const [fileToDelete, setFileToDelete] = useState(null) const [deleting, setDeleting] = useState(false) const [downloading, setDownloading] = useState(false) // Estados para el modal de preview de PDF const [previewModalOpen, setPreviewModalOpen] = useState(false) const [previewFileUrl, setPreviewFileUrl] = useState(null) const [previewFileName, setPreviewFileName] = useState('') const [previewFileTema, setPreviewFileTema] = useState(undefined) const [loadingPreview, setLoadingPreview] = useState(false) // Estados para el modal de chunks const [chunkViewerOpen, setChunkViewerOpen] = useState(false) const [chunkFileName, setChunkFileName] = useState('') const [chunkFileTema, setChunkFileTema] = useState('') // Estados para chunking const [chunkingConfigOpen, setChunkingConfigOpen] = useState(false) const [chunkingFileName, setChunkingFileName] = useState('') const [chunkingFileTema, setChunkingFileTema] = useState('') const [chunkingCollectionName, setChunkingCollectionName] = useState('') const [processing, setProcessing] = useState(false) useEffect(() => { loadFiles() }, [selectedTema]) const loadFiles = async () => { try { setLoading(true) const response = await api.getFiles(selectedTema || undefined) setFiles(response.files) } catch (error) { console.error('Error loading files:', error) } finally { setLoading(false) } } const handleUploadSuccess = () => { loadFiles() } // Eliminar archivo individual const handleDeleteSingle = async (filename: string) => { setFileToDelete(filename) setDeleteDialogOpen(true) } // Eliminar archivos seleccionados const handleDeleteMultiple = () => { if (selectedFiles.size === 0) return setFileToDelete(null) setDeleteDialogOpen(true) } // Confirmar eliminación const confirmDelete = async () => { if (!fileToDelete && selectedFiles.size === 0) return setDeleting(true) try { if (fileToDelete) { // Eliminar archivo individual await api.deleteFile(fileToDelete, selectedTema || undefined) } else { // Eliminar archivos seleccionados const filesToDelete = Array.from(selectedFiles) await api.deleteFiles(filesToDelete, selectedTema || undefined) clearSelection() } // Recargar archivos await loadFiles() setDeleteDialogOpen(false) setFileToDelete(null) } catch (error) { console.error('Error deleting files:', error) } finally { setDeleting(false) } } // Descargar archivo individual const handleDownloadSingle = async (filename: string) => { try { setDownloading(true) await api.downloadFile(filename, selectedTema || undefined) } catch (error) { console.error('Error downloading file:', error) } finally { setDownloading(false) } } // Descargar archivos seleccionados const handleDownloadMultiple = async () => { if (selectedFiles.size === 0) return try { setDownloading(true) const filesToDownload = Array.from(selectedFiles) const zipName = selectedTema ? `${selectedTema}_archivos` : 'archivos_seleccionados' await api.downloadMultipleFiles(filesToDownload, selectedTema || undefined, zipName) } catch (error) { console.error('Error downloading files:', error) } finally { setDownloading(false) } } // Abrir preview de PDF const handlePreviewFile = async (filename: string, tema?: string) => { // Solo permitir preview de archivos PDF if (!filename.toLowerCase().endsWith('.pdf')) { console.log('Solo se pueden previsualizar archivos PDF') return } try { setLoadingPreview(true) setPreviewFileName(filename) setPreviewFileTema(tema) // Obtener la URL temporal (SAS) para el archivo const url = await api.getPreviewUrl(filename, tema) setPreviewFileUrl(url) setPreviewModalOpen(true) } catch (error) { console.error('Error obteniendo URL de preview:', error) alert('Error al cargar la vista previa del archivo') } finally { setLoadingPreview(false) } } // Manejar descarga desde el modal de preview const handleDownloadFromPreview = async () => { if (previewFileName) { await handleDownloadSingle(previewFileName) } } // Abrir modal de chunks const handleViewChunks = (filename: string, tema: string) => { if (!tema) { alert('No hay tema seleccionado. Por favor selecciona un tema primero.') return } setChunkFileName(filename) setChunkFileTema(tema) setChunkViewerOpen(true) } // Handlers para chunking const handleStartChunking = (filename: string, tema: string) => { if (!tema) { alert('No hay tema seleccionado. Por favor selecciona un tema primero.') return } setChunkingFileName(filename) setChunkingFileTema(tema) setChunkingCollectionName(tema) // Usar el tema como nombre de colección setChunkingConfigOpen(true) } const handleProcessWithLandingAI = async (config: LandingAIConfig) => { setProcessing(true) onProcessingChange?.(true) setChunkingConfigOpen(false) try { const result = await api.processWithLandingAI(config) // Mensaje detallado let message = `Completado\n\n` message += `• Modo: ${result.mode === 'quick' ? 'Rápido' : 'Con Extracción'}\n` message += `• Chunks procesados: ${result.total_chunks}\n` message += `• Chunks agregados: ${result.chunks_added}\n` message += `• Colección: ${result.collection_name}\n` message += `• Tiempo: ${result.processing_time_seconds}s\n` if (result.schema_used) { message += `• Schema usado: ${result.schema_used}\n` } if (result.extracted_data) { message += `\nDatos extraídos disponibles en metadata` } alert(message) // Recargar archivos loadFiles() } catch (error: any) { console.error('Error processing with LandingAI:', error) alert(`❌ Error: ${error.message}`) } finally { setProcessing(false) onProcessingChange?.(false) } } const filteredFiles = files.filter(file => file.name.toLowerCase().includes(searchTerm.toLowerCase()) ) const formatFileSize = (bytes: number) => { 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] } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('es-ES', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } // Preparar datos para el modal de confirmación const getDeleteDialogProps = () => { if (fileToDelete) { return { title: 'Eliminar archivo', description: `¿Estás seguro de que quieres eliminar "${fileToDelete}"? Esta acción no se puede deshacer.`, fileList: [fileToDelete] } } else { const filesToDelete = Array.from(selectedFiles) return { title: `Eliminar ${filesToDelete.length} archivos`, description: `¿Estás seguro de que quieres eliminar ${filesToDelete.length} archivo${filesToDelete.length !== 1 ? 's' : ''}? Esta acción no se puede deshacer.`, fileList: filesToDelete } } } return (
{/* Processing Banner */} {processing && (

Procesando archivo con LandingAI... Por favor no navegues ni realices otras acciones.

)} {/* Header */}

{selectedTema ? `Tema: ${selectedTema}` : 'Todos los archivos'}

{filteredFiles.length} archivo{filteredFiles.length !== 1 ? 's' : ''}

{/* Search and Actions */}
setSearchTerm(e.target.value)} className="pl-10" disabled={processing} />
{selectedFiles.size > 0 && (
)}
{/* Table */}
{loading ? (

Cargando archivos...

) : filteredFiles.length === 0 ? (

{searchTerm ? 'No se encontraron archivos' : 'No hay archivos en este tema'}

) : ( 0} onCheckedChange={(checked: boolean) => { if (checked) { selectAllFiles() } else { clearSelection() } }} /> Nombre Tamaño Fecha Tema Acciones {filteredFiles.map((file) => { const isPDF = file.name.toLowerCase().endsWith('.pdf') return ( toggleFileSelection(file.name)} /> {isPDF ? ( ) : ( {file.name} )} {formatFileSize(file.size)} {formatDate(file.last_modified)} {file.tema || 'General'}
) })}
)}
{/* Upload Dialog */} {/* Delete Confirmation Dialog */} {/* PDF Preview Modal */} {/* Collection Verifier - Verifica/crea colección cuando se selecciona un tema */} { console.log(`Collection ${selectedTema} exists: ${exists}`) }} /> {/* Chunk Viewer Modal */} setChunkViewerOpen(false)} fileName={chunkFileName} tema={chunkFileTema} /> {/* Modal de configuración de chunking con LandingAI */} setChunkingConfigOpen(false)} fileName={chunkingFileName} tema={chunkingFileTema} collectionName={chunkingCollectionName} onProcess={handleProcessWithLandingAI} />
) }