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 { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; 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 { ChunkViewerModal } from "./ChunkViewerModal"; import { ChunkingConfigModalLandingAI, type LandingAIConfig, } from "./ChunkingConfigModalLandingAI"; import { Upload, Download, Trash2, Search, FileText, Eye, MessageSquare, Scissors, Loader2, CheckCircle2, AlertCircle, } 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); // Collection status states const [isCheckingCollection, setIsCheckingCollection] = useState(false); const [collectionExists, setCollectionExists] = useState( null, ); const [collectionError, setCollectionError] = useState(null); useEffect(() => { loadFiles(); }, [selectedTema]); // Check collection status when tema changes useEffect(() => { checkCollectionStatus(); }, [selectedTema]); const checkCollectionStatus = async () => { if (!selectedTema) { setCollectionExists(null); return; } setIsCheckingCollection(true); setCollectionError(null); try { const result = await api.checkCollectionExists(selectedTema); setCollectionExists(result.exists); } catch (err) { console.error("Error checking collection:", err); setCollectionError( err instanceof Error ? err.message : "Error al verificar colección", ); setCollectionExists(null); } finally { setIsCheckingCollection(false); } }; const handleCreateCollection = async () => { if (!selectedTema) return; setIsCheckingCollection(true); setCollectionError(null); try { const result = await api.createCollection(selectedTema); if (result.success) { setCollectionExists(true); console.log(`Collection "${selectedTema}" created successfully`); } } catch (err) { console.error("Error creating collection:", err); setCollectionError( err instanceof Error ? err.message : "Error al crear colección", ); } finally { setIsCheckingCollection(false); } }; const loadFiles = async () => { // Don't load files if no dataroom is selected if (!selectedTema) { setFiles([]); return; } try { setLoading(true); const response = await api.getFiles(selectedTema); 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 totalFiles = files.length; 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 (

{selectedTema ? `Dataroom: ${selectedTema}` : "Selecciona un dataroom"}

{/* Collection Status Indicator */} {selectedTema && (
{isCheckingCollection ? ( <> Verificando... ) : collectionExists === true ? ( <> Colección disponible ) : collectionExists === false ? ( <> ) : collectionError ? ( <> Error de conexión ) : null}
)}

{selectedTema ? `${totalFiles} archivo${totalFiles !== 1 ? "s" : ""}` : "Selecciona un dataroom de la barra lateral para ver sus archivos"}

Dashboard Files Chat
Este panel se llenará con métricas generales próximamente.
{processing && (

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

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

Cargando archivos...

) : filteredFiles.length === 0 ? (

{!selectedTema ? "Selecciona un dataroom para ver sus archivos" : searchTerm ? "No se encontraron archivos" : "No hay archivos en este dataroom"}

) : ( 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"}
); })}
)}
El chat estará disponible próximamente.
{/* Upload Dialog */} {/* Delete Confirmation Dialog */} {/* PDF Preview Modal */} {/* 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} />
); }