refactor(ui): replace native window.confirm with ConfirmDialog for task deletion

Unify confirmation UX — task-list and board-container now use the shared
ConfirmDialog component instead of browser-native window.confirm().
This commit is contained in:
viettranx committed 2026-03-20 07:23:19 +07:00
1 parent d9dffc66e7
commit a8fecfb3b1
2 files changed
+52 -6

No files matched your search

@@ -9,6 +9,7 @@ import { BoardToolbar } from "./board-toolbar";
import { KanbanBoard } from "./kanban-board";
import { TaskDetailDialog } from "../task-sections/task-detail-dialog";
import { TaskList } from "../task-sections";
import { ConfirmDialog } from "@/components/shared/confirm-dialog";
import type {
TeamTaskData, TeamTaskComment, TeamTaskEvent, TeamTaskAttachment,
TeamMemberData, ScopeEntry,
@@ -72,6 +73,8 @@ export const BoardContainer = memo(function BoardContainer({
const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
const [selectedScope, setSelectedScope] = useState<ScopeEntry | null>(null);
const [selectedTask, setSelectedTask] = useState<TeamTaskData | null>(null);
const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null);
const [singleDeleting, setSingleDeleting] = useState(false);
// Lookups for name resolution
const taskLookup = useMemo(() => buildTaskLookup(tasks), [tasks]);
@@ -227,16 +230,24 @@ export const BoardContainer = memo(function BoardContainer({
const deleteTaskRef = useRef(deleteTask);
deleteTaskRef.current = deleteTask;
const handleDeleteTask = useCallback(async (taskId: string) => {
const handleDeleteTask = useCallback((taskId: string) => {
if (!deleteTaskRef.current) return;
if (!window.confirm(t("tasks.deleteConfirm"))) return;
setDeleteTargetId(taskId);
}, []);
const confirmDeleteTask = useCallback(async () => {
if (!deleteTaskRef.current || !deleteTargetId) return;
setSingleDeleting(true);
try {
await deleteTaskRef.current(teamId, taskId);
await deleteTaskRef.current(teamId, deleteTargetId);
toast.success(t("toast.taskDeleted"));
setDeleteTargetId(null);
} catch {
toast.error(t("toast.failedDeleteTask"));
} finally {
setSingleDeleting(false);
}
}, [teamId, t]);
}, [teamId, deleteTargetId, t]);
return (
<div className="flex flex-1 flex-col gap-3 overflow-hidden p-3 sm:p-4">
@@ -280,6 +291,17 @@ export const BoardContainer = memo(function BoardContainer({
)}
</div>
<ConfirmDialog
open={!!deleteTargetId}
onOpenChange={(v) => !v && setDeleteTargetId(null)}
title={t("tasks.delete")}
description={t("tasks.deleteConfirm")}
confirmLabel={t("tasks.delete")}
variant="destructive"
onConfirm={confirmDeleteTask}
loading={singleDeleting}
/>
{selectedTask && (
<TaskDetailDialog
task={selectedTask}
@@ -3,6 +3,7 @@ import { ClipboardList, Trash2 } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { useTranslation } from "react-i18next";
import { ConfirmDialog } from "@/components/shared/confirm-dialog";
import { ConfirmDeleteDialog } from "@/components/shared/confirm-delete-dialog";
import { Pagination } from "@/components/shared/pagination";
import { usePagination } from "@/hooks/use-pagination";
@@ -36,6 +37,8 @@ export function TaskList({
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
const [confirmOpen, setConfirmOpen] = useState(false);
const [deleting, setDeleting] = useState(false);
const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null);
const [singleDeleting, setSingleDeleting] = useState(false);
const taskLookup = useMemo(() => buildTaskLookup(tasks), [tasks]);
const memberLookup = useMemo(() => buildMemberLookup(members), [members]);
const { pageItems, pagination, setPage, setPageSize } = usePagination(tasks, { defaultPageSize: 20 });
@@ -107,10 +110,20 @@ export function TaskList({
const handleDelete = (e: React.MouseEvent, taskId: string) => {
e.stopPropagation();
if (!deleteTask) return;
if (!window.confirm(t("tasks.deleteConfirm"))) return;
deleteTask(teamId, taskId);
setDeleteTargetId(taskId);
};
const handleSingleDelete = useCallback(async () => {
if (!deleteTask || !deleteTargetId) return;
setSingleDeleting(true);
try {
await deleteTask(teamId, deleteTargetId);
setDeleteTargetId(null);
} finally {
setSingleDeleting(false);
}
}, [deleteTask, teamId, deleteTargetId]);
const hasBulkDelete = !!deleteTasksBulk && pageTerminalIds.length > 0;
const allPageSelected = pageTerminalIds.length > 0 && pageTerminalIds.every((id) => selectedIds.has(id));
const someSelected = selectedIds.size > 0 && !allPageSelected;
@@ -255,6 +268,17 @@ export function TaskList({
/>
)}
<ConfirmDialog
open={!!deleteTargetId}
onOpenChange={(v) => !v && setDeleteTargetId(null)}
title={t("tasks.delete")}
description={t("tasks.deleteConfirm")}
confirmLabel={t("tasks.delete")}
variant="destructive"
onConfirm={handleSingleDelete}
loading={singleDeleting}
/>
<ConfirmDeleteDialog
open={confirmOpen}
onOpenChange={setConfirmOpen}