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
View File
@@ -32,6 +32,10 @@ export const CAPABILITIES = [
'project-env-files',
'compose-storage',
'cross-node-rbac',
'stack-down-remove-volumes',
] as const;
export type Capability = (typeof CAPABILITIES)[number];
/** Must stay in sync with backend CapabilityRegistry.STACK_DOWN_REMOVE_VOLUMES_CAPABILITY */
export const STACK_DOWN_REMOVE_VOLUMES_CAPABILITY = 'stack-down-remove-volumes' as const satisfies Capability;
@@ -6,6 +6,7 @@ export const CATEGORY_LABELS: Record<NotificationCategory, string> = {
stack_started: 'Stack started',
stack_stopped: 'Stack stopped',
stack_restarted: 'Stack restarted',
stack_taken_down: 'Stack taken down',
image_update_available: 'Update available',
image_update_applied: 'Update applied',
autoheal_triggered: 'Auto-heal',
@@ -5,6 +5,7 @@ const PANEL_HIDDEN_CATEGORIES = new Set<NotificationCategory>([
'stack_started',
'stack_stopped',
'stack_restarted',
'stack_taken_down',
'image_update_applied',
]);