From 24dddd56c59f3ec452cc5c78b042236ccc26278e Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Wed, 15 Oct 2025 10:03:34 -0300 Subject: [PATCH] fix: keep button in loading state after start request is made (#20294) After requesting a workspace start, it may take a while to become ready. Show a loading state in the meantime. Fixes https://github.com/coder/coder/issues/20233 --- site/src/pages/TaskPage/TaskPage.stories.tsx | 54 ++++++++++++++++++++ site/src/pages/TaskPage/TaskPage.tsx | 13 +++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.stories.tsx b/site/src/pages/TaskPage/TaskPage.stories.tsx index e084789dca..f1a0d3d05b 100644 --- a/site/src/pages/TaskPage/TaskPage.stories.tsx +++ b/site/src/pages/TaskPage/TaskPage.stories.tsx @@ -320,6 +320,60 @@ export const ActivePreview: Story = { }, }; +export const WorkspaceStarting: Story = { + decorators: [withGlobalSnackbar], + beforeEach: () => { + spyOn(API, "startWorkspace").mockResolvedValue( + MockStartingWorkspace.latest_build, + ); + }, + parameters: { + reactRouter: reactRouterParameters({ + location: { + pathParams: { + username: MockStoppedWorkspace.owner_name, + workspace: MockStoppedWorkspace.name, + }, + }, + routing: { + path: "/tasks/:username/:workspace", + }, + }), + queries: [ + { + key: [ + "tasks", + MockStoppedWorkspace.owner_name, + MockStoppedWorkspace.name, + ], + data: { + prompt: "Create competitors page", + workspace: MockStoppedWorkspace, + }, + }, + { + key: ["workspace", MockStoppedWorkspace.id, "parameters"], + data: { + templateVersionRichParameters: [], + buildParameters: [], + }, + }, + ], + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const startButton = await canvas.findByText("Start workspace"); + expect(startButton).toBeInTheDocument(); + + await userEvent.click(startButton); + + await waitFor(async () => { + expect(API.startWorkspace).toBeCalled(); + }); + }, +}; + export const WorkspaceStartFailure: Story = { decorators: [withGlobalSnackbar], beforeEach: () => { diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index 771cd172a6..1b4b2c1c38 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -13,6 +13,7 @@ import { displayError } from "components/GlobalSnackbar/utils"; import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; import { ScrollArea } from "components/ScrollArea/ScrollArea"; +import { Spinner } from "components/Spinner/Spinner"; import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs"; import { ArrowLeftIcon, RotateCcwIcon } from "lucide-react"; import { AgentLogs } from "modules/resources/AgentLogs/AgentLogs"; @@ -206,6 +207,11 @@ const WorkspaceNotRunning: FC = ({ task }) => { }, }); + // After requesting a workspace start, it may take a while to become ready. + // Show a loading state in the meantime. + const isWaitingForStart = + mutateStartWorkspace.isPending || mutateStartWorkspace.isSuccess; + const apiError = isApiError(mutateStartWorkspace.error) ? mutateStartWorkspace.error : undefined; @@ -223,16 +229,15 @@ const WorkspaceNotRunning: FC = ({ task }) => {