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:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1047,7 +1047,10 @@
|
||||
"unfiled": "بدون مجلد",
|
||||
"rename": "إعادة تسمية المجلد",
|
||||
"delete": "حذف المجلد",
|
||||
"deleteTitle": "حذف هذا المجلد؟",
|
||||
"deleteAction": "حذف",
|
||||
"deleteConfirm": "حذف هذا المجلد؟ ستنتقل الملاحظات بداخله إلى \"بدون مجلد\".",
|
||||
"deleteFailed": "تعذّر حذف المجلد",
|
||||
"dragHint": "اسحب ملاحظة إلى هنا",
|
||||
"moved": "تم نقل الملاحظة"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user