feat(ws): Implement Start restart and stop workspace actions (#162)
Signed-off-by: Elay Aharoni (EXT-Nokia) <elay.aharoni.ext@nokia.com> Co-authored-by: Elay Aharoni (EXT-Nokia) <elay.aharoni.ext@nokia.com>
This commit is contained in:
parent
f9da864e1d
commit
4cbc26eaf4
|
|
@ -11,8 +11,8 @@ import {
|
|||
Pagination,
|
||||
Button,
|
||||
Content,
|
||||
Tooltip,
|
||||
Brand,
|
||||
Tooltip,
|
||||
} from '@patternfly/react-core';
|
||||
import {
|
||||
Table,
|
||||
|
|
@ -34,9 +34,21 @@ import DeleteModal from '~/shared/components/DeleteModal';
|
|||
import { buildKindLogoDictionary } from '~/app/actions/WorkspaceKindsActions';
|
||||
import useWorkspaceKinds from '~/app/hooks/useWorkspaceKinds';
|
||||
import { WorkspaceConnectAction } from '~/app/pages/Workspaces/WorkspaceConnectAction';
|
||||
import { WorkspaceStartActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceStartActionModal';
|
||||
import { WorkspaceRestartActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceRestartActionModal';
|
||||
import { WorkspaceStopActionModal } from '~/app/pages/Workspaces/workspaceActions/WorkspaceStopActionModal';
|
||||
import Filter, { FilteredColumn } from 'shared/components/Filter';
|
||||
import { formatRam } from 'shared/utilities/WorkspaceResources';
|
||||
|
||||
export enum ActionType {
|
||||
ViewDetails,
|
||||
Edit,
|
||||
Delete,
|
||||
Start,
|
||||
Restart,
|
||||
Stop,
|
||||
}
|
||||
|
||||
export const Workspaces: React.FunctionComponent = () => {
|
||||
/* Mocked workspaces, to be removed after fetching info from backend */
|
||||
const mockWorkspaces: Workspace[] = [
|
||||
|
|
@ -85,7 +97,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
lastUpdate: 0,
|
||||
},
|
||||
pauseTime: 0,
|
||||
pendingRestart: false,
|
||||
pendingRestart: true,
|
||||
podTemplateOptions: {
|
||||
imageConfig: {
|
||||
desired: '',
|
||||
|
|
@ -170,7 +182,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
lastActivity: 'Last Activity',
|
||||
};
|
||||
|
||||
const filterableColumns: WorkspacesColumnNames = {
|
||||
const filterableColumns = {
|
||||
name: 'Name',
|
||||
kind: 'Kind',
|
||||
image: 'Image',
|
||||
|
|
@ -182,10 +194,12 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
|
||||
// change when fetch workspaces is implemented
|
||||
const initialWorkspaces = mockWorkspaces;
|
||||
const [workspaces, setWorkspaces] = useState(initialWorkspaces);
|
||||
const [workspaces, setWorkspaces] = useState<Workspace[]>(initialWorkspaces);
|
||||
const [expandedWorkspacesNames, setExpandedWorkspacesNames] = React.useState<string[]>([]);
|
||||
const [selectedWorkspace, setSelectedWorkspace] = React.useState<Workspace | null>(null);
|
||||
const [workspaceToDelete, setWorkspaceToDelete] = React.useState<Workspace | null>(null);
|
||||
const [isActionAlertModalOpen, setIsActionAlertModalOpen] = React.useState(false);
|
||||
const [activeActionType, setActiveActionType] = React.useState<ActionType | null>(null);
|
||||
|
||||
const selectWorkspace = React.useCallback(
|
||||
(newSelectedWorkspace) => {
|
||||
|
|
@ -197,6 +211,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
},
|
||||
[selectedWorkspace],
|
||||
);
|
||||
|
||||
const setWorkspaceExpanded = (workspace: Workspace, isExpanding = true) =>
|
||||
setExpandedWorkspacesNames((prevExpanded) => {
|
||||
const newExpandedWorkspacesNames = prevExpanded.filter((wsName) => wsName !== workspace.name);
|
||||
|
|
@ -300,20 +315,31 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
|
||||
// Actions
|
||||
|
||||
const viewDetailsClick = React.useCallback((workspace: Workspace) => {
|
||||
setSelectedWorkspace(workspace);
|
||||
setActiveActionType(ActionType.ViewDetails);
|
||||
}, []);
|
||||
|
||||
const editAction = React.useCallback((workspace: Workspace) => {
|
||||
console.log(`Clicked on edit, on row ${workspace.name}`);
|
||||
setSelectedWorkspace(workspace);
|
||||
setActiveActionType(ActionType.Edit);
|
||||
}, []);
|
||||
|
||||
const deleteAction = React.useCallback((workspace: Workspace) => {
|
||||
console.log(`Clicked on delete, on row ${workspace.name}`);
|
||||
setSelectedWorkspace(workspace);
|
||||
setActiveActionType(ActionType.Delete);
|
||||
}, []);
|
||||
|
||||
const startRestartAction = React.useCallback((workspace: Workspace) => {
|
||||
console.log(`Clicked on start/restart, on row ${workspace.name}`);
|
||||
const startRestartAction = React.useCallback((workspace: Workspace, action: ActionType) => {
|
||||
setSelectedWorkspace(workspace);
|
||||
setActiveActionType(action);
|
||||
setIsActionAlertModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const stopAction = React.useCallback((workspace: Workspace) => {
|
||||
console.log(`Clicked on stop, on row ${workspace.name}`);
|
||||
setSelectedWorkspace(workspace);
|
||||
setActiveActionType(ActionType.Stop);
|
||||
setIsActionAlertModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleDeleteClick = React.useCallback((workspace: Workspace) => {
|
||||
|
|
@ -322,12 +348,18 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
setWorkspaceToDelete(workspace); // Open the modal and set workspace to delete
|
||||
}, []);
|
||||
|
||||
const defaultActions = React.useCallback(
|
||||
(workspace: Workspace): IActions =>
|
||||
[
|
||||
const onCloseActionAlertDialog = () => {
|
||||
setIsActionAlertModalOpen(false);
|
||||
setSelectedWorkspace(null);
|
||||
setActiveActionType(null);
|
||||
};
|
||||
|
||||
const workspaceDefaultActions = (workspace: Workspace): IActions => {
|
||||
const workspaceState = workspace.status.state;
|
||||
const workspaceActions = [
|
||||
{
|
||||
title: 'View Details',
|
||||
onClick: () => selectWorkspace(workspace),
|
||||
onClick: () => viewDetailsClick(workspace),
|
||||
},
|
||||
{
|
||||
title: 'Edit',
|
||||
|
|
@ -340,17 +372,55 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
{
|
||||
isSeparator: true,
|
||||
},
|
||||
{
|
||||
title: 'Start/restart',
|
||||
onClick: () => startRestartAction(workspace),
|
||||
workspaceState !== WorkspaceState.Running
|
||||
? {
|
||||
title: 'Start',
|
||||
onClick: () => startRestartAction(workspace, ActionType.Start),
|
||||
}
|
||||
: {
|
||||
title: 'Restart',
|
||||
onClick: () => startRestartAction(workspace, ActionType.Restart),
|
||||
},
|
||||
{
|
||||
] as IActions;
|
||||
|
||||
if (workspaceState === WorkspaceState.Running) {
|
||||
workspaceActions.push({
|
||||
title: 'Stop',
|
||||
onClick: () => stopAction(workspace),
|
||||
},
|
||||
] as IActions,
|
||||
[selectWorkspace, editAction, handleDeleteClick, startRestartAction, stopAction],
|
||||
});
|
||||
}
|
||||
return workspaceActions;
|
||||
};
|
||||
|
||||
const chooseAlertModal = () => {
|
||||
switch (activeActionType) {
|
||||
case ActionType.Start:
|
||||
return (
|
||||
<WorkspaceStartActionModal
|
||||
onClose={onCloseActionAlertDialog}
|
||||
isOpen={isActionAlertModalOpen}
|
||||
workspace={selectedWorkspace}
|
||||
/>
|
||||
);
|
||||
case ActionType.Restart:
|
||||
return (
|
||||
<WorkspaceRestartActionModal
|
||||
onClose={onCloseActionAlertDialog}
|
||||
isOpen={isActionAlertModalOpen}
|
||||
workspace={selectedWorkspace}
|
||||
/>
|
||||
);
|
||||
case ActionType.Stop:
|
||||
return (
|
||||
<WorkspaceStopActionModal
|
||||
onClose={onCloseActionAlertDialog}
|
||||
isOpen={isActionAlertModalOpen}
|
||||
workspace={selectedWorkspace}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// States
|
||||
|
||||
|
|
@ -401,7 +471,10 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
);
|
||||
|
||||
return (
|
||||
<Drawer isInline isExpanded={selectedWorkspace != null}>
|
||||
<Drawer
|
||||
isInline
|
||||
isExpanded={selectedWorkspace != null && activeActionType === ActionType.ViewDetails}
|
||||
>
|
||||
<DrawerContent panelContent={workspaceDetailsContent}>
|
||||
<DrawerContentBody>
|
||||
<PageSection isFilled>
|
||||
|
|
@ -483,7 +556,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
</Td>
|
||||
<Td isActionCell data-testid="action-column">
|
||||
<ActionsColumn
|
||||
items={defaultActions(workspace).map((action) => ({
|
||||
items={workspaceDefaultActions(workspace).map((action) => ({
|
||||
...action,
|
||||
'data-testid': `action-${typeof action.title === 'string' ? action.title.toLowerCase() : ''}`,
|
||||
}))}
|
||||
|
|
@ -496,6 +569,7 @@ export const Workspaces: React.FunctionComponent = () => {
|
|||
</Tbody>
|
||||
))}
|
||||
</Table>
|
||||
{isActionAlertModalOpen && chooseAlertModal()}
|
||||
<DeleteModal
|
||||
isOpen={workspaceToDelete != null}
|
||||
resourceName={workspaceToDelete?.name || ''}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,188 @@
|
|||
import { ExpandableSection, Icon, Tab, Tabs, TabTitleText, Content } from '@patternfly/react-core';
|
||||
import {
|
||||
ExclamationCircleIcon,
|
||||
ExclamationTriangleIcon,
|
||||
InfoCircleIcon,
|
||||
} from '@patternfly/react-icons';
|
||||
import * as React from 'react';
|
||||
|
||||
// remove when changing to fetch data from BE
|
||||
const mockedWorkspaceKind = {
|
||||
name: 'jupyter-lab',
|
||||
displayName: 'JupyterLab Notebook',
|
||||
description: 'A Workspace which runs JupyterLab in a Pod',
|
||||
deprecated: false,
|
||||
deprecationMessage: '',
|
||||
hidden: false,
|
||||
icon: {
|
||||
url: 'https://jupyter.org/assets/favicons/apple-touch-icon-152x152.png',
|
||||
},
|
||||
logo: {
|
||||
url: 'https://upload.wikimedia.org/wikipedia/commons/3/38/Jupyter_logo.svg',
|
||||
},
|
||||
podTemplate: {
|
||||
podMetadata: {
|
||||
labels: { myWorkspaceKindLabel: 'my-value' },
|
||||
annotations: { myWorkspaceKindAnnotation: 'my-value' },
|
||||
},
|
||||
volumeMounts: { home: '/home/jovyan' },
|
||||
options: {
|
||||
imageConfig: {
|
||||
default: 'jupyterlab_scipy_190',
|
||||
values: [
|
||||
{
|
||||
id: 'jupyterlab_scipy_180',
|
||||
displayName: 'jupyter-scipy:v1.8.0',
|
||||
labels: { pythonVersion: '3.11' },
|
||||
hidden: true,
|
||||
redirect: {
|
||||
to: 'jupyterlab_scipy_190',
|
||||
message: {
|
||||
text: 'This update will change...',
|
||||
level: 'Info',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'jupyterlab_scipy_190',
|
||||
displayName: 'jupyter-scipy:v1.9.0',
|
||||
labels: { pythonVersion: '3.11' },
|
||||
hidden: true,
|
||||
redirect: {
|
||||
to: 'jupyterlab_scipy_200',
|
||||
message: {
|
||||
text: 'This update will change...',
|
||||
level: 'Warning',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
podConfig: {
|
||||
default: 'tiny_cpu',
|
||||
values: [
|
||||
{
|
||||
id: 'tiny_cpu',
|
||||
displayName: 'Tiny CPU',
|
||||
description: 'Pod with 0.1 CPU, 128 Mb RAM',
|
||||
labels: { cpu: '100m', memory: '128Mi' },
|
||||
redirect: {
|
||||
to: 'small_cpu',
|
||||
message: {
|
||||
text: 'This update will change...',
|
||||
level: 'Danger',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const getLevelIcon = (level: string) => {
|
||||
switch (level) {
|
||||
case 'Info':
|
||||
return (
|
||||
<Icon status="info">
|
||||
<InfoCircleIcon />
|
||||
</Icon>
|
||||
);
|
||||
case 'Warning':
|
||||
return (
|
||||
<Icon status="warning">
|
||||
<ExclamationTriangleIcon />
|
||||
</Icon>
|
||||
);
|
||||
case 'Danger':
|
||||
return (
|
||||
<Icon status="danger">
|
||||
<ExclamationCircleIcon />
|
||||
</Icon>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Icon status="info">
|
||||
<InfoCircleIcon />
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const WorkspaceRedirectInformationView: React.FC = () => {
|
||||
const [activeKey, setActiveKey] = React.useState<string | number>(0);
|
||||
// change this to get from BE, and use the workspaceKinds API
|
||||
const workspaceKind = mockedWorkspaceKind;
|
||||
|
||||
const { imageConfig } = workspaceKind.podTemplate.options;
|
||||
const { podConfig } = workspaceKind.podTemplate.options;
|
||||
|
||||
const imageConfigRedirects = imageConfig.values.map((value) => ({
|
||||
src: value.id,
|
||||
dest: value.redirect.to,
|
||||
message: value.redirect.message.text,
|
||||
level: value.redirect.message.level,
|
||||
}));
|
||||
const podConfigRedirects = podConfig.values.map((value) => ({
|
||||
src: value.id,
|
||||
dest: value.redirect.to,
|
||||
message: value.redirect.message.text,
|
||||
level: value.redirect.message.level,
|
||||
}));
|
||||
|
||||
const getMaxLevel = (
|
||||
redirects: { dest: string; level: string; message: string; src: string }[],
|
||||
) => {
|
||||
let maxLevel = redirects[0].level;
|
||||
redirects.forEach((redirect) => {
|
||||
if (
|
||||
(maxLevel === 'Info' && (redirect.level === 'Warning' || redirect.level === 'Danger')) ||
|
||||
(maxLevel === 'Warning' && redirect.level === 'Danger')
|
||||
) {
|
||||
maxLevel = redirect.level;
|
||||
}
|
||||
});
|
||||
return maxLevel;
|
||||
};
|
||||
|
||||
return (
|
||||
<Tabs activeKey={activeKey} onSelect={(event, eventKey) => setActiveKey(eventKey)}>
|
||||
{imageConfigRedirects.length > 0 && (
|
||||
<Tab
|
||||
eventKey={0}
|
||||
title={
|
||||
<TabTitleText>
|
||||
Image Config {getLevelIcon(getMaxLevel(imageConfigRedirects))}
|
||||
</TabTitleText>
|
||||
}
|
||||
>
|
||||
{imageConfigRedirects.map((redirect, index) => (
|
||||
<Content style={{ display: 'flex', alignItems: 'baseline' }} key={index}>
|
||||
{getLevelIcon(redirect.level)}
|
||||
<ExpandableSection toggleText={` ${redirect.src} -> ${redirect.dest}`}>
|
||||
<Content>{redirect.message}</Content>
|
||||
</ExpandableSection>
|
||||
</Content>
|
||||
))}
|
||||
</Tab>
|
||||
)}
|
||||
{podConfigRedirects.length > 0 && (
|
||||
<Tab
|
||||
eventKey={1}
|
||||
title={
|
||||
<TabTitleText>Pod Config {getLevelIcon(getMaxLevel(podConfigRedirects))}</TabTitleText>
|
||||
}
|
||||
>
|
||||
{podConfigRedirects.map((redirect, index) => (
|
||||
<Content style={{ display: 'flex', alignItems: 'baseline' }} key={index}>
|
||||
{getLevelIcon(redirect.level)}
|
||||
<ExpandableSection toggleText={` ${redirect.src} -> ${redirect.dest}`}>
|
||||
<Content>{redirect.message}</Content>
|
||||
</ExpandableSection>
|
||||
</Content>
|
||||
))}
|
||||
</Tab>
|
||||
)}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import * as React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Content,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
TabTitleText,
|
||||
} from '@patternfly/react-core';
|
||||
import { Workspace } from '~/shared/types';
|
||||
import { WorkspaceRedirectInformationView } from '~/app/pages/Workspaces/workspaceActions/WorkspaceRedirectInformationView';
|
||||
|
||||
interface RestartActionAlertProps {
|
||||
onClose: () => void;
|
||||
isOpen: boolean;
|
||||
workspace: Workspace | null;
|
||||
}
|
||||
|
||||
export const WorkspaceRestartActionModal: React.FC<RestartActionAlertProps> = ({
|
||||
onClose,
|
||||
isOpen,
|
||||
workspace,
|
||||
}) => {
|
||||
const workspacePendingUpdate = workspace?.status.pendingRestart;
|
||||
const handleClick = (isUpdate = false) => {
|
||||
if (isUpdate) {
|
||||
console.log(`Update ${workspace?.name}`);
|
||||
}
|
||||
console.log(`Restart ${workspace?.name}`);
|
||||
onClose();
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
variant="medium"
|
||||
isOpen={isOpen}
|
||||
aria-describedby="modal-title-icon-description"
|
||||
aria-labelledby="title-icon-modal-title"
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalHeader title="Restart Workspace" />
|
||||
<ModalBody>
|
||||
{workspacePendingUpdate ? (
|
||||
<>
|
||||
<TabTitleText>
|
||||
There are pending redirect updates for that workspace. Are you sure you want to
|
||||
proceed?
|
||||
</TabTitleText>
|
||||
<WorkspaceRedirectInformationView />
|
||||
</>
|
||||
) : (
|
||||
<Content>Are you sure you want to restart the workspace?</Content>
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{workspacePendingUpdate && (
|
||||
<Button onClick={() => handleClick(true)}>Update and Restart</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={() => handleClick(false)}
|
||||
variant={workspacePendingUpdate ? 'secondary' : 'primary'}
|
||||
>
|
||||
Restart
|
||||
</Button>
|
||||
<Button variant="link" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import * as React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
TabTitleText,
|
||||
} from '@patternfly/react-core';
|
||||
import { Workspace } from '~/shared/types';
|
||||
import { WorkspaceRedirectInformationView } from '~/app/pages/Workspaces/workspaceActions/WorkspaceRedirectInformationView';
|
||||
|
||||
interface StartActionAlertProps {
|
||||
onClose: () => void;
|
||||
isOpen: boolean;
|
||||
workspace: Workspace | null;
|
||||
}
|
||||
|
||||
export const WorkspaceStartActionModal: React.FC<StartActionAlertProps> = ({
|
||||
onClose,
|
||||
isOpen,
|
||||
workspace,
|
||||
}) => {
|
||||
const handleClick = (isUpdate = false) => {
|
||||
if (isUpdate) {
|
||||
console.log(`Update ${workspace?.name}`);
|
||||
}
|
||||
console.log(`Start ${workspace?.name}`);
|
||||
onClose();
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
variant="medium"
|
||||
isOpen={isOpen}
|
||||
aria-describedby="modal-title-icon-description"
|
||||
aria-labelledby="title-icon-modal-title"
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalHeader title="Start Workspace" />
|
||||
<ModalBody>
|
||||
<TabTitleText>
|
||||
There are pending redirect updates for that workspace. Are you sure you want to proceed?
|
||||
</TabTitleText>
|
||||
<WorkspaceRedirectInformationView />
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button onClick={() => handleClick(true)}>Update and Start</Button>
|
||||
<Button onClick={() => handleClick(false)} variant="secondary">
|
||||
Start
|
||||
</Button>
|
||||
<Button variant="link" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import * as React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
TabTitleText,
|
||||
Content,
|
||||
} from '@patternfly/react-core';
|
||||
import { Workspace } from '~/shared/types';
|
||||
import { WorkspaceRedirectInformationView } from '~/app/pages/Workspaces/workspaceActions/WorkspaceRedirectInformationView';
|
||||
|
||||
interface StopActionAlertProps {
|
||||
onClose: () => void;
|
||||
isOpen: boolean;
|
||||
workspace: Workspace | null;
|
||||
}
|
||||
|
||||
export const WorkspaceStopActionModal: React.FC<StopActionAlertProps> = ({
|
||||
onClose,
|
||||
isOpen,
|
||||
workspace,
|
||||
}) => {
|
||||
const workspacePendingUpdate = workspace?.status.pendingRestart;
|
||||
const handleClick = (isUpdate = false) => {
|
||||
if (isUpdate) {
|
||||
console.log(`Update ${workspace?.name}`);
|
||||
}
|
||||
console.log(`Stop ${workspace?.name}`);
|
||||
onClose();
|
||||
};
|
||||
return (
|
||||
<Modal
|
||||
variant="medium"
|
||||
isOpen={isOpen}
|
||||
aria-describedby="modal-title-icon-description"
|
||||
aria-labelledby="title-icon-modal-title"
|
||||
onClose={onClose}
|
||||
>
|
||||
<ModalHeader title="Stop Workspace" />
|
||||
<ModalBody>
|
||||
{workspacePendingUpdate ? (
|
||||
<>
|
||||
<TabTitleText>
|
||||
There are pending redirect updates for that workspace. Are you sure you want to
|
||||
proceed?
|
||||
</TabTitleText>
|
||||
<WorkspaceRedirectInformationView />
|
||||
</>
|
||||
) : (
|
||||
<Content>Are you sure you want to stop the workspace?</Content>
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{workspacePendingUpdate && (
|
||||
<Button onClick={() => handleClick(true)}>Update and Stop</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={() => handleClick(false)}
|
||||
variant={workspacePendingUpdate ? 'secondary' : 'primary'}
|
||||
>
|
||||
{workspacePendingUpdate ? 'Stop and defer updates' : 'Stop'}
|
||||
</Button>
|
||||
<Button variant="link" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
Loading…
Reference in New Issue