// Primus IQ — minimalist sidebar (collapsible) const PIcon = ({ name, size = 18, color = 'currentColor' }) => { const s = { stroke: color, fill: 'none', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' }; const w = size, h = size; switch (name) { case 'plus': return ; case 'chat': return ; case 'folder': return ; case 'book': return ; case 'trophy': return ; case 'search': return ; case 'apps': return ; case 'chev': return ; case 'star': return ; case 'settings': return ; case 'panel-left': return ; case 'users': return ; default: return null; } }; const PrimusMark = ({ size = 32 }) => ( P ); // ────────────────────────────────────────────────────────────────────────────── // Item row // ────────────────────────────────────────────────────────────────────────────── const NavItem = ({ icon, label, active, onClick, collapsed }) => { const inner = ( ); return inner; }; const sidebarSectionLabel = { fontSize: 10.5, fontWeight: 700, color: 'var(--mute)', letterSpacing: '0.08em', textTransform: 'uppercase', padding: '0 12px 8px', marginTop: 22, }; const _relWhen = (iso) => { if (!iso) return ''; const diff = Date.now() - new Date(iso).getTime(); if (diff < 60000) return 'now'; if (diff < 3600000) return `${Math.floor(diff / 60000)}m`; if (diff < 86400000) return `${Math.floor(diff / 3600000)}h`; if (diff < 604800000) return `${Math.floor(diff / 86400000)}d`; return new Date(iso).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' }); }; // Session row in the main sidebar. Click opens; hover reveals star + delete. const SidebarChatRow = ({ session, active, onOpen, onStar, onDelete }) => { const [hovered, setHovered] = React.useState(false); return (
setHovered(true)} onMouseLeave={() => setHovered(false)} onClick={() => onOpen(session.session_id)} style={{ height: 34, padding: '0 8px 0 12px', borderRadius: 9, cursor: 'pointer', background: active ? 'rgba(122,28,28,0.09)' : hovered ? 'rgba(255,255,255,0.5)' : 'transparent', display: 'flex', alignItems: 'center', gap: 8, width: '100%', transition: 'background 0.15s', flexShrink: 0, }} > {session.is_starred && } {session.title || 'New chat'} {hovered ? ( e.stopPropagation()}> ) : ( {_relWhen(session.last_message_at || session.created_at)} )}
); }; const sidebarRowBtn = { width: 22, height: 22, borderRadius: 5, border: 'none', background: 'transparent', cursor: 'pointer', display: 'grid', placeItems: 'center', }; // ────────────────────────────────────────────────────────────────────────────── // Sidebar // ────────────────────────────────────────────────────────────────────────────── const BellIcon = ({ size = 18, color = 'currentColor' }) => ( ); const NotificationPanel = ({ notifications, onClose, onManage, onMarkAllRead }) => { const ref = React.useRef(null); React.useEffect(() => { const handler = (e) => { if (ref.current && !ref.current.contains(e.target) && !e.target.closest('#notif-bell-btn')) { onClose(); } }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, []); const typeIcon = (type) => { if (type === 'join_request') return '🙋'; if (type === 'join_approved') return '✅'; if (type === 'join_rejected') return '❌'; if (type === 'project_invite') return '📩'; return '🔔'; }; return (
Notifications {notifications.some(n => !n.is_read) && ( )}
{notifications.length === 0 ? (
No notifications yet
) : notifications.map(n => (
{typeIcon(n.notification_type)} {n.message}
{new Date(n.created_at).toLocaleDateString('en-IN', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' })} {n.notification_type === 'join_request' && ( )}
))}
); }; const PrimusSidebar = ({ active, setRoute, collapsed, setCollapsed, currentUser, onLogout, notifications = [], unreadCount = 0, onManageJoinRequest, onMarkAllRead, onOpenNotifications, chatSessions = [], activeChatId = null, onNewChat, onSelectChat, onStarChat, onDeleteChat, mobileOpen }) => { const [settingsOpen, setSettingsOpen] = React.useState(false); const [notifOpen, setNotifOpen] = React.useState(false); const displayName = currentUser?.full_name || 'You'; const initials = displayName.split(' ').filter(Boolean).map(w => w[0].toUpperCase()).slice(0, 2).join(''); const designation = currentUser?.designation ? currentUser.designation.split(' ').map(w => w[0].toUpperCase() + w.slice(1)).join(' ') : 'Primus Partners'; const w = collapsed ? 68 : 264; return ( ); }; const settingItemStyle = { width: '100%', padding: '9px 14px', background: 'transparent', border: 'none', cursor: 'pointer', textAlign: 'left', fontSize: 12, fontFamily: 'inherit', color: 'var(--ink)', transition: 'background 0.12s', display: 'block', }; Object.assign(window, { PrimusSidebar, PIcon, NavItem, PrimusMark, SidebarChatRow, BellIcon, NotificationPanel, settingItemStyle });