From 147fb2ee6676ecf6b2ad1cc2a1a1f682439f35d0 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:11:52 +0800 Subject: [PATCH] fix(autopilot): confirm before deleting autopilot or trigger (#1604) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Destructive actions in the autopilot detail page fired immediately on click. Wrap "Delete autopilot" and per-trigger delete with AlertDialog confirmation, matching the existing issue-delete pattern. Also fix a latent bug in trigger deletion where the success toast was shown synchronously after mutate(), so failures still reported success — switch to mutateAsync + try/catch. --- .../components/autopilot-detail-page.tsx | 79 +++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/packages/views/autopilots/components/autopilot-detail-page.tsx b/packages/views/autopilots/components/autopilot-detail-page.tsx index 65d3e5047..a35c8b484 100644 --- a/packages/views/autopilots/components/autopilot-detail-page.tsx +++ b/packages/views/autopilots/components/autopilot-detail-page.tsx @@ -27,6 +27,16 @@ import { DialogContent, DialogTitle, } from "@multica/ui/components/ui/dialog"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@multica/ui/components/ui/alert-dialog"; import { TriggerConfigSection, getDefaultTriggerConfig, @@ -91,6 +101,21 @@ function RunRow({ run }: { run: AutopilotRun }) { function TriggerRow({ trigger, autopilotId }: { trigger: AutopilotTrigger; autopilotId: string }) { const deleteTrigger = useDeleteAutopilotTrigger(); + const [confirmOpen, setConfirmOpen] = useState(false); + const [deleting, setDeleting] = useState(false); + + const handleDelete = async () => { + setDeleting(true); + try { + await deleteTrigger.mutateAsync({ autopilotId, triggerId: trigger.id }); + toast.success("Trigger deleted"); + setConfirmOpen(false); + } catch { + toast.error("Failed to delete trigger"); + } finally { + setDeleting(false); + } + }; return (
@@ -121,13 +146,30 @@ function TriggerRow({ trigger, autopilotId }: { trigger: AutopilotTrigger; autop size="icon" variant="ghost" className="h-7 w-7 shrink-0" - onClick={() => { - deleteTrigger.mutate({ autopilotId, triggerId: trigger.id }); - toast.success("Trigger deleted"); - }} + onClick={() => setConfirmOpen(true)} > + { if (!v && !deleting) setConfirmOpen(false); }}> + + + Delete trigger + + This trigger will be removed and the autopilot will stop firing on this schedule. This action cannot be undone. + + + + Cancel + + {deleting ? "Deleting..." : "Delete"} + + + +
); } @@ -211,6 +253,8 @@ export function AutopilotDetailPage({ autopilotId }: { autopilotId: string }) { const [triggerDialogOpen, setTriggerDialogOpen] = useState(false); const [editDialogOpen, setEditDialogOpen] = useState(false); + const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false); + const [deleting, setDeleting] = useState(false); if (isLoading) { return ( @@ -271,12 +315,14 @@ export function AutopilotDetailPage({ autopilotId }: { autopilotId: string }) { }; const handleDelete = async () => { + setDeleting(true); try { await deleteAutopilot.mutateAsync(autopilotId); toast.success("Autopilot deleted"); router.push(wsPaths.autopilots()); } catch { toast.error("Failed to delete autopilot"); + setDeleting(false); } }; @@ -401,7 +447,7 @@ export function AutopilotDetailPage({ autopilotId }: { autopilotId: string }) { {/* Danger zone */}

Danger Zone

- @@ -429,6 +475,29 @@ export function AutopilotDetailPage({ autopilotId }: { autopilotId: string }) { triggers={triggers} /> )} + { if (!v && !deleting) setDeleteConfirmOpen(false); }} + > + + + Delete autopilot + + This will permanently delete “{autopilot.title}”, along with its triggers and run history. This action cannot be undone. + + + + Cancel + + {deleting ? "Deleting..." : "Delete"} + + + + ); }