From 84db1e1085116abe52216c6f02112664aebaf3a2 Mon Sep 17 00:00:00 2001 From: mptyl Date: Mon, 29 Jun 2026 21:32:32 +0200 Subject: [PATCH] feat(frontend): rename group (reassign members via setSessionGroup) Co-Authored-By: Claude Sonnet 4.6 --- .../src/shell/AppShell.session-mgmt.test.tsx | 19 +++++++ frontend/src/shell/AppShell.tsx | 50 +++++++++++++++---- 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/frontend/src/shell/AppShell.session-mgmt.test.tsx b/frontend/src/shell/AppShell.session-mgmt.test.tsx index 1ecf8623..e2d1541f 100644 --- a/frontend/src/shell/AppShell.session-mgmt.test.tsx +++ b/frontend/src/shell/AppShell.session-mgmt.test.tsx @@ -77,3 +77,22 @@ test("a failed resume keeps the panel open and does not activate the session", a // panel stays open (document still visible) after the failed resume await waitFor(() => expect(screen.getByText("Domanda originale")).toBeInTheDocument()); }); + +test("renaming a group reassigns its members via setSessionGroup", async () => { + const groupSets: Array<{ id: string; group: string }> = []; + server.use( + http.post("http://localhost:8787/sessions/:id/group", async ({ params, request }) => { + const body = (await request.json()) as { group: string }; + groupSets.push({ id: params.id as string, group: body.group }); + return new HttpResponse(null, { status: 204 }); + }), + ); + wrap(); + await screen.findByText("Aritmologia"); + await userEvent.click(screen.getByRole("button", { name: /rename group aritmologia/i })); + const input = await screen.findByLabelText(/name/i, { selector: "input" }); + await userEvent.clear(input); + await userEvent.type(input, "Cardiologia"); + await userEvent.click(screen.getByRole("button", { name: /save/i })); + await waitFor(() => expect(groupSets).toEqual([{ id: "s1", group: "Cardiologia" }])); +}); diff --git a/frontend/src/shell/AppShell.tsx b/frontend/src/shell/AppShell.tsx index 2274c1f4..4cac7a5d 100644 --- a/frontend/src/shell/AppShell.tsx +++ b/frontend/src/shell/AppShell.tsx @@ -9,6 +9,7 @@ import { RenameDialog } from "./RenameDialog"; import { DeleteConfirmDialog } from "./DeleteConfirmDialog"; import { SteerInput, ComposerFooter } from "./SteerInput"; import { WorkflowBar } from "./WorkflowBar"; +import { Pencil } from "lucide-react"; import { Button } from "../components/ui/button"; import { Toaster } from "../components/ui/sonner"; import { toast } from "sonner"; @@ -40,6 +41,7 @@ export function AppShell() { const [renameTarget, setRenameTarget] = useState(null); const [deleteTarget, setDeleteTarget] = useState(null); const [collapsedGroups, setCollapsedGroups] = useState>({}); + const [renameGroupTarget, setRenameGroupTarget] = useState(null); const groups = useMemo( () => [...new Set(sessions.map((s) => s.group).filter((g): g is string => !!g))].sort(), @@ -79,6 +81,18 @@ export function AppShell() { } } } + async function renameGroup(oldName: string, newName: string) { + const trimmed = newName.trim(); + if (!trimmed || trimmed === oldName) return; + try { + for (const s of sessions.filter((x) => x.group === oldName)) { + await setSessionGroup(s.id, trimmed); + } + refresh(); + } catch { + toast.error("Failed to rename group."); + } + } async function toggleArchive(s: SessionSummary) { try { await (s.archived ? unarchiveSession(s.id) : archiveSession(s.id)); @@ -225,15 +239,25 @@ export function AppShell() {
{groups.map((g) => (
- +
+ + +
{!collapsedGroups[g] && ( s.group === g)} @@ -291,6 +315,14 @@ export function AppShell() { }} /> )} + {renameGroupTarget && ( + { if (!o) setRenameGroupTarget(null); }} + onSubmit={async (name) => { await renameGroup(renameGroupTarget, name); setRenameGroupTarget(null); }} + /> + )} {deleteTarget && (