Refactor Form View to Edit only
Signed-off-by: Charles Thao <cthao@redhat.com>
This commit is contained in:
parent
28f2471bb5
commit
bd6a9c9ac0
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Content,
|
Content,
|
||||||
|
|
@ -13,6 +13,7 @@ import {
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
import { useTypedNavigate } from '~/app/routerHelper';
|
import { useTypedNavigate } from '~/app/routerHelper';
|
||||||
import useGenericObjectState from '~/app/hooks/useGenericObjectState';
|
import useGenericObjectState from '~/app/hooks/useGenericObjectState';
|
||||||
|
import { useNotebookAPI } from '~/app/hooks/useNotebookAPI';
|
||||||
import { WorkspaceKindFormData } from '~/app/types';
|
import { WorkspaceKindFormData } from '~/app/types';
|
||||||
import { WorkspaceKindFileUpload } from './fileUpload/WorkspaceKindFileUpload';
|
import { WorkspaceKindFileUpload } from './fileUpload/WorkspaceKindFileUpload';
|
||||||
import { WorkspaceKindFormProperties } from './properties/WorkspaceKindFormProperties';
|
import { WorkspaceKindFormProperties } from './properties/WorkspaceKindFormProperties';
|
||||||
|
|
@ -27,6 +28,7 @@ export type ValidationStatus = 'success' | 'error' | 'default';
|
||||||
|
|
||||||
export const WorkspaceKindForm: React.FC = () => {
|
export const WorkspaceKindForm: React.FC = () => {
|
||||||
const navigate = useTypedNavigate();
|
const navigate = useTypedNavigate();
|
||||||
|
const { api } = useNotebookAPI();
|
||||||
// TODO: Detect mode by route
|
// TODO: Detect mode by route
|
||||||
const [mode] = useState('create');
|
const [mode] = useState('create');
|
||||||
const [yamlValue, setYamlValue] = useState('');
|
const [yamlValue, setYamlValue] = useState('');
|
||||||
|
|
@ -35,14 +37,6 @@ export const WorkspaceKindForm: React.FC = () => {
|
||||||
const [validated, setValidated] = useState<ValidationStatus>('default');
|
const [validated, setValidated] = useState<ValidationStatus>('default');
|
||||||
const workspaceKindFileUploadId = 'workspace-kind-form-fileupload-view';
|
const workspaceKindFileUploadId = 'workspace-kind-form-fileupload-view';
|
||||||
|
|
||||||
const handleViewClick = (event: React.MouseEvent<unknown> | React.KeyboardEvent | MouseEvent) => {
|
|
||||||
const { id } = event.currentTarget as HTMLElement;
|
|
||||||
setView(
|
|
||||||
id === workspaceKindFileUploadId
|
|
||||||
? WorkspaceKindFormView.FileUpload
|
|
||||||
: WorkspaceKindFormView.Form,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const [data, setData, resetData] = useGenericObjectState<WorkspaceKindFormData>({
|
const [data, setData, resetData] = useGenericObjectState<WorkspaceKindFormData>({
|
||||||
properties: {
|
properties: {
|
||||||
displayName: '',
|
displayName: '',
|
||||||
|
|
@ -59,16 +53,41 @@ export const WorkspaceKindForm: React.FC = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCreate = useCallback(() => {
|
const handleViewClick = useCallback(
|
||||||
// TODO: Complete handleCreate with API call to create a new WS kind
|
(event: React.MouseEvent<unknown> | React.KeyboardEvent | MouseEvent) => {
|
||||||
if (!Object.keys(data).length) {
|
const { id } = event.currentTarget as HTMLElement;
|
||||||
return;
|
setView(
|
||||||
}
|
id === workspaceKindFileUploadId
|
||||||
|
? WorkspaceKindFormView.FileUpload
|
||||||
|
: WorkspaceKindFormView.Form,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(async () => {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
}, [data]);
|
// TODO: Complete handleCreate with API call to create a new WS kind
|
||||||
|
try {
|
||||||
|
if (mode === 'create') {
|
||||||
|
const newWorkspaceKind = await api.createWorkspaceKind({}, yamlValue);
|
||||||
|
console.info('New workspace kind created:', JSON.stringify(newWorkspaceKind));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error ${mode === 'edit' ? 'editing' : 'creating'} workspace kind: ${err}`);
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
navigate('workspaceKinds');
|
||||||
|
}, [navigate, mode, api, yamlValue]);
|
||||||
|
|
||||||
|
const canSubmit = useMemo(
|
||||||
|
() => !isSubmitting && yamlValue.length > 0 && validated === 'success',
|
||||||
|
[yamlValue, isSubmitting, validated],
|
||||||
|
);
|
||||||
|
|
||||||
const cancel = useCallback(() => {
|
const cancel = useCallback(() => {
|
||||||
navigate('workspaceKindCreate');
|
navigate('workspaceKinds');
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -83,29 +102,30 @@ export const WorkspaceKindForm: React.FC = () => {
|
||||||
</Content>
|
</Content>
|
||||||
<Content component={ContentVariants.p}>
|
<Content component={ContentVariants.p}>
|
||||||
{view === WorkspaceKindFormView.FileUpload
|
{view === WorkspaceKindFormView.FileUpload
|
||||||
? `Please upload a Workspace Kind YAML file. Select 'Form View' to view
|
? `Please upload or drag and drop a Workspace Kind YAML file.`
|
||||||
and edit the workspace kind's information`
|
|
||||||
: `View and edit the Workspace Kind's information. Some fields may not be
|
: `View and edit the Workspace Kind's information. Some fields may not be
|
||||||
represented in this form`}
|
represented in this form`}
|
||||||
</Content>
|
</Content>
|
||||||
</FlexItem>
|
</FlexItem>
|
||||||
<FlexItem>
|
{mode === 'edit' && (
|
||||||
<ToggleGroup className="workspace-kind-form-header" aria-label="Toggle form view">
|
<FlexItem>
|
||||||
<ToggleGroupItem
|
<ToggleGroup className="workspace-kind-form-header" aria-label="Toggle form view">
|
||||||
text="YAML Upload"
|
<ToggleGroupItem
|
||||||
buttonId={workspaceKindFileUploadId}
|
text="YAML Upload"
|
||||||
isSelected={view === WorkspaceKindFormView.FileUpload}
|
buttonId={workspaceKindFileUploadId}
|
||||||
onChange={handleViewClick}
|
isSelected={view === WorkspaceKindFormView.FileUpload}
|
||||||
/>
|
onChange={handleViewClick}
|
||||||
<ToggleGroupItem
|
/>
|
||||||
text="Form View"
|
<ToggleGroupItem
|
||||||
buttonId="workspace-kind-form-form-view"
|
text="Form View"
|
||||||
isSelected={view === WorkspaceKindFormView.Form}
|
buttonId="workspace-kind-form-form-view"
|
||||||
onChange={handleViewClick}
|
isSelected={view === WorkspaceKindFormView.Form}
|
||||||
isDisabled={yamlValue === '' || validated === 'error'}
|
onChange={handleViewClick}
|
||||||
/>
|
isDisabled={yamlValue === '' || validated === 'error'}
|
||||||
</ToggleGroup>
|
/>
|
||||||
</FlexItem>
|
</ToggleGroup>
|
||||||
|
</FlexItem>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
|
|
@ -144,8 +164,8 @@ export const WorkspaceKindForm: React.FC = () => {
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
ouiaId="Primary"
|
ouiaId="Primary"
|
||||||
onClick={handleCreate}
|
onClick={handleSubmit}
|
||||||
isDisabled={!isSubmitting}
|
isDisabled={!canSubmit}
|
||||||
>
|
>
|
||||||
{mode === 'create' ? 'Create' : 'Edit'}
|
{mode === 'create' ? 'Create' : 'Edit'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,48 @@ export const restDELETE = <T>(
|
||||||
parseJSON: options?.parseJSON,
|
parseJSON: options?.parseJSON,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** POST -- but with YAML content directly in body */
|
||||||
|
export const restYAML = <T>(
|
||||||
|
host: string,
|
||||||
|
path: string,
|
||||||
|
yamlContent: string,
|
||||||
|
queryParams?: Record<string, unknown>,
|
||||||
|
options?: APIOptions,
|
||||||
|
): Promise<T> => {
|
||||||
|
const { method, ...otherOptions } = mergeRequestInit(options, { method: 'POST' });
|
||||||
|
|
||||||
|
const sanitizedQueryParams = queryParams
|
||||||
|
? Object.entries(queryParams).reduce((acc, [key, value]) => {
|
||||||
|
if (value) {
|
||||||
|
return { ...acc, [key]: value };
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const searchParams = sanitizedQueryParams
|
||||||
|
? new URLSearchParams(sanitizedQueryParams).toString()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return fetch(`${host}${path}${searchParams ? `?${searchParams}` : ''}`, {
|
||||||
|
...otherOptions,
|
||||||
|
headers: {
|
||||||
|
...otherOptions.headers,
|
||||||
|
...(DEV_MODE && { [AUTH_HEADER]: localStorage.getItem(AUTH_HEADER) }),
|
||||||
|
'Content-Type': 'application/vnd.kubeflow-notebooks.manifest+yaml',
|
||||||
|
},
|
||||||
|
method,
|
||||||
|
body: yamlContent,
|
||||||
|
}).then((response) =>
|
||||||
|
response.text().then((fetchedData) => {
|
||||||
|
if (options?.parseJSON !== false) {
|
||||||
|
return JSON.parse(fetchedData);
|
||||||
|
}
|
||||||
|
return fetchedData;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const isNotebookResponse = <T>(response: unknown): response is ResponseBody<T> => {
|
export const isNotebookResponse = <T>(response: unknown): response is ResponseBody<T> => {
|
||||||
if (typeof response === 'object' && response !== null) {
|
if (typeof response === 'object' && response !== null) {
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ export interface WorkspaceKindPodTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
export interface WorkspaceKindCreate {}
|
export type WorkspaceKindCreate = string;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
export interface WorkspaceKindUpdate {}
|
export interface WorkspaceKindUpdate {}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import {
|
||||||
Workspace,
|
Workspace,
|
||||||
WorkspaceCreate,
|
WorkspaceCreate,
|
||||||
WorkspaceKind,
|
WorkspaceKind,
|
||||||
WorkspaceKindCreate,
|
|
||||||
WorkspaceKindPatch,
|
WorkspaceKindPatch,
|
||||||
WorkspaceKindUpdate,
|
WorkspaceKindUpdate,
|
||||||
WorkspacePatch,
|
WorkspacePatch,
|
||||||
|
|
@ -63,10 +62,7 @@ export type StartWorkspace = (
|
||||||
// WorkspaceKind
|
// WorkspaceKind
|
||||||
export type ListWorkspaceKinds = (opts: APIOptions) => Promise<WorkspaceKind[]>;
|
export type ListWorkspaceKinds = (opts: APIOptions) => Promise<WorkspaceKind[]>;
|
||||||
export type GetWorkspaceKind = (opts: APIOptions, kind: string) => Promise<WorkspaceKind>;
|
export type GetWorkspaceKind = (opts: APIOptions, kind: string) => Promise<WorkspaceKind>;
|
||||||
export type CreateWorkspaceKind = (
|
export type CreateWorkspaceKind = (opts: APIOptions, data: string) => Promise<WorkspaceKind>;
|
||||||
opts: APIOptions,
|
|
||||||
data: RequestData<WorkspaceKindCreate>,
|
|
||||||
) => Promise<WorkspaceKind>;
|
|
||||||
export type UpdateWorkspaceKind = (
|
export type UpdateWorkspaceKind = (
|
||||||
opts: APIOptions,
|
opts: APIOptions,
|
||||||
kind: string,
|
kind: string,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {
|
||||||
restGET,
|
restGET,
|
||||||
restPATCH,
|
restPATCH,
|
||||||
restUPDATE,
|
restUPDATE,
|
||||||
|
restYAML,
|
||||||
} from '~/shared/api/apiUtils';
|
} from '~/shared/api/apiUtils';
|
||||||
import { handleRestFailures } from '~/shared/api/errorUtils';
|
import { handleRestFailures } from '~/shared/api/errorUtils';
|
||||||
import {
|
import {
|
||||||
|
|
@ -96,7 +97,7 @@ export const getWorkspaceKind: GetWorkspaceKindAPI = (hostPath) => (opts, kind)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const createWorkspaceKind: CreateWorkspaceKindAPI = (hostPath) => (opts, data) =>
|
export const createWorkspaceKind: CreateWorkspaceKindAPI = (hostPath) => (opts, data) =>
|
||||||
handleRestFailures(restCREATE(hostPath, `/workspacekinds`, data, {}, opts)).then((response) =>
|
handleRestFailures(restYAML(hostPath, `/workspacekinds`, data, {}, opts)).then((response) =>
|
||||||
extractNotebookResponse<WorkspaceKind>(response),
|
extractNotebookResponse<WorkspaceKind>(response),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue