Add a confirmation dialog before deleting a folder

Integrates an AlertDialog component for folder deletion, replacing the native confirm dialog, and updates corresponding locale files and test cases.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 6a869b6f-2c2b-4d44-aab0-25864af19415
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/uqawMO0
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
riyadhafraa
2026-05-06 09:29:46 +00:00
parent 3a8590cf8d
commit 42e57e6e0b
4 changed files with 92 additions and 22 deletions
@@ -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<number | null>(null);
const [editingName, setEditingName] = useState("");
const [hoverTarget, setHoverTarget] = useState<string | null>(null);
const [pendingDelete, setPendingDelete] = useState<NoteFolder | null>(null);
const create = useCreateFolder();
const update = useUpdateFolder();
const del = useDeleteFolder();
@@ -409,27 +420,7 @@ export function FoldersRail({
{t("notes.folders.rename", "Rename folder")}
</DropdownMenuItem>
<DropdownMenuItem
onSelect={() => {
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.",
)}
</div>
<AlertDialog
open={pendingDelete !== null}
onOpenChange={(open) => {
if (!open) setPendingDelete(null);
}}
>
<AlertDialogContent
data-testid={
pendingDelete
? `folder-delete-dialog-${pendingDelete.id}`
: "folder-delete-dialog"
}
>
<AlertDialogHeader>
<AlertDialogTitle>
{t("notes.folders.deleteTitle", "Delete this folder?")}
</AlertDialogTitle>
<AlertDialogDescription>
{t(
"notes.folders.deleteConfirm",
"Delete this folder? Notes inside will move to Unfiled.",
)}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel
data-testid={
pendingDelete
? `folder-delete-cancel-${pendingDelete.id}`
: "folder-delete-cancel"
}
>
{t("common.cancel", "Cancel")}
</AlertDialogCancel>
<AlertDialogAction
className="bg-red-600 text-white hover:bg-red-700 focus:ring-red-600"
data-testid={
pendingDelete
? `folder-delete-confirm-${pendingDelete.id}`
: "folder-delete-confirm"
}
onClick={() => {
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")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</aside>
);
}
+3
View File
@@ -1047,7 +1047,10 @@
"unfiled": "بدون مجلد",
"rename": "إعادة تسمية المجلد",
"delete": "حذف المجلد",
"deleteTitle": "حذف هذا المجلد؟",
"deleteAction": "حذف",
"deleteConfirm": "حذف هذا المجلد؟ ستنتقل الملاحظات بداخله إلى \"بدون مجلد\".",
"deleteFailed": "تعذّر حذف المجلد",
"dragHint": "اسحب ملاحظة إلى هنا",
"moved": "تم نقل الملاحظة"
},
+3
View File
@@ -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"
},
+5 -1
View File
@@ -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,