// Primus IQ — Library Screen const LIB_FILES = [ { name: 'Public Policy Whitepaper 2026.pdf', ext: 'pdf', size: '6.2 MB', modified: '12 May 2026', conf: 'public', practice: 'Public Policy Realization' }, { name: 'GCC Fintech Sector Map.png', ext: 'png', size: '2.1 MB', modified: '10 May 2026', conf: 'internal', practice: 'Sector Potential Realization' }, { name: 'Sector Outlook · KSA.pptx', ext: 'pptx', size: '14.8 MB', modified: '08 May 2026', conf: 'internal', practice: 'Sector Potential Realization' }, { name: 'TCFD Compliance Matrix.xlsx', ext: 'xlsx', size: '3.4 MB', modified: '04 May 2026', conf: 'public', practice: 'Economic Potential Realization' }, { name: 'Renewables Capex 2026.pdf', ext: 'pdf', size: '4.8 MB', modified: '30 Apr 2026', conf: 'public', practice: 'Economic Potential Realization' }, { name: 'CFO interview transcript.docx', ext: 'docx', size: '180 KB', modified: '22 Apr 2026', conf: 'client', practice: 'Sector Potential Realization' }, { name: 'Cost build · Phase 1.xlsx', ext: 'xlsx', size: '2.4 MB', modified: '18 Apr 2026', conf: 'client', practice: 'Transaction Realization' }, ]; const adaptLibraryFile = (f) => { const ext = (f.file_name || '').split('.').pop().toLowerCase() || 'doc'; const bytes = f.file_size || 0; const size = bytes < 1024 ? `${bytes} B` : bytes < 1048576 ? `${(bytes / 1024).toFixed(1)} KB` : `${(bytes / 1048576).toFixed(1)} MB`; return { id: f.id, name: f.file_name || f.name || '', ext, size: size || '', modified: f.uploaded_at ? new Date(f.uploaded_at).toLocaleDateString('en-IN', { day: 'numeric', month: 'short', year: 'numeric' }) : '—', conf: f.confidentiality || 'internal', tags: f.tags || [], practice: (f.tags && f.tags.length > 0) ? f.tags[0] : '', status: f.status || 'uploading', }; }; const ScreenLibrary = ({ openAddSources, libraryFiles, onDeleteFile, onDownloadFile, onEditFile }) => { const [filter, setFilter] = React.useState('all'); const [q, setQ] = React.useState(''); // Instant client-side filter — the library list is already loaded, so no network round-trip. const sourceFiles = (libraryFiles && libraryFiles.length > 0) ? libraryFiles.map(adaptLibraryFile) : []; const ql = q.trim().toLowerCase(); const filtered = sourceFiles.filter(f => (filter === 'all' || (filter === 'images' && (f.ext === 'png' || f.ext === 'jpg')) || (filter === 'docs' && (f.ext === 'pdf' || f.ext === 'docx')) || (filter === 'sheets' && f.ext === 'xlsx') || (filter === 'decks' && f.ext === 'pptx') ) && (!ql || f.name.toLowerCase().includes(ql)) ); return (

Library

Firm-wide knowledge base — searchable across every chat.

{[ { id: 'all', l: 'All', n: sourceFiles.length }, { id: 'docs', l: 'Docs', n: sourceFiles.filter(f => f.ext === 'pdf' || f.ext === 'docx').length }, { id: 'decks', l: 'Decks', n: sourceFiles.filter(f => f.ext === 'pptx').length }, { id: 'sheets', l: 'Sheets', n: sourceFiles.filter(f => f.ext === 'xlsx').length }, { id: 'images', l: 'Images', n: sourceFiles.filter(f => f.ext === 'png' || f.ext === 'jpg').length }, ].map(t => ( ))}
setQ(e.target.value)} placeholder="Search library"/>
{sourceFiles.length === 0 && !q && (
No files in the library yet.{' '}
)} {sourceFiles.length > 0 && (
{/* Column headers - sticky so they stay aligned with rows under the scrollbar */}
Name
Confidentiality
Status
Modified
{filtered.length === 0 && (
No files match your filter.
)} {filtered.map((f, i) => ( onDownloadFile(f.id) : null} onDelete={onDeleteFile ? () => onDeleteFile(f.id) : null} onEdit={onEditFile ? () => onEditFile(f) : null} /> ))}
)}
); }; const LibraryFileRow = ({ f, isLast, onDownload, onDelete, onEdit }) => { const [menuOpen, setMenuOpen] = React.useState(false); const menuRef = React.useRef(null); React.useEffect(() => { if (!menuOpen) return; const handler = (e) => { if (menuRef.current && !menuRef.current.contains(e.target)) setMenuOpen(false); }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, [menuOpen]); return (
e.currentTarget.style.background = 'var(--bg-soft)'} onMouseLeave={e => e.currentTarget.style.background = 'transparent'} >
{f.name}
{f.size}
{CONF_LABELS[f.conf] || f.conf}
{ f.status === 'complete' ? 'File Saved' : f.status === 'failed' ? 'Save Failed' : f.status }
{f.modified}
{menuOpen && (
{onDownload && ( )} {onEdit && ( )} {onDelete && ( )}
)}
); }; Object.assign(window, { LIB_FILES, adaptLibraryFile, ScreenLibrary, LibraryFileRow, });