Skip to content

Commit ca8e94c

Browse files
authored
feat: refactor Form View to Edit only (#451)
Signed-off-by: Charles Thao <[email protected]>
1 parent 28f2471 commit ca8e94c

File tree

5 files changed

+102
-43
lines changed

5 files changed

+102
-43
lines changed

workspaces/frontend/src/app/pages/WorkspaceKinds/Form/WorkspaceKindForm.tsx

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useState } from 'react';
1+
import React, { useCallback, useMemo, useState } from 'react';
22
import {
33
Button,
44
Content,
@@ -13,6 +13,7 @@ import {
1313
} from '@patternfly/react-core';
1414
import { useTypedNavigate } from '~/app/routerHelper';
1515
import useGenericObjectState from '~/app/hooks/useGenericObjectState';
16+
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
1617
import { WorkspaceKindFormData } from '~/app/types';
1718
import { WorkspaceKindFileUpload } from './fileUpload/WorkspaceKindFileUpload';
1819
import { WorkspaceKindFormProperties } from './properties/WorkspaceKindFormProperties';
@@ -27,6 +28,7 @@ export type ValidationStatus = 'success' | 'error' | 'default';
2728

2829
export const WorkspaceKindForm: React.FC = () => {
2930
const navigate = useTypedNavigate();
31+
const { api } = useNotebookAPI();
3032
// TODO: Detect mode by route
3133
const [mode] = useState('create');
3234
const [yamlValue, setYamlValue] = useState('');
@@ -35,14 +37,6 @@ export const WorkspaceKindForm: React.FC = () => {
3537
const [validated, setValidated] = useState<ValidationStatus>('default');
3638
const workspaceKindFileUploadId = 'workspace-kind-form-fileupload-view';
3739

38-
const handleViewClick = (event: React.MouseEvent<unknown> | React.KeyboardEvent | MouseEvent) => {
39-
const { id } = event.currentTarget as HTMLElement;
40-
setView(
41-
id === workspaceKindFileUploadId
42-
? WorkspaceKindFormView.FileUpload
43-
: WorkspaceKindFormView.Form,
44-
);
45-
};
4640
const [data, setData, resetData] = useGenericObjectState<WorkspaceKindFormData>({
4741
properties: {
4842
displayName: '',
@@ -59,16 +53,41 @@ export const WorkspaceKindForm: React.FC = () => {
5953
},
6054
});
6155

62-
const handleCreate = useCallback(() => {
56+
const handleViewClick = useCallback(
57+
(event: React.MouseEvent<unknown> | React.KeyboardEvent | MouseEvent) => {
58+
const { id } = event.currentTarget as HTMLElement;
59+
setView(
60+
id === workspaceKindFileUploadId
61+
? WorkspaceKindFormView.FileUpload
62+
: WorkspaceKindFormView.Form,
63+
);
64+
},
65+
[],
66+
);
67+
68+
const handleSubmit = useCallback(async () => {
69+
setIsSubmitting(true);
6370
// TODO: Complete handleCreate with API call to create a new WS kind
64-
if (!Object.keys(data).length) {
65-
return;
71+
try {
72+
if (mode === 'create') {
73+
const newWorkspaceKind = await api.createWorkspaceKind({}, yamlValue);
74+
console.info('New workspace kind created:', JSON.stringify(newWorkspaceKind));
75+
}
76+
} catch (err) {
77+
console.error(`Error ${mode === 'edit' ? 'editing' : 'creating'} workspace kind: ${err}`);
78+
} finally {
79+
setIsSubmitting(false);
6680
}
67-
setIsSubmitting(true);
68-
}, [data]);
81+
navigate('workspaceKinds');
82+
}, [navigate, mode, api, yamlValue]);
83+
84+
const canSubmit = useMemo(
85+
() => !isSubmitting && yamlValue.length > 0 && validated === 'success',
86+
[yamlValue, isSubmitting, validated],
87+
);
6988

7089
const cancel = useCallback(() => {
71-
navigate('workspaceKindCreate');
90+
navigate('workspaceKinds');
7291
}, [navigate]);
7392

7493
return (
@@ -83,29 +102,30 @@ export const WorkspaceKindForm: React.FC = () => {
83102
</Content>
84103
<Content component={ContentVariants.p}>
85104
{view === WorkspaceKindFormView.FileUpload
86-
? `Please upload a Workspace Kind YAML file. Select 'Form View' to view
87-
and edit the workspace kind's information`
105+
? `Please upload or drag and drop a Workspace Kind YAML file.`
88106
: `View and edit the Workspace Kind's information. Some fields may not be
89107
represented in this form`}
90108
</Content>
91109
</FlexItem>
92-
<FlexItem>
93-
<ToggleGroup className="workspace-kind-form-header" aria-label="Toggle form view">
94-
<ToggleGroupItem
95-
text="YAML Upload"
96-
buttonId={workspaceKindFileUploadId}
97-
isSelected={view === WorkspaceKindFormView.FileUpload}
98-
onChange={handleViewClick}
99-
/>
100-
<ToggleGroupItem
101-
text="Form View"
102-
buttonId="workspace-kind-form-form-view"
103-
isSelected={view === WorkspaceKindFormView.Form}
104-
onChange={handleViewClick}
105-
isDisabled={yamlValue === '' || validated === 'error'}
106-
/>
107-
</ToggleGroup>
108-
</FlexItem>
110+
{mode === 'edit' && (
111+
<FlexItem>
112+
<ToggleGroup className="workspace-kind-form-header" aria-label="Toggle form view">
113+
<ToggleGroupItem
114+
text="YAML Upload"
115+
buttonId={workspaceKindFileUploadId}
116+
isSelected={view === WorkspaceKindFormView.FileUpload}
117+
onChange={handleViewClick}
118+
/>
119+
<ToggleGroupItem
120+
text="Form View"
121+
buttonId="workspace-kind-form-form-view"
122+
isSelected={view === WorkspaceKindFormView.Form}
123+
onChange={handleViewClick}
124+
isDisabled={yamlValue === '' || validated === 'error'}
125+
/>
126+
</ToggleGroup>
127+
</FlexItem>
128+
)}
109129
</Flex>
110130
</Stack>
111131
</PageSection>
@@ -144,8 +164,8 @@ export const WorkspaceKindForm: React.FC = () => {
144164
<Button
145165
variant="primary"
146166
ouiaId="Primary"
147-
onClick={handleCreate}
148-
isDisabled={!isSubmitting}
167+
onClick={handleSubmit}
168+
isDisabled={!canSubmit}
149169
>
150170
{mode === 'create' ? 'Create' : 'Edit'}
151171
</Button>

workspaces/frontend/src/shared/api/apiUtils.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,48 @@ export const restDELETE = <T>(
171171
parseJSON: options?.parseJSON,
172172
});
173173

174+
/** POST -- but with YAML content directly in body */
175+
export const restYAML = <T>(
176+
host: string,
177+
path: string,
178+
yamlContent: string,
179+
queryParams?: Record<string, unknown>,
180+
options?: APIOptions,
181+
): Promise<T> => {
182+
const { method, ...otherOptions } = mergeRequestInit(options, { method: 'POST' });
183+
184+
const sanitizedQueryParams = queryParams
185+
? Object.entries(queryParams).reduce((acc, [key, value]) => {
186+
if (value) {
187+
return { ...acc, [key]: value };
188+
}
189+
return acc;
190+
}, {})
191+
: null;
192+
193+
const searchParams = sanitizedQueryParams
194+
? new URLSearchParams(sanitizedQueryParams).toString()
195+
: null;
196+
197+
return fetch(`${host}${path}${searchParams ? `?${searchParams}` : ''}`, {
198+
...otherOptions,
199+
headers: {
200+
...otherOptions.headers,
201+
...(DEV_MODE && { [AUTH_HEADER]: localStorage.getItem(AUTH_HEADER) }),
202+
'Content-Type': 'application/vnd.kubeflow-notebooks.manifest+yaml',
203+
},
204+
method,
205+
body: yamlContent,
206+
}).then((response) =>
207+
response.text().then((fetchedData) => {
208+
if (options?.parseJSON !== false) {
209+
return JSON.parse(fetchedData);
210+
}
211+
return fetchedData;
212+
}),
213+
);
214+
};
215+
174216
export const isNotebookResponse = <T>(response: unknown): response is ResponseBody<T> => {
175217
if (typeof response === 'object' && response !== null) {
176218
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions

workspaces/frontend/src/shared/api/backendApiTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export interface WorkspaceKindPodTemplate {
9090
}
9191

9292
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
93-
export interface WorkspaceKindCreate {}
93+
export type WorkspaceKindCreate = string;
9494

9595
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
9696
export interface WorkspaceKindUpdate {}

workspaces/frontend/src/shared/api/notebookApi.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
Workspace,
55
WorkspaceCreate,
66
WorkspaceKind,
7-
WorkspaceKindCreate,
87
WorkspaceKindPatch,
98
WorkspaceKindUpdate,
109
WorkspacePatch,
@@ -63,10 +62,7 @@ export type StartWorkspace = (
6362
// WorkspaceKind
6463
export type ListWorkspaceKinds = (opts: APIOptions) => Promise<WorkspaceKind[]>;
6564
export type GetWorkspaceKind = (opts: APIOptions, kind: string) => Promise<WorkspaceKind>;
66-
export type CreateWorkspaceKind = (
67-
opts: APIOptions,
68-
data: RequestData<WorkspaceKindCreate>,
69-
) => Promise<WorkspaceKind>;
65+
export type CreateWorkspaceKind = (opts: APIOptions, data: string) => Promise<WorkspaceKind>;
7066
export type UpdateWorkspaceKind = (
7167
opts: APIOptions,
7268
kind: string,

workspaces/frontend/src/shared/api/notebookService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
restGET,
66
restPATCH,
77
restUPDATE,
8+
restYAML,
89
} from '~/shared/api/apiUtils';
910
import { handleRestFailures } from '~/shared/api/errorUtils';
1011
import {
@@ -96,7 +97,7 @@ export const getWorkspaceKind: GetWorkspaceKindAPI = (hostPath) => (opts, kind)
9697
);
9798

9899
export const createWorkspaceKind: CreateWorkspaceKindAPI = (hostPath) => (opts, data) =>
99-
handleRestFailures(restCREATE(hostPath, `/workspacekinds`, data, {}, opts)).then((response) =>
100+
handleRestFailures(restYAML(hostPath, `/workspacekinds`, data, {}, opts)).then((response) =>
100101
extractNotebookResponse<WorkspaceKind>(response),
101102
);
102103

0 commit comments

Comments
 (0)