From 75a456289275c33907048b990039b66bd46a3708 Mon Sep 17 00:00:00 2001 From: "blink-so[bot]" <211532188+blink-so[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 00:29:50 +0000 Subject: [PATCH] feat: add visual distinction for workspaces with running startup scripts - Add helper function hasStartingAgents to check agent lifecycle states - Update getDisplayWorkspaceStatus to show 'Running (Starting...)' when agents are still starting - Enhance WorkspaceStatusIndicator with tooltips for starting workspaces - Add comprehensive tests for the new functionality --- .../WorkspaceStatusIndicator.tsx | 51 +++++--- site/src/utils/workspace.test.ts | 123 ++++++++++++++++++ site/src/utils/workspace.tsx | 20 ++- 3 files changed, 173 insertions(+), 21 deletions(-) diff --git a/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx index 972096314e1ee..f017b8325b6c0 100644 --- a/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx +++ b/site/src/modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator.tsx @@ -41,12 +41,24 @@ export const WorkspaceStatusIndicator: FC = ({ let { text, type } = getDisplayWorkspaceStatus( workspace.latest_build.status, workspace.latest_build.job, + workspace, ); if (!workspace.health.healthy) { type = "warning"; } + // Check if workspace is running but agents are still starting + const isStarting = + workspace.latest_build.status === "running" && + workspace.latest_build.resources.some((resource) => + resource.agents?.some( + (agent) => + agent.lifecycle_state === "starting" || + agent.lifecycle_state === "created", + ), + ); + const statusIndicator = ( @@ -55,24 +67,27 @@ export const WorkspaceStatusIndicator: FC = ({ ); - if (workspace.health.healthy) { - return statusIndicator; + // Show tooltip for unhealthy or starting workspaces + if (!workspace.health.healthy || isStarting) { + const tooltipMessage = !workspace.health.healthy + ? "Your workspace is running but some agents are unhealthy." + : "Your workspace is running but startup scripts are still executing."; + + return ( + + + + + + Workspace status: {text} + {children} + + + {tooltipMessage} + + + ); } - return ( - - - - - - Workspace status: {text} - {children} - - - - Your workspace is running but some agents are unhealthy. - - - - ); + return statusIndicator; }; diff --git a/site/src/utils/workspace.test.ts b/site/src/utils/workspace.test.ts index 4e6f4b287fe0e..a3ba68060831f 100644 --- a/site/src/utils/workspace.test.ts +++ b/site/src/utils/workspace.test.ts @@ -7,6 +7,8 @@ import { getDisplayVersionStatus, getDisplayWorkspaceBuildInitiatedBy, getDisplayWorkspaceTemplateName, + getDisplayWorkspaceStatus, + hasStartingAgents, isWorkspaceOn, } from "./workspace"; @@ -157,4 +159,125 @@ describe("util > workspace", () => { expect(displayed).toEqual(workspace.template_display_name); }); }); + + describe("hasStartingAgents", () => { + it("returns true when agents are starting", () => { + const workspace: TypesGen.Workspace = { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + resources: [ + { + ...Mocks.MockWorkspaceResource, + agents: [ + { + ...Mocks.MockWorkspaceAgent, + lifecycle_state: "starting", + }, + ], + }, + ], + }, + }; + expect(hasStartingAgents(workspace)).toBe(true); + }); + + it("returns true when agents are created", () => { + const workspace: TypesGen.Workspace = { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + resources: [ + { + ...Mocks.MockWorkspaceResource, + agents: [ + { + ...Mocks.MockWorkspaceAgent, + lifecycle_state: "created", + }, + ], + }, + ], + }, + }; + expect(hasStartingAgents(workspace)).toBe(true); + }); + + it("returns false when all agents are ready", () => { + const workspace: TypesGen.Workspace = { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + resources: [ + { + ...Mocks.MockWorkspaceResource, + agents: [ + { + ...Mocks.MockWorkspaceAgent, + lifecycle_state: "ready", + }, + ], + }, + ], + }, + }; + expect(hasStartingAgents(workspace)).toBe(false); + }); + }); + + describe("getDisplayWorkspaceStatus with starting agents", () => { + it("shows 'Running (Starting...)' when workspace is running with starting agents", () => { + const workspace: TypesGen.Workspace = { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + status: "running", + resources: [ + { + ...Mocks.MockWorkspaceResource, + agents: [ + { + ...Mocks.MockWorkspaceAgent, + lifecycle_state: "starting", + }, + ], + }, + ], + }, + }; + const status = getDisplayWorkspaceStatus("running", undefined, workspace); + expect(status.text).toBe("Running (Starting...)"); + expect(status.type).toBe("active"); + }); + + it("shows 'Running' when workspace is running with all agents ready", () => { + const workspace: TypesGen.Workspace = { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspaceBuild, + status: "running", + resources: [ + { + ...Mocks.MockWorkspaceResource, + agents: [ + { + ...Mocks.MockWorkspaceAgent, + lifecycle_state: "ready", + }, + ], + }, + ], + }, + }; + const status = getDisplayWorkspaceStatus("running", undefined, workspace); + expect(status.text).toBe("Running"); + expect(status.type).toBe("success"); + }); + + it("shows 'Running' when workspace parameter is not provided", () => { + const status = getDisplayWorkspaceStatus("running", undefined); + expect(status.text).toBe("Running"); + expect(status.type).toBe("success"); + }); + }); }); diff --git a/site/src/utils/workspace.tsx b/site/src/utils/workspace.tsx index c88ffc9d8edaa..0866cf98fb735 100644 --- a/site/src/utils/workspace.tsx +++ b/site/src/utils/workspace.tsx @@ -182,9 +182,21 @@ type DisplayWorkspaceStatus = { icon: React.ReactNode; }; +// Helper function to check if any agents are still starting +export const hasStartingAgents = (workspace: TypesGen.Workspace): boolean => { + return workspace.latest_build.resources.some((resource) => + resource.agents?.some( + (agent) => + agent.lifecycle_state === "starting" || + agent.lifecycle_state === "created", + ), + ); +}; + export const getDisplayWorkspaceStatus = ( workspaceStatus: TypesGen.WorkspaceStatus, provisionerJob?: TypesGen.ProvisionerJob, + workspace?: TypesGen.Workspace, ): DisplayWorkspaceStatus => { switch (workspaceStatus) { case undefined: @@ -194,10 +206,12 @@ export const getDisplayWorkspaceStatus = ( icon: , } as const; case "running": + // Check if workspace has agents that are still starting + const isStarting = workspace && hasStartingAgents(workspace); return { - type: "success", - text: "Running", - icon: , + type: isStarting ? "active" : "success", + text: isStarting ? "Running (Starting...)" : "Running", + icon: isStarting ? : , } as const; case "starting": return { 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