Skip to content

Commit 718972f

Browse files
committed
fix: add in retry button for failed workspaces
1 parent c249a40 commit 718972f

File tree

4 files changed

+118
-106
lines changed

4 files changed

+118
-106
lines changed

site/src/pages/WorkspacePage/Workspace.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
201201
handleUpdate={handleUpdate}
202202
handleCancel={handleCancel}
203203
handleSettings={handleSettings}
204+
handleRetry={handleBuildRetry}
204205
handleChangeVersion={handleChangeVersion}
205206
handleDormantActivate={handleDormantActivate}
206207
canChangeVersions={canChangeVersions}
@@ -308,7 +309,6 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
308309
actions={
309310
canRetryDebugMode && (
310311
<Button
311-
key={0}
312312
onClick={handleBuildRetry}
313313
variant="text"
314314
size="small"

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

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1+
import { type FC } from "react";
2+
import type { Workspace, WorkspaceBuildParameter } from "api/typesGenerated";
3+
import { BuildParametersPopover } from "./BuildParametersPopover";
4+
15
import Button from "@mui/material/Button";
2-
import BlockIcon from "@mui/icons-material/Block";
6+
import LoadingButton from "@mui/lab/LoadingButton";
7+
import ButtonGroup from "@mui/material/ButtonGroup";
38
import CloudQueueIcon from "@mui/icons-material/CloudQueue";
49
import CropSquareIcon from "@mui/icons-material/CropSquare";
510
import PlayCircleOutlineIcon from "@mui/icons-material/PlayCircleOutline";
611
import ReplayIcon from "@mui/icons-material/Replay";
7-
import { FC } from "react";
8-
import BlockOutlined from "@mui/icons-material/BlockOutlined";
9-
import ButtonGroup from "@mui/material/ButtonGroup";
10-
import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated";
11-
import { BuildParametersPopover } from "./BuildParametersPopover";
12+
import BlockIcon from "@mui/icons-material/Block";
13+
import OutlinedBlockIcon from "@mui/icons-material/BlockOutlined";
1214
import PowerSettingsNewIcon from "@mui/icons-material/PowerSettingsNew";
13-
import LoadingButton from "@mui/lab/LoadingButton";
15+
import RetryIcon from "@mui/icons-material/BuildOutlined";
1416

15-
interface WorkspaceAction {
17+
interface WorkspaceActionProps {
1618
loading?: boolean;
1719
handleAction: () => void;
1820
}
1921

20-
export const UpdateButton: FC<WorkspaceAction> = ({
22+
export const UpdateButton: FC<WorkspaceActionProps> = ({
2123
handleAction,
2224
loading,
2325
}) => {
@@ -34,7 +36,7 @@ export const UpdateButton: FC<WorkspaceAction> = ({
3436
);
3537
};
3638

37-
export const ActivateButton: FC<WorkspaceAction> = ({
39+
export const ActivateButton: FC<WorkspaceActionProps> = ({
3840
handleAction,
3941
loading,
4042
}) => {
@@ -51,7 +53,7 @@ export const ActivateButton: FC<WorkspaceAction> = ({
5153
};
5254

5355
export const StartButton: FC<
54-
Omit<WorkspaceAction, "handleAction"> & {
56+
Omit<WorkspaceActionProps, "handleAction"> & {
5557
workspace: Workspace;
5658
handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void;
5759
}
@@ -83,7 +85,10 @@ export const StartButton: FC<
8385
);
8486
};
8587

86-
export const StopButton: FC<WorkspaceAction> = ({ handleAction, loading }) => {
88+
export const StopButton: FC<WorkspaceActionProps> = ({
89+
handleAction,
90+
loading,
91+
}) => {
8792
return (
8893
<LoadingButton
8994
loading={loading}
@@ -98,7 +103,7 @@ export const StopButton: FC<WorkspaceAction> = ({ handleAction, loading }) => {
98103
};
99104

100105
export const RestartButton: FC<
101-
Omit<WorkspaceAction, "handleAction"> & {
106+
Omit<WorkspaceActionProps, "handleAction"> & {
102107
workspace: Workspace;
103108
handleAction: (buildParameters?: WorkspaceBuildParameter[]) => void;
104109
}
@@ -131,7 +136,7 @@ export const RestartButton: FC<
131136
);
132137
};
133138

134-
export const CancelButton: FC<WorkspaceAction> = ({ handleAction }) => {
139+
export const CancelButton: FC<WorkspaceActionProps> = ({ handleAction }) => {
135140
return (
136141
<Button startIcon={<BlockIcon />} onClick={handleAction}>
137142
Cancel
@@ -145,7 +150,7 @@ interface DisabledProps {
145150

146151
export const DisabledButton: FC<DisabledProps> = ({ label }) => {
147152
return (
148-
<Button startIcon={<BlockOutlined />} disabled>
153+
<Button startIcon={<OutlinedBlockIcon />} disabled>
149154
{label}
150155
</Button>
151156
);
@@ -162,3 +167,13 @@ export const ActionLoadingButton: FC<LoadingProps> = ({ label }) => {
162167
</LoadingButton>
163168
);
164169
};
170+
171+
export function RetryButton({
172+
handleAction,
173+
}: Omit<WorkspaceActionProps, "loading">) {
174+
return (
175+
<Button startIcon={<RetryIcon />} onClick={handleAction}>
176+
Retry
177+
</Button>
178+
);
179+
}

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

Lines changed: 40 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FC, Fragment, ReactNode } from "react";
1+
import { type FC, type ReactNode, Fragment } from "react";
22
import { Workspace, WorkspaceBuildParameter } from "api/typesGenerated";
33
import { useWorkspaceDuplication } from "pages/CreateWorkspacePage/useWorkspaceDuplication";
44
import {
@@ -10,12 +10,9 @@ import {
1010
RestartButton,
1111
UpdateButton,
1212
ActivateButton,
13+
RetryButton,
1314
} from "./Buttons";
14-
import {
15-
ButtonMapping,
16-
ButtonTypesEnum,
17-
actionsByWorkspaceStatus,
18-
} from "./constants";
15+
import { type ButtonType, actionsByWorkspaceStatus } from "./constants";
1916

2017
import Divider from "@mui/material/Divider";
2118
import DuplicateIcon from "@mui/icons-material/FileCopyOutlined";
@@ -41,6 +38,7 @@ export interface WorkspaceActionsProps {
4138
handleCancel: () => void;
4239
handleSettings: () => void;
4340
handleChangeVersion: () => void;
41+
handleRetry: () => void;
4442
handleDormantActivate: () => void;
4543
isUpdating: boolean;
4644
isRestarting: boolean;
@@ -57,81 +55,72 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
5755
handleUpdate,
5856
handleCancel,
5957
handleSettings,
58+
handleRetry,
6059
handleChangeVersion,
6160
handleDormantActivate: handleDormantActivate,
6261
isUpdating,
6362
isRestarting,
6463
canChangeVersions,
6564
}) => {
66-
const {
67-
canCancel,
68-
canAcceptJobs,
69-
actions: actionsByStatus,
70-
} = actionsByWorkspaceStatus(
71-
workspace,
72-
workspace.latest_build.status,
73-
canChangeVersions,
74-
);
75-
const canBeUpdated = workspace.outdated && canAcceptJobs;
7665
const { duplicateWorkspace, isDuplicationReady } =
7766
useWorkspaceDuplication(workspace);
7867

79-
// A mapping of button type to the corresponding React component
80-
const buttonMapping: ButtonMapping = {
81-
[ButtonTypesEnum.update]: <UpdateButton handleAction={handleUpdate} />,
82-
[ButtonTypesEnum.updating]: (
83-
<UpdateButton loading handleAction={handleUpdate} />
84-
),
85-
[ButtonTypesEnum.start]: (
86-
<StartButton workspace={workspace} handleAction={handleStart} />
87-
),
88-
[ButtonTypesEnum.starting]: (
68+
// A mapping of button type to their corresponding React components
69+
const buttonMapping: Record<ButtonType, ReactNode> = {
70+
update: <UpdateButton handleAction={handleUpdate} />,
71+
updating: <UpdateButton loading handleAction={handleUpdate} />,
72+
start: <StartButton workspace={workspace} handleAction={handleStart} />,
73+
starting: (
8974
<StartButton loading workspace={workspace} handleAction={handleStart} />
9075
),
91-
[ButtonTypesEnum.stop]: <StopButton handleAction={handleStop} />,
92-
[ButtonTypesEnum.stopping]: (
93-
<StopButton loading handleAction={handleStop} />
94-
),
95-
[ButtonTypesEnum.restart]: (
76+
stop: <StopButton handleAction={handleStop} />,
77+
stopping: <StopButton loading handleAction={handleStop} />,
78+
restart: (
9679
<RestartButton workspace={workspace} handleAction={handleRestart} />
9780
),
98-
[ButtonTypesEnum.restarting]: (
81+
restarting: (
9982
<RestartButton
10083
loading
10184
workspace={workspace}
10285
handleAction={handleRestart}
10386
/>
10487
),
105-
[ButtonTypesEnum.deleting]: <ActionLoadingButton label="Deleting" />,
106-
[ButtonTypesEnum.canceling]: <DisabledButton label="Canceling..." />,
107-
[ButtonTypesEnum.deleted]: <DisabledButton label="Deleted" />,
108-
[ButtonTypesEnum.pending]: <ActionLoadingButton label="Pending..." />,
109-
[ButtonTypesEnum.activate]: (
110-
<ActivateButton handleAction={handleDormantActivate} />
111-
),
112-
[ButtonTypesEnum.activating]: (
113-
<ActivateButton loading handleAction={handleDormantActivate} />
114-
),
88+
deleting: <ActionLoadingButton label="Deleting" />,
89+
canceling: <DisabledButton label="Canceling..." />,
90+
deleted: <DisabledButton label="Deleted" />,
91+
pending: <ActionLoadingButton label="Pending..." />,
92+
activate: <ActivateButton handleAction={handleDormantActivate} />,
93+
activating: <ActivateButton loading handleAction={handleDormantActivate} />,
94+
retry: <RetryButton handleAction={handleRetry} />,
11595
};
11696

97+
const { actions, canCancel, canAcceptJobs } = actionsByWorkspaceStatus(
98+
workspace,
99+
workspace.latest_build.status,
100+
canChangeVersions,
101+
);
102+
const canBeUpdated = workspace.outdated && canAcceptJobs;
103+
117104
return (
118105
<div
119106
css={{ display: "flex", alignItems: "center", gap: 12 }}
120107
data-testid="workspace-actions"
121108
>
109+
{/*
110+
* Parentheses important – if canBeUpdated is false, nothing should
111+
* appear in the UI
112+
*/}
122113
{canBeUpdated &&
123-
(isUpdating
124-
? buttonMapping[ButtonTypesEnum.updating]
125-
: buttonMapping[ButtonTypesEnum.update])}
114+
(isUpdating ? buttonMapping.updating : buttonMapping.update)}
126115

127-
{isRestarting && buttonMapping[ButtonTypesEnum.restarting]}
128-
129-
{!isRestarting &&
130-
actionsByStatus.map((action) => (
131-
<Fragment key={action}>{buttonMapping[action]}</Fragment>
132-
))}
116+
{isRestarting
117+
? buttonMapping.restarting
118+
: actions.map((action) => (
119+
<Fragment key={action}>{buttonMapping[action]}</Fragment>
120+
))}
133121

134122
{canCancel && <CancelButton handleAction={handleCancel} />}
123+
135124
<MoreMenu>
136125
<MoreMenuTrigger>
137126
<ThreeDotsButton

0 commit comments

Comments
 (0)