Skip to content

Commit 782fe84

Browse files
authored
feat: disable start/restart if active version required (#10809)
1 parent 214123d commit 782fe84

File tree

5 files changed

+96
-33
lines changed

5 files changed

+96
-33
lines changed

site/src/pages/WorkspacePage/WorkspaceActions/Buttons.tsx

+23-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated";
1111
import { BuildParametersPopover } from "./BuildParametersPopover";
1212
import PowerSettingsNewIcon from "@mui/icons-material/PowerSettingsNew";
1313
import LoadingButton from "@mui/lab/LoadingButton";
14+
import Tooltip from "@mui/material/Tooltip";
1415

1516
interface WorkspaceAction {
1617
loading?: boolean;
1718
handleAction: () => void;
19+
disabled?: boolean;
20+
tooltipText?: string;
1821
}
1922

2023
export const UpdateButton: FC<WorkspaceAction> = ({
@@ -55,8 +58,8 @@ export const StartButton: FC<
5558
workspace: Workspace;
5659
handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void;
5760
}
58-
> = ({ handleAction, workspace, loading }) => {
59-
return (
61+
> = ({ handleAction, workspace, loading, disabled, tooltipText }) => {
62+
const buttonContent = (
6063
<ButtonGroup
6164
variant="outlined"
6265
sx={{
@@ -65,12 +68,14 @@ export const StartButton: FC<
6568
borderLeft: "1px solid #FFF",
6669
},
6770
}}
71+
disabled={disabled}
6872
>
6973
<LoadingButton
7074
loading={loading}
7175
loadingPosition="start"
7276
startIcon={<PlayCircleOutlineIcon />}
7377
onClick={() => handleAction()}
78+
disabled={disabled}
7479
>
7580
{loading ? <>Starting&hellip;</> : "Start"}
7681
</LoadingButton>
@@ -81,6 +86,12 @@ export const StartButton: FC<
8186
/>
8287
</ButtonGroup>
8388
);
89+
90+
return tooltipText ? (
91+
<Tooltip title={tooltipText}>{buttonContent}</Tooltip>
92+
) : (
93+
buttonContent
94+
);
8495
};
8596

8697
export const StopButton: FC<WorkspaceAction> = ({ handleAction, loading }) => {
@@ -102,8 +113,8 @@ export const RestartButton: FC<
102113
workspace: Workspace;
103114
handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void;
104115
}
105-
> = ({ handleAction, loading, workspace }) => {
106-
return (
116+
> = ({ handleAction, loading, workspace, disabled, tooltipText }) => {
117+
const buttonContent = (
107118
<ButtonGroup
108119
variant="outlined"
109120
sx={{
@@ -112,13 +123,15 @@ export const RestartButton: FC<
112123
borderLeft: "1px solid #FFF",
113124
},
114125
}}
126+
disabled={disabled}
115127
>
116128
<LoadingButton
117129
loading={loading}
118130
loadingPosition="start"
119131
startIcon={<ReplayIcon />}
120132
onClick={() => handleAction()}
121133
data-testid="workspace-restart-button"
134+
disabled={disabled}
122135
>
123136
{loading ? <>Restarting&hellip;</> : <>Restart&hellip;</>}
124137
</LoadingButton>
@@ -129,6 +142,12 @@ export const RestartButton: FC<
129142
/>
130143
</ButtonGroup>
131144
);
145+
146+
return tooltipText ? (
147+
<Tooltip title={tooltipText}>{buttonContent}</Tooltip>
148+
) : (
149+
buttonContent
150+
);
132151
};
133152

134153
export const CancelButton: FC<WorkspaceAction> = ({ handleAction }) => {

site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.stories.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,17 @@ export const RequireActiveVersionStopped: Story = {
9393
canChangeVersions: false,
9494
},
9595
};
96+
97+
export const AlwaysUpdateStarted: Story = {
98+
args: {
99+
workspace: Mocks.MockOutdatedRunningWorkspaceAlwaysUpdate,
100+
canChangeVersions: true,
101+
},
102+
};
103+
104+
export const AlwaysUpdateStopped: Story = {
105+
args: {
106+
workspace: Mocks.MockOutdatedStoppedWorkspaceAlwaysUpdate,
107+
canChangeVersions: true,
108+
},
109+
};

site/src/pages/WorkspacePage/WorkspaceActions/WorkspaceActions.tsx

+40-8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
MoreMenuTrigger,
3131
ThreeDotsButton,
3232
} from "components/MoreMenu/MoreMenu";
33+
import { workspaceUpdatePolicy } from "utils/workspace";
3334

3435
export interface WorkspaceActionsProps {
3536
workspace: Workspace;
@@ -67,39 +68,70 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
6768
canCancel,
6869
canAcceptJobs,
6970
actions: actionsByStatus,
70-
} = actionsByWorkspaceStatus(
71-
workspace,
72-
workspace.latest_build.status,
73-
canChangeVersions,
74-
);
71+
} = actionsByWorkspaceStatus(workspace, workspace.latest_build.status);
7572
const canBeUpdated = workspace.outdated && canAcceptJobs;
7673
const { duplicateWorkspace, isDuplicationReady } =
7774
useWorkspaceDuplication(workspace);
7875

76+
const disabled =
77+
workspaceUpdatePolicy(workspace, canChangeVersions) === "always" &&
78+
workspace.outdated;
79+
80+
const tooltipText = ((): string => {
81+
if (!disabled) {
82+
return "";
83+
}
84+
if (workspace.template_require_active_version) {
85+
return "This template requires automatic updates";
86+
}
87+
if (workspace.automatic_updates === "always") {
88+
return "You have enabled automatic updates for this workspace";
89+
}
90+
return "";
91+
})();
92+
7993
// A mapping of button type to the corresponding React component
8094
const buttonMapping: ButtonMapping = {
8195
[ButtonTypesEnum.update]: <UpdateButton handleAction={handleUpdate} />,
8296
[ButtonTypesEnum.updating]: (
8397
<UpdateButton loading handleAction={handleUpdate} />
8498
),
8599
[ButtonTypesEnum.start]: (
86-
<StartButton workspace={workspace} handleAction={handleStart} />
100+
<StartButton
101+
workspace={workspace}
102+
handleAction={handleStart}
103+
disabled={disabled}
104+
tooltipText={tooltipText}
105+
/>
87106
),
88107
[ButtonTypesEnum.starting]: (
89-
<StartButton loading workspace={workspace} handleAction={handleStart} />
108+
<StartButton
109+
loading
110+
workspace={workspace}
111+
handleAction={handleStart}
112+
disabled={disabled}
113+
tooltipText={tooltipText}
114+
/>
90115
),
91116
[ButtonTypesEnum.stop]: <StopButton handleAction={handleStop} />,
92117
[ButtonTypesEnum.stopping]: (
93118
<StopButton loading handleAction={handleStop} />
94119
),
95120
[ButtonTypesEnum.restart]: (
96-
<RestartButton workspace={workspace} handleAction={handleRestart} />
121+
<RestartButton
122+
workspace={workspace}
123+
handleAction={handleRestart}
124+
disabled={disabled}
125+
tooltipText={tooltipText}
126+
/>
97127
),
98128
[ButtonTypesEnum.restarting]: (
99129
<RestartButton
100130
loading
101131
workspace={workspace}
102132
handleAction={handleRestart}
133+
disabled={disabled}
134+
tooltipText={tooltipText}
103135
/>
104136
),
105137
[ButtonTypesEnum.deleting]: <ActionLoadingButton label="Deleting" />,

site/src/pages/WorkspacePage/WorkspaceActions/constants.ts

-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Workspace, WorkspaceStatus } from "api/typesGenerated";
22
import { ReactNode } from "react";
3-
import { workspaceUpdatePolicy } from "utils/workspace";
43

54
// the button types we have
65
export enum ButtonTypesEnum {
@@ -34,7 +33,6 @@ interface WorkspaceAbilities {
3433
export const actionsByWorkspaceStatus = (
3534
workspace: Workspace,
3635
status: WorkspaceStatus,
37-
canChangeVersions: boolean,
3836
): WorkspaceAbilities => {
3937
if (workspace.dormant_at) {
4038
return {
@@ -43,25 +41,6 @@ export const actionsByWorkspaceStatus = (
4341
canAcceptJobs: false,
4442
};
4543
}
46-
if (
47-
workspace.outdated &&
48-
workspaceUpdatePolicy(workspace, canChangeVersions) === "always"
49-
) {
50-
if (status === "running") {
51-
return {
52-
actions: [ButtonTypesEnum.stop],
53-
canCancel: false,
54-
canAcceptJobs: true,
55-
};
56-
}
57-
if (status === "stopped") {
58-
return {
59-
actions: [],
60-
canCancel: false,
61-
canAcceptJobs: true,
62-
};
63-
}
64-
}
6544
return statusToActions[status];
6645
};
6746

site/src/testHelpers/entities.ts

+19
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,17 @@ export const MockOutdatedRunningWorkspaceRequireActiveVersion: TypesGen.Workspac
10571057
},
10581058
};
10591059

1060+
export const MockOutdatedRunningWorkspaceAlwaysUpdate: TypesGen.Workspace = {
1061+
...MockWorkspace,
1062+
id: "test-outdated-workspace-always-update",
1063+
outdated: true,
1064+
automatic_updates: "always",
1065+
latest_build: {
1066+
...MockWorkspaceBuild,
1067+
status: "running",
1068+
},
1069+
};
1070+
10601071
export const MockOutdatedStoppedWorkspaceRequireActiveVersion: TypesGen.Workspace =
10611072
{
10621073
...MockOutdatedRunningWorkspaceRequireActiveVersion,
@@ -1066,6 +1077,14 @@ export const MockOutdatedStoppedWorkspaceRequireActiveVersion: TypesGen.Workspac
10661077
},
10671078
};
10681079

1080+
export const MockOutdatedStoppedWorkspaceAlwaysUpdate: TypesGen.Workspace = {
1081+
...MockOutdatedRunningWorkspaceAlwaysUpdate,
1082+
latest_build: {
1083+
...MockWorkspaceBuild,
1084+
status: "stopped",
1085+
},
1086+
};
1087+
10691088
export const MockPendingWorkspace: TypesGen.Workspace = {
10701089
...MockWorkspace,
10711090
id: "test-pending-workspace",

0 commit comments

Comments
 (0)