diff --git a/site/src/pages/TaskPage/TaskPage.stories.tsx b/site/src/pages/TaskPage/TaskPage.stories.tsx index a24968d483e38..03f8cfe739d89 100644 --- a/site/src/pages/TaskPage/TaskPage.stories.tsx +++ b/site/src/pages/TaskPage/TaskPage.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, spyOn, within } from "@storybook/test"; +import { API } from "api/api"; import type { Workspace, WorkspaceApp, @@ -9,6 +10,7 @@ import { MockFailedWorkspace, MockStartingWorkspace, MockStoppedWorkspace, + MockTemplate, MockWorkspace, MockWorkspaceAgent, MockWorkspaceApp, @@ -59,6 +61,16 @@ export const WaitingOnBuild: Story = { }, }; +export const WaitingOnBuildWithTemplate: Story = { + beforeEach: () => { + spyOn(API, "getTemplate").mockResolvedValue(MockTemplate); + spyOn(data, "fetchTask").mockResolvedValue({ + prompt: "Create competitors page", + workspace: MockStartingWorkspace, + }); + }, +}; + export const WaitingOnStatus: Story = { beforeEach: () => { spyOn(data, "fetchTask").mockResolvedValue({ diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index a46e0f09c7cc9..c340a96cfef11 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -1,10 +1,12 @@ import { API } from "api/api"; import { getErrorDetail, getErrorMessage } from "api/errors"; +import { template as templateQueryOptions } from "api/queries/templates"; import type { Workspace, WorkspaceStatus } from "api/typesGenerated"; import { Button } from "components/Button/Button"; import { Loader } from "components/Loader/Loader"; import { Margins } from "components/Margins/Margins"; import { Spinner } from "components/Spinner/Spinner"; +import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs"; import { ArrowLeftIcon, RotateCcwIcon } from "lucide-react"; import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks"; import type { ReactNode } from "react"; @@ -14,6 +16,10 @@ import { useParams } from "react-router-dom"; import { Link as RouterLink } from "react-router-dom"; import { ellipsizeText } from "utils/ellipsizeText"; import { pageTitle } from "utils/page"; +import { + ActiveTransition, + WorkspaceBuildProgress, +} from "../WorkspacePage/WorkspaceBuildProgress"; import { TaskApps } from "./TaskApps"; import { TaskSidebar } from "./TaskSidebar"; @@ -32,6 +38,19 @@ const TaskPage = () => { refetchInterval: 5_000, }); + const { data: template } = useQuery({ + ...templateQueryOptions(task?.workspace.template_id ?? ""), + enabled: Boolean(task), + }); + + const waitingStatuses: WorkspaceStatus[] = ["starting", "pending"]; + const shouldStreamBuildLogs = + task && waitingStatuses.includes(task.workspace.latest_build.status); + const buildLogs = useWorkspaceBuildLogs( + task?.workspace.latest_build.id ?? "", + shouldStreamBuildLogs, + ); + if (error) { return ( <> @@ -77,7 +96,6 @@ const TaskPage = () => { } let content: ReactNode = null; - const waitingStatuses: WorkspaceStatus[] = ["starting", "pending"]; const terminatedStatuses: WorkspaceStatus[] = [ "canceled", "canceling", @@ -88,16 +106,25 @@ const TaskPage = () => { ]; if (waitingStatuses.includes(task.workspace.latest_build.status)) { + // If no template yet, use an indeterminate progress bar. + const transition = (template && + ActiveTransition(template, task.workspace)) || { P50: 0, P95: null }; + const lastStage = + buildLogs?.[buildLogs.length - 1]?.stage || "Waiting for build status"; content = ( -
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: