From 782fe84c7c0ff375e0e3e7865ca274c3dfd6f35f Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 21 Nov 2023 18:06:30 -0600 Subject: [PATCH] feat: disable start/restart if active version required (#10809) --- .../WorkspaceActions/Buttons.tsx | 27 +++++++++-- .../WorkspaceActions.stories.tsx | 14 ++++++ .../WorkspaceActions/WorkspaceActions.tsx | 48 +++++++++++++++---- .../WorkspaceActions/constants.ts | 21 -------- site/src/testHelpers/entities.ts | 19 ++++++++ 5 files changed, 96 insertions(+), 33 deletions(-) diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx index 12eaf5c9a3..f44d226286 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx @@ -11,10 +11,13 @@ import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated"; import { BuildParametersPopover } from "./BuildParametersPopover"; import PowerSettingsNewIcon from "@mui/icons-material/PowerSettingsNew"; import LoadingButton from "@mui/lab/LoadingButton"; +import Tooltip from "@mui/material/Tooltip"; interface WorkspaceAction { loading?: boolean; handleAction: () => void; + disabled?: boolean; + tooltipText?: string; } export const UpdateButton: FC = ({ @@ -55,8 +58,8 @@ export const StartButton: FC< workspace: Workspace; handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void; } -> = ({ handleAction, workspace, loading }) => { - return ( +> = ({ handleAction, workspace, loading, disabled, tooltipText }) => { + const buttonContent = ( } onClick={() => handleAction()} + disabled={disabled} > {loading ? <>Starting… : "Start"} @@ -81,6 +86,12 @@ export const StartButton: FC< /> ); + + return tooltipText ? ( + {buttonContent} + ) : ( + buttonContent + ); }; export const StopButton: FC = ({ handleAction, loading }) => { @@ -102,8 +113,8 @@ export const RestartButton: FC< workspace: Workspace; handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void; } -> = ({ handleAction, loading, workspace }) => { - return ( +> = ({ handleAction, loading, workspace, disabled, tooltipText }) => { + const buttonContent = ( } onClick={() => handleAction()} data-testid="workspace-restart-button" + disabled={disabled} > {loading ? <>Restarting… : <>Restart…} @@ -129,6 +142,12 @@ export const RestartButton: FC< /> ); + + return tooltipText ? ( + {buttonContent} + ) : ( + buttonContent + ); }; export const CancelButton: FC = ({ handleAction }) => { diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx index bbe9afe598..e85886da1e 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx @@ -93,3 +93,17 @@ export const RequireActiveVersionStopped: Story = { canChangeVersions: false, }, }; + +export const AlwaysUpdateStarted: Story = { + args: { + workspace: Mocks.MockOutdatedRunningWorkspaceAlwaysUpdate, + canChangeVersions: true, + }, +}; + +export const AlwaysUpdateStopped: Story = { + args: { + workspace: Mocks.MockOutdatedStoppedWorkspaceAlwaysUpdate, + canChangeVersions: true, + }, +}; diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx index 4efc94df53..b319628536 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx @@ -30,6 +30,7 @@ import { MoreMenuTrigger, ThreeDotsButton, } from "components/MoreMenu/MoreMenu"; +import { workspaceUpdatePolicy } from "utils/workspace"; export interface WorkspaceActionsProps { workspace: Workspace; @@ -67,15 +68,28 @@ export const WorkspaceActions: FC = ({ canCancel, canAcceptJobs, actions: actionsByStatus, - } = actionsByWorkspaceStatus( - workspace, - workspace.latest_build.status, - canChangeVersions, - ); + } = actionsByWorkspaceStatus(workspace, workspace.latest_build.status); const canBeUpdated = workspace.outdated && canAcceptJobs; const { duplicateWorkspace, isDuplicationReady } = useWorkspaceDuplication(workspace); + const disabled = + workspaceUpdatePolicy(workspace, canChangeVersions) === "always" && + workspace.outdated; + + const tooltipText = ((): string => { + if (!disabled) { + return ""; + } + if (workspace.template_require_active_version) { + return "This template requires automatic updates"; + } + if (workspace.automatic_updates === "always") { + return "You have enabled automatic updates for this workspace"; + } + return ""; + })(); + // A mapping of button type to the corresponding React component const buttonMapping: ButtonMapping = { [ButtonTypesEnum.update]: , @@ -83,23 +97,41 @@ export const WorkspaceActions: FC = ({ ), [ButtonTypesEnum.start]: ( - + ), [ButtonTypesEnum.starting]: ( - + ), [ButtonTypesEnum.stop]: , [ButtonTypesEnum.stopping]: ( ), [ButtonTypesEnum.restart]: ( - + ), [ButtonTypesEnum.restarting]: ( ), [ButtonTypesEnum.deleting]: , diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts index 4209f15f48..d6f2704a18 100644 --- a/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts +++ b/site/src/pages/WorkspacePage/WorkspaceActions/constants.ts @@ -1,6 +1,5 @@ import { Workspace, WorkspaceStatus } from "api/typesGenerated"; import { ReactNode } from "react"; -import { workspaceUpdatePolicy } from "utils/workspace"; // the button types we have export enum ButtonTypesEnum { @@ -34,7 +33,6 @@ interface WorkspaceAbilities { export const actionsByWorkspaceStatus = ( workspace: Workspace, status: WorkspaceStatus, - canChangeVersions: boolean, ): WorkspaceAbilities => { if (workspace.dormant_at) { return { @@ -43,25 +41,6 @@ export const actionsByWorkspaceStatus = ( canAcceptJobs: false, }; } - if ( - workspace.outdated && - workspaceUpdatePolicy(workspace, canChangeVersions) === "always" - ) { - if (status === "running") { - return { - actions: [ButtonTypesEnum.stop], - canCancel: false, - canAcceptJobs: true, - }; - } - if (status === "stopped") { - return { - actions: [], - canCancel: false, - canAcceptJobs: true, - }; - } - } return statusToActions[status]; }; diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 9aeb3560ce..7dcfe4733f 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1057,6 +1057,17 @@ export const MockOutdatedRunningWorkspaceRequireActiveVersion: TypesGen.Workspac }, }; +export const MockOutdatedRunningWorkspaceAlwaysUpdate: TypesGen.Workspace = { + ...MockWorkspace, + id: "test-outdated-workspace-always-update", + outdated: true, + automatic_updates: "always", + latest_build: { + ...MockWorkspaceBuild, + status: "running", + }, +}; + export const MockOutdatedStoppedWorkspaceRequireActiveVersion: TypesGen.Workspace = { ...MockOutdatedRunningWorkspaceRequireActiveVersion, @@ -1066,6 +1077,14 @@ export const MockOutdatedStoppedWorkspaceRequireActiveVersion: TypesGen.Workspac }, }; +export const MockOutdatedStoppedWorkspaceAlwaysUpdate: TypesGen.Workspace = { + ...MockOutdatedRunningWorkspaceAlwaysUpdate, + latest_build: { + ...MockWorkspaceBuild, + status: "stopped", + }, +}; + export const MockPendingWorkspace: TypesGen.Workspace = { ...MockWorkspace, id: "test-pending-workspace",