mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
fix: fix workspace actions options (#13572)
This commit is contained in:
committed by
GitHub
parent
eed9794516
commit
07cd9acb2c
@ -99,6 +99,21 @@ export const StartButton: FC<ActionButtonPropsWithWorkspace> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const UpdateAndStartButton: FC<ActionButtonProps> = ({
|
||||
handleAction,
|
||||
}) => {
|
||||
return (
|
||||
<Tooltip title="This template requires automatic updates on workspace startup. Contact your administrator if you want to preserve the template version.">
|
||||
<TopbarButton
|
||||
startIcon={<PlayCircleOutlineIcon />}
|
||||
onClick={() => handleAction()}
|
||||
>
|
||||
Update and start…
|
||||
</TopbarButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export const StopButton: FC<ActionButtonProps> = ({
|
||||
handleAction,
|
||||
loading,
|
||||
@ -148,16 +163,13 @@ export const RestartButton: FC<ActionButtonPropsWithWorkspace> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const UpdateAndStartButton: FC<ActionButtonProps> = ({
|
||||
export const UpdateAndRestartButton: FC<ActionButtonProps> = ({
|
||||
handleAction,
|
||||
}) => {
|
||||
return (
|
||||
<Tooltip title="This template requires automatic updates on workspace startup. Contact your administrator if you want to preserve the template version.">
|
||||
<TopbarButton
|
||||
startIcon={<PlayCircleOutlineIcon />}
|
||||
onClick={() => handleAction()}
|
||||
>
|
||||
Update and start…
|
||||
<TopbarButton startIcon={<ReplayIcon />} onClick={() => handleAction()}>
|
||||
Update and restart…
|
||||
</TopbarButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
@ -34,6 +34,37 @@ export const Running: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const RunningUpdateAvailable: Story = {
|
||||
name: "Running (Update available)",
|
||||
args: {
|
||||
workspace: {
|
||||
...Mocks.MockWorkspace,
|
||||
outdated: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const RunningRequireActiveVersion: Story = {
|
||||
name: "Running (No required update)",
|
||||
args: {
|
||||
workspace: {
|
||||
...Mocks.MockWorkspace,
|
||||
template_require_active_version: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const RunningUpdateRequired: Story = {
|
||||
name: "Running (Update Required)",
|
||||
args: {
|
||||
workspace: {
|
||||
...Mocks.MockWorkspace,
|
||||
template_require_active_version: true,
|
||||
outdated: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Stopping: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockStoppingWorkspace,
|
||||
@ -46,15 +77,54 @@ export const Stopped: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Canceling: Story = {
|
||||
export const StoppedUpdateAvailable: Story = {
|
||||
name: "Stopped (Update available)",
|
||||
args: {
|
||||
workspace: Mocks.MockCancelingWorkspace,
|
||||
workspace: {
|
||||
...Mocks.MockStoppedWorkspace,
|
||||
outdated: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Canceled: Story = {
|
||||
export const StoppedRequireActiveVersion: Story = {
|
||||
name: "Stopped (No required update)",
|
||||
args: {
|
||||
workspace: Mocks.MockCanceledWorkspace,
|
||||
workspace: {
|
||||
...Mocks.MockStoppedWorkspace,
|
||||
template_require_active_version: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const StoppedUpdateRequired: Story = {
|
||||
name: "Stopped (Update Required)",
|
||||
args: {
|
||||
workspace: {
|
||||
...Mocks.MockStoppedWorkspace,
|
||||
template_require_active_version: true,
|
||||
outdated: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Updating: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockOutdatedWorkspace,
|
||||
isUpdating: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Restarting: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockStoppingWorkspace,
|
||||
isRestarting: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Canceling: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockCancelingWorkspace,
|
||||
},
|
||||
};
|
||||
|
||||
@ -89,41 +159,6 @@ export const FailedWithDebug: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const Updating: Story = {
|
||||
args: {
|
||||
isUpdating: true,
|
||||
workspace: Mocks.MockOutdatedWorkspace,
|
||||
},
|
||||
};
|
||||
|
||||
export const RequireActiveVersionStarted: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockOutdatedRunningWorkspaceRequireActiveVersion,
|
||||
canChangeVersions: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const RequireActiveVersionStopped: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockOutdatedStoppedWorkspaceRequireActiveVersion,
|
||||
canChangeVersions: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const AlwaysUpdateStarted: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockOutdatedRunningWorkspaceAlwaysUpdate,
|
||||
canChangeVersions: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const AlwaysUpdateStopped: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockOutdatedStoppedWorkspaceAlwaysUpdate,
|
||||
canChangeVersions: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const CancelShownForOwner: Story = {
|
||||
args: {
|
||||
workspace: {
|
||||
@ -133,6 +168,7 @@ export const CancelShownForOwner: Story = {
|
||||
isOwner: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const CancelShownForUser: Story = {
|
||||
args: {
|
||||
workspace: Mocks.MockStartingWorkspace,
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
ActivateButton,
|
||||
FavoriteButton,
|
||||
UpdateAndStartButton,
|
||||
UpdateAndRestartButton,
|
||||
} from "./Buttons";
|
||||
import { type ActionType, abilitiesByWorkspaceStatus } from "./constants";
|
||||
import { DebugButton } from "./DebugButton";
|
||||
@ -89,12 +90,12 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
|
||||
|
||||
const mustUpdate = mustUpdateWorkspace(workspace, canChangeVersions);
|
||||
const tooltipText = getTooltipText(workspace, mustUpdate, canChangeVersions);
|
||||
const canBeUpdated = workspace.outdated && canAcceptJobs;
|
||||
|
||||
// A mapping of button type to the corresponding React component
|
||||
const buttonMapping: Record<ActionType, ReactNode> = {
|
||||
update: <UpdateButton handleAction={handleUpdate} />,
|
||||
updateAndStart: <UpdateAndStartButton handleAction={handleUpdate} />,
|
||||
updateAndRestart: <UpdateAndRestartButton handleAction={handleUpdate} />,
|
||||
updating: <UpdateButton loading handleAction={handleUpdate} />,
|
||||
start: (
|
||||
<StartButton
|
||||
@ -152,13 +153,6 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
|
||||
enableBuildParameters={workspace.latest_build.transition === "start"}
|
||||
/>
|
||||
),
|
||||
toggleFavorite: (
|
||||
<FavoriteButton
|
||||
workspaceID={workspace.id}
|
||||
isFavorite={workspace.favorite}
|
||||
onToggle={handleToggleFavorite}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
@ -166,30 +160,22 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
|
||||
css={{ display: "flex", alignItems: "center", gap: 8 }}
|
||||
data-testid="workspace-actions"
|
||||
>
|
||||
{canBeUpdated && (
|
||||
<>
|
||||
{isUpdating
|
||||
? buttonMapping.updating
|
||||
: workspace.template_require_active_version
|
||||
? buttonMapping.updateAndStart
|
||||
: buttonMapping.update}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!canBeUpdated &&
|
||||
!isUpdating &&
|
||||
workspace.template_require_active_version &&
|
||||
buttonMapping.start}
|
||||
|
||||
{isRestarting
|
||||
? buttonMapping.restarting
|
||||
: actions.map((action) => (
|
||||
<Fragment key={action}>{buttonMapping[action]}</Fragment>
|
||||
))}
|
||||
{/* Restarting must be handled separately, because it otherwise would appear as stopping */}
|
||||
{isUpdating
|
||||
? buttonMapping.updating
|
||||
: isRestarting
|
||||
? buttonMapping.restarting
|
||||
: actions.map((action) => (
|
||||
<Fragment key={action}>{buttonMapping[action]}</Fragment>
|
||||
))}
|
||||
|
||||
{showCancel && <CancelButton handleAction={handleCancel} />}
|
||||
|
||||
{buttonMapping.toggleFavorite}
|
||||
<FavoriteButton
|
||||
workspaceID={workspace.id}
|
||||
isFavorite={workspace.favorite}
|
||||
onToggle={handleToggleFavorite}
|
||||
/>
|
||||
|
||||
<MoreMenu>
|
||||
<MoreMenuTrigger>
|
||||
|
@ -6,16 +6,19 @@ import type { Workspace } from "api/typesGenerated";
|
||||
export const actionTypes = [
|
||||
"start",
|
||||
"starting",
|
||||
// Replaces start when an update is required.
|
||||
"updateAndStart",
|
||||
"stop",
|
||||
"stopping",
|
||||
"restart",
|
||||
"restarting",
|
||||
// Replaces restart when an update is required.
|
||||
"updateAndRestart",
|
||||
"deleting",
|
||||
"update",
|
||||
"updating",
|
||||
"activate",
|
||||
"activating",
|
||||
"toggleFavorite",
|
||||
|
||||
// There's no need for a retrying state because retrying starts a transition
|
||||
// into one of the starting, stopping, or deleting states (based on the
|
||||
@ -23,10 +26,6 @@ export const actionTypes = [
|
||||
"retry",
|
||||
"debug",
|
||||
|
||||
// When a template requires updates, we aim to display a distinct update
|
||||
// button that clearly indicates a mandatory update.
|
||||
"updateAndStart",
|
||||
|
||||
// These are buttons that should be used with disabled UI elements
|
||||
"canceling",
|
||||
"deleted",
|
||||
@ -54,13 +53,6 @@ export const abilitiesByWorkspaceStatus = (
|
||||
}
|
||||
|
||||
const status = workspace.latest_build.status;
|
||||
if (status === "failed" && canDebug) {
|
||||
return {
|
||||
actions: ["retry", "debug"],
|
||||
canCancel: false,
|
||||
canAcceptJobs: true,
|
||||
};
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case "starting": {
|
||||
@ -73,10 +65,12 @@ export const abilitiesByWorkspaceStatus = (
|
||||
case "running": {
|
||||
const actions: ActionType[] = ["stop"];
|
||||
|
||||
// If the template requires the latest version, we prevent the user from
|
||||
// restarting the workspace without updating it first. In the Buttons
|
||||
// component, we display an UpdateAndStart component to facilitate this.
|
||||
if (!workspace.template_require_active_version) {
|
||||
if (workspace.template_require_active_version && workspace.outdated) {
|
||||
actions.push("updateAndRestart");
|
||||
} else {
|
||||
if (workspace.outdated) {
|
||||
actions.unshift("update");
|
||||
}
|
||||
actions.push("restart");
|
||||
}
|
||||
|
||||
@ -96,10 +90,12 @@ export const abilitiesByWorkspaceStatus = (
|
||||
case "stopped": {
|
||||
const actions: ActionType[] = [];
|
||||
|
||||
// If the template requires the latest version, we prevent the user from
|
||||
// starting the workspace without updating it first. In the Buttons
|
||||
// component, we display an UpdateAndStart component to facilitate this.
|
||||
if (!workspace.template_require_active_version) {
|
||||
if (workspace.template_require_active_version && workspace.outdated) {
|
||||
actions.push("updateAndStart");
|
||||
} else {
|
||||
if (workspace.outdated) {
|
||||
actions.unshift("update");
|
||||
}
|
||||
actions.push("start");
|
||||
}
|
||||
|
||||
@ -117,14 +113,31 @@ export const abilitiesByWorkspaceStatus = (
|
||||
};
|
||||
}
|
||||
case "failed": {
|
||||
const actions: ActionType[] = ["retry"];
|
||||
|
||||
if (canDebug) {
|
||||
actions.push("debug");
|
||||
}
|
||||
|
||||
if (workspace.outdated) {
|
||||
actions.unshift("update");
|
||||
}
|
||||
|
||||
return {
|
||||
actions: ["retry"],
|
||||
actions,
|
||||
canCancel: false,
|
||||
canAcceptJobs: true,
|
||||
};
|
||||
}
|
||||
|
||||
// Disabled states
|
||||
case "pending": {
|
||||
return {
|
||||
actions: ["pending"],
|
||||
canCancel: false,
|
||||
canAcceptJobs: false,
|
||||
};
|
||||
}
|
||||
case "canceling": {
|
||||
return {
|
||||
actions: ["canceling"],
|
||||
@ -146,15 +159,8 @@ export const abilitiesByWorkspaceStatus = (
|
||||
canAcceptJobs: false,
|
||||
};
|
||||
}
|
||||
case "pending": {
|
||||
return {
|
||||
actions: ["pending"],
|
||||
canCancel: false,
|
||||
canAcceptJobs: false,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown workspace status: ${status}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1175,10 +1175,6 @@ export const MockOutdatedRunningWorkspaceRequireActiveVersion: TypesGen.Workspac
|
||||
id: "test-outdated-workspace-require-active-version",
|
||||
outdated: true,
|
||||
template_require_active_version: true,
|
||||
latest_build: {
|
||||
...MockWorkspaceBuild,
|
||||
status: "running",
|
||||
},
|
||||
};
|
||||
|
||||
export const MockOutdatedRunningWorkspaceAlwaysUpdate: TypesGen.Workspace = {
|
||||
|
Reference in New Issue
Block a user