Files
chat-proxy/frontend/src/SessionTable.jsx
T
2025-08-22 17:11:11 +00:00

134 lines
4.3 KiB
React
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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>
);
}