diff --git a/site/src/modules/apps/AppStatusIcon.tsx b/site/src/modules/apps/AppStatusIcon.tsx index c73f0af0f1..3de4ef4194 100644 --- a/site/src/modules/apps/AppStatusIcon.tsx +++ b/site/src/modules/apps/AppStatusIcon.tsx @@ -25,22 +25,22 @@ export const AppStatusIcon: FC = ({ switch (status.state) { case "complete": return ( - + ); case "failure": return ( - + ); case "working": return latest ? ( ) : ( - + ); default: return ( ); } diff --git a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx index bcb1af8de3..d95444e658 100644 --- a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx +++ b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.stories.tsx @@ -48,3 +48,10 @@ export const LongMessage: Story = { }, }, }; + +export const Disabled: Story = { + args: { + status: MockWorkspaceAppStatus, + disabled: true, + }, +}; diff --git a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx index f2eab7f208..aba002b2cd 100644 --- a/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx +++ b/site/src/modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus.tsx @@ -1,30 +1,22 @@ -import type { - WorkspaceAppStatus as APIWorkspaceAppStatus, - WorkspaceAppStatusState, -} from "api/typesGenerated"; -import { Spinner } from "components/Spinner/Spinner"; +import type { WorkspaceAppStatus as APIWorkspaceAppStatus } from "api/typesGenerated"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "components/Tooltip/Tooltip"; -import { CircleAlertIcon, CircleCheckIcon } from "lucide-react"; -import type { ReactNode } from "react"; +import { AppStatusIcon } from "modules/apps/AppStatusIcon"; +import { cn } from "utils/cn"; -const iconByState: Record = { - complete: ( - - ), - failure: , - working: , +type WorkspaceAppStatusProps = { + status: APIWorkspaceAppStatus | null; + disabled?: boolean; }; export const WorkspaceAppStatus = ({ status, -}: { - status: APIWorkspaceAppStatus | null; -}) => { + disabled, +}: WorkspaceAppStatusProps) => { if (!status) { return ( @@ -39,7 +31,13 @@ export const WorkspaceAppStatus = ({
- {iconByState[status.state]} + {status.message} diff --git a/site/src/pages/WorkspacePage/AppStatuses.stories.tsx b/site/src/pages/WorkspacePage/AppStatuses.stories.tsx index 02e38a8c47..56dff1c93c 100644 --- a/site/src/pages/WorkspacePage/AppStatuses.stories.tsx +++ b/site/src/pages/WorkspacePage/AppStatuses.stories.tsx @@ -1,9 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react"; +import type { WorkspaceAppStatus } from "api/typesGenerated"; import { MockWorkspace, MockWorkspaceAgent, MockWorkspaceApp, MockWorkspaceAppStatus, + MockWorkspaceAppStatuses, + createTimestamp, } from "testHelpers/entities"; import { withProxyProvider } from "testHelpers/storybook"; import { AppStatuses } from "./AppStatuses"; @@ -13,6 +16,8 @@ const meta: Meta = { component: AppStatuses, args: { referenceDate: new Date("2024-03-26T15:15:00Z"), + agent: mockAgent(MockWorkspaceAppStatuses), + workspace: MockWorkspace, }, decorators: [withProxyProvider()], }; @@ -21,163 +26,70 @@ export default meta; type Story = StoryObj; -export const Default: Story = { - args: { - workspace: MockWorkspace, - agent: { - ...MockWorkspaceAgent, - apps: [ - { - ...MockWorkspaceApp, - statuses: [ - { - // This is the latest status chronologically (15:04:38) - ...MockWorkspaceAppStatus, - id: "status-7", - icon: "/emojis/1f4dd.png", // 📝 - message: "Creating PR with gh CLI", - created_at: createTimestamp(4, 38), // 15:04:38 - uri: "https://github.com/coder/coder/pull/5678", - state: "complete" as const, - }, - { - // (15:03:56) - ...MockWorkspaceAppStatus, - id: "status-6", - icon: "/emojis/1f680.png", // 🚀 - message: "Pushing branch to remote", - created_at: createTimestamp(3, 56), // 15:03:56 - uri: "", - state: "complete" as const, - }, - { - // (15:02:29) - ...MockWorkspaceAppStatus, - id: "status-5", - icon: "/emojis/1f527.png", // 🔧 - message: "Configuring git identity", - created_at: createTimestamp(2, 29), // 15:02:29 - uri: "", - state: "complete" as const, - }, - { - // (15:02:04) - ...MockWorkspaceAppStatus, - id: "status-4", - icon: "/emojis/1f4be.png", // 💾 - message: "Committing changes", - created_at: createTimestamp(2, 4), // 15:02:04 - uri: "", - state: "complete" as const, - }, - { - // (15:01:44) - ...MockWorkspaceAppStatus, - id: "status-3", - icon: "/emojis/2795.png", // + - message: "Adding files to staging", - created_at: createTimestamp(1, 44), // 15:01:44 - uri: "", - state: "complete" as const, - }, - { - // (15:01:32) - ...MockWorkspaceAppStatus, - id: "status-2", - icon: "/emojis/1f33f.png", // 🌿 - message: "Creating a new branch for PR", - created_at: createTimestamp(1, 32), // 15:01:32 - uri: "", - state: "complete" as const, - }, - { - // (15:01:00) - Oldest - ...MockWorkspaceAppStatus, - id: "status-1", - icon: "/emojis/1f680.png", // 🚀 - message: "Starting to create a PR", - created_at: createTimestamp(1, 0), // 15:01:00 - uri: "", - state: "complete" as const, - }, - ].sort( - (a, b) => - new Date(b.created_at).getTime() - - new Date(a.created_at).getTime(), - ), // Ensure sorted correctly for component input if needed - }, - ], - }, - - // Pass the reference date to the component for Storybook rendering - }, -}; +export const Default: Story = {}; // Add a story with a "Working" status as the latest export const WorkingState: Story = { args: { - workspace: MockWorkspace, - agent: { - ...MockWorkspaceAgent, - apps: [ - { - ...MockWorkspaceApp, - statuses: [ - { - // This is now the latest (15:05:15) and is "working" - ...MockWorkspaceAppStatus, - id: "status-8", - icon: "", // Let the component handle the spinner icon - message: "Processing final checks...", - created_at: createTimestamp(5, 15), // 15:05:15 (after referenceDate) - uri: "", - state: "working" as const, - }, - { - // Previous latest (15:04:38) - ...MockWorkspaceAppStatus, - id: "status-7", - icon: "/emojis/1f4dd.png", // 📝 - message: "Creating PR with gh CLI", - created_at: createTimestamp(4, 38), // 15:04:38 - uri: "https://github.com/coder/coder/pull/5678", - state: "complete" as const, - }, - { - // (15:03:56) - ...MockWorkspaceAppStatus, - id: "status-6", - icon: "/emojis/1f680.png", // 🚀 - message: "Pushing branch to remote", - created_at: createTimestamp(3, 56), // 15:03:56 - uri: "", - state: "complete" as const, - }, - // ... include other older statuses if desired ... - { - // (15:01:00) - Oldest - ...MockWorkspaceAppStatus, - id: "status-1", - icon: "/emojis/1f680.png", // 🚀 - message: "Starting to create a PR", - created_at: createTimestamp(1, 0), // 15:01:00 - uri: "", - state: "complete" as const, - }, - ].sort( - (a, b) => - new Date(b.created_at).getTime() - - new Date(a.created_at).getTime(), - ), - }, - ], - }, + agent: mockAgent([ + { + // This is now the latest (15:05:15) and is "working" + ...MockWorkspaceAppStatus, + id: "status-8", + icon: "", // Let the component handle the spinner icon + message: "Processing final checks...", + created_at: createTimestamp(5, 15), // 15:05:15 (after referenceDate) + uri: "", + state: "working" as const, + }, + ...MockWorkspaceAppStatuses, + ]), }, }; -function createTimestamp(minuteOffset: number, secondOffset: number) { - const baseDate = new Date("2024-03-26T15:00:00Z"); - baseDate.setMinutes(baseDate.getMinutes() + minuteOffset); - baseDate.setSeconds(baseDate.getSeconds() + secondOffset); - return baseDate.toISOString(); +export const LongStatusText: Story = { + args: { + agent: mockAgent([ + { + // This is now the latest (15:05:15) and is "working" + ...MockWorkspaceAppStatus, + id: "status-8", + icon: "", // Let the component handle the spinner icon + message: + "Processing final checks with a very long message that exceeds the usual length to test how the component handles overflow and truncation in the UI. This should be long enough to ensure it wraps correctly and doesn't break the layout.", + created_at: createTimestamp(5, 15), // 15:05:15 (after referenceDate) + uri: "", + state: "complete" as const, + }, + ...MockWorkspaceAppStatuses, + ]), + }, +}; + +export const SingleStatus: Story = { + args: { + agent: mockAgent([ + { + ...MockWorkspaceAppStatus, + id: "status-1", + icon: "", + message: "Initial setup complete.", + created_at: createTimestamp(5, 10), // 15:05:10 (after referenceDate) + uri: "", + state: "complete" as const, + }, + ]), + }, +}; + +function mockAgent(statuses: WorkspaceAppStatus[]) { + return { + ...MockWorkspaceAgent, + apps: [ + { + ...MockWorkspaceApp, + statuses, + }, + ], + }; } diff --git a/site/src/pages/WorkspacePage/AppStatuses.tsx b/site/src/pages/WorkspacePage/AppStatuses.tsx index bfa7131a9f..9975fccbc0 100644 --- a/site/src/pages/WorkspacePage/AppStatuses.tsx +++ b/site/src/pages/WorkspacePage/AppStatuses.tsx @@ -103,15 +103,17 @@ export const AppStatuses: FC = ({
-
- +
+
- {latestStatus.message} - + + {latestStatus.message} + +
{timeFrom(new Date(latestStatus.created_at), comparisonDate)} @@ -154,6 +156,7 @@ export const AppStatuses: FC = ({