Files
chat-proxy/frontend/src/SessionTable.jsx
T

134 lines
4.3 KiB
React

// src/SessionTable.jsx
import React, { useEffect, useState } from "react";
import { getSessionId } from "./useSessionId";
export default function SessionTable({ userId, onSelectSession, onClosePanel }) {
const [sessions, setSessions] = useState([]);
const [loading, setLoading] = useState(false);
const [editingSessionId, setEditingSessionId] = useState(null);
const [editName, setEditName] = useState("");
const activeSessionId = getSessionId();
const fetchSessions = async () => {
setLoading(true);
try {
const res = await fetch(`/v1/sessions?user_id=${userId}`);
const data = await res.json();
setSessions(data);
} catch (err) {
console.error("Failed to fetch sessions", err);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (userId) fetchSessions();
}, [userId]);
const startEditing = (session) => {
setEditingSessionId(session.session_id);
setEditName(session.session_name);
};
const saveName = async (sessionId) => {
try {
await fetch(`/v1/sessions/${sessionId}?user_id=${userId}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ session_name: editName }),
});
setEditingSessionId(null);
fetchSessions();
} catch (err) {
console.error("Failed to rename session", err);
}
};
const deleteSession = async (sessionId) => {
if (!window.confirm("Delete this session and its history?")) return;
try {
await fetch(`/v1/sessions/${sessionId}?user_id=${userId}`, { method: "DELETE" });
fetchSessions();
} catch (err) {
console.error("Failed to delete session", err);
}
};
return (
<div className="table-responsive session-table">
<table className="table table-striped table-hover align-middle">
<thead>
<tr>
<th></th>
<th>Session</th>
<th>Created</th>
<th className="text-nowrap"># mess</th>
<th></th>
</tr>
</thead>
<tbody>
{loading && <tr><td colSpan="5" className="text-center">Loading</td></tr>}
{!loading && sessions.length === 0 && (
<tr><td colSpan="5" className="text-center">No sessions found</td></tr>
)}
{!loading && sessions.map((s) => (
<tr
key={s.session_id}
className={s.session_id === activeSessionId ? "table-primary" : ""}
>
<td className="text-end text-nowrap">
<button
className="btn btn-sm px-1 btn-outline-secondary me-1"
onClick={() => startEditing(s)}
title="Rename"
>✏️</button>
</td>
<td>
{editingSessionId === s.session_id ? (
<input
type="text"
className="form-control form-control-sm"
value={editName}
onChange={(e) => setEditName(e.target.value)}
onBlur={() => saveName(s.session_id)}
onKeyDown={(e) => {
if (e.key === "Enter") saveName(s.session_id);
if (e.key === "Escape") setEditingSessionId(null);
}}
autoFocus
/>
) : (
<span
role="button"
className="text-primary"
onClick={() => {
onSelectSession(s.session_id);
if (typeof onClosePanel === "function") onClosePanel();
}}
title="Click to load this session"
>
{s.session_name}
</span>
)}
</td>
<td>{new Date(s.created_at).toLocaleString()}</td>
<td title={s.history_size_bytes}>
{s.message_count}
</td>
<td className="text-end text-nowrap">
<button
className="btn btn-sm px-1 btn-outline-danger"
onClick={() => deleteSession(s.session_id)}
title="Delete"
>🗑️</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}