Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: add workspace build status to task page#18520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
code-asher merged 7 commits intomainfromasher/task-build-status
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletionssite/src/pages/TaskPage/TaskPage.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -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,
Expand All@@ -9,6 +10,7 @@ import {
MockFailedWorkspace,
MockStartingWorkspace,
MockStoppedWorkspace,
MockTemplate,
MockWorkspace,
MockWorkspaceAgent,
MockWorkspaceApp,
Expand DownExpand Up@@ -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({
Expand Down
41 changes: 34 additions & 7 deletionssite/src/pages/TaskPage/TaskPage.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -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";
Expand All@@ -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";

Expand All@@ -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 (
<>
Expand DownExpand Up@@ -77,7 +96,6 @@ const TaskPage = () => {
}

let content: ReactNode = null;
const waitingStatuses: WorkspaceStatus[] = ["starting", "pending"];
const terminatedStatuses: WorkspaceStatus[] = [
"canceled",
"canceling",
Expand All@@ -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 = (
<div className="w-full min-h-80 flex items-center justify-center">
<div className="flex flex-col items-center">
<Spinner loading className="mb-4" />
<div className="w-full min-h-80 flex flex-col">
<div className="flex flex-col items-center grow justify-center">
<h3 className="m-0 font-medium text-content-primary text-base">
Starting your workspace
</h3>
<span className="text-content-secondary text-sm">
This should take a few minutes
</span>
<div className="text-content-secondary text-sm">{lastStage}</div>
</div>
<div className="w-full">
<WorkspaceBuildProgress
workspace={task.workspace}
transitionStats={transition}
variant="task"
/>
</div>
</div>
);
Expand Down
51 changes: 35 additions & 16 deletionssite/src/pages/WorkspacePage/WorkspaceBuildProgress.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,11 +62,18 @@ const estimateFinish = (
interface WorkspaceBuildProgressProps {
workspace: Workspace;
transitionStats: TransitionStats;
// 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<WorkspaceBuildProgressProps> = ({
workspace,
transitionStats,
variant,
}) => {
const job = workspace.latest_build.job;
const [progressValue, setProgressValue] = useState<number | undefined>(0);
Expand DownExpand Up@@ -114,6 +121,13 @@ export const WorkspaceBuildProgress: FC<WorkspaceBuildProgressProps> = ({
}
return (
<div css={styles.stack}>
{variant === "task" && (
<div className="mb-1 text-center">
<div css={styles.label} data-chromatic="ignore">
{progressText}
</div>
</div>
)}
<LinearProgress
data-chromatic="ignore"
value={progressValue !== undefined ? progressValue : 0}
Expand All@@ -126,26 +140,36 @@ export const WorkspaceBuildProgress: FC<WorkspaceBuildProgressProps> = ({
? "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={{ bar: classNames.bar }}
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,
// With the "task" variant, the progress bar is fullscreen, so remove
// the border radius.
root: variant === "task" ? classNames.root : undefined,
}}
/>
<div css={styles.barHelpers}>
<div css={styles.label}>
{capitalize(workspace.latest_build.status)} workspace...
</div>
<div css={styles.label} data-chromatic="ignore">
{progressText}
{variant !== "task" && (
<div className="flex mt-1 justify-between">
<div css={styles.label}>
{capitalize(workspace.latest_build.status)} workspace...
</div>
<div css={styles.label} data-chromatic="ignore">
{progressText}
</div>
</div>
</div>
)}
</div>
);
};

const classNames = {
bar: css`
transition: none;
`,
root: css`
border-radius: 0;
`,
};

Expand All@@ -154,11 +178,6 @@ const styles = {
paddingLeft: 2,
paddingRight: 2,
},
barHelpers: {
display: "flex",
justifyContent: "space-between",
marginTop: 4,
},
label: (theme) => ({
fontSize: 12,
display: "block",
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp