feat: add confirmed Take down stack action with optional volume removal (#1599)

* feat: add confirmed Take down stack action with optional volume removal

Expose Take down in the stack header and sidebar with a confirmation dialog
that runs compose down while keeping the stack definition on disk. Optional
volume removal is gated by node capability and stack:deploy permission, with
remote gateway preflight before proxying removeVolumes requests.

Closes #1582

* fix: reset take-down volume checkbox when dialog closes

* test: align getStackMenuVisibility assertions with showTakeDown key

getStackMenuVisibility now returns a fifth lifecycle flag, showTakeDown,
but three exhaustive toEqual assertions still listed only the prior four
keys and failed. Add the expected showTakeDown value to each: true for
the partial and exited running-stack cases, false for the self stack.

* test: cover Take down visibility for running non-self stacks

The getStackMenuVisibility assertions exercised the partial and exited
branches and the self-stack guard, but not the raw === 'running' literal
that drives showTakeDown for a normal running stack. Add a case so a
regression dropping 'running' from that check is caught.

* fix: drop Take down from header overflow and wire activity shortcut

Remove duplicate Take down from More actions.

Keep inline button when running, sidebar menu, and Cmd+ArrowDown.

Record stack_taken_down in activity on successful POST /down.
This commit is contained in:
Anso
2026-07-09 12:20:13 -04:00
committed by GitHub
parent 296ddff2a0
commit d113004359
48 changed files with 952 additions and 110 deletions
@@ -4,6 +4,7 @@ import { PreDeployScanDialog } from '../stack/PreDeployScanDialog';
import { UpdateReadinessDialog } from '../stack/UpdateReadinessDialog';
import { SelfStackProtectedDialog } from '../stack/SelfStackProtectedDialog';
import { DeleteStackDialog } from './DeleteStackDialog';
import { TakeDownStackDialog } from './TakeDownStackDialog';
import { UnsavedChangesDialog } from './UnsavedChangesDialog';
import { StackAlertSheet } from '../StackAlertSheet';
import { GitSourcePanel } from '../stack/GitSourcePanel';
@@ -25,6 +26,7 @@ interface ShellOverlaysProps {
gitSourceOpen: boolean;
setGitSourceOpen: (open: boolean) => void;
canSelfUpdate: boolean;
canOfferVolumeRemoval: boolean;
onOpenFleetNodeUpdates: () => void;
}
@@ -39,10 +41,12 @@ export function ShellOverlays({
gitSourceOpen,
setGitSourceOpen,
canSelfUpdate,
canOfferVolumeRemoval,
onOpenFleetNodeUpdates,
}: ShellOverlaysProps) {
const {
deleteDialogOpen, closeDeleteDialog, stackToDelete,
takeDownDialogOpen, closeTakeDownDialog, stackToTakeDown,
pendingUnsavedLoad, pendingLeaveAction,
bashModalOpen, selectedContainer,
logViewerOpen, logContainer,
@@ -64,6 +68,14 @@ export function ShellOverlays({
onConfirm={stackActions.deleteStack}
/>
<TakeDownStackDialog
open={takeDownDialogOpen}
onOpenChange={(open) => { if (!open) closeTakeDownDialog(); }}
stackName={stackToTakeDown}
showVolumeOption={canOfferVolumeRemoval}
onConfirm={stackActions.takeDownStack}
/>
<SelfStackProtectedDialog
open={selfStackProtectedOpen}
onOpenChange={setSelfStackProtectedOpen}