diff --git a/site/src/pages/WorkspacesPage/BatchStopConfirmation.tsx b/site/src/pages/WorkspacesPage/BatchStopConfirmation.tsx new file mode 100644 index 00000000000..6cee45d1455 --- /dev/null +++ b/site/src/pages/WorkspacesPage/BatchStopConfirmation.tsx @@ -0,0 +1,36 @@ +import type { FC } from "react"; +import type { Workspace } from "#/api/typesGenerated"; +import { ConfirmDialog } from "#/components/Dialogs/ConfirmDialog/ConfirmDialog"; + +type BatchStopConfirmationProps = { + workspacesToStop: readonly Workspace[]; + open: boolean; + isLoading: boolean; + onClose: () => void; + onConfirm: () => void; +}; + +export const BatchStopConfirmation: FC = ({ + workspacesToStop, + open, + onClose, + onConfirm, + isLoading, +}) => { + const workspaceCount = `${workspacesToStop.length} ${ + workspacesToStop.length === 1 ? "workspace" : "workspaces" + }`; + + return ( + + ); +}; diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.stories.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.stories.tsx index 0fec96ff6da..cf5ff0ab72b 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.stories.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.stories.tsx @@ -433,6 +433,9 @@ export const StopsOnlySelectedWorkspaces: Story = { await openBulkActions(canvas, user); await user.click(await body.findByRole("menuitem", { name: /stop/i })); + const dialog = await body.findByRole("dialog"); + await user.click(within(dialog).getByRole("button", { name: "Stop" })); + await waitFor(() => expect(API.stopWorkspace).toHaveBeenCalledTimes(2)); expect(API.stopWorkspace).toHaveBeenCalledWith("1"); expect(API.stopWorkspace).toHaveBeenCalledWith("2"); @@ -532,6 +535,9 @@ export const StopIgnoresAlreadyStoppedWorkspaces: Story = { expect(stopItem).not.toHaveAttribute("data-disabled"); await user.click(stopItem); + const dialog = await body.findByRole("dialog"); + await user.click(within(dialog).getByRole("button", { name: "Stop" })); + await waitFor(() => expect(API.stopWorkspace).toHaveBeenCalledTimes(1)); expect(API.stopWorkspace).toHaveBeenCalledWith("2"); }, diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index 2c08c716e85..1712f2ea7f2 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -23,6 +23,7 @@ import { useOrganizationsFilterMenu } from "#/modules/tableFiltering/options"; import { ACTIVE_BUILD_STATUSES } from "#/modules/workspaces/status"; import { pageTitle } from "#/utils/page"; import { BatchDeleteConfirmation } from "./BatchDeleteConfirmation"; +import { BatchStopConfirmation } from "./BatchStopConfirmation"; import { BatchUpdateModalForm } from "./BatchUpdateModalForm"; import { useBatchActions } from "./batchActions"; import { useStatusFilterMenu, useTemplateFilterMenu } from "./filter/menus"; @@ -52,7 +53,7 @@ function useSafeSearchParams() { >; } -type BatchAction = "delete" | "update"; +type BatchAction = "delete" | "stop" | "update"; const WorkspacesPage: FC = () => { const queryClient = useQueryClient(); @@ -163,6 +164,13 @@ const WorkspacesPage: FC = () => { const checkedWorkspaces = data?.workspaces.filter((w) => checkedWorkspaceIds.has(w.id)) ?? []; + // Bulk stop only affects running workspaces, so the confirmation dialog and + // the mutation should both operate on that subset to avoid over-reporting + // how many workspaces will actually be stopped. + const workspacesToStop = checkedWorkspaces.filter( + (w) => w.latest_build.status === "running", + ); + return ( <> Codestin Search App @@ -197,7 +205,7 @@ const WorkspacesPage: FC = () => { isRunningBatchAction={batchActions.isProcessing} onBatchDeleteTransition={() => setActiveBatchAction("delete")} onBatchStartTransition={() => batchActions.start(checkedWorkspaces)} - onBatchStopTransition={() => batchActions.stop(checkedWorkspaces)} + onBatchStopTransition={() => setActiveBatchAction("stop")} onBatchUpdateTransition={() => { // Just because batch-updating can be really dangerous // action for running workspaces, we're going to invalidate @@ -241,6 +249,17 @@ const WorkspacesPage: FC = () => { }} /> + setActiveBatchAction(undefined)} + onConfirm={async () => { + await batchActions.stop(workspacesToStop); + setActiveBatchAction(undefined); + }} + /> +