mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 12:14:40 +00:00
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:
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}
|
||||
|
||||
Reference in new issue
Block a user