// Primus IQ — Project Detail Screen const ScreenProjectDetail = ({ setRoute, openTagging, openSharePoint, openTextInput, openUrlInput, openInvite, openEditProject, onDeleteProject, uploadedFiles, removeFile, downloadFile, editFile, activeProject, projectMembers }) => { const [dragHover, setDragHover] = React.useState(false); const [pendingCount, setPendingCount] = React.useState(0); const [tab, setTab] = React.useState('chats'); // 'chats' | 'sources' const [chats, setChats] = React.useState([]); // local chat sessions const [activeChat, setActiveChat] = React.useState(null); // currently open chat const [composerVal, setComposerVal] = React.useState(''); const [composerFocused, setComposerFocused] = React.useState(false); const [filter, setFilter] = React.useState(''); const [menuOpen, setMenuOpen] = React.useState(false); // project actions dropdown 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]); const projectName = activeProject?.name || 'Project'; const projectPractice = activeProject?.practice || ''; const fq = filter.trim().toLowerCase(); const filteredChats = fq ? chats.filter(c => `${c.title || ''} ${c.preview || ''}`.toLowerCase().includes(fq)) : chats; const filteredFiles = fq ? uploadedFiles.filter(f => (f.name || '').toLowerCase().includes(fq)) : uploadedFiles; React.useEffect(() => { if (!activeProject?.id) return; PrimusAPI.listJoinRequests(activeProject.id) .then(data => setPendingCount((data || []).length)) .catch(() => setPendingCount(0)); }, [activeProject?.id]); // Reset chats when active project changes React.useEffect(() => { setChats([]); setActiveChat(null); }, [activeProject?.id]); const submitComposer = () => { const q = composerVal.trim(); if (!q) return; const id = 'c-' + Date.now(); const newChat = { id, title: q.length > 60 ? q.slice(0, 60) + '…' : q, preview: q, updated: 'just now', messages: [{ role: 'user', text: q }], }; setChats(prev => [newChat, ...prev]); setComposerVal(''); setActiveChat(newChat); setTab('chats'); }; // ── Full-screen chat view (opened from this project) ── if (activeChat) { return ( setActiveChat(null)} onSend={(text) => { setActiveChat(prev => ({ ...prev, messages: [...(prev.messages || []), { role: 'user', text }] })); setChats(prev => prev.map(c => c.id === activeChat.id ? { ...c, preview: text, updated: 'just now' } : c)); }} /> ); } return (
{/* Crumb */}
/ {projectName}
{/* Header */}

{projectName}

{projectPractice && {projectPractice}} · {uploadedFiles.length} files
{projectMembers && projectMembers.length > 0 && (
{projectMembers.slice(0, 3).map((m, i) => (
{(m.full_name || '?')[0].toUpperCase()}
))} {projectMembers.length > 3 && (
+{projectMembers.length - 3}
)}
)}
{menuOpen && (
)}
{/* TOP ROW: New chat composer (left) + Add sources (right) — sized to content */}
{/* LEFT: New chat in */}
New chat in {projectName}
Ask anything — this project's files are referenced automatically.