import { useState, useEffect } from 'react'; import { FileText, Search, Filter, Plus, ChevronRight, Clock, User, Check, X, AlertCircle, Eye, Edit, Archive, Send, ThumbsUp, FolderOpen, Book, ClipboardList, FileCheck, HelpCircle, MoreVertical, History, Download } from 'lucide-react'; import { documentsApi, Document, DocumentType, DocumentStatus, DocumentVersion } from '../lib/documentsApi'; const TYPE_CONFIG: Record = { SOP: { icon: Book, color: 'text-indigo-500 bg-indigo-50 dark:bg-indigo-900/30', label: 'Standard Operating Procedure' }, POLICY: { icon: FileCheck, color: 'text-purple-500 bg-purple-50 dark:bg-purple-900/30', label: 'Policy' }, FORM: { icon: ClipboardList, color: 'text-blue-500 bg-blue-50 dark:bg-blue-900/30', label: 'Form' }, CHECKLIST: { icon: Check, color: 'text-emerald-500 bg-emerald-50 dark:bg-emerald-900/30', label: 'Checklist' }, GUIDE: { icon: HelpCircle, color: 'text-amber-500 bg-amber-50 dark:bg-amber-900/30', label: 'Guide' }, OTHER: { icon: FileText, color: 'text-slate-500 bg-slate-50 dark:bg-slate-700', label: 'Document' } }; const STATUS_CONFIG: Record = { DRAFT: { color: 'bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-300', label: 'Draft' }, PENDING_APPROVAL: { color: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400', label: 'Pending Approval' }, APPROVED: { color: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400', label: 'Approved' }, ARCHIVED: { color: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400', label: 'Archived' } }; export default function DocumentsPage() { const [documents, setDocuments] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [filterType, setFilterType] = useState(''); const [filterStatus, setFilterStatus] = useState(''); const [filterCategory, setFilterCategory] = useState(''); const [selectedDoc, setSelectedDoc] = useState(null); const [versions, setVersions] = useState([]); const [showVersions, setShowVersions] = useState(false); const [pendingAcks, setPendingAcks] = useState([]); useEffect(() => { loadDocuments(); loadPendingAcks(); }, [filterType, filterStatus, filterCategory]); async function loadDocuments() { setLoading(true); try { const docs = await documentsApi.getDocuments({ type: filterType || undefined, status: filterStatus || undefined, category: filterCategory || undefined, search: searchTerm || undefined }); setDocuments(docs); } catch (error) { console.error('Failed to load documents:', error); } finally { setLoading(false); } } async function loadPendingAcks() { try { const pending = await documentsApi.getPendingAcks(); setPendingAcks(pending); } catch (error) { console.error('Failed to load pending acks:', error); } } async function handleViewDocument(doc: Document) { setSelectedDoc(doc); try { const vers = await documentsApi.getVersions(doc.id); setVersions(vers); } catch (error) { console.error('Failed to load versions:', error); } } async function handleAcknowledge(docId: string) { try { await documentsApi.acknowledgeDocument(docId); setPendingAcks(prev => prev.filter(d => d.id !== docId)); if (selectedDoc?.id === docId) { const updated = await documentsApi.getDocument(docId); setSelectedDoc(updated); } } catch (error) { console.error('Failed to acknowledge:', error); } } const categories = [...new Set(documents.map(d => d.category).filter(Boolean))]; const filteredDocs = documents.filter(doc => { if (searchTerm && !doc.title.toLowerCase().includes(searchTerm.toLowerCase())) { return false; } return true; }); const groupedDocs = filteredDocs.reduce((acc, doc) => { const cat = doc.category || 'Uncategorized'; if (!acc[cat]) acc[cat] = []; acc[cat].push(doc); return acc; }, {} as Record); return (
{/* Header */}

SOP Library

Standard Operating Procedures & Compliance Documents

{/* Pending Acknowledgements Banner */} {pendingAcks.length > 0 && (

{pendingAcks.length} document{pendingAcks.length > 1 ? 's require' : ' requires'} your acknowledgement

Please review and acknowledge these documents to remain compliant.

)} {/* Filters */}
setSearchTerm(e.target.value)} className="w-full pl-9 pr-4 py-2 rounded-lg border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-800 text-sm focus:ring-2 focus:ring-indigo-500" />
{categories.length > 0 && ( )}
{/* Documents Grid */} {loading ? (
Loading documents...
) : filteredDocs.length === 0 ? (

No documents found

Create your first document or adjust filters

) : (
{Object.entries(groupedDocs).map(([category, docs]) => (

{category} ({docs.length})

{docs.map(doc => { const typeConfig = TYPE_CONFIG[doc.type]; const TypeIcon = typeConfig.icon; const statusConfig = STATUS_CONFIG[doc.status]; const isPending = pendingAcks.some(p => p.id === doc.id); return (
handleViewDocument(doc)} className={`bg-white dark:bg-slate-800 rounded-xl border transition-all cursor-pointer hover:shadow-md ${isPending ? 'border-amber-300 dark:border-amber-700 ring-2 ring-amber-100 dark:ring-amber-900/30' : 'border-slate-200 dark:border-slate-700 hover:border-indigo-300 dark:hover:border-indigo-700' }`} >

{doc.title}

{typeConfig.label} • v{doc.version}

{isPending && ( )}
{statusConfig.label}
{new Date(doc.updatedAt).toLocaleDateString()}
); })}
))}
)} {/* Document Viewer Modal */} {selectedDoc && (
setSelectedDoc(null)}>
e.stopPropagation()} > {/* Modal Header */}
{(() => { const Icon = TYPE_CONFIG[selectedDoc.type].icon; return ; })()}

{selectedDoc.title}

{TYPE_CONFIG[selectedDoc.type].label} • Version {selectedDoc.version}

{/* Document Status Bar */}
{STATUS_CONFIG[selectedDoc.status].label} {selectedDoc.effectiveDate && ( Effective: {new Date(selectedDoc.effectiveDate).toLocaleDateString()} )} {selectedDoc.expiresAt && ( Expires: {new Date(selectedDoc.expiresAt).toLocaleDateString()} )}
{selectedDoc.createdBy && ( {selectedDoc.createdBy.name} )}
{/* Content Area */}
{/* Main Content */}
{/* Version Sidebar */} {showVersions && (

Version History

{versions.map(ver => (
v{ver.version} {new Date(ver.createdAt).toLocaleDateString()}
{ver.changeNotes && (

{ver.changeNotes}

)}
))}
)}
{/* Modal Footer */}
{selectedDoc.requiresAck && ( )}
)}
); }