Skip to content

Commit 78deaba

Browse files
feat(site): show "update and start" button when update is forced (#13334)
1 parent f27f5c0 commit 78deaba

File tree

3 files changed

+123
-71
lines changed

3 files changed

+123
-71
lines changed

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

+15-9
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,8 @@ export const RestartButton: FC<ActionButtonPropsWithWorkspace> = ({
117117
handleAction,
118118
loading,
119119
workspace,
120-
disabled,
121-
tooltipText,
122120
}) => {
123-
const buttonContent = (
121+
return (
124122
<ButtonGroup
125123
variant="outlined"
126124
css={{
@@ -129,13 +127,12 @@ export const RestartButton: FC<ActionButtonPropsWithWorkspace> = ({
129127
borderLeft: "1px solid #FFF",
130128
},
131129
}}
132-
disabled={disabled}
133130
>
134131
<TopbarButton
135132
startIcon={<ReplayIcon />}
136133
onClick={() => handleAction()}
137134
data-testid="workspace-restart-button"
138-
disabled={disabled || loading}
135+
disabled={loading}
139136
>
140137
{loading ? <>Restarting&hellip;</> : <>Restart&hellip;</>}
141138
</TopbarButton>
@@ -147,11 +144,20 @@ export const RestartButton: FC<ActionButtonPropsWithWorkspace> = ({
147144
/>
148145
</ButtonGroup>
149146
);
147+
};
150148

151-
return tooltipText ? (
152-
<Tooltip title={tooltipText}>{buttonContent}</Tooltip>
153-
) : (
154-
buttonContent
149+
export const UpdateAndStartButton: FC<ActionButtonProps> = ({
150+
handleAction,
151+
}) => {
152+
return (
153+
<Tooltip title="This template requires automatic updates on workspace startup. Contact your administrator if you want to preserve the template version.">
154+
<TopbarButton
155+
startIcon={<PlayCircleOutlineIcon />}
156+
onClick={() => handleAction()}
157+
>
158+
Update and start&hellip;
159+
</TopbarButton>
160+
</Tooltip>
155161
);
156162
};
157163

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

+9-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
UpdateButton,
2525
ActivateButton,
2626
FavoriteButton,
27+
UpdateAndStartButton,
2728
} from "./Buttons";
2829
import { type ActionType, abilitiesByWorkspaceStatus } from "./constants";
2930
import { DebugButton } from "./DebugButton";
@@ -89,6 +90,7 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
8990
// A mapping of button type to the corresponding React component
9091
const buttonMapping: Record<ActionType, ReactNode> = {
9192
update: <UpdateButton handleAction={handleUpdate} />,
93+
updateAndStart: <UpdateAndStartButton handleAction={handleUpdate} />,
9294
updating: <UpdateButton loading handleAction={handleUpdate} />,
9395
start: (
9496
<StartButton
@@ -161,7 +163,13 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
161163
data-testid="workspace-actions"
162164
>
163165
{canBeUpdated && (
164-
<>{isUpdating ? buttonMapping.updating : buttonMapping.update}</>
166+
<>
167+
{isUpdating
168+
? buttonMapping.updating
169+
: workspace.template_require_active_version
170+
? buttonMapping.updateAndStart
171+
: buttonMapping.update}
172+
</>
165173
)}
166174

167175
{isRestarting
@@ -236,10 +244,6 @@ function getTooltipText(
236244
return "This template requires automatic updates on workspace startup, but template administrators can ignore this policy.";
237245
}
238246

239-
if (workspace.template_require_active_version) {
240-
return "This template requires automatic updates on workspace startup. Contact your administrator if you want to preserve the template version.";
241-
}
242-
243247
if (workspace.automatic_updates === "always") {
244248
return "Automatic updates are enabled for this workspace. Modify the update policy in workspace settings if you want to preserve the template version.";
245249
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Workspace, WorkspaceStatus } from "api/typesGenerated";
1+
import type { Workspace } from "api/typesGenerated";
22

33
/**
44
* An iterable of all action types supported by the workspace UI
@@ -23,6 +23,10 @@ export const actionTypes = [
2323
"retry",
2424
"debug",
2525

26+
// When a template requires updates, we aim to display a distinct update
27+
// button that clearly indicates a mandatory update.
28+
"updateAndStart",
29+
2630
// These are buttons that should be used with disabled UI elements
2731
"canceling",
2832
"deleted",
@@ -52,67 +56,105 @@ export const abilitiesByWorkspaceStatus = (
5256
const status = workspace.latest_build.status;
5357
if (status === "failed" && canDebug) {
5458
return {
55-
...statusToAbility.failed,
5659
actions: ["retry", "debug"],
60+
canCancel: false,
61+
canAcceptJobs: true,
5762
};
5863
}
5964

60-
return statusToAbility[status];
61-
};
65+
switch (status) {
66+
case "starting": {
67+
return {
68+
actions: ["starting"],
69+
canCancel: true,
70+
canAcceptJobs: false,
71+
};
72+
}
73+
case "running": {
74+
const actions: ActionType[] = ["stop"];
6275

63-
const statusToAbility: Record<WorkspaceStatus, WorkspaceAbilities> = {
64-
starting: {
65-
actions: ["starting"],
66-
canCancel: true,
67-
canAcceptJobs: false,
68-
},
69-
running: {
70-
actions: ["stop", "restart"],
71-
canCancel: false,
72-
canAcceptJobs: true,
73-
},
74-
stopping: {
75-
actions: ["stopping"],
76-
canCancel: true,
77-
canAcceptJobs: false,
78-
},
79-
stopped: {
80-
actions: ["start"],
81-
canCancel: false,
82-
canAcceptJobs: true,
83-
},
84-
canceled: {
85-
actions: ["start", "stop"],
86-
canCancel: false,
87-
canAcceptJobs: true,
88-
},
76+
// If the template requires the latest version, we prevent the user from
77+
// restarting the workspace without updating it first. In the Buttons
78+
// component, we display an UpdateAndStart component to facilitate this.
79+
if (!workspace.template_require_active_version) {
80+
actions.push("restart");
81+
}
8982

90-
// in the case of an error
91-
failed: {
92-
actions: ["retry"],
93-
canCancel: false,
94-
canAcceptJobs: true,
95-
},
83+
return {
84+
actions,
85+
canCancel: false,
86+
canAcceptJobs: true,
87+
};
88+
}
89+
case "stopping": {
90+
return {
91+
actions: ["stopping"],
92+
canCancel: true,
93+
canAcceptJobs: false,
94+
};
95+
}
96+
case "stopped": {
97+
const actions: ActionType[] = [];
9698

97-
// Disabled states
98-
canceling: {
99-
actions: ["canceling"],
100-
canCancel: false,
101-
canAcceptJobs: false,
102-
},
103-
deleting: {
104-
actions: ["deleting"],
105-
canCancel: true,
106-
canAcceptJobs: false,
107-
},
108-
deleted: {
109-
actions: ["deleted"],
110-
canCancel: false,
111-
canAcceptJobs: false,
112-
},
113-
pending: {
114-
actions: ["pending"],
115-
canCancel: false,
116-
canAcceptJobs: false,
117-
},
99+
// If the template requires the latest version, we prevent the user from
100+
// starting the workspace without updating it first. In the Buttons
101+
// component, we display an UpdateAndStart component to facilitate this.
102+
if (!workspace.template_require_active_version) {
103+
actions.push("start");
104+
}
105+
106+
return {
107+
actions,
108+
canCancel: false,
109+
canAcceptJobs: true,
110+
};
111+
}
112+
case "canceled": {
113+
return {
114+
actions: ["start", "stop"],
115+
canCancel: false,
116+
canAcceptJobs: true,
117+
};
118+
}
119+
case "failed": {
120+
return {
121+
actions: ["retry"],
122+
canCancel: false,
123+
canAcceptJobs: true,
124+
};
125+
}
126+
127+
// Disabled states
128+
case "canceling": {
129+
return {
130+
actions: ["canceling"],
131+
canCancel: false,
132+
canAcceptJobs: false,
133+
};
134+
}
135+
case "deleting": {
136+
return {
137+
actions: ["deleting"],
138+
canCancel: true,
139+
canAcceptJobs: false,
140+
};
141+
}
142+
case "deleted": {
143+
return {
144+
actions: ["deleted"],
145+
canCancel: false,
146+
canAcceptJobs: false,
147+
};
148+
}
149+
case "pending": {
150+
return {
151+
actions: ["pending"],
152+
canCancel: false,
153+
canAcceptJobs: false,
154+
};
155+
}
156+
default: {
157+
throw new Error(`Unknown workspace status: ${status}`);
158+
}
159+
}
118160
};

0 commit comments

Comments
 (0)