Skip to content

Commit db40107

Browse files
author
Elay Aharoni (EXT-Nokia)
committed
Implement Start restart and stop workspace actions
Signed-off-by: Elay Aharoni (EXT-Nokia) <[email protected]>
1 parent 055150b commit db40107

File tree

4 files changed

+436
-40
lines changed

4 files changed

+436
-40
lines changed
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
import * as React from 'react';
2+
import {
3+
Button,
4+
Content,
5+
ExpandableSection,
6+
Icon,
7+
ModalFooter,
8+
Tab,
9+
Tabs,
10+
TabTitleText,
11+
} from '@patternfly/react-core';
12+
import {
13+
ExclamationCircleIcon,
14+
ExclamationTriangleIcon,
15+
InfoCircleIcon,
16+
} from '@patternfly/react-icons';
17+
import { AlertModalContent, WorkspaceActionData } from '~/shared/types';
18+
import AlertModal from '~/shared/components/AlertModal';
19+
20+
// remove when changing to fetch data from BE
21+
const mockedWorkspaceKind = {
22+
name: 'jupyter-lab',
23+
displayName: 'JupyterLab Notebook',
24+
description: 'A Workspace which runs JupyterLab in a Pod',
25+
deprecated: false,
26+
deprecationMessage: '',
27+
hidden: false,
28+
icon: {
29+
url: 'https://jupyter.org/assets/favicons/apple-touch-icon-152x152.png',
30+
},
31+
logo: {
32+
url: 'https://upload.wikimedia.org/wikipedia/commons/3/38/Jupyter_logo.svg',
33+
},
34+
podTemplate: {
35+
podMetadata: {
36+
labels: { myWorkspaceKindLabel: 'my-value' },
37+
annotations: { myWorkspaceKindAnnotation: 'my-value' },
38+
},
39+
volumeMounts: { home: '/home/jovyan' },
40+
options: {
41+
imageConfig: {
42+
default: 'jupyterlab_scipy_190',
43+
values: [
44+
{
45+
id: 'jupyterlab_scipy_180',
46+
displayName: 'jupyter-scipy:v1.8.0',
47+
labels: { pythonVersion: '3.11' },
48+
hidden: true,
49+
redirect: {
50+
to: 'jupyterlab_scipy_190',
51+
message: {
52+
text: 'This update will change...',
53+
level: 'Info',
54+
},
55+
},
56+
},
57+
{
58+
id: 'jupyterlab_scipy_190',
59+
displayName: 'jupyter-scipy:v1.9.0',
60+
labels: { pythonVersion: '3.11' },
61+
hidden: true,
62+
redirect: {
63+
to: 'jupyterlab_scipy_200',
64+
message: {
65+
text: 'This update will change...',
66+
level: 'Warning',
67+
},
68+
},
69+
},
70+
],
71+
},
72+
podConfig: {
73+
default: 'tiny_cpu',
74+
values: [
75+
{
76+
id: 'tiny_cpu',
77+
displayName: 'Tiny CPU',
78+
description: 'Pod with 0.1 CPU, 128 Mb RAM',
79+
labels: { cpu: '100m', memory: '128Mi' },
80+
redirect: {
81+
to: 'small_cpu',
82+
message: {
83+
text: 'This update will change...',
84+
level: 'Danger',
85+
},
86+
},
87+
},
88+
],
89+
},
90+
},
91+
},
92+
};
93+
94+
interface WorkspaceActionAlertProps {
95+
onClose: () => void;
96+
isOpen: boolean;
97+
activeActionData: WorkspaceActionData;
98+
}
99+
100+
export const WorkspaceActionAlert: React.FC<WorkspaceActionAlertProps> = ({
101+
onClose,
102+
isOpen,
103+
activeActionData,
104+
}) => {
105+
const { action, workspace, isPendingUpdates } = activeActionData;
106+
107+
console.log(workspace);
108+
const getLevelIcon = (level: string) => {
109+
switch (level) {
110+
case 'Info':
111+
return (
112+
<Icon status="info">
113+
<InfoCircleIcon />
114+
</Icon>
115+
);
116+
case 'Warning':
117+
return (
118+
<Icon status="warning">
119+
<ExclamationTriangleIcon />
120+
</Icon>
121+
);
122+
case 'Danger':
123+
return (
124+
<Icon status="danger">
125+
<ExclamationCircleIcon />
126+
</Icon>
127+
);
128+
default:
129+
return (
130+
<Icon status="info">
131+
<InfoCircleIcon />
132+
</Icon>
133+
);
134+
}
135+
};
136+
137+
const ActionWithUpdatesBody = () => {
138+
const [activeKey, setActiveKey] = React.useState<string | number>(0);
139+
// change this to get from BE, and use the workspaceKinds API
140+
const workspaceKind = mockedWorkspaceKind;
141+
142+
const { imageConfig } = workspaceKind.podTemplate.options;
143+
const { podConfig } = workspaceKind.podTemplate.options;
144+
145+
const imageConfigRedirects = imageConfig.values.map((value) => ({
146+
src: value.id,
147+
dest: value.redirect.to,
148+
message: value.redirect.message.text,
149+
level: value.redirect.message.level,
150+
}));
151+
const podConfigRedirects = podConfig.values.map((value) => ({
152+
src: value.id,
153+
dest: value.redirect.to,
154+
message: value.redirect.message.text,
155+
level: value.redirect.message.level,
156+
}));
157+
158+
return (
159+
<>
160+
<TabTitleText>
161+
There are pending redirect updates for that workspace, are you sure you want to proceed?
162+
</TabTitleText>
163+
<Tabs activeKey={activeKey} onSelect={(event, eventKey) => setActiveKey(eventKey)}>
164+
{imageConfigRedirects.length > 0 && (
165+
<Tab eventKey={0} title={<TabTitleText>Image Config</TabTitleText>}>
166+
{imageConfigRedirects.map((redirect, index) => (
167+
<Content style={{ display: 'flex', alignItems: 'baseline' }} key={index}>
168+
{getLevelIcon(redirect.level)}
169+
<ExpandableSection toggleText={`From ${redirect.src} to ${redirect.dest}`}>
170+
<Content>{redirect.message}</Content>
171+
</ExpandableSection>
172+
</Content>
173+
))}
174+
</Tab>
175+
)}
176+
{podConfigRedirects.length > 0 && (
177+
<Tab eventKey={1} title={<TabTitleText>Pod Config</TabTitleText>}>
178+
{podConfigRedirects.map((redirect, index) => (
179+
<Content style={{ display: 'flex', alignItems: 'baseline' }} key={index}>
180+
{getLevelIcon(redirect.level)}
181+
<ExpandableSection toggleText={`From ${redirect.src} to ${redirect.dest}`}>
182+
<Content>{redirect.message}</Content>
183+
</ExpandableSection>
184+
</Content>
185+
))}
186+
</Tab>
187+
)}
188+
</Tabs>
189+
</>
190+
);
191+
};
192+
193+
const handleClick = (isUpdate = false) => {
194+
if (isUpdate) {
195+
console.log('update'); // change to use the API for updating
196+
}
197+
switch (action) {
198+
case 'start':
199+
console.log('start'); // change to use the API for starting the workspace
200+
break;
201+
case 'restart':
202+
console.log('restart'); // change to use the API for restarting the workspace
203+
break;
204+
case 'stop':
205+
console.log('stop'); // change to use the API for stopping the workspace
206+
break;
207+
default:
208+
break;
209+
}
210+
onClose();
211+
};
212+
213+
const getActionModalContent = (): AlertModalContent => {
214+
if (isPendingUpdates) {
215+
switch (action) {
216+
case 'start':
217+
return {
218+
header: 'Start Workspace',
219+
body: <ActionWithUpdatesBody />,
220+
footer: (
221+
<ModalFooter>
222+
<Button onClick={() => handleClick(true)}>Update and Start</Button>
223+
<Button onClick={() => handleClick(false)} variant="secondary">
224+
Only Start
225+
</Button>
226+
</ModalFooter>
227+
),
228+
};
229+
case 'restart':
230+
return {
231+
header: 'Restart Workspace',
232+
body: <ActionWithUpdatesBody />,
233+
footer: (
234+
<ModalFooter>
235+
<Button onClick={() => handleClick(true)}>Update and Restart</Button>
236+
<Button onClick={() => handleClick(false)} variant="secondary">
237+
Only Restart
238+
</Button>
239+
</ModalFooter>
240+
),
241+
};
242+
case 'stop':
243+
return {
244+
header: 'Stop Workspace',
245+
body: <ActionWithUpdatesBody />,
246+
footer: (
247+
<ModalFooter>
248+
<Button onClick={() => handleClick(true)}>Stop and update</Button>
249+
<Button onClick={() => handleClick(false)} variant="secondary">
250+
Stop and defer update
251+
</Button>
252+
</ModalFooter>
253+
),
254+
};
255+
default:
256+
return {
257+
header: '',
258+
body: undefined,
259+
footer: undefined,
260+
};
261+
}
262+
} else {
263+
switch (action) {
264+
case 'start':
265+
return {
266+
header: '',
267+
body: undefined,
268+
footer: undefined,
269+
};
270+
case 'restart':
271+
return {
272+
header: 'Restart Workspace',
273+
body: <div>are you sure you want to Restart the workspace?</div>,
274+
footer: (
275+
<ModalFooter>
276+
<Button onClick={() => handleClick(false)}>Restart</Button>
277+
</ModalFooter>
278+
),
279+
};
280+
case 'stop':
281+
return {
282+
header: 'Stop Workspace',
283+
body: <div>are you sure you want to Stop the workspace?</div>,
284+
footer: (
285+
<ModalFooter>
286+
<Button onClick={() => handleClick(false)}>Stop</Button>
287+
</ModalFooter>
288+
),
289+
};
290+
default:
291+
return {
292+
header: '',
293+
body: undefined,
294+
footer: undefined,
295+
};
296+
}
297+
}
298+
};
299+
300+
return <AlertModal onClose={onClose} isOpen={isOpen} content={getActionModalContent()} />;
301+
};

0 commit comments

Comments
 (0)