"use client" import { useState, useEffect } from "react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Badge } from "@/components/ui/badge" import { Separator } from "@/components/ui/separator" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" import { Search, FileText, Trash2, Eye, Download, Calendar, Hash, FileIcon, Filter } from "lucide-react" import { useToast } from "@/hooks/use-toast" import { apiClient } from "@/lib/api-client" import { config } from "@/lib/config" import { downloadFile } from "@/lib/file-download" interface Collection { id: string name: string } interface Document { id: string filename: string original_filename: string file_type: string size: number created_at: string processed_at: string word_count: number status: 'processed' | 'processing' | 'error' collection_id: string collection_name: string converted_content?: string metadata?: { language?: string entities?: Array<{ text: string; label: string }> keywords?: string[] } } interface DocumentBrowserProps { collections: Collection[] selectedCollection: string | null onCollectionSelected: (collectionId: string | null) => void } export function DocumentBrowser({ collections, selectedCollection, onCollectionSelected }: DocumentBrowserProps) { const [documents, setDocuments] = useState([]) const [filteredDocuments, setFilteredDocuments] = useState([]) const [loading, setLoading] = useState(false) const [searchTerm, setSearchTerm] = useState("") const [filterCollection, setFilterCollection] = useState(selectedCollection || "all") const [filterType, setFilterType] = useState("all") const [filterStatus, setFilterStatus] = useState("all") const [selectedDocument, setSelectedDocument] = useState(null) const [deleting, setDeleting] = useState(null) const { toast } = useToast() useEffect(() => { loadDocuments() }, []) useEffect(() => { filterDocuments() }, [documents, searchTerm, filterCollection, filterType, filterStatus]) useEffect(() => { if (selectedCollection !== filterCollection) { setFilterCollection(selectedCollection || "all") } }, [selectedCollection]) const loadDocuments = async () => { setLoading(true) try { const data = await apiClient.get('/api-internal/v1/rag/documents') setDocuments(data.documents || []) } catch (error) { console.error('Failed to load documents:', error) } finally { setLoading(false) } } const filterDocuments = () => { let filtered = [...documents] // Search filter if (searchTerm) { filtered = filtered.filter(doc => doc.original_filename.toLowerCase().includes(searchTerm.toLowerCase()) || doc.metadata?.keywords?.some(keyword => keyword.toLowerCase().includes(searchTerm.toLowerCase()) ) ) } // Collection filter if (filterCollection !== "all") { filtered = filtered.filter(doc => doc.collection_id === filterCollection) } // Type filter if (filterType !== "all") { filtered = filtered.filter(doc => doc.file_type === filterType) } // Status filter if (filterStatus !== "all") { filtered = filtered.filter(doc => doc.status === filterStatus) } setFilteredDocuments(filtered) } const handleDeleteDocument = async (documentId: string) => { setDeleting(documentId) try { await apiClient.delete(`/api-internal/v1/rag/documents/${documentId}`) setDocuments(prev => prev.filter(doc => doc.id !== documentId)) toast({ title: "Success", description: "Document deleted successfully", }) } catch (error) { toast({ title: "Error", description: error instanceof Error ? error.message : "Failed to delete document", variant: "destructive", }) } finally { setDeleting(null) } } const handleDownloadDocument = async (document: Document) => { try { await downloadFile( `/api-internal/v1/rag/documents/${document.id}/download`, document.original_filename ) } catch (error) { toast({ title: "Error", description: "Failed to download document", variant: "destructive", }) } } 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('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } const getFileTypeIcon = (fileType: string) => { const iconClass = "h-4 w-4" switch (fileType.toLowerCase()) { case 'pdf': return case 'docx': case 'doc': return case 'xlsx': case 'xls': return default: return } } const getStatusBadge = (status: string) => { const variants = { processed: 'bg-green-100 text-green-800', processing: 'bg-yellow-100 text-yellow-800', error: 'bg-red-100 text-red-800' } return ( {status.charAt(0).toUpperCase() + status.slice(1)} ) } // Get unique file types for filter const uniqueFileTypes = Array.from(new Set(documents.map(doc => doc.file_type))) return (
{/* Filters */} Search & Filter Documents
setSearchTerm(e.target.value)} className="pl-8" />
{/* Results Summary */}

Showing {filteredDocuments.length} of {documents.length} documents

{/* Documents List */}
{filteredDocuments.length === 0 ? (

No Documents Found

{searchTerm || filterCollection !== "all" || filterType !== "all" || filterStatus !== "all" ? "Try adjusting your search criteria or filters." : "Upload some documents to get started."}

) : ( filteredDocuments.map((document) => (
{getFileTypeIcon(document.file_type)}

{document.original_filename}

{getStatusBadge(document.status)}
{document.collection_name} {formatFileSize(document.size)} {document.word_count} words {formatDate(document.created_at)}
{document.metadata?.keywords && document.metadata.keywords.length > 0 && (
{document.metadata.keywords.slice(0, 5).map((keyword, index) => ( {keyword} ))} {document.metadata.keywords.length > 5 && ( +{document.metadata.keywords.length - 5} more )}
)}
{document.original_filename} Document details and converted content {selectedDocument && (

File Information

Type: {selectedDocument.file_type.toUpperCase()}

Size: {formatFileSize(selectedDocument.size)}

Words: {selectedDocument.word_count}

Collection: {selectedDocument.collection_name}

Processing Info

Status: {getStatusBadge(selectedDocument.status)}

Uploaded: {formatDate(selectedDocument.created_at)}

Processed: {formatDate(selectedDocument.processed_at)}

{selectedDocument.metadata?.language && (

Language: {selectedDocument.metadata.language}

)}
{selectedDocument.metadata?.entities && selectedDocument.metadata.entities.length > 0 && (

Entities

{selectedDocument.metadata.entities.slice(0, 10).map((entity, index) => ( {entity.text} ({entity.label}) ))}
)} {selectedDocument.converted_content && (

Converted Content

                                    {selectedDocument.converted_content.substring(0, 2000)}
                                    {selectedDocument.converted_content.length > 2000 && "..."}
                                  
)}
)}
Delete Document Are you sure you want to delete "{document.original_filename}"? This action cannot be undone and will remove the document from the collection and search index. Cancel handleDeleteDocument(document.id)} className="bg-red-600 hover:bg-red-700" disabled={deleting === document.id} > {deleting === document.id ? "Deleting..." : "Delete"}
)) )}
) }