// 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 }) => (
);
// ──────────────────────────────────────────────────────────────────────────────
// 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 });