import { useEffect, useState, useCallback } from 'react'; import { api, type ClientDocument } from '@/lib/api'; import { FileText, Upload, Trash2, Download, File, FileImage, FileSpreadsheet, Filter } from 'lucide-react'; import { formatDate } from '@/lib/utils'; const CATEGORIES = [ { value: '', label: 'All' }, { value: 'contract', label: 'Contract' }, { value: 'agreement', label: 'Agreement' }, { value: 'id', label: 'ID Copy' }, { value: 'statement', label: 'Statement' }, { value: 'correspondence', label: 'Correspondence' }, { value: 'other', label: 'Other' }, ]; const categoryColors: Record = { contract: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300', agreement: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300', id: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', statement: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300', correspondence: 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300', other: 'bg-slate-100 text-slate-700 dark:bg-slate-700 dark:text-slate-300', }; function fileIcon(mimeType: string) { if (mimeType.startsWith('image/')) return ; if (mimeType.includes('spreadsheet') || mimeType.includes('csv') || mimeType.includes('excel')) return ; if (mimeType.includes('pdf')) return ; return ; } function formatSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } export default function ClientDocuments({ clientId }: { clientId: string }) { const [documents, setDocuments] = useState([]); const [loading, setLoading] = useState(true); const [uploading, setUploading] = useState(false); const [category, setCategory] = useState(''); const [dragOver, setDragOver] = useState(false); const [uploadCategory, setUploadCategory] = useState('other'); const fetchDocs = useCallback(async () => { try { const docs = await api.getClientDocuments(clientId, category || undefined); setDocuments(docs); } catch { /* silently handled */ } setLoading(false); }, [clientId, category]); // eslint-disable-next-line react-hooks/set-state-in-effect useEffect(() => { fetchDocs(); }, [fetchDocs]); const handleUpload = async (files: FileList | File[]) => { setUploading(true); try { for (const file of Array.from(files)) { await api.uploadDocument(clientId, file, { category: uploadCategory }); } await fetchDocs(); } catch (e: unknown) { alert(e instanceof Error ? e.message : 'Upload failed'); } setUploading(false); }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); setDragOver(false); if (e.dataTransfer.files.length) handleUpload(e.dataTransfer.files); }; const handleDelete = async (docId: string) => { if (!confirm('Delete this document?')) return; try { await api.deleteDocument(docId); setDocuments(prev => prev.filter(d => d.id !== docId)); } catch { /* silently handled */ } }; const handleDownload = (docId: string, name: string) => { const token = localStorage.getItem('network-auth-token'); const url = api.getDocumentDownloadUrl(docId); // Use fetch with auth header then download fetch(url, { headers: token ? { Authorization: `Bearer ${token}` } : {}, credentials: 'include', }) .then(r => r.blob()) .then(blob => { const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = name; a.click(); URL.revokeObjectURL(a.href); }); }; return (
{/* Upload Area */}
{ e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} className={`border-2 border-dashed rounded-xl p-8 text-center transition-colors ${ dragOver ? 'border-blue-400 bg-blue-50 dark:bg-blue-900/20' : 'border-slate-300 dark:border-slate-600 hover:border-blue-300 dark:hover:border-blue-500' }`} >

{uploading ? 'Uploading...' : 'Drag & drop files here, or click to browse'}

{/* Category Filter */}
{CATEGORIES.map(c => ( ))}
{/* Document List */}
{loading ? (

Loading...

) : documents.length === 0 ? (

No documents uploaded yet

) : ( documents.map(doc => (
{fileIcon(doc.mimeType)}

{doc.name}

{doc.category} {formatSize(doc.size)} {formatDate(doc.createdAt)}
{doc.notes &&

{doc.notes}

}
)) )}
); }