Skip to content

Commit cb7a768

Browse files
committed
feat: add workspace build status to task page
1 parent 4fd0312 commit cb7a768

File tree

2 files changed

+62
-4
lines changed

2 files changed

+62
-4
lines changed

site/src/pages/TaskPage/TaskPage.stories.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Meta, StoryObj } from "@storybook/react";
22
import { expect, spyOn, within } from "@storybook/test";
3+
import { API } from "api/api";
34
import type {
45
Workspace,
56
WorkspaceApp,
@@ -9,14 +10,16 @@ import {
910
MockFailedWorkspace,
1011
MockStartingWorkspace,
1112
MockStoppedWorkspace,
13+
MockTemplate,
1214
MockWorkspace,
1315
MockWorkspaceAgent,
1416
MockWorkspaceApp,
1517
MockWorkspaceAppStatus,
18+
MockWorkspaceBuildLogs,
1619
MockWorkspaceResource,
1720
mockApiError,
1821
} from "testHelpers/entities";
19-
import { withProxyProvider } from "testHelpers/storybook";
22+
import { withProxyProvider, withWebSocket } from "testHelpers/storybook";
2023
import TaskPage, { data, WorkspaceDoesNotHaveAITaskError } from "./TaskPage";
2124

2225
const meta: Meta<typeof TaskPage> = {
@@ -59,6 +62,33 @@ export const WaitingOnBuild: Story = {
5962
},
6063
};
6164

65+
export const WaitingOnBuildWithTemplate: Story = {
66+
beforeEach: () => {
67+
spyOn(API, "getTemplate").mockResolvedValue(MockTemplate);
68+
spyOn(data, "fetchTask").mockResolvedValue({
69+
prompt: "Create competitors page",
70+
workspace: MockStartingWorkspace,
71+
});
72+
},
73+
};
74+
75+
export const WaitingOnBuildWithLogs: Story = {
76+
parameters: {
77+
decorators: [withWebSocket],
78+
webSocket: MockWorkspaceBuildLogs.map((log) => ({
79+
event: "message",
80+
data: JSON.stringify(log),
81+
})),
82+
},
83+
beforeEach: () => {
84+
spyOn(API, "getTemplate").mockResolvedValue(MockTemplate);
85+
spyOn(data, "fetchTask").mockResolvedValue({
86+
prompt: "Create competitors page",
87+
workspace: MockStartingWorkspace,
88+
});
89+
},
90+
};
91+
6292
export const WaitingOnStatus: Story = {
6393
beforeEach: () => {
6494
spyOn(data, "fetchTask").mockResolvedValue({

site/src/pages/TaskPage/TaskPage.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { API } from "api/api";
22
import { getErrorDetail, getErrorMessage } from "api/errors";
3+
import { template as templateQueryOptions } from "api/queries/templates";
34
import type { Workspace, WorkspaceStatus } from "api/typesGenerated";
45
import { Button } from "components/Button/Button";
56
import { Loader } from "components/Loader/Loader";
67
import { Margins } from "components/Margins/Margins";
78
import { Spinner } from "components/Spinner/Spinner";
9+
import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs";
810
import { ArrowLeftIcon, RotateCcwIcon } from "lucide-react";
911
import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks";
1012
import type { ReactNode } from "react";
@@ -14,6 +16,10 @@ import { useParams } from "react-router-dom";
1416
import { Link as RouterLink } from "react-router-dom";
1517
import { ellipsizeText } from "utils/ellipsizeText";
1618
import { pageTitle } from "utils/page";
19+
import {
20+
ActiveTransition,
21+
WorkspaceBuildProgress,
22+
} from "../WorkspacePage/WorkspaceBuildProgress";
1723
import { TaskApps } from "./TaskApps";
1824
import { TaskSidebar } from "./TaskSidebar";
1925

@@ -32,6 +38,19 @@ const TaskPage = () => {
3238
refetchInterval: 5_000,
3339
});
3440

41+
const { data: template } = useQuery({
42+
...templateQueryOptions(task?.workspace.template_id ?? ""),
43+
enabled: Boolean(task),
44+
});
45+
46+
const waitingStatuses: WorkspaceStatus[] = ["starting", "pending"];
47+
const shouldStreamBuildLogs =
48+
task && waitingStatuses.includes(task.workspace.latest_build.status);
49+
const buildLogs = useWorkspaceBuildLogs(
50+
task?.workspace.latest_build.id ?? "",
51+
shouldStreamBuildLogs,
52+
);
53+
3554
if (error) {
3655
return (
3756
<>
@@ -77,7 +96,6 @@ const TaskPage = () => {
7796
}
7897

7998
let content: ReactNode = null;
80-
const waitingStatuses: WorkspaceStatus[] = ["starting", "pending"];
8199
const terminatedStatuses: WorkspaceStatus[] = [
82100
"canceled",
83101
"canceling",
@@ -88,17 +106,27 @@ const TaskPage = () => {
88106
];
89107

90108
if (waitingStatuses.includes(task.workspace.latest_build.status)) {
109+
// If no template yet, use null values for an indeterminate progress bar.
110+
const transition = (template &&
111+
ActiveTransition(template, task.workspace)) || { P50: null, P95: null };
112+
const lastStage = buildLogs?.[buildLogs.length - 1]?.stage;
91113
content = (
92-
<div className="w-full min-h-80 flex items-center justify-center">
114+
<div className="w-full min-h-80 flex flex-col items-center justify-center gap-2">
93115
<div className="flex flex-col items-center">
94-
<Spinner loading className="mb-4" />
95116
<h3 className="m-0 font-medium text-content-primary text-base">
96117
Starting your workspace
97118
</h3>
98119
<span className="text-content-secondary text-sm">
99120
This should take a few minutes
100121
</span>
101122
</div>
123+
{lastStage && (
124+
<div className="text-content-secondary text-sm">{lastStage}</div>
125+
)}
126+
<WorkspaceBuildProgress
127+
workspace={task.workspace}
128+
transitionStats={transition}
129+
/>
102130
</div>
103131
);
104132
} else if (task.workspace.latest_build.status === "failed") {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy