OK display sessioni ed editing
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
// src/SessionTable.jsx
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { getSessionId } from "./useSessionId";
|
||||
import SessionEditor from "./SessionEditor";
|
||||
|
||||
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 wsRef = useRef(null);
|
||||
|
||||
const [showEditor, setShowEditor] = useState(false);
|
||||
const [editorSession, setEditorSession] = useState(null);
|
||||
|
||||
const fetchSessions = async () => {
|
||||
if (!userId) return;
|
||||
setLoading(true);
|
||||
@@ -27,10 +29,8 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
|
||||
useEffect(() => {
|
||||
if (!userId) return;
|
||||
|
||||
// Caricamento iniziale
|
||||
fetchSessions();
|
||||
|
||||
// Apri WebSocket
|
||||
const wsUrl = `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}/v1/ws/sessions?user_id=${userId}`;
|
||||
const ws = new WebSocket(wsUrl);
|
||||
wsRef.current = ws;
|
||||
@@ -42,7 +42,6 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const msg = JSON.parse(event.data);
|
||||
// Possibili tipi di evento: full_list, created, updated, deleted
|
||||
if (msg.type === "full_list") {
|
||||
setSessions(msg.sessions);
|
||||
} else if (msg.type === "created") {
|
||||
@@ -73,84 +72,76 @@ export default function SessionTable({ userId, onSelectSession, onClosePanel })
|
||||
}, [userId]);
|
||||
|
||||
const startEditing = (session) => {
|
||||
setEditingSessionId(session.session_id);
|
||||
setEditName(session.session_name);
|
||||
setEditorSession(session);
|
||||
setShowEditor(true);
|
||||
};
|
||||
|
||||
const saveName = async (sessionId) => {
|
||||
const handleEditorConfirm = async ({ session_name, model_name }) => {
|
||||
try {
|
||||
await fetch(`/v1/sessions/${sessionId}?user_id=${userId}`, {
|
||||
await fetch(`/v1/sessions/${editorSession.session_id}?user_id=${userId}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ session_name: editName }),
|
||||
body: JSON.stringify({ session_name, model_name })
|
||||
});
|
||||
// Aggiornamento ottimistico
|
||||
setSessions((prev) =>
|
||||
prev.map((s) =>
|
||||
s.session_id === sessionId ? { ...s, session_name: editName } : s
|
||||
s.session_id === editorSession.session_id
|
||||
? { ...s, session_name, model_name }
|
||||
: s
|
||||
)
|
||||
);
|
||||
setEditingSessionId(null);
|
||||
setShowEditor(false);
|
||||
setEditorSession(null);
|
||||
} catch (err) {
|
||||
console.error("Failed to rename session", err);
|
||||
console.error("Failed to update session", err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditorCancel = () => {
|
||||
setShowEditor(false);
|
||||
setEditorSession(null);
|
||||
};
|
||||
|
||||
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" });
|
||||
// Non serve fetchSessions: il WS notificherà la cancellazione
|
||||
} 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
|
||||
<>
|
||||
<div className="table-responsive session-table">
|
||||
<table className="table table-striped table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Session/Model</th>
|
||||
<th class="text-end">Created/Mess</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{loading && <tr><td colSpan="6" className="text-center">Loading…</td></tr>}
|
||||
{!loading && sessions.length === 0 && (
|
||||
<tr><td colSpan="6" 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="Edit session"
|
||||
>✏️</button>
|
||||
</td>
|
||||
<td>
|
||||
<div
|
||||
role="button"
|
||||
className="text-primary"
|
||||
onClick={() => {
|
||||
@@ -160,26 +151,47 @@ export default function SessionTable({ userId, onSelectSession, 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>
|
||||
</div>
|
||||
<div className="text-muted small">
|
||||
{s.model_name
|
||||
? s.model_name.length > 20
|
||||
? `${s.model_name.slice(0, 20)}…`
|
||||
: s.model_name
|
||||
: "default"}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="small">{new Date(s.created_at).toLocaleString()}</div>
|
||||
<div class="small">
|
||||
# mess: {s.message_count}
|
||||
</div>
|
||||
</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>
|
||||
|
||||
{showEditor && (
|
||||
<>
|
||||
<div className="modal-backdrop fade show"></div>
|
||||
<SessionEditor
|
||||
mode="edit"
|
||||
initialName={editorSession?.session_name || ""}
|
||||
initialModel={editorSession?.model_name || ""}
|
||||
onConfirm={handleEditorConfirm}
|
||||
onCancel={handleEditorCancel}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user