remove old form builder and references to it w/ burnettk (#985)
Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
parent
cf03d6e8a8
commit
9f188c1204
|
@ -367,23 +367,6 @@ export interface PermissionCheckResponseBody {
|
||||||
results: PermissionCheckResult;
|
results: PermissionCheckResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormField {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
required?: boolean;
|
|
||||||
type: string;
|
|
||||||
enum?: string[];
|
|
||||||
default?: any;
|
|
||||||
pattern?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface JsonSchemaForm {
|
|
||||||
file_contents: string;
|
|
||||||
name: string;
|
|
||||||
process_model_id: string;
|
|
||||||
required: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProcessInstanceEventErrorDetail {
|
export interface ProcessInstanceEventErrorDetail {
|
||||||
id: number;
|
id: number;
|
||||||
message: string;
|
message: string;
|
||||||
|
|
|
@ -1,445 +0,0 @@
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
// @ts-ignore
|
|
||||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Select,
|
|
||||||
SelectItem,
|
|
||||||
TextInput,
|
|
||||||
TextArea,
|
|
||||||
Grid,
|
|
||||||
Column,
|
|
||||||
// @ts-ignore
|
|
||||||
} from '@carbon/react';
|
|
||||||
import validator from '@rjsf/validator-ajv8';
|
|
||||||
import { FormField, JsonSchemaForm } from '../interfaces';
|
|
||||||
import { Form } from '../rjsf/carbon_theme';
|
|
||||||
import {
|
|
||||||
modifyProcessIdentifierForPathParam,
|
|
||||||
slugifyString,
|
|
||||||
underscorizeString,
|
|
||||||
} from '../helpers';
|
|
||||||
import HttpService from '../services/HttpService';
|
|
||||||
import { Notification } from '../components/Notification';
|
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
|
||||||
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
|
|
||||||
|
|
||||||
export default function JsonSchemaFormBuilder() {
|
|
||||||
const params = useParams();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [searchParams] = useSearchParams();
|
|
||||||
const formFieldTypes = ['textbox', 'checkbox', 'select'];
|
|
||||||
|
|
||||||
const [formTitle, setFormTitle] = useState<string>('');
|
|
||||||
const [formDescription, setFormDescription] = useState<string>('');
|
|
||||||
const [formId, setFormId] = useState<string>('');
|
|
||||||
const [formFields, setFormFields] = useState<FormField[]>([]);
|
|
||||||
const [showNewFormField, setShowNewFormField] = useState<boolean>(false);
|
|
||||||
const [formFieldSelectOptions, setFormFieldSelectOptions] =
|
|
||||||
useState<string>('');
|
|
||||||
const [formIdHasBeenUpdatedByUser, setFormIdHasBeenUpdatedByUser] =
|
|
||||||
useState<boolean>(false);
|
|
||||||
const [formFieldIdHasBeenUpdatedByUser, setFormFieldIdHasBeenUpdatedByUser] =
|
|
||||||
useState<boolean>(false);
|
|
||||||
const [showFormFieldSelectTextField, setShowFormFieldSelectTextField] =
|
|
||||||
useState<boolean>(false);
|
|
||||||
|
|
||||||
const [formFieldId, setFormFieldId] = useState<string>('');
|
|
||||||
const [formFieldTitle, setFormFieldTitle] = useState<string>('');
|
|
||||||
const [formFieldType, setFormFieldType] = useState<string>('');
|
|
||||||
const [requiredFields, setRequiredFields] = useState<string[]>([]);
|
|
||||||
const [savedJsonSchema, setSavedJsonSchema] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const modifiedProcessModelId = modifyProcessIdentifierForPathParam(
|
|
||||||
`${params.process_model_id}`
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const processResult = (result: JsonSchemaForm) => {
|
|
||||||
const jsonForm = JSON.parse(result.file_contents);
|
|
||||||
setFormTitle(jsonForm.title);
|
|
||||||
setFormDescription(jsonForm.description);
|
|
||||||
setRequiredFields(jsonForm.required);
|
|
||||||
const newFormId = (searchParams.get('file_name') || '').replace(
|
|
||||||
'-schema.json',
|
|
||||||
''
|
|
||||||
);
|
|
||||||
setFormId(newFormId);
|
|
||||||
const newFormFields: FormField[] = [];
|
|
||||||
Object.keys(jsonForm.properties).forEach((propertyId: string) => {
|
|
||||||
const propertyDetails = jsonForm.properties[propertyId];
|
|
||||||
newFormFields.push({
|
|
||||||
id: propertyId,
|
|
||||||
title: propertyDetails.title,
|
|
||||||
required: propertyDetails.required,
|
|
||||||
type: propertyDetails.type,
|
|
||||||
enum: propertyDetails.enum,
|
|
||||||
default: propertyDetails.default,
|
|
||||||
pattern: propertyDetails.pattern,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setFormFields(newFormFields);
|
|
||||||
};
|
|
||||||
if (searchParams.get('file_name')) {
|
|
||||||
HttpService.makeCallToBackend({
|
|
||||||
path: `/process-models/${modifiedProcessModelId}/files/${searchParams.get(
|
|
||||||
'file_name'
|
|
||||||
)}`,
|
|
||||||
successCallback: processResult,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [modifiedProcessModelId, searchParams]);
|
|
||||||
|
|
||||||
const formSubmitResultElement = () => {
|
|
||||||
if (savedJsonSchema) {
|
|
||||||
return (
|
|
||||||
<Notification
|
|
||||||
title="Form Saved"
|
|
||||||
onClose={() => setSavedJsonSchema(false)}
|
|
||||||
>
|
|
||||||
It saved
|
|
||||||
</Notification>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderFormJson = () => {
|
|
||||||
const formJson = {
|
|
||||||
title: formTitle,
|
|
||||||
description: formDescription,
|
|
||||||
properties: {},
|
|
||||||
required: requiredFields,
|
|
||||||
};
|
|
||||||
|
|
||||||
formFields.forEach((formField: FormField) => {
|
|
||||||
let jsonSchemaFieldType = formField.type;
|
|
||||||
if (['checkbox'].includes(formField.type)) {
|
|
||||||
jsonSchemaFieldType = 'boolean';
|
|
||||||
}
|
|
||||||
const formJsonObject: any = {
|
|
||||||
type: jsonSchemaFieldType || 'string',
|
|
||||||
title: formField.title,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (formField.enum) {
|
|
||||||
formJsonObject.enum = formField.enum;
|
|
||||||
}
|
|
||||||
if (formField.default !== undefined) {
|
|
||||||
formJsonObject.default = formField.default;
|
|
||||||
}
|
|
||||||
if (formField.pattern) {
|
|
||||||
formJsonObject.pattern = formField.pattern;
|
|
||||||
}
|
|
||||||
(formJson.properties as any)[formField.id] = formJsonObject;
|
|
||||||
});
|
|
||||||
|
|
||||||
return JSON.stringify(formJson, null, 2);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderFormUiJson = () => {
|
|
||||||
const uiOrder = formFields.map((formField: FormField) => {
|
|
||||||
return formField.id;
|
|
||||||
});
|
|
||||||
return JSON.stringify({ 'ui:order': uiOrder }, null, 2);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFormFieldTitleChange = (newFormFieldTitle: string) => {
|
|
||||||
if (!formFieldIdHasBeenUpdatedByUser) {
|
|
||||||
setFormFieldId(underscorizeString(newFormFieldTitle));
|
|
||||||
}
|
|
||||||
setFormFieldTitle(newFormFieldTitle);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFormTitleChange = (newFormTitle: string) => {
|
|
||||||
if (!formIdHasBeenUpdatedByUser) {
|
|
||||||
setFormId(slugifyString(newFormTitle));
|
|
||||||
}
|
|
||||||
setFormTitle(newFormTitle);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFormFieldType = (indicatedType: string) => {
|
|
||||||
if (indicatedType === 'checkbox') {
|
|
||||||
return 'boolean';
|
|
||||||
}
|
|
||||||
// undefined or 'select' or 'textbox'
|
|
||||||
return 'string';
|
|
||||||
};
|
|
||||||
|
|
||||||
const addFormField = () => {
|
|
||||||
const newFormField: FormField = {
|
|
||||||
id: formFieldId,
|
|
||||||
title: formFieldTitle,
|
|
||||||
required: false,
|
|
||||||
type: getFormFieldType(formFieldType),
|
|
||||||
enum: showFormFieldSelectTextField
|
|
||||||
? formFieldSelectOptions.split(',')
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
setFormFieldIdHasBeenUpdatedByUser(false);
|
|
||||||
setShowNewFormField(false);
|
|
||||||
setFormFields([...formFields, newFormField]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFormFieldTypeChange = (event: any) => {
|
|
||||||
setFormFieldType(event.srcElement.value);
|
|
||||||
|
|
||||||
if (event.srcElement.value === 'select') {
|
|
||||||
setShowFormFieldSelectTextField(true);
|
|
||||||
} else {
|
|
||||||
setShowFormFieldSelectTextField(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const newFormFieldComponent = () => {
|
|
||||||
if (showNewFormField) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<TextInput
|
|
||||||
id="form-field-title"
|
|
||||||
name="title"
|
|
||||||
labelText="Title"
|
|
||||||
value={formFieldTitle}
|
|
||||||
onChange={(event: any) => {
|
|
||||||
onFormFieldTitleChange(event.srcElement.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
id="json-form-field-id"
|
|
||||||
name="id"
|
|
||||||
labelText="ID"
|
|
||||||
value={formFieldId}
|
|
||||||
onChange={(event: any) => {
|
|
||||||
setFormFieldIdHasBeenUpdatedByUser(true);
|
|
||||||
setFormFieldId(event.srcElement.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Select
|
|
||||||
id="form-field-type"
|
|
||||||
labelText="Type"
|
|
||||||
onChange={handleFormFieldTypeChange}
|
|
||||||
>
|
|
||||||
{formFieldTypes.map((fft: string) => {
|
|
||||||
return <SelectItem text={fft} value={fft} />;
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
{showFormFieldSelectTextField ? (
|
|
||||||
<TextInput
|
|
||||||
id="json-form-field-select-options"
|
|
||||||
name="select-options"
|
|
||||||
labelText="Select Options"
|
|
||||||
onChange={(event: any) => {
|
|
||||||
setFormFieldSelectOptions(event.srcElement.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<Button onClick={addFormField}>Add Field</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const formFieldArea = () => {
|
|
||||||
if (formFields.length > 0) {
|
|
||||||
return formFields.map((formField: FormField) => {
|
|
||||||
return <p>Form Field: {formField.id}</p>;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSaveCallback = () => {
|
|
||||||
setSavedJsonSchema(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const uploadFile = (file: File) => {
|
|
||||||
const url = `/process-models/${modifiedProcessModelId}/files`;
|
|
||||||
const httpMethod = 'POST';
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', file);
|
|
||||||
formData.append('fileName', file.name);
|
|
||||||
|
|
||||||
HttpService.makeCallToBackend({
|
|
||||||
path: url,
|
|
||||||
successCallback: handleSaveCallback,
|
|
||||||
httpMethod,
|
|
||||||
postBody: formData,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveFile = () => {
|
|
||||||
setSavedJsonSchema(false);
|
|
||||||
let formJsonFileName = `${formId}-schema.json`;
|
|
||||||
let formUiJsonFileName: string | null = `${formId}-uischema.json`;
|
|
||||||
if (searchParams.get('file_name')) {
|
|
||||||
formJsonFileName = searchParams.get('file_name') as any;
|
|
||||||
if (formJsonFileName.match(/-schema\.json$/)) {
|
|
||||||
formUiJsonFileName = (searchParams.get('file_name') as any).replace(
|
|
||||||
'-schema.json',
|
|
||||||
'-uischema.json'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
formUiJsonFileName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadFile(new File([renderFormJson()], formJsonFileName));
|
|
||||||
if (formUiJsonFileName) {
|
|
||||||
uploadFile(new File([renderFormUiJson()], formUiJsonFileName));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteFile = () => {
|
|
||||||
const url = `/process-models/${modifiedProcessModelId}/files/${params.file_name}`;
|
|
||||||
const httpMethod = 'DELETE';
|
|
||||||
|
|
||||||
const navigateToProcessModelShow = (_httpResult: any) => {
|
|
||||||
navigate(`/process-models/${modifiedProcessModelId}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
HttpService.makeCallToBackend({
|
|
||||||
path: url,
|
|
||||||
successCallback: navigateToProcessModelShow,
|
|
||||||
httpMethod,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const formIdTextField = () => {
|
|
||||||
if (searchParams.get('file_name')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TextInput
|
|
||||||
id="json-form-id"
|
|
||||||
name="id"
|
|
||||||
labelText="ID"
|
|
||||||
value={formId}
|
|
||||||
onChange={(event: any) => {
|
|
||||||
setFormIdHasBeenUpdatedByUser(true);
|
|
||||||
setFormId(event.srcElement.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const jsonFormButton = () => {
|
|
||||||
if (!searchParams.get('file_name')) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ButtonWithConfirmation
|
|
||||||
data-qa="delete-process-model-file"
|
|
||||||
description={`Delete file ${searchParams.get('file_name')}?`}
|
|
||||||
onConfirmation={deleteFile}
|
|
||||||
buttonLabel="Delete"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
onClick={() =>
|
|
||||||
navigate(
|
|
||||||
`/process-models/${
|
|
||||||
params.process_model_id
|
|
||||||
}/form/${searchParams.get('file_name')}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
variant="danger"
|
|
||||||
data-qa="form-builder-button"
|
|
||||||
>
|
|
||||||
View Json
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const processModelFileName = searchParams.get('file_name') || '';
|
|
||||||
|
|
||||||
const topOfPageElements = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ProcessBreadcrumb
|
|
||||||
hotCrumbs={[
|
|
||||||
['Process Groups', '/process-groups'],
|
|
||||||
{
|
|
||||||
entityToExplode: params.process_model_id || '',
|
|
||||||
entityType: 'process-model-id',
|
|
||||||
linkLastItem: true,
|
|
||||||
},
|
|
||||||
[processModelFileName],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<h1>
|
|
||||||
Process Model File{processModelFileName ? ': ' : ''}
|
|
||||||
{processModelFileName}
|
|
||||||
</h1>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const jsonFormArea = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{formSubmitResultElement()}
|
|
||||||
<Button onClick={saveFile}>Save</Button>
|
|
||||||
{jsonFormButton()}
|
|
||||||
<TextInput
|
|
||||||
id="json-form-title"
|
|
||||||
name="title"
|
|
||||||
labelText="Title"
|
|
||||||
value={formTitle}
|
|
||||||
onChange={(event: any) => {
|
|
||||||
onFormTitleChange(event.srcElement.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{formIdTextField()}
|
|
||||||
<TextArea
|
|
||||||
id="form-description"
|
|
||||||
name="description"
|
|
||||||
labelText="Description"
|
|
||||||
value={formDescription}
|
|
||||||
onChange={(event: any) => {
|
|
||||||
setFormDescription(event.srcElement.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setFormFieldId('');
|
|
||||||
setFormFieldTitle('');
|
|
||||||
setFormFieldType('');
|
|
||||||
setFormFieldSelectOptions('');
|
|
||||||
setShowFormFieldSelectTextField(false);
|
|
||||||
setShowNewFormField(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
New Field
|
|
||||||
</Button>
|
|
||||||
{formFieldArea()}
|
|
||||||
{newFormFieldComponent()}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const schemaString = renderFormJson();
|
|
||||||
const uiSchemaString = renderFormUiJson();
|
|
||||||
const schema = JSON.parse(schemaString);
|
|
||||||
const uiSchema = JSON.parse(uiSchemaString);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{topOfPageElements()}
|
|
||||||
<Grid fullWidth>
|
|
||||||
<Column md={5} lg={8} sm={4}>
|
|
||||||
{jsonFormArea()}
|
|
||||||
</Column>
|
|
||||||
<Column md={5} lg={8} sm={4}>
|
|
||||||
<h2>Form Preview</h2>
|
|
||||||
<Form
|
|
||||||
formData={{}}
|
|
||||||
schema={schema}
|
|
||||||
uiSchema={uiSchema}
|
|
||||||
validator={validator}
|
|
||||||
/>
|
|
||||||
</Column>
|
|
||||||
</Grid>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Route, Routes } from 'react-router-dom';
|
import { Route, Routes } from 'react-router-dom';
|
||||||
import JsonSchemaFormBuilder from './JsonSchemaFormBuilder';
|
|
||||||
import ProcessModelEdit from './ProcessModelEdit';
|
import ProcessModelEdit from './ProcessModelEdit';
|
||||||
import ProcessModelNew from './ProcessModelNew';
|
import ProcessModelNew from './ProcessModelNew';
|
||||||
import ProcessModelNewExperimental from './ProcessModelNewExperimental';
|
import ProcessModelNewExperimental from './ProcessModelNewExperimental';
|
||||||
|
@ -21,10 +20,6 @@ export default function ProcessModelRoutes() {
|
||||||
path=":process_model_id/form/:file_name"
|
path=":process_model_id/form/:file_name"
|
||||||
element={<ReactFormEditor />}
|
element={<ReactFormEditor />}
|
||||||
/>
|
/>
|
||||||
<Route
|
|
||||||
path=":process_model_id/form-builder"
|
|
||||||
element={<JsonSchemaFormBuilder />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,6 @@ export default function ReactFormEditor() {
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const hasDiagram = fileExtension === 'bpmn' || fileExtension === 'dmn';
|
const hasDiagram = fileExtension === 'bpmn' || fileExtension === 'dmn';
|
||||||
const hasFormBuilder = fileExtension === 'json';
|
|
||||||
const defaultFileName = searchParams.get('default_file_name');
|
const defaultFileName = searchParams.get('default_file_name');
|
||||||
|
|
||||||
const editorDefaultLanguage = (() => {
|
const editorDefaultLanguage = (() => {
|
||||||
|
@ -248,9 +247,6 @@ export default function ReactFormEditor() {
|
||||||
|
|
||||||
if (processModelFile || !params.file_name) {
|
if (processModelFile || !params.file_name) {
|
||||||
const processModelFileName = processModelFile ? processModelFile.name : '';
|
const processModelFileName = processModelFile ? processModelFile.name : '';
|
||||||
const formBuildFileParam = params.file_name
|
|
||||||
? `?file_name=${params.file_name}`
|
|
||||||
: '';
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<ProcessBreadcrumb
|
<ProcessBreadcrumb
|
||||||
|
@ -299,25 +295,6 @@ export default function ReactFormEditor() {
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Can>
|
</Can>
|
||||||
<Can
|
|
||||||
I="PUT"
|
|
||||||
a={targetUris.processModelFileShowPath}
|
|
||||||
ability={ability}
|
|
||||||
>
|
|
||||||
{hasFormBuilder ? (
|
|
||||||
<Button
|
|
||||||
onClick={() =>
|
|
||||||
navigate(
|
|
||||||
`/process-models/${params.process_model_id}/form-builder${formBuildFileParam}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
variant="danger"
|
|
||||||
data-qa="form-builder-button"
|
|
||||||
>
|
|
||||||
Form Builder
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</Can>
|
|
||||||
<Can
|
<Can
|
||||||
I="GET"
|
I="GET"
|
||||||
a={targetUris.processModelFileShowPath}
|
a={targetUris.processModelFileShowPath}
|
||||||
|
|
Loading…
Reference in New Issue