diff --git a/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx b/site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx index 12eaf5c9a3edc..f44d22628652d 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 bbe9afe598fba..e85886da1e1e5 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 4efc94df53623..b31962853660f 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 4209f15f48f1e..d6f2704a18f80 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 ba8dc0458918a..9693c253235ee 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1053,6 +1053,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, @@ -1062,6 +1073,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",