diff --git a/artifacts/tx-os/src/components/notes/folders-rail.tsx b/artifacts/tx-os/src/components/notes/folders-rail.tsx index b40e4925..42626984 100644 --- a/artifacts/tx-os/src/components/notes/folders-rail.tsx +++ b/artifacts/tx-os/src/components/notes/folders-rail.tsx @@ -20,6 +20,16 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; import { type NoteFolder, useCreateFolder, @@ -136,6 +146,7 @@ export function FoldersRail({ const [editingId, setEditingId] = useState(null); const [editingName, setEditingName] = useState(""); const [hoverTarget, setHoverTarget] = useState(null); + const [pendingDelete, setPendingDelete] = useState(null); const create = useCreateFolder(); const update = useUpdateFolder(); const del = useDeleteFolder(); @@ -409,27 +420,7 @@ export function FoldersRail({ {t("notes.folders.rename", "Rename folder")} { - if ( - confirm( - t( - "notes.folders.deleteConfirm", - "Delete this folder? Notes inside will move to Unfiled.", - ), - ) - ) { - del.mutate(f.id, { - onSuccess: () => { - if ( - selected.kind === "folder" && - selected.id === f.id - ) { - onSelect({ kind: "all" }); - } - }, - }); - } - }} + onSelect={() => setPendingDelete(f)} className="text-red-600 focus:text-red-600" data-testid={`folder-delete-${f.id}`} > @@ -492,6 +483,75 @@ export function FoldersRail({ "Drag a note onto a folder to move it.", )} + + { + if (!open) setPendingDelete(null); + }} + > + + + + {t("notes.folders.deleteTitle", "Delete this folder?")} + + + {t( + "notes.folders.deleteConfirm", + "Delete this folder? Notes inside will move to Unfiled.", + )} + + + + + {t("common.cancel", "Cancel")} + + { + const target = pendingDelete; + if (!target) return; + del.mutate(target.id, { + onSuccess: () => { + if ( + selected.kind === "folder" && + selected.id === target.id + ) { + onSelect({ kind: "all" }); + } + }, + onError: (e) => + reportFolderError( + e, + "notes.folders.deleteFailed", + "Could not delete folder", + ), + }); + setPendingDelete(null); + }} + > + {t("notes.folders.deleteAction", "Delete")} + + + + ); } diff --git a/artifacts/tx-os/src/locales/ar.json b/artifacts/tx-os/src/locales/ar.json index 0e8c91a8..d4540e17 100644 --- a/artifacts/tx-os/src/locales/ar.json +++ b/artifacts/tx-os/src/locales/ar.json @@ -1047,7 +1047,10 @@ "unfiled": "بدون مجلد", "rename": "إعادة تسمية المجلد", "delete": "حذف المجلد", + "deleteTitle": "حذف هذا المجلد؟", + "deleteAction": "حذف", "deleteConfirm": "حذف هذا المجلد؟ ستنتقل الملاحظات بداخله إلى \"بدون مجلد\".", + "deleteFailed": "تعذّر حذف المجلد", "dragHint": "اسحب ملاحظة إلى هنا", "moved": "تم نقل الملاحظة" }, diff --git a/artifacts/tx-os/src/locales/en.json b/artifacts/tx-os/src/locales/en.json index 30126e8c..d565c6be 100644 --- a/artifacts/tx-os/src/locales/en.json +++ b/artifacts/tx-os/src/locales/en.json @@ -948,7 +948,10 @@ "unfiled": "Unfiled", "rename": "Rename folder", "delete": "Delete folder", + "deleteTitle": "Delete this folder?", + "deleteAction": "Delete", "deleteConfirm": "Delete this folder? Notes inside will move to Unfiled.", + "deleteFailed": "Could not delete folder", "dragHint": "Drag a note here", "moved": "Note moved" }, diff --git a/artifacts/tx-os/tests/notes-folders.spec.mjs b/artifacts/tx-os/tests/notes-folders.spec.mjs index b8af2c56..2e84357e 100644 --- a/artifacts/tx-os/tests/notes-folders.spec.mjs +++ b/artifacts/tx-os/tests/notes-folders.spec.mjs @@ -285,7 +285,6 @@ test("create folder, drag note in, filter, move between folders, rename, delete" page.getByTestId(`folder-drop-${personal.id}-count`), ).toHaveText(/^1$/); - page.once("dialog", (d) => d.accept()); const deletePromise = page.waitForResponse( (r) => new URL(r.url()).pathname === `/api/note-folders/${personal.id}` && @@ -295,7 +294,12 @@ test("create folder, drag note in, filter, move between folders, rename, delete" ); await page.getByTestId(`folder-menu-${personal.id}`).click(); await page.getByTestId(`folder-delete-${personal.id}`).click(); + // In-app AlertDialog (replacing native window.confirm) — click confirm. + await page.getByTestId(`folder-delete-confirm-${personal.id}`).click(); await deletePromise; + await expect( + page.getByTestId(`folder-delete-dialog-${personal.id}`), + ).toHaveCount(0); await expect(page.getByTestId(`folder-select-${personal.id}`)).toHaveCount(0); // The orphaned note should have folderId reset to null via FK SET NULL,