diff --git a/canvas/src/components/BatchActionBar.tsx b/canvas/src/components/BatchActionBar.tsx new file mode 100644 index 00000000..5421fbe2 --- /dev/null +++ b/canvas/src/components/BatchActionBar.tsx @@ -0,0 +1,124 @@ +"use client"; + +import { useState } from "react"; +import { createPortal } from "react-dom"; +import { useCanvasStore } from "@/store/canvas"; +import { ConfirmDialog } from "./ConfirmDialog"; +import { showToast } from "./Toaster"; + +type BatchAction = "restart" | "pause" | "delete" | null; + +export function BatchActionBar() { + const selectedNodeIds = useCanvasStore((s) => s.selectedNodeIds); + const clearSelection = useCanvasStore((s) => s.clearSelection); + const batchRestart = useCanvasStore((s) => s.batchRestart); + const batchPause = useCanvasStore((s) => s.batchPause); + const batchDelete = useCanvasStore((s) => s.batchDelete); + + const [pending, setPending] = useState(null); + const [busy, setBusy] = useState(false); + + const count = selectedNodeIds.size; + if (count < 2) return null; + + const confirmMessages: Record, string> = { + restart: `Restart ${count} workspace${count !== 1 ? "s" : ""}? Each will briefly go offline while it restarts.`, + pause: `Pause ${count} workspace${count !== 1 ? "s" : ""}? Their containers will be stopped.`, + delete: `Permanently delete ${count} workspace${count !== 1 ? "s" : ""}? This cannot be undone.`, + }; + + const confirmLabels: Record, string> = { + restart: "Restart All", + pause: "Pause All", + delete: "Delete All", + }; + + async function execute() { + if (!pending) return; + setBusy(true); + try { + if (pending === "restart") await batchRestart(); + if (pending === "pause") await batchPause(); + if (pending === "delete") await batchDelete(); + showToast(`${pending.charAt(0).toUpperCase() + pending.slice(1)} applied to ${count} workspace${count !== 1 ? "s" : ""}`, "success"); + clearSelection(); + } catch { + showToast(`Batch ${pending} failed`, "error"); + } finally { + setBusy(false); + setPending(null); + } + } + + const bar = ( +
+ {/* Selection count badge */} + + {count} selected + + +