{activeVersion ? (
diff --git a/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx b/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx
index 73c53e72b84e4..798b70bfde592 100644
--- a/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx
+++ b/site/src/components/WorkspaceStatusBadge/WorkspaceStatusBadge.tsx
@@ -47,13 +47,25 @@ export const WorkspaceStatusBadge: FC
= ({
}
placement="top"
>
-
+
{text}
-
+
{text}
diff --git a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx
index 960b81a0e481e..a4d585e32534d 100644
--- a/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx
+++ b/site/src/pages/TemplateVersionEditorPage/TemplateVersionEditor.tsx
@@ -1,4 +1,4 @@
-import Button, { type ButtonProps } from "@mui/material/Button";
+import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import Tooltip from "@mui/material/Tooltip";
import CreateIcon from "@mui/icons-material/AddOutlined";
@@ -12,7 +12,6 @@ import type {
} from "api/typesGenerated";
import { Link as RouterLink } from "react-router-dom";
import { Alert, AlertDetail } from "components/Alert/Alert";
-import { Avatar } from "components/Avatar/Avatar";
import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable";
import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs";
import { PublishVersionData } from "pages/TemplateVersionEditorPage/types";
@@ -45,6 +44,14 @@ import ArrowBackOutlined from "@mui/icons-material/ArrowBackOutlined";
import CloseOutlined from "@mui/icons-material/CloseOutlined";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
import { Loader } from "components/Loader/Loader";
+import {
+ Topbar,
+ TopbarAvatar,
+ TopbarButton,
+ TopbarData,
+ TopbarDivider,
+ TopbarIconButton,
+} from "components/FullPageLayout/Topbar";
type Tab = "logs" | "resources" | undefined; // Undefined is to hide the tab
@@ -176,48 +183,26 @@ export const TemplateVersionEditor: FC = ({
return (
<>
-
-
-
+
+
= ({
>
{template.display_name || template.name}
- /
+
{templateVersion.name}
-
+
= ({
Publish
-
+
= ({
= ({
);
};
-const TopbarButton: FC
= ({ children, ...buttonProps }) => {
- return (
-
- );
-};
-
const styles = {
tab: (theme) => ({
"&:not(:disabled)": {
diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx
index 22e24c73c9fce..b7d966603e457 100644
--- a/site/src/pages/WorkspacePage/Workspace.tsx
+++ b/site/src/pages/WorkspacePage/Workspace.tsx
@@ -9,25 +9,17 @@ import { Alert, AlertDetail } from "components/Alert/Alert";
import { Margins } from "components/Margins/Margins";
import { Resources } from "components/Resources/Resources";
import { Stack } from "components/Stack/Stack";
-import {
- FullWidthPageHeader,
- PageHeaderActions,
- PageHeaderTitle,
- PageHeaderSubtitle,
-} from "components/PageHeader/FullWidthPageHeader";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { DormantWorkspaceBanner } from "components/WorkspaceDeletion";
-import { Avatar } from "components/Avatar/Avatar";
import { AgentRow } from "components/Resources/AgentRow";
import { useLocalStorage } from "hooks";
-import { WorkspaceActions } from "pages/WorkspacePage/WorkspaceActions/WorkspaceActions";
import {
ActiveTransition,
WorkspaceBuildProgress,
} from "./WorkspaceBuildProgress";
import { BuildsTable } from "./BuildsTable";
import { WorkspaceDeletedBanner } from "./WorkspaceDeletedBanner";
-import { WorkspaceStats } from "./WorkspaceStats";
+import { WorkspaceTopbar } from "./WorkspaceTopbar/WorkspaceTopbar";
export type WorkspaceError =
| "getBuildsError"
@@ -59,7 +51,6 @@ export interface WorkspaceProps {
buildInfo?: TypesGen.BuildInfoResponse;
sshPrefix?: string;
template?: TypesGen.Template;
- quotaBudget?: number;
canRetryDebugMode: boolean;
handleBuildRetry: () => void;
handleBuildRetryDebug: () => void;
@@ -159,51 +150,25 @@ export const Workspace: FC> = ({
return (
<>
-
-
-
- {workspace.name}
-
-
-
{workspace.name}
-
{workspace.owner_name}
-
-
-
-
-
- {canUpdateWorkspace && (
-
-
-
- )}
-
+
diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx
index f7756a2c8cb52..46e84c49eb5e6 100644
--- a/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx
+++ b/site/src/pages/WorkspacePage/WorkspaceActions/BuildParametersPopover.tsx
@@ -28,6 +28,7 @@ import {
PopoverTrigger,
usePopover,
} from "components/Popover/Popover";
+import { TopbarButton } from "components/FullPageLayout/Topbar";
interface BuildParametersPopoverProps {
workspace: Workspace;
@@ -51,14 +52,14 @@ export const BuildParametersPopover: FC = ({
return (
-
+
+
= ({
loading,
}) => {
return (
- }
onClick={() => handleAction()}
>
{loading ? <>Updating…> : <>Update…>}
-
+
);
};
@@ -44,14 +42,13 @@ export const ActivateButton: FC = ({
loading,
}) => {
return (
- }
onClick={() => handleAction()}
>
{loading ? <>Activating…> : "Activate"}
-
+
);
};
@@ -77,15 +74,13 @@ export const StartButton: FC = ({
}}
disabled={disabled}
>
- }
onClick={() => handleAction()}
- disabled={disabled}
+ disabled={disabled || loading}
>
{loading ? <>Starting…> : "Start"}
-
+
= ({
loading,
}) => {
return (
- }
onClick={() => handleAction()}
data-testid="workspace-stop-button"
>
{loading ? <>Stopping…> : "Stop"}
-
+
);
};
@@ -136,16 +130,14 @@ export const RestartButton: FC = ({
}}
disabled={disabled}
>
- }
onClick={() => handleAction()}
data-testid="workspace-restart-button"
- disabled={disabled}
+ disabled={disabled || loading}
>
{loading ? <>Restarting…> : <>Restart…>}
-
+
= ({
export const CancelButton: FC = ({ handleAction }) => {
return (
- } onClick={() => handleAction()}>
+ } onClick={() => handleAction()}>
Cancel
-
+
);
};
@@ -175,21 +167,9 @@ interface DisabledButtonProps {
export const DisabledButton: FC = ({ label }) => {
return (
- } disabled>
+ } disabled>
{label}
-
- );
-};
-
-interface LoadingProps {
- label: string;
-}
-
-export const ActionLoadingButton: FC = ({ label }) => {
- return (
- }>
- {label}
-
+
);
};
@@ -202,11 +182,11 @@ export const RetryButton: FC = ({
debug = false,
}) => {
return (
- : }
onClick={() => handleAction()}
>
Retry{debug && " (Debug)"}
-
+
);
};
diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx
index 9da528838608c..0032c933e3b03 100644
--- a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx
+++ b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx
@@ -1,12 +1,9 @@
import { type FC, type ReactNode, Fragment } from "react";
import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated";
import { useWorkspaceDuplication } from "pages/CreateWorkspacePage/useWorkspaceDuplication";
-
import { workspaceUpdatePolicy } from "utils/workspace";
import { type ActionType, abilitiesByWorkspaceStatus } from "./constants";
-
import {
- ActionLoadingButton,
CancelButton,
DisabledButton,
StartButton,
@@ -22,14 +19,14 @@ import DuplicateIcon from "@mui/icons-material/FileCopyOutlined";
import SettingsIcon from "@mui/icons-material/SettingsOutlined";
import HistoryIcon from "@mui/icons-material/HistoryOutlined";
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
-
import {
MoreMenu,
MoreMenuContent,
MoreMenuItem,
MoreMenuTrigger,
- ThreeDotsButton,
} from "components/MoreMenu/MoreMenu";
+import { TopbarIconButton } from "components/FullPageLayout/Topbar";
+import MoreVertOutlined from "@mui/icons-material/MoreVertOutlined";
export interface WorkspaceActionsProps {
workspace: Workspace;
@@ -124,10 +121,10 @@ export const WorkspaceActions: FC = ({
tooltipText={tooltipText}
/>
),
- deleting: ,
+ deleting: ,
canceling: ,
deleted: ,
- pending: ,
+ pending: ,
activate: ,
activating: ,
retry: ,
@@ -136,7 +133,7 @@ export const WorkspaceActions: FC = ({
return (
{canBeUpdated && (
@@ -153,13 +150,14 @@ export const WorkspaceActions: FC
= ({
-
+ >
+
+
diff --git a/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx b/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx
index 1ef0fb78b0eb5..3327c3035ada6 100644
--- a/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx
+++ b/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx
@@ -74,7 +74,7 @@ export const WorkspaceScheduleControls: FC = ({
) : (
- {autostartDisplay(workspace.autostart_schedule)}
+ Starts at {autostartDisplay(workspace.autostart_schedule)}
)}
@@ -251,7 +251,7 @@ const AutoStopDisplay: FC = ({ workspace }) => {
: undefined,
})}
>
- {display.message}
+ Stop {display.message}
);
@@ -268,6 +268,7 @@ const ScheduleSettingsLink = forwardRef(
component={RouterLink}
to="settings/schedule"
css={{
+ color: "inherit",
"&:first-letter": {
textTransform: "uppercase",
},
@@ -310,10 +311,6 @@ const isShutdownSoon = (workspace: Workspace): boolean => {
return diff < oneHour;
};
-export const scheduleLabel = (workspace: Workspace) => {
- return isWorkspaceOn(workspace) ? "Stops" : "Starts at";
-};
-
const classNames = {
paper: css`
padding: 24px;
diff --git a/site/src/pages/WorkspacePage/WorkspaceStats.stories.tsx b/site/src/pages/WorkspacePage/WorkspaceStats.stories.tsx
deleted file mode 100644
index de444ba38960f..0000000000000
--- a/site/src/pages/WorkspacePage/WorkspaceStats.stories.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Meta, StoryObj } from "@storybook/react";
-import {
- MockWorkspace,
- MockAppearanceConfig,
- MockBuildInfo,
- MockEntitlementsWithScheduling,
- MockExperiments,
-} from "testHelpers/entities";
-import { WorkspaceStats } from "./WorkspaceStats";
-import { DashboardProviderContext } from "components/Dashboard/DashboardProvider";
-
-const MockedAppearance = {
- config: MockAppearanceConfig,
- isPreview: false,
- setPreview: () => {},
-};
-
-const meta: Meta = {
- title: "pages/WorkspacePage/WorkspaceStats",
- component: WorkspaceStats,
- decorators: [
- (Story) => (
-
-
-
- ),
- ],
-};
-
-export default meta;
-type Story = StoryObj;
-
-export const Example: Story = {
- args: {
- workspace: MockWorkspace,
- },
-};
-
-export const Outdated: Story = {
- args: {
- workspace: {
- ...MockWorkspace,
- outdated: true,
- },
- },
-};
diff --git a/site/src/pages/WorkspacePage/WorkspaceStats.tsx b/site/src/pages/WorkspacePage/WorkspaceStats.tsx
deleted file mode 100644
index 8f76c671e2a6b..0000000000000
--- a/site/src/pages/WorkspacePage/WorkspaceStats.tsx
+++ /dev/null
@@ -1,140 +0,0 @@
-import { type Interpolation, type Theme } from "@emotion/react";
-import Link from "@mui/material/Link";
-import { WorkspaceOutdatedTooltip } from "components/WorkspaceOutdatedTooltip/WorkspaceOutdatedTooltip";
-import { type FC } from "react";
-import { Link as RouterLink } from "react-router-dom";
-import { getDisplayWorkspaceTemplateName } from "utils/workspace";
-import type { Workspace } from "api/typesGenerated";
-import { Stats, StatsItem } from "components/Stats/Stats";
-import { WorkspaceStatusText } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge";
-import { DormantDeletionStat } from "components/WorkspaceDeletion";
-import { workspaceQuota } from "api/queries/workspaceQuota";
-import { useQuery } from "react-query";
-import _ from "lodash";
-import {
- WorkspaceScheduleControls,
- scheduleLabel,
- shouldDisplayScheduleControls,
-} from "./WorkspaceScheduleControls";
-
-const Language = {
- workspaceDetails: "Workspace Details",
- templateLabel: "Template",
- costLabel: "Daily cost",
- updatePolicy: "Update policy",
-};
-
-export interface WorkspaceStatsProps {
- workspace: Workspace;
- canUpdateWorkspace: boolean;
- handleUpdate: () => void;
-}
-
-export const WorkspaceStats: FC = ({
- workspace,
- canUpdateWorkspace,
- handleUpdate,
-}) => {
- const displayTemplateName = getDisplayWorkspaceTemplateName(workspace);
- const quotaQuery = useQuery(workspaceQuota(workspace.owner_name));
- const quotaBudget = quotaQuery.data?.budget;
-
- return (
- <>
-
- }
- />
-
-
- {displayTemplateName}
-
- }
- />
-
-
-
- {workspace.latest_build.template_version_name}
-
-
- {workspace.outdated && (
-
- )}
-
- }
- />
-
- {shouldDisplayScheduleControls(workspace) && (
-
- }
- />
- )}
- {workspace.latest_build.daily_cost > 0 && (
-
- )}
-
- >
- );
-};
-
-const styles = {
- stats: (theme) => ({
- padding: 0,
- border: 0,
- gap: 48,
- rowGap: 24,
- flex: 1,
-
- [theme.breakpoints.down("md")]: {
- display: "flex",
- flexDirection: "column",
- alignItems: "flex-start",
- gap: 8,
- },
- }),
-
- statsItem: {
- flexDirection: "column",
- gap: 0,
- padding: 0,
-
- "& > span:first-of-type": {
- fontSize: 12,
- fontWeight: 500,
- },
- },
-} satisfies Record>;
diff --git a/site/src/pages/WorkspacePage/WorkspaceTopbar/WorkspaceTopbar.stories.tsx b/site/src/pages/WorkspacePage/WorkspaceTopbar/WorkspaceTopbar.stories.tsx
new file mode 100644
index 0000000000000..5490bf2a3c096
--- /dev/null
+++ b/site/src/pages/WorkspacePage/WorkspaceTopbar/WorkspaceTopbar.stories.tsx
@@ -0,0 +1,82 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { MockUser, MockWorkspace } from "testHelpers/entities";
+import { WorkspaceTopbar } from "./WorkspaceTopbar";
+import { withDashboardProvider } from "testHelpers/storybook";
+import { addDays } from "date-fns";
+import { getWorkspaceQuotaQueryKey } from "api/queries/workspaceQuota";
+
+// We want a workspace without a deadline to not pollute the screenshot
+const baseWorkspace = {
+ ...MockWorkspace,
+ latest_build: {
+ ...MockWorkspace.latest_build,
+ deadline: undefined,
+ },
+};
+
+const meta: Meta = {
+ title: "pages/WorkspacePage/WorkspaceTopbar",
+ component: WorkspaceTopbar,
+ decorators: [withDashboardProvider],
+ args: {
+ workspace: baseWorkspace,
+ },
+ parameters: {
+ layout: "fullscreen",
+ features: ["advanced_template_scheduling"],
+ experiments: ["workspace_actions"],
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Example: Story = {};
+
+export const Outdated: Story = {
+ args: {
+ workspace: {
+ ...MockWorkspace,
+ outdated: true,
+ },
+ },
+};
+
+export const Dormant: Story = {
+ args: {
+ workspace: {
+ ...baseWorkspace,
+ deleting_at: addDays(new Date(), 7).toISOString(),
+ latest_build: {
+ ...baseWorkspace.latest_build,
+ status: "failed",
+ },
+ },
+ },
+};
+
+export const WithDeadline: Story = {
+ args: {
+ workspace: {
+ ...MockWorkspace,
+ latest_build: {
+ ...MockWorkspace.latest_build,
+ deadline: MockWorkspace.latest_build.deadline,
+ },
+ },
+ },
+};
+
+export const WithQuota: Story = {
+ parameters: {
+ queries: [
+ {
+ key: getWorkspaceQuotaQueryKey(MockUser.username),
+ data: {
+ credits_consumed: 2,
+ budget: 40,
+ },
+ },
+ ],
+ },
+};
diff --git a/site/src/pages/WorkspacePage/WorkspaceTopbar/WorkspaceTopbar.tsx b/site/src/pages/WorkspacePage/WorkspaceTopbar/WorkspaceTopbar.tsx
new file mode 100644
index 0000000000000..f87eb238f7c8d
--- /dev/null
+++ b/site/src/pages/WorkspacePage/WorkspaceTopbar/WorkspaceTopbar.tsx
@@ -0,0 +1,255 @@
+import { Link as RouterLink } from "react-router-dom";
+import type * as TypesGen from "api/typesGenerated";
+import { WorkspaceActions } from "pages/WorkspacePage/WorkspaceActions/WorkspaceActions";
+import {
+ Topbar,
+ TopbarAvatar,
+ TopbarData,
+ TopbarDivider,
+ TopbarIcon,
+ TopbarIconButton,
+} from "components/FullPageLayout/Topbar";
+import Tooltip from "@mui/material/Tooltip";
+import ArrowBackOutlined from "@mui/icons-material/ArrowBackOutlined";
+import PersonOutlineOutlined from "@mui/icons-material/PersonOutlineOutlined";
+import { WorkspaceOutdatedTooltipContent } from "components/WorkspaceOutdatedTooltip/WorkspaceOutdatedTooltip";
+import { Popover, PopoverTrigger } from "components/Popover/Popover";
+import ScheduleOutlined from "@mui/icons-material/ScheduleOutlined";
+import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge";
+import { Pill } from "components/Pill/Pill";
+import {
+ WorkspaceScheduleControls,
+ shouldDisplayScheduleControls,
+} from "../WorkspaceScheduleControls";
+import { workspaceQuota } from "api/queries/workspaceQuota";
+import { useQuery } from "react-query";
+import MonetizationOnOutlined from "@mui/icons-material/MonetizationOnOutlined";
+import { useTheme } from "@mui/material/styles";
+import InfoOutlined from "@mui/icons-material/InfoOutlined";
+import Link from "@mui/material/Link";
+import { useDashboard } from "components/Dashboard/DashboardProvider";
+import { displayDormantDeletion } from "utils/dormant";
+import DeleteOutline from "@mui/icons-material/DeleteOutline";
+
+export type WorkspaceError =
+ | "getBuildsError"
+ | "buildError"
+ | "cancellationError";
+
+export type WorkspaceErrors = Partial>;
+
+export interface WorkspaceProps {
+ handleStart: (buildParameters?: TypesGen.WorkspaceBuildParameter[]) => void;
+ handleStop: () => void;
+ handleRestart: (buildParameters?: TypesGen.WorkspaceBuildParameter[]) => void;
+ handleDelete: () => void;
+ handleUpdate: () => void;
+ handleCancel: () => void;
+ handleSettings: () => void;
+ handleChangeVersion: () => void;
+ handleDormantActivate: () => void;
+ isUpdating: boolean;
+ isRestarting: boolean;
+ workspace: TypesGen.Workspace;
+ canUpdateWorkspace: boolean;
+ canChangeVersions: boolean;
+ canRetryDebugMode: boolean;
+ handleBuildRetry: () => void;
+ handleBuildRetryDebug: () => void;
+}
+
+export const WorkspaceTopbar = (props: WorkspaceProps) => {
+ const {
+ handleStart,
+ handleStop,
+ handleRestart,
+ handleDelete,
+ handleUpdate,
+ handleCancel,
+ handleSettings,
+ handleChangeVersion,
+ handleDormantActivate,
+ workspace,
+ isUpdating,
+ isRestarting,
+ canUpdateWorkspace,
+ canChangeVersions,
+ canRetryDebugMode,
+ handleBuildRetry,
+ handleBuildRetryDebug,
+ } = props;
+ const theme = useTheme();
+
+ // Quota
+ const hasDailyCost = workspace.latest_build.daily_cost > 0;
+ const { data: quota } = useQuery({
+ ...workspaceQuota(workspace.owner_name),
+ enabled: hasDailyCost,
+ });
+
+ // Dormant
+ const { entitlements, experiments } = 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
+ const allowWorkspaceActions = experiments.includes("workspace_actions");
+ const shouldDisplayDormantData = displayDormantDeletion(
+ workspace,
+ allowAdvancedScheduling,
+ allowWorkspaceActions,
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {workspace.name}
+
+
+ {workspace.template_display_name ?? workspace.template_name}
+
+
+ {workspace.outdated ? (
+
+
+ {/* Added to give some bottom space from the popover content */}
+
+
+ }
+ >
+
+ {workspace.latest_build.template_version_name}
+
+
+
+
+
+
+ ) : (
+ {workspace.latest_build.template_version_name}
+ )}
+
+
+
+
+
+
+
+
+ {workspace.owner_name}
+
+
+ {shouldDisplayDormantData && (
+
+
+
+
+
+ Deletion on {new Date(workspace.deleting_at!).toLocaleString()}
+
+
+ )}
+
+ {shouldDisplayScheduleControls(workspace) && (
+
+
+
+
+
+
+
+
+ )}
+
+ {quota && (
+
+
+
+
+
+
+
+ {workspace.latest_build.daily_cost}{" "}
+
+ credits of
+ {" "}
+ {quota.budget}
+
+
+ )}
+
+
+
+
+
+
+
+ );
+};
diff --git a/site/src/testHelpers/storybook.tsx b/site/src/testHelpers/storybook.tsx
new file mode 100644
index 0000000000000..bd7d81a14f275
--- /dev/null
+++ b/site/src/testHelpers/storybook.tsx
@@ -0,0 +1,48 @@
+import { DashboardProviderContext } from "components/Dashboard/DashboardProvider";
+import {
+ MockAppearanceConfig,
+ MockBuildInfo,
+ MockEntitlements,
+} from "./entities";
+import { FC } from "react";
+import { StoryContext } from "@storybook/react";
+import * as _storybook_types from "@storybook/react";
+import { Entitlements } from "api/typesGenerated";
+import { withDefaultFeatures } from "api/api";
+
+export const withDashboardProvider = (
+ Story: FC,
+ { parameters }: StoryContext,
+) => {
+ const { features = [], experiments = [] } = parameters;
+
+ const entitlements: Entitlements = {
+ ...MockEntitlements,
+ features: withDefaultFeatures(
+ features.reduce(
+ (acc, feature) => {
+ acc[feature] = { enabled: true, entitlement: "entitled" };
+ return acc;
+ },
+ {} as Entitlements["features"],
+ ),
+ ),
+ };
+
+ return (
+ {},
+ },
+ }}
+ >
+
+
+ );
+};
diff --git a/site/src/components/WorkspaceDeletion/utils.test.ts b/site/src/utils/dormant.test.ts
similarity index 96%
rename from site/src/components/WorkspaceDeletion/utils.test.ts
rename to site/src/utils/dormant.test.ts
index caca6c5661993..ae02ef017690c 100644
--- a/site/src/components/WorkspaceDeletion/utils.test.ts
+++ b/site/src/utils/dormant.test.ts
@@ -1,6 +1,6 @@
import * as TypesGen from "api/typesGenerated";
import * as Mocks from "testHelpers/entities";
-import { displayDormantDeletion } from "./utils";
+import { displayDormantDeletion } from "./dormant";
describe("displayDormantDeletion", () => {
const today = new Date();
diff --git a/site/src/components/WorkspaceDeletion/utils.ts b/site/src/utils/dormant.ts
similarity index 100%
rename from site/src/components/WorkspaceDeletion/utils.ts
rename to site/src/utils/dormant.ts