mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add workspace status on tasks (#20037)
<img width="1206" height="722" alt="Screenshot 2025-09-30 at 11 29 31" src="https://github.com/user-attachments/assets/f109552d-af5e-41e1-a0e8-fdfcb3f973b7" /> Closes https://github.com/coder/coder/issues/19988
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { MockDormantWorkspace, MockWorkspace } from "testHelpers/entities";
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { WorkspaceStatus } from "./WorkspaceStatus";
|
||||
|
||||
const meta: Meta<typeof WorkspaceStatus> = {
|
||||
title: "modules/workspaces/WorkspaceStatus",
|
||||
component: WorkspaceStatus,
|
||||
args: {
|
||||
workspace: MockWorkspace,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof WorkspaceStatus>;
|
||||
|
||||
export const Running: Story = {};
|
||||
|
||||
export const Dormant: Story = {
|
||||
args: {
|
||||
workspace: MockDormantWorkspace,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { Workspace } from "api/typesGenerated";
|
||||
import type { FC } from "react";
|
||||
import { lastUsedMessage } from "utils/workspace";
|
||||
import { WorkspaceDormantBadge } from "../WorkspaceDormantBadge/WorkspaceDormantBadge";
|
||||
import { WorkspaceStatusIndicator } from "../WorkspaceStatusIndicator/WorkspaceStatusIndicator";
|
||||
|
||||
type WorkspaceStatusProps = {
|
||||
workspace: Workspace;
|
||||
};
|
||||
|
||||
export const WorkspaceStatus: FC<WorkspaceStatusProps> = ({ workspace }) => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<WorkspaceStatusIndicator workspace={workspace}>
|
||||
{workspace.dormant_at && (
|
||||
<WorkspaceDormantBadge workspace={workspace} />
|
||||
)}
|
||||
</WorkspaceStatusIndicator>
|
||||
<time
|
||||
dateTime={workspace.last_used_at}
|
||||
className="text-xs font-medium text-content-secondary ml-6 whitespace-nowrap"
|
||||
>
|
||||
{lastUsedMessage(workspace.last_used_at)}
|
||||
</time>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -26,6 +26,7 @@ import { RotateCcwIcon, TrashIcon } from "lucide-react";
|
||||
import { TaskDeleteDialog } from "modules/tasks/TaskDeleteDialog/TaskDeleteDialog";
|
||||
import type { Task } from "modules/tasks/tasks";
|
||||
import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus";
|
||||
import { WorkspaceStatus } from "modules/workspaces/WorkspaceStatus/WorkspaceStatus";
|
||||
import { type FC, type ReactNode, useState } from "react";
|
||||
import { Link as RouterLink } from "react-router";
|
||||
import { relativeTime } from "utils/time";
|
||||
@@ -54,7 +55,8 @@ export const TasksTable: FC<TasksTableProps> = ({ tasks, error, onRetry }) => {
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Task</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Agent status</TableHead>
|
||||
<TableHead>Workspace status</TableHead>
|
||||
<TableHead>Created by</TableHead>
|
||||
<TableHead />
|
||||
</TableRow>
|
||||
@@ -153,6 +155,9 @@ const TaskRow: FC<TaskRowProps> = ({ task }) => {
|
||||
status={task.workspace.latest_app_status}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<WorkspaceStatus workspace={task.workspace} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<AvatarData
|
||||
title={task.workspace.owner_name}
|
||||
@@ -205,6 +210,9 @@ const TasksSkeleton: FC = () => {
|
||||
<TableCell>
|
||||
<Skeleton className="w-[100px] h-6" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Skeleton className="w-[100px] h-6" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<AvatarDataSkeleton />
|
||||
</TableCell>
|
||||
|
||||
@@ -65,10 +65,9 @@ import { useAppLink } from "modules/apps/useAppLink";
|
||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||
import { abilitiesByWorkspaceStatus } from "modules/workspaces/actions";
|
||||
import { WorkspaceBuildCancelDialog } from "modules/workspaces/WorkspaceBuildCancelDialog/WorkspaceBuildCancelDialog";
|
||||
import { WorkspaceDormantBadge } from "modules/workspaces/WorkspaceDormantBadge/WorkspaceDormantBadge";
|
||||
import { WorkspaceMoreActions } from "modules/workspaces/WorkspaceMoreActions/WorkspaceMoreActions";
|
||||
import { WorkspaceOutdatedTooltip } from "modules/workspaces/WorkspaceOutdatedTooltip/WorkspaceOutdatedTooltip";
|
||||
import { WorkspaceStatusIndicator } from "modules/workspaces/WorkspaceStatusIndicator/WorkspaceStatusIndicator";
|
||||
import { WorkspaceStatus } from "modules/workspaces/WorkspaceStatus/WorkspaceStatus";
|
||||
import {
|
||||
useWorkspaceUpdate,
|
||||
WorkspaceUpdateDialogs,
|
||||
@@ -83,10 +82,7 @@ import {
|
||||
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||
import { useNavigate } from "react-router";
|
||||
import { cn } from "utils/cn";
|
||||
import {
|
||||
getDisplayWorkspaceTemplateName,
|
||||
lastUsedMessage,
|
||||
} from "utils/workspace";
|
||||
import { getDisplayWorkspaceTemplateName } from "utils/workspace";
|
||||
import { WorkspacesEmpty } from "./WorkspacesEmpty";
|
||||
|
||||
interface WorkspacesTableProps {
|
||||
@@ -256,7 +252,9 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<WorkspaceStatusCell workspace={workspace} />
|
||||
<TableCell>
|
||||
<WorkspaceStatus workspace={workspace} />
|
||||
</TableCell>
|
||||
|
||||
<WorkspaceActionsCell
|
||||
workspace={workspace}
|
||||
@@ -354,27 +352,6 @@ const cantBeChecked = (workspace: Workspace) => {
|
||||
return ["deleting", "pending"].includes(workspace.latest_build.status);
|
||||
};
|
||||
|
||||
type WorkspaceStatusCellProps = {
|
||||
workspace: Workspace;
|
||||
};
|
||||
|
||||
const WorkspaceStatusCell: FC<WorkspaceStatusCellProps> = ({ workspace }) => {
|
||||
return (
|
||||
<TableCell>
|
||||
<div className="flex flex-col">
|
||||
<WorkspaceStatusIndicator workspace={workspace}>
|
||||
{workspace.dormant_at && (
|
||||
<WorkspaceDormantBadge workspace={workspace} />
|
||||
)}
|
||||
</WorkspaceStatusIndicator>
|
||||
<span className="text-xs font-medium text-content-secondary ml-6 whitespace-nowrap">
|
||||
{lastUsedMessage(workspace.last_used_at)}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
);
|
||||
};
|
||||
|
||||
type WorkspaceActionsCellProps = {
|
||||
workspace: Workspace;
|
||||
onActionSuccess: () => Promise<void>;
|
||||
|
||||
Reference in New Issue
Block a user