Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ws): Implement Start restart and stop workspace actions #162

Open
wants to merge 1 commit into
base: notebooks-v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 301 additions & 0 deletions workspaces/frontend/src/app/pages/Workspaces/WorkspaceActionAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
import * as React from 'react';
import {
Button,
Content,
ExpandableSection,
Icon,
ModalFooter,
Tab,
Tabs,
TabTitleText,
} from '@patternfly/react-core';
import {
ExclamationCircleIcon,
ExclamationTriangleIcon,
InfoCircleIcon,
} from '@patternfly/react-icons';
import { AlertModalContent, WorkspaceActionData } from '~/shared/types';
import AlertModal from '~/shared/components/AlertModal';

// 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',
},
},
},
],
},
},
},
};

interface WorkspaceActionAlertProps {
onClose: () => void;
isOpen: boolean;
activeActionData: WorkspaceActionData;
}

export const WorkspaceActionAlert: React.FC<WorkspaceActionAlertProps> = ({
onClose,
isOpen,
activeActionData,
}) => {
const { action, workspace, isPendingUpdates } = activeActionData;

console.log(workspace);
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>
);
}
};

const ActionWithUpdatesBody = () => {
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,
}));

return (
<>
<TabTitleText>
There are pending redirect updates for that workspace, are you sure you want to proceed?
</TabTitleText>
<Tabs activeKey={activeKey} onSelect={(event, eventKey) => setActiveKey(eventKey)}>
{imageConfigRedirects.length > 0 && (
<Tab eventKey={0} title={<TabTitleText>Image Config</TabTitleText>}>
{imageConfigRedirects.map((redirect, index) => (
<Content style={{ display: 'flex', alignItems: 'baseline' }} key={index}>
{getLevelIcon(redirect.level)}
<ExpandableSection toggleText={`From ${redirect.src} to ${redirect.dest}`}>
<Content>{redirect.message}</Content>
</ExpandableSection>
</Content>
))}
</Tab>
)}
{podConfigRedirects.length > 0 && (
<Tab eventKey={1} title={<TabTitleText>Pod Config</TabTitleText>}>
{podConfigRedirects.map((redirect, index) => (
<Content style={{ display: 'flex', alignItems: 'baseline' }} key={index}>
{getLevelIcon(redirect.level)}
<ExpandableSection toggleText={`From ${redirect.src} to ${redirect.dest}`}>
<Content>{redirect.message}</Content>
</ExpandableSection>
</Content>
))}
</Tab>
)}
</Tabs>
</>
);
};

const handleClick = (isUpdate = false) => {
if (isUpdate) {
console.log('update'); // change to use the API for updating
}
switch (action) {
case 'start':
console.log('start'); // change to use the API for starting the workspace
break;
case 'restart':
console.log('restart'); // change to use the API for restarting the workspace
break;
case 'stop':
console.log('stop'); // change to use the API for stopping the workspace
break;
default:
break;
}
onClose();
};

const getActionModalContent = (): AlertModalContent => {
if (isPendingUpdates) {
switch (action) {
case 'start':
return {
header: 'Start Workspace',
body: <ActionWithUpdatesBody />,
footer: (
<ModalFooter>
<Button onClick={() => handleClick(true)}>Update and Start</Button>
<Button onClick={() => handleClick(false)} variant="secondary">
Only Start
</Button>
</ModalFooter>
),
};
case 'restart':
return {
header: 'Restart Workspace',
body: <ActionWithUpdatesBody />,
footer: (
<ModalFooter>
<Button onClick={() => handleClick(true)}>Update and Restart</Button>
<Button onClick={() => handleClick(false)} variant="secondary">
Only Restart
</Button>
</ModalFooter>
),
};
case 'stop':
return {
header: 'Stop Workspace',
body: <ActionWithUpdatesBody />,
footer: (
<ModalFooter>
<Button onClick={() => handleClick(true)}>Stop and update</Button>
<Button onClick={() => handleClick(false)} variant="secondary">
Stop and defer update
</Button>
</ModalFooter>
),
};
default:
return {
header: '',
body: undefined,
footer: undefined,
};
}
} else {
switch (action) {
case 'start':
return {
header: '',
body: undefined,
footer: undefined,
};
case 'restart':
return {
header: 'Restart Workspace',
body: <div>are you sure you want to Restart the workspace?</div>,
footer: (
<ModalFooter>
<Button onClick={() => handleClick(false)}>Restart</Button>
</ModalFooter>
),
};
case 'stop':
return {
header: 'Stop Workspace',
body: <div>are you sure you want to Stop the workspace?</div>,
footer: (
<ModalFooter>
<Button onClick={() => handleClick(false)}>Stop</Button>
</ModalFooter>
),
};
default:
return {
header: '',
body: undefined,
footer: undefined,
};
}
}
};

return <AlertModal onClose={onClose} isOpen={isOpen} content={getActionModalContent()} />;
};
Loading