From 5d89a1075476d454048a245922e992619a716eb2 Mon Sep 17 00:00:00 2001 From: Anso Date: Mon, 3 Aug 2026 22:14:24 -0400 Subject: [PATCH] fix(ui): show delayed busy feedback on confirm actions (#1763) Wire ConfirmModal and BusyButton so async confirms lock immediately, show spinner and progressive labels after duration-base, and block dismiss mid-flight. Connect stack delete and take-down to the existing stackAction map so the dialog is not idle until the toast. --- .../components/ComposeDiffPreviewDialog.tsx | 11 +-- frontend/src/components/EditorLayout.tsx | 2 + .../EditorLayout/DeleteStackDialog.tsx | 13 ++- .../components/EditorLayout/ShellOverlays.tsx | 17 ++++ .../EditorLayout/TakeDownStackDialog.tsx | 7 +- .../__tests__/DeleteStackDialog.test.tsx | 16 +++ .../__tests__/TakeDownStackDialog.test.tsx | 7 ++ .../__tests__/resolveStackFileKey.test.ts | 22 +++++ .../EditorLayout/hooks/resolveStackFileKey.ts | 12 +++ .../EditorLayout/hooks/useStackActions.ts | 11 +-- .../components/files/DeleteFileConfirm.tsx | 15 +-- .../ui/__tests__/busy-button.test.tsx | 55 +++++++++++ .../components/ui/__tests__/modal.test.tsx | 94 ++++++++++++++++++ frontend/src/components/ui/alert-dialog.tsx | 6 +- frontend/src/components/ui/busy-button.tsx | 75 ++++++++++++++ frontend/src/components/ui/modal.tsx | 58 +++++++---- .../src/hooks/__tests__/useVisualBusy.test.ts | 98 +++++++++++++++++++ frontend/src/hooks/useVisualBusy.ts | 36 +++++++ 18 files changed, 509 insertions(+), 46 deletions(-) create mode 100644 frontend/src/components/EditorLayout/hooks/__tests__/resolveStackFileKey.test.ts create mode 100644 frontend/src/components/EditorLayout/hooks/resolveStackFileKey.ts create mode 100644 frontend/src/components/ui/__tests__/busy-button.test.tsx create mode 100644 frontend/src/components/ui/__tests__/modal.test.tsx create mode 100644 frontend/src/components/ui/busy-button.tsx create mode 100644 frontend/src/hooks/__tests__/useVisualBusy.test.ts create mode 100644 frontend/src/hooks/useVisualBusy.ts diff --git a/frontend/src/components/ComposeDiffPreviewDialog.tsx b/frontend/src/components/ComposeDiffPreviewDialog.tsx index 8f0f03a2..d6d788b6 100644 --- a/frontend/src/components/ComposeDiffPreviewDialog.tsx +++ b/frontend/src/components/ComposeDiffPreviewDialog.tsx @@ -1,8 +1,8 @@ import { Suspense } from 'react'; import { DiffEditor } from '@/lib/monacoLoader'; -import { Loader2 } from 'lucide-react'; import { Modal, ModalHeader, ModalFooter } from '@/components/ui/modal'; import { Button } from '@/components/ui/button'; +import { BusyButton } from '@/components/ui/busy-button'; import type { ComposeDiffActionLabel } from '@/components/resolveComposeDiffActionLabel'; export interface ComposeDiffPreviewDialogProps { @@ -75,16 +75,13 @@ export function ComposeDiffPreviewDialog({ } primary={ - + } /> diff --git a/frontend/src/components/EditorLayout.tsx b/frontend/src/components/EditorLayout.tsx index 8c73458b..10be5d72 100644 --- a/frontend/src/components/EditorLayout.tsx +++ b/frontend/src/components/EditorLayout.tsx @@ -1056,6 +1056,8 @@ export default function EditorLayout() { void; stackName: string | null; onConfirm: (pruneVolumes: boolean) => void | Promise; + /** True while the stack delete request owns the flow (from stackActionMap). */ + confirming?: boolean; } -export function DeleteStackDialog({ open, onOpenChange, stackName, onConfirm }: DeleteStackDialogProps) { +export function DeleteStackDialog({ + open, + onOpenChange, + stackName, + onConfirm, + confirming = false, +}: DeleteStackDialogProps) { const [pruneVolumes, setPruneVolumes] = useState(false); const handleOpenChange = (next: boolean) => { @@ -42,6 +50,8 @@ export function DeleteStackDialog({ open, onOpenChange, stackName, onConfirm }: description={`Confirm deletion of ${stackName ?? 'stack'}.`} hint={pruneVolumes ? 'VOLUMES PRUNED' : 'VOLUMES KEPT'} confirmLabel="Delete" + busyConfirmLabel="Deleting..." + confirming={confirming} onConfirm={() => onConfirm(pruneVolumes)} >

This action cannot be undone.

@@ -49,6 +59,7 @@ export function DeleteStackDialog({ open, onOpenChange, stackName, onConfirm }: setPruneVolumes(v === true)} />