import React, { useState, useEffect } from 'react';
import {
getUserSessions,
deleteSession,
deleteAllSessions,
getSessionTokenStatus
} from '../services/apiService';
import './SessionSidebar.css';
const SessionSidebar = ({ featureName, currentSessionId, onSwitchSession, onNewSession }) => {
const [isOpen, setIsOpen] = useState(false);
const [sessions, setSessions] = useState([]);
const [tokenHoverData, setTokenHoverData] = useState({});
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (isOpen) fetchSessions();
}, [isOpen, featureName, currentSessionId]);
const fetchSessions = async () => {
setIsLoading(true);
try {
const data = await getUserSessions(featureName);
setSessions(data || []);
} catch (err) {
console.error('Failed to fetch sessions:', err);
} finally {
setIsLoading(false);
}
};
const handleMouseEnter = async (sessionId) => {
if (tokenHoverData[sessionId]) return;
try {
const data = await getSessionTokenStatus(sessionId);
setTokenHoverData(prev => ({ ...prev, [sessionId]: data }));
} catch (err) { /* silent */ }
};
const handleDelete = async (e, sessionId) => {
e.stopPropagation();
if (!window.confirm('Delete this session?')) return;
try {
await deleteSession(sessionId);
fetchSessions();
if (Number(currentSessionId) === sessionId) {
localStorage.removeItem(`sessionId_${featureName}`);
if (onNewSession) onNewSession();
}
} catch { alert('Failed to delete session.'); }
};
const handleDeleteAll = async () => {
if (!window.confirm('Delete ALL history for this feature?')) return;
try {
await deleteAllSessions(featureName);
fetchSessions();
if (onNewSession) onNewSession();
} catch { alert('Failed to delete all sessions.'); }
};
const formatDate = (iso) => {
const d = new Date(iso);
const now = new Date();
const diffDays = Math.floor((now - d) / 86400000);
if (diffDays === 0) return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
if (diffDays === 1) return 'Yesterday';
if (diffDays < 7) return d.toLocaleDateString([], { weekday: 'short' });
return d.toLocaleDateString([], { month: 'short', day: 'numeric' });
};
const prettyFeatureName = featureName
.split('_')
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
.join(' ');
return (
<div className={`session-sidebar ${isOpen ? 'open' : ''}`}>
{/* ▶/◀ Tab handle */}
<div className="sidebar-toggle" onClick={() => setIsOpen(!isOpen)}>
<span className="sidebar-toggle-arrow">{isOpen ? '◀' : '▶'}</span>
<span className="sidebar-toggle-label">History</span>
</div>
{isOpen && (
<div className="sidebar-content">
<div className="sidebar-header">
<h3>{prettyFeatureName} History</h3>
<button className="delete-all" onClick={handleDeleteAll}>
Clear All
</button>
</div>
<div className="sidebar-list">
{isLoading ? (
<p className="sidebar-loading">Loading sessions…</p>
) : sessions.length === 0 ? (
<p className="sidebar-empty">No past sessions yet.</p>
) : (
sessions.map(s => {
const isActive = Number(currentSessionId) === s.id;
const td = tokenHoverData[s.id];
const tooltip = td
? `Context: ${td.token_count.toLocaleString()} / ${td.token_limit.toLocaleString()} tokens (${td.percentage}%)`
: 'Hover to load token usage';
// Derive a display title: prefer session.title, fall back gracefully
const displayTitle = s.title &&
s.title !== 'New Chat Session'
? s.title
: `Session #${s.id}`;
return (
<div
key={s.id}
className={`sidebar-item ${isActive ? 'active' : ''}`}
onClick={() => onSwitchSession(s.id)}
onMouseEnter={() => handleMouseEnter(s.id)}
title={tooltip}
>
<div className="sidebar-item-info">
<span className="sidebar-item-title">{displayTitle}</span>
<div className="sidebar-item-meta">
<span className="sidebar-item-date">{formatDate(s.created_at)}</span>
{s.provider_name && (
<span className="sidebar-item-provider">{s.provider_name}</span>
)}
</div>
</div>
<button
className="sidebar-item-delete"
onClick={(e) => handleDelete(e, s.id)}
title="Delete this session"
>
×
</button>
</div>
);
})
)}
</div>
</div>
)}
</div>
);
};
export default SessionSidebar;