fix(frontend): English UI strings for session menu/dialogs; rename empty-guard + cancel test

- SessionMenu: anglicize all Italian labels (Vista divisa → Split view, etc.)
- RenameDialog: add empty-name guard + change title to "Rename session"
- DeleteConfirmDialog: translate title and description to English
- SessionActions.test: add 2 new tests (empty name guard, cancel on DeleteConfirmDialog)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 13:01:54 +02:00
co-authored by Claude Opus 4.8
parent 013a84ed4b
commit 4f97ea9fc2
4 changed files with 28 additions and 11 deletions
+2 -2
View File
@@ -12,9 +12,9 @@ export function DeleteConfirmDialog({ open, label, onOpenChange, onConfirm }: Pr
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader><DialogTitle>Elimina definitivamente</DialogTitle></DialogHeader> <DialogHeader><DialogTitle>Delete permanently</DialogTitle></DialogHeader>
<DialogDescription> <DialogDescription>
"{label}" verrà eliminata definitivamente, inclusi tutti i suoi documenti. L'operazione non è reversibile. "{label}" will be permanently deleted, including all of its documents. This action cannot be undone.
</DialogDescription> </DialogDescription>
<DialogFooter> <DialogFooter>
<Button type="button" variant="outline" size="sm" onClick={() => onOpenChange(false)}>Cancel</Button> <Button type="button" variant="outline" size="sm" onClick={() => onOpenChange(false)}>Cancel</Button>
+2 -1
View File
@@ -15,6 +15,7 @@ export function RenameDialog({ open, initial, onOpenChange, onSubmit }: Props) {
function submit(e: React.FormEvent) { function submit(e: React.FormEvent) {
e.preventDefault(); e.preventDefault();
if (!name.trim()) return;
onSubmit(name.trim()); onSubmit(name.trim());
onOpenChange(false); onOpenChange(false);
} }
@@ -22,7 +23,7 @@ export function RenameDialog({ open, initial, onOpenChange, onSubmit }: Props) {
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader><DialogTitle>Rinomina sessione</DialogTitle></DialogHeader> <DialogHeader><DialogTitle>Rename session</DialogTitle></DialogHeader>
<form onSubmit={submit} className="flex flex-col gap-3"> <form onSubmit={submit} className="flex flex-col gap-3">
<div> <div>
<label className="mb-1.5 block text-xs font-medium text-muted-foreground" htmlFor="rename-name">Name</label> <label className="mb-1.5 block text-xs font-medium text-muted-foreground" htmlFor="rename-name">Name</label>
+17 -1
View File
@@ -7,7 +7,7 @@ import { DeleteConfirmDialog } from "./DeleteConfirmDialog";
test("RenameDialog submits the edited name", async () => { test("RenameDialog submits the edited name", async () => {
const onSubmit = vi.fn(); const onSubmit = vi.fn();
render(<RenameDialog open initial="Vecchio" onOpenChange={vi.fn()} onSubmit={onSubmit} />); render(<RenameDialog open initial="Vecchio" onOpenChange={vi.fn()} onSubmit={onSubmit} />);
const input = screen.getByLabelText(/name/i); const input = screen.getByDisplayValue("Vecchio");
await userEvent.clear(input); await userEvent.clear(input);
await userEvent.type(input, "Nuovo"); await userEvent.type(input, "Nuovo");
await userEvent.click(screen.getByRole("button", { name: /save/i })); await userEvent.click(screen.getByRole("button", { name: /save/i }));
@@ -21,3 +21,19 @@ test("DeleteConfirmDialog confirms only on the destructive button", async () =>
await userEvent.click(screen.getByRole("button", { name: /delete/i })); await userEvent.click(screen.getByRole("button", { name: /delete/i }));
expect(onConfirm).toHaveBeenCalled(); expect(onConfirm).toHaveBeenCalled();
}); });
test("RenameDialog does not submit an empty name", async () => {
const onSubmit = vi.fn();
render(<RenameDialog open initial="Old" onOpenChange={vi.fn()} onSubmit={onSubmit} />);
const input = screen.getByDisplayValue("Old");
await userEvent.clear(input);
await userEvent.click(screen.getByRole("button", { name: /save/i }));
expect(onSubmit).not.toHaveBeenCalled();
});
test("DeleteConfirmDialog does not confirm when Cancel is clicked", async () => {
const onConfirm = vi.fn();
render(<DeleteConfirmDialog open label="X" onOpenChange={vi.fn()} onConfirm={onConfirm} />);
await userEvent.click(screen.getByRole("button", { name: /cancel/i }));
expect(onConfirm).not.toHaveBeenCalled();
});
+7 -7
View File
@@ -29,10 +29,10 @@ export function SessionMenu({ session, groups, onView, onRename, onMove, onNewGr
<Menu.Portal> <Menu.Portal>
<Menu.Positioner side="bottom" align="end" sideOffset={4}> <Menu.Positioner side="bottom" align="end" sideOffset={4}>
<Menu.Popup className="z-50 min-w-44 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-none"> <Menu.Popup className="z-50 min-w-44 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-none">
<Menu.Item className={itemCls} onClick={onView}>Vista divisa</Menu.Item> <Menu.Item className={itemCls} onClick={onView}>Split view</Menu.Item>
<Menu.Item className={itemCls} onClick={onRename}>Rinomina</Menu.Item> <Menu.Item className={itemCls} onClick={onRename}>Rename</Menu.Item>
<Menu.SubmenuRoot> <Menu.SubmenuRoot>
<Menu.SubmenuTrigger className={itemCls}>Sposta nel gruppo ›</Menu.SubmenuTrigger> <Menu.SubmenuTrigger className={itemCls}>Move to group ›</Menu.SubmenuTrigger>
<Menu.Portal> <Menu.Portal>
<Menu.Positioner side="right" align="start"> <Menu.Positioner side="right" align="start">
<Menu.Popup className="z-50 min-w-44 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-none"> <Menu.Popup className="z-50 min-w-44 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-none">
@@ -40,18 +40,18 @@ export function SessionMenu({ session, groups, onView, onRename, onMove, onNewGr
<Menu.Item key={g} className={itemCls} onClick={() => onMove(g)}>{g}</Menu.Item> <Menu.Item key={g} className={itemCls} onClick={() => onMove(g)}>{g}</Menu.Item>
))} ))}
{session.group && ( {session.group && (
<Menu.Item className={itemCls} onClick={() => onMove("")}>Senza gruppo</Menu.Item> <Menu.Item className={itemCls} onClick={() => onMove("")}>No group</Menu.Item>
)} )}
<Menu.Item className={itemCls} onClick={onNewGroup}>Nuovo gruppo…</Menu.Item> <Menu.Item className={itemCls} onClick={onNewGroup}>New group…</Menu.Item>
</Menu.Popup> </Menu.Popup>
</Menu.Positioner> </Menu.Positioner>
</Menu.Portal> </Menu.Portal>
</Menu.SubmenuRoot> </Menu.SubmenuRoot>
<Menu.Item className={itemCls} onClick={onArchiveToggle}> <Menu.Item className={itemCls} onClick={onArchiveToggle}>
{session.archived ? "Ripristina" : "Archivia"} {session.archived ? "Restore" : "Archive"}
</Menu.Item> </Menu.Item>
<Menu.Separator className="my-1 h-px bg-border" /> <Menu.Separator className="my-1 h-px bg-border" />
<Menu.Item className={`${itemCls} text-destructive`} onClick={onDelete}>Elimina</Menu.Item> <Menu.Item className={`${itemCls} text-destructive`} onClick={onDelete}>Delete</Menu.Item>
</Menu.Popup> </Menu.Popup>
</Menu.Positioner> </Menu.Positioner>
</Menu.Portal> </Menu.Portal>