From f9045a983a160398e7cd56855064f1d8d1c4c21e Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 23 Jun 2025 18:55:33 -0800 Subject: [PATCH 1/7] feat: add workspace build status to task page --- site/src/pages/TaskPage/TaskPage.stories.tsx | 12 +++++++ site/src/pages/TaskPage/TaskPage.tsx | 36 ++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) 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..bb2d8baa0e0ae 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,10 +106,13 @@ 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; content = ( -
+
-

Starting your workspace

@@ -99,6 +120,15 @@ const TaskPage = () => { This should take a few minutes
+ {lastStage && ( +
{lastStage}
+ )} +
+ +
); } else if (task.workspace.latest_build.status === "failed") { From 1a59d0bdc85f871a4c896702b0c7ea8be1503493 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 24 Jun 2025 11:07:09 -0800 Subject: [PATCH 2/7] Remove redundant starting text --- site/src/pages/TaskPage/TaskPage.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index bb2d8baa0e0ae..4d390dfa42e54 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -116,9 +116,6 @@ const TaskPage = () => {

Starting your workspace

- - This should take a few minutes -
{lastStage && (
{lastStage}
From a5a511ee853e71330b931320166e4cf72d9d620f Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 24 Jun 2025 11:22:39 -0800 Subject: [PATCH 3/7] Stack progress text on tasks --- site/src/pages/TaskPage/TaskPage.tsx | 1 + .../src/pages/WorkspacePage/WorkspaceBuildProgress.tsx | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index 4d390dfa42e54..b586985d30bb6 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -124,6 +124,7 @@ const TaskPage = () => { diff --git a/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx b/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx index 715ceb136c262..d31dbcd46fed6 100644 --- a/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx @@ -62,11 +62,14 @@ const estimateFinish = ( interface WorkspaceBuildProgressProps { workspace: Workspace; transitionStats: TransitionStats; + // stack indicates to stack the text vertically, otherwise put it on one line. + stack?: boolean; } export const WorkspaceBuildProgress: FC = ({ workspace, transitionStats, + stack, }) => { const job = workspace.latest_build.job; const [progressValue, setProgressValue] = useState(0); @@ -131,7 +134,7 @@ export const WorkspaceBuildProgress: FC = ({ // is not indicative of true progress. classes={{ bar: classNames.bar }} /> -
+
{capitalize(workspace.latest_build.status)} workspace...
@@ -154,11 +157,6 @@ const styles = { paddingLeft: 2, paddingRight: 2, }, - barHelpers: { - display: "flex", - justifyContent: "space-between", - marginTop: 4, - }, label: (theme) => ({ fontSize: 12, display: "block", From d0bc61d35c22735d9c121b2e1f5675868ed1e039 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 24 Jun 2025 11:32:12 -0800 Subject: [PATCH 4/7] Move progress to bottom And remove the duplicate "Starting workspace..." text. --- site/src/pages/TaskPage/TaskPage.tsx | 17 ++++----- .../WorkspacePage/WorkspaceBuildProgress.tsx | 36 +++++++++++++------ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index b586985d30bb6..6c7191d0bf9a0 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -109,22 +109,23 @@ const TaskPage = () => { // 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; + const lastStage = + buildLogs?.[buildLogs.length - 1]?.stage || "Waiting for build status"; content = ( -
-
+
+

Starting your workspace

+ {lastStage && ( +
{lastStage}
+ )}
- {lastStage && ( -
{lastStage}
- )} -
+
diff --git a/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx b/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx index d31dbcd46fed6..98ccd2978e63c 100644 --- a/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx @@ -62,14 +62,13 @@ const estimateFinish = ( interface WorkspaceBuildProgressProps { workspace: Workspace; transitionStats: TransitionStats; - // stack indicates to stack the text vertically, otherwise put it on one line. - stack?: boolean; + style?: "workspace" | "task"; } export const WorkspaceBuildProgress: FC = ({ workspace, transitionStats, - stack, + style, }) => { const job = workspace.latest_build.job; const [progressValue, setProgressValue] = useState(0); @@ -117,6 +116,13 @@ export const WorkspaceBuildProgress: FC = ({ } return (
+ {style === "task" && ( +
+
+ {progressText} +
+
+ )} = ({ // If a transition is set, there is a moment on new load where the // bar accelerates to progressValue and then rapidly decelerates, which // is not indicative of true progress. - classes={{ bar: classNames.bar }} + classes={{ + bar: classNames.bar, + root: style === "task" ? classNames.root : undefined, + }} /> -
-
- {capitalize(workspace.latest_build.status)} workspace... -
-
- {progressText} + {style !== "task" && ( +
+
+ {capitalize(workspace.latest_build.status)} workspace... +
+
+ {progressText} +
-
+ )}
); }; @@ -149,6 +160,9 @@ export const WorkspaceBuildProgress: FC = ({ const classNames = { bar: css` transition: none; + `, + root: css` + border-radius: 0; `, }; From c16557a28f9b022cc258b66647a4c2bd2c8853ef Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 24 Jun 2025 12:59:36 -0800 Subject: [PATCH 5/7] Omit useless braces --- site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx b/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx index 98ccd2978e63c..9b406c2e5fca3 100644 --- a/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx @@ -117,7 +117,7 @@ export const WorkspaceBuildProgress: FC = ({ return (
{style === "task" && ( -
+
{progressText}
@@ -144,7 +144,7 @@ export const WorkspaceBuildProgress: FC = ({ }} /> {style !== "task" && ( -
+
{capitalize(workspace.latest_build.status)} workspace...
From 77e3034fbf7b20b14436a7e1fcb32549743f2d26 Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 24 Jun 2025 13:03:46 -0800 Subject: [PATCH 6/7] Rename style to variant --- site/src/pages/TaskPage/TaskPage.tsx | 2 +- .../WorkspacePage/WorkspaceBuildProgress.tsx | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index 6c7191d0bf9a0..b56e69827846d 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -125,7 +125,7 @@ const TaskPage = () => {
diff --git a/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx b/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx index 9b406c2e5fca3..306da719be0ca 100644 --- a/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx @@ -62,13 +62,18 @@ const estimateFinish = ( interface WorkspaceBuildProgressProps { workspace: Workspace; transitionStats: TransitionStats; - style?: "workspace" | "task"; + // variant changes how the progress bar is displayed: with the workspace + // variant the workspace transition and time remaining are displayed under the + // bar aligned to the left and right respectively. With the task variant the + // workspace transition is not displayed and the time remaining is displayed + // centered above the bar, and the bar's border radius is removed. + variant?: "workspace" | "task"; } export const WorkspaceBuildProgress: FC = ({ workspace, transitionStats, - style, + variant, }) => { const job = workspace.latest_build.job; const [progressValue, setProgressValue] = useState(0); @@ -116,7 +121,7 @@ export const WorkspaceBuildProgress: FC = ({ } return (
- {style === "task" && ( + {variant === "task" && (
{progressText} @@ -135,15 +140,17 @@ export const WorkspaceBuildProgress: FC = ({ ? "determinate" : "indeterminate" } - // If a transition is set, there is a moment on new load where the - // bar accelerates to progressValue and then rapidly decelerates, which - // is not indicative of true progress. classes={{ + // If a transition is set, there is a moment on new load where the bar + // accelerates to progressValue and then rapidly decelerates, which is + // not indicative of true progress. bar: classNames.bar, - root: style === "task" ? classNames.root : undefined, + // With the "task" variant, the progress bar is fullscreen, so remove + // the border radius. + root: variant === "task" ? classNames.root : undefined, }} /> - {style !== "task" && ( + {variant !== "task" && (
{capitalize(workspace.latest_build.status)} workspace... From 20c1823496bba55a262b212b15a1f60954bf7aff Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 24 Jun 2025 13:07:47 -0800 Subject: [PATCH 7/7] Build stage var is always set now --- site/src/pages/TaskPage/TaskPage.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index b56e69827846d..c340a96cfef11 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -117,9 +117,7 @@ const TaskPage = () => {

Starting your workspace

- {lastStage && ( -
{lastStage}
- )} +
{lastStage}