mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore(site): refactor dormant badge and add stories (#12555)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { userEvent, within } from "@storybook/test";
|
||||
import { MockDormantWorkspace } from "testHelpers/entities";
|
||||
import { WorkspaceDormantBadge } from "./WorkspaceDormantBadge";
|
||||
|
||||
const meta: Meta<typeof WorkspaceDormantBadge> = {
|
||||
title: "modules/workspaces/WorkspaceDormantBadge",
|
||||
component: WorkspaceDormantBadge,
|
||||
args: {
|
||||
workspace: MockDormantWorkspace,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof WorkspaceDormantBadge>;
|
||||
|
||||
export const Default: Story = {
|
||||
play: async ({ canvasElement, step }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await step("Open tooltip", async () => {
|
||||
await userEvent.hover(canvas.getByRole("status"));
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const DeletingAt: Story = {
|
||||
args: {
|
||||
workspace: {
|
||||
...MockDormantWorkspace,
|
||||
deleting_at: "2024-03-12T14:17:12.196Z",
|
||||
},
|
||||
},
|
||||
play: async ({ canvasElement, step }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await step("Open tooltip", async () => {
|
||||
await userEvent.hover(canvas.getByRole("status"));
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
import AutoDeleteIcon from "@mui/icons-material/AutoDelete";
|
||||
import RecyclingIcon from "@mui/icons-material/Recycling";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import type { FC } from "react";
|
||||
import type { Workspace } from "api/typesGenerated";
|
||||
import { Pill } from "components/Pill/Pill";
|
||||
|
||||
export type WorkspaceDormantBadgeProps = {
|
||||
workspace: Workspace;
|
||||
};
|
||||
|
||||
export const WorkspaceDormantBadge: FC<WorkspaceDormantBadgeProps> = ({
|
||||
workspace,
|
||||
}) => {
|
||||
const formatDate = (dateStr: string): string => {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleDateString(undefined, {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
return workspace.deleting_at ? (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
This workspace has not been used for{" "}
|
||||
{formatDistanceToNow(Date.parse(workspace.last_used_at))} and has been
|
||||
marked dormant. It is scheduled to be deleted on{" "}
|
||||
{formatDate(workspace.deleting_at)}.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Pill role="status" icon={<AutoDeleteIcon />} type="error">
|
||||
Deletion Pending
|
||||
</Pill>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
This workspace has not been used for{" "}
|
||||
{formatDistanceToNow(Date.parse(workspace.last_used_at))} and has been
|
||||
marked dormant. It is not scheduled for auto-deletion but will become
|
||||
a candidate if auto-deletion is enabled on this template.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Pill role="status" icon={<RecyclingIcon />} type="warning">
|
||||
Dormant
|
||||
</Pill>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
import type { FC } from "react";
|
||||
import type { Workspace } from "api/typesGenerated";
|
||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||
import { displayDormantDeletion } from "utils/dormant";
|
||||
|
||||
interface DormantDeletionTextProps {
|
||||
workspace: Workspace;
|
||||
}
|
||||
|
||||
export const DormantDeletionText: FC<DormantDeletionTextProps> = ({
|
||||
workspace,
|
||||
}) => {
|
||||
const { entitlements } = useDashboard();
|
||||
const allowAdvancedScheduling =
|
||||
entitlements.features["advanced_template_scheduling"].enabled;
|
||||
// This check can be removed when https://github.com/coder/coder/milestone/19
|
||||
// is merged up
|
||||
|
||||
if (!displayDormantDeletion(workspace, allowAdvancedScheduling)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
role="status"
|
||||
css={(theme) => ({
|
||||
color: theme.palette.warning.light,
|
||||
fontWeight: 600,
|
||||
})}
|
||||
>
|
||||
Impending deletion
|
||||
</span>
|
||||
);
|
||||
};
|
||||
@@ -1,18 +1,14 @@
|
||||
import AutoDeleteIcon from "@mui/icons-material/AutoDelete";
|
||||
import ErrorOutline from "@mui/icons-material/ErrorOutline";
|
||||
import RecyclingIcon from "@mui/icons-material/Recycling";
|
||||
import Tooltip, {
|
||||
type TooltipProps,
|
||||
tooltipClasses,
|
||||
} from "@mui/material/Tooltip";
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import type { FC, ReactNode } from "react";
|
||||
import type { Workspace } from "api/typesGenerated";
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
|
||||
import { Pill } from "components/Pill/Pill";
|
||||
import { useClassName } from "hooks/useClassName";
|
||||
import { getDisplayWorkspaceStatus } from "utils/workspace";
|
||||
import { DormantDeletionText } from "./DormantDeletionText";
|
||||
|
||||
export type WorkspaceStatusBadgeProps = {
|
||||
workspace: Workspace;
|
||||
@@ -73,105 +69,6 @@ export const WorkspaceStatusBadge: FC<WorkspaceStatusBadgeProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export type DormantStatusBadgeProps = {
|
||||
workspace: Workspace;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const DormantStatusBadge: FC<DormantStatusBadgeProps> = ({
|
||||
workspace,
|
||||
className,
|
||||
}) => {
|
||||
if (!workspace.dormant_at) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const formatDate = (dateStr: string): string => {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleDateString(undefined, {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
return workspace.deleting_at ? (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
This workspace has not been used for{" "}
|
||||
{formatDistanceToNow(Date.parse(workspace.last_used_at))} and has been
|
||||
marked dormant. It is scheduled to be deleted on{" "}
|
||||
{formatDate(workspace.deleting_at)}.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Pill
|
||||
role="status"
|
||||
className={className}
|
||||
icon={<AutoDeleteIcon />}
|
||||
type="error"
|
||||
>
|
||||
Deletion Pending
|
||||
</Pill>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
This workspace has not been used for{" "}
|
||||
{formatDistanceToNow(Date.parse(workspace.last_used_at))} and has been
|
||||
marked dormant. It is not scheduled for auto-deletion but will become
|
||||
a candidate if auto-deletion is enabled on this template.
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Pill
|
||||
role="status"
|
||||
className={className}
|
||||
icon={<RecyclingIcon />}
|
||||
type="warning"
|
||||
>
|
||||
Dormant
|
||||
</Pill>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export const WorkspaceStatusText: FC<WorkspaceStatusBadgeProps> = ({
|
||||
workspace,
|
||||
className,
|
||||
}) => {
|
||||
const { text, type } = getDisplayWorkspaceStatus(
|
||||
workspace.latest_build.status,
|
||||
);
|
||||
|
||||
return (
|
||||
<ChooseOne>
|
||||
<Cond condition={Boolean(DormantDeletionText({ workspace }))}>
|
||||
<DormantDeletionText workspace={workspace} />
|
||||
</Cond>
|
||||
<Cond>
|
||||
<span
|
||||
role="status"
|
||||
data-testid="build-status"
|
||||
className={className}
|
||||
css={(theme) => ({
|
||||
fontWeight: 600,
|
||||
color: type
|
||||
? theme.roles[type].fill.solid
|
||||
: theme.experimental.l1.text,
|
||||
})}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
</Cond>
|
||||
</ChooseOne>
|
||||
);
|
||||
};
|
||||
|
||||
const FailureTooltip: FC<TooltipProps> = ({ children, ...tooltipProps }) => {
|
||||
const popper = useClassName(
|
||||
(css, theme) => css`
|
||||
|
||||
@@ -22,11 +22,9 @@ import {
|
||||
TableRowSkeleton,
|
||||
} from "components/TableLoader/TableLoader";
|
||||
import { useClickableTableRow } from "hooks/useClickableTableRow";
|
||||
import { WorkspaceDormantBadge } from "modules/workspaces/WorkspaceDormantBadge/WorkspaceDormantBadge";
|
||||
import { WorkspaceOutdatedTooltip } from "modules/workspaces/WorkspaceOutdatedTooltip/WorkspaceOutdatedTooltip";
|
||||
import {
|
||||
DormantStatusBadge,
|
||||
WorkspaceStatusBadge,
|
||||
} from "modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge";
|
||||
import { WorkspaceStatusBadge } from "modules/workspaces/WorkspaceStatusBadge/WorkspaceStatusBadge";
|
||||
import { LastUsed } from "pages/WorkspacesPage/LastUsed";
|
||||
import { getDisplayWorkspaceTemplateName } from "utils/workspace";
|
||||
import { WorkspacesEmpty } from "./WorkspacesEmpty";
|
||||
@@ -214,7 +212,7 @@ export const WorkspacesTable: FC<WorkspacesTableProps> = ({
|
||||
/>
|
||||
)}
|
||||
{workspace.dormant_at && (
|
||||
<DormantStatusBadge workspace={workspace} />
|
||||
<WorkspaceDormantBadge workspace={workspace} />
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
Reference in New Issue
Block a user