diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 8b763bb644595..3cd0d3a7ec29c 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1,4 +1,5 @@ import axios, { AxiosRequestHeaders } from "axios" +import * as Types from "./types" import { WorkspaceBuildTransition } from "./types" import * as TypesGen from "./typesGenerated" @@ -161,6 +162,11 @@ export const startWorkspace = postWorkspaceBuild("start") export const stopWorkspace = postWorkspaceBuild("stop") export const deleteWorkspace = postWorkspaceBuild("delete") +export const cancelWorkspaceBuild = async (workspaceBuildId: TypesGen.WorkspaceBuild["id"]): Promise => { + const response = await axios.patch(`/api/v2/workspacebuilds/${workspaceBuildId}/cancel`) + return response.data +} + export const createUser = async (user: TypesGen.CreateUserRequest): Promise => { const response = await axios.post("/api/v2/users", user) return response.data diff --git a/site/src/api/types.ts b/site/src/api/types.ts index 5ea8d8ff86285..daf4e451ac5e8 100644 --- a/site/src/api/types.ts +++ b/site/src/api/types.ts @@ -12,3 +12,5 @@ export interface ReconnectingPTYRequest { } export type WorkspaceBuildTransition = "start" | "stop" | "delete" + +export type Message = { message: string } diff --git a/site/src/components/Workspace/Workspace.stories.tsx b/site/src/components/Workspace/Workspace.stories.tsx index d5087ebf949cd..44a26919588cb 100644 --- a/site/src/components/Workspace/Workspace.stories.tsx +++ b/site/src/components/Workspace/Workspace.stories.tsx @@ -31,7 +31,6 @@ Started.args = { workspace: MockWorkspace, handleStart: action("start"), handleStop: action("stop"), - handleRetry: action("retry"), resources: [MockWorkspaceResource, MockWorkspaceResource2], builds: [MockWorkspaceBuild], } diff --git a/site/src/components/Workspace/Workspace.tsx b/site/src/components/Workspace/Workspace.tsx index d773d1ee874eb..1194d024acb48 100644 --- a/site/src/components/Workspace/Workspace.tsx +++ b/site/src/components/Workspace/Workspace.tsx @@ -14,8 +14,8 @@ import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats" export interface WorkspaceProps { handleStart: () => void handleStop: () => void - handleRetry: () => void handleUpdate: () => void + handleCancel: () => void workspace: TypesGen.Workspace resources?: TypesGen.WorkspaceResource[] getResourcesError?: Error @@ -28,8 +28,8 @@ export interface WorkspaceProps { export const Workspace: React.FC = ({ handleStart, handleStop, - handleRetry, handleUpdate, + handleCancel, workspace, resources, getResourcesError, @@ -55,8 +55,8 @@ export const Workspace: React.FC = ({ workspace={workspace} handleStart={handleStart} handleStop={handleStop} - handleRetry={handleRetry} handleUpdate={handleUpdate} + handleCancel={handleCancel} /> diff --git a/site/src/components/WorkspaceActionButton/WorkspaceActionButton.stories.tsx b/site/src/components/WorkspaceActionButton/WorkspaceActionButton.stories.tsx index 0a3b9b315a83e..4995769fabe4a 100644 --- a/site/src/components/WorkspaceActionButton/WorkspaceActionButton.stories.tsx +++ b/site/src/components/WorkspaceActionButton/WorkspaceActionButton.stories.tsx @@ -14,14 +14,4 @@ export const Example = Template.bind({}) Example.args = { icon: , label: "Start workspace", - loadingLabel: "Starting workspace", - isLoading: false, -} - -export const Loading = Template.bind({}) -Loading.args = { - icon: , - label: "Start workspace", - loadingLabel: "Starting workspace", - isLoading: true, } diff --git a/site/src/components/WorkspaceActionButton/WorkspaceActionButton.tsx b/site/src/components/WorkspaceActionButton/WorkspaceActionButton.tsx index 1188dec6a771c..631cff5f9bf4a 100644 --- a/site/src/components/WorkspaceActionButton/WorkspaceActionButton.tsx +++ b/site/src/components/WorkspaceActionButton/WorkspaceActionButton.tsx @@ -1,42 +1,17 @@ import Button from "@material-ui/core/Button" -import CircularProgress from "@material-ui/core/CircularProgress" -import { makeStyles } from "@material-ui/core/styles" import React from "react" export interface WorkspaceActionButtonProps { label: string - loadingLabel: string - isLoading: boolean icon: JSX.Element onClick: () => void className?: string } -export const WorkspaceActionButton: React.FC = ({ - label, - loadingLabel, - isLoading, - icon, - onClick, - className, -}) => { - const styles = useStyles() - +export const WorkspaceActionButton: React.FC = ({ label, icon, onClick, className }) => { return ( - ) } - -const useStyles = makeStyles((theme) => ({ - spinner: { - color: theme.palette.text.disabled, - marginRight: theme.spacing(1), - }, -})) diff --git a/site/src/components/WorkspaceActions/WorkspaceActions.tsx b/site/src/components/WorkspaceActions/WorkspaceActions.tsx index 15d0d391297a0..23f179107d55a 100644 --- a/site/src/components/WorkspaceActions/WorkspaceActions.tsx +++ b/site/src/components/WorkspaceActions/WorkspaceActions.tsx @@ -1,9 +1,9 @@ import Button from "@material-ui/core/Button" import Link from "@material-ui/core/Link" import { makeStyles } from "@material-ui/core/styles" +import CancelIcon from "@material-ui/icons/Cancel" import CloudDownloadIcon from "@material-ui/icons/CloudDownload" import PlayArrowRoundedIcon from "@material-ui/icons/PlayArrowRounded" -import ReplayIcon from "@material-ui/icons/Replay" import StopIcon from "@material-ui/icons/Stop" import React from "react" import { Link as RouterLink } from "react-router-dom" @@ -17,7 +17,7 @@ export const Language = { stopping: "Stopping workspace", start: "Start workspace", starting: "Starting workspace", - retry: "Retry", + cancel: "Cancel action", update: "Update workspace", } @@ -28,20 +28,32 @@ export const Language = { const canAcceptJobs = (workspaceStatus: WorkspaceStatus) => ["started", "stopped", "deleted", "error", "canceled"].includes(workspaceStatus) +/** + * Jobs that are in progress (queued or pending) can be canceled. + * @param workspaceStatus WorkspaceStatus + * @returns boolean + */ +const canCancelJobs = (workspaceStatus: WorkspaceStatus) => + ["starting", "stopping", "deleting"].includes(workspaceStatus) + +const canStart = (workspaceStatus: WorkspaceStatus) => ["stopped", "canceled", "error"].includes(workspaceStatus) + +const canStop = (workspaceStatus: WorkspaceStatus) => ["started", "canceled", "error"].includes(workspaceStatus) + export interface WorkspaceActionsProps { workspace: Workspace handleStart: () => void handleStop: () => void - handleRetry: () => void handleUpdate: () => void + handleCancel: () => void } export const WorkspaceActions: React.FC = ({ workspace, handleStart, handleStop, - handleRetry, handleUpdate, + handleCancel, }) => { const styles = useStyles() const workspaceStatus = getWorkspaceStatus(workspace.latest_build) @@ -51,31 +63,30 @@ export const WorkspaceActions: React.FC = ({ - {(workspaceStatus === "started" || workspaceStatus === "stopping") && ( + {canStart(workspaceStatus) && ( + } + onClick={handleStart} + label={Language.start} + /> + )} + {canStop(workspaceStatus) && ( } onClick={handleStop} label={Language.stop} - loadingLabel={Language.stopping} - isLoading={workspaceStatus === "stopping"} /> )} - {(workspaceStatus === "stopped" || workspaceStatus === "starting") && ( + {canCancelJobs(workspaceStatus) && ( } - onClick={handleStart} - label={Language.start} - loadingLabel={Language.starting} - isLoading={workspaceStatus === "starting"} + icon={} + onClick={handleCancel} + label={Language.cancel} /> )} - {workspaceStatus === "error" && ( - - )} {workspace.outdated && canAcceptJobs(workspaceStatus) && (