Files
nexusAI/packages/chat-client/src/components/SessionList.jsx

103 lines
4.0 KiB
JavaScript

import React from 'react';
export default function SessionList({ sessions, activeSession, onSelectSession, onNewChat, isOpen, onToggle }) {
function formatDate(ts) {
if (!ts) return '';
const date = new Date(ts * 1000);
const now = new Date();
const diffDays = Math.floor((now - date) / (1000 * 60 * 60 * 24));
if (diffDays === 0) return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
if (diffDays === 1) return 'Yesterday';
if (diffDays < 7) return date.toLocaleDateString([], { weekday: 'long' });
return date.toLocaleDateString([], { month: 'short', day: 'numeric' });
}
function getPreview(session) {
if (session.isNew) return 'New conversation';
return session.external_id;
}
return (
<div style={{
width: isOpen ? 'var(--sidebar-width)' : '56px',
flexShrink: 0,
background: 'var(--bg-surface)',
borderRight: '1px solid var(--border)',
transition: 'width 0.2s ease',
overflow: 'hidden',
}} className="flex-col">
{/* Header */}
<div className="panel-header" style={{
justifyContent: isOpen ? 'space-between' : 'center',
padding: isOpen ? '0 12px 0 16px' : '0',
}}>
{isOpen && <span className="text-base" style={{ fontWeight: 500, color: 'var(--text-secondary)' }}>Conversations</span>}
<button className="btn-icon" onClick={onToggle}>{isOpen ? '◀' : '▶'}</button>
</div>
{/* New chat button */}
<div style={{ padding: isOpen ? '12px' : '12px 8px', flexShrink: 0 }}>
<button className="btn-primary" onClick={onNewChat} style={{
width: '100%',
padding: isOpen ? '8px 12px' : '8px',
display: 'flex',
alignItems: 'center',
justifyContent: isOpen ? 'flex-start' : 'center',
gap: '8px',
whiteSpace: 'nowrap',
overflow: 'hidden',
}}>
<span style={{ fontSize: '18px', lineHeight: 1, flexShrink: 0 }}>+</span>
{isOpen && <span>New Chat</span>}
</button>
</div>
{/* Session list */}
<div className="flex-1 scroll-y">
{isOpen && sessions.map(session => {
const isActive = activeSession?.external_id === session.external_id;
return (
<button
key={session.external_id}
onClick={() => onSelectSession(session)}
className="btn-reset"
style={{
width: '100%',
padding: '10px 16px',
background: isActive ? 'var(--bg-elevated)' : 'transparent',
borderLeft: isActive ? '2px solid var(--accent)' : '2px solid transparent',
textAlign: 'left',
flexDirection: 'column',
gap: '3px',
transition: 'background 0.1s',
}}
onMouseEnter={e => { if (!isActive) e.currentTarget.style.background = 'var(--bg-elevated)'; }}
onMouseLeave={e => { if (!isActive) e.currentTarget.style.background = 'transparent'; }}
>
<div className="flex truncate" style={{ justifyContent: 'space-between', gap: '8px' }}>
<span className="text-base truncate" style={{
color: isActive ? 'var(--text-primary)' : 'var(--text-secondary)',
fontWeight: isActive ? 500 : 400,
flex: 1,
}}>
{getPreview(session)}
</span>
<span className="text-xs text-muted flex-shrink">{formatDate(session.updated_at)}</span>
</div>
{session.isNew && (
<span className="text-xs text-accent" style={{ fontStyle: 'italic' }}>Unsaved</span>
)}
</button>
);
})}
{isOpen && sessions.length === 0 && (
<div className="text-base text-muted" style={{ padding: '24px 16px', textAlign: 'center' }}>
No conversations yet
</div>
)}
</div>
</div>
);
}