From 2bacbd3cbed3a1b11026c63462dfbc95743201c2 Mon Sep 17 00:00:00 2001 From: jasquat Date: Sat, 19 Nov 2022 17:18:46 -0500 Subject: [PATCH 01/17] pass the the correct model to id on create --- spiffworkflow-frontend/src/components/ProcessModelForm.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ProcessModelForm.tsx b/spiffworkflow-frontend/src/components/ProcessModelForm.tsx index 532e39279..9725f2422 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelForm.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelForm.tsx @@ -24,7 +24,6 @@ export default function ProcessModelForm({ useState(false); const [displayNameInvalid, setDisplayNameInvalid] = useState(false); const navigate = useNavigate(); - const modifiedProcessModelPath = modifyProcessModelPath(processModel.id); const navigateToProcessModel = (result: ProcessModel) => { if ('id' in result) { @@ -53,7 +52,7 @@ export default function ProcessModelForm({ if (hasErrors) { return; } - const path = `/process-models/${modifiedProcessModelPath}`; + const path = `/process-models/${processGroupId}`; let httpMethod = 'POST'; if (mode === 'edit') { httpMethod = 'PUT'; @@ -64,7 +63,7 @@ export default function ProcessModelForm({ }; if (mode === 'new') { Object.assign(postBody, { - id: `${processGroupId}:${processModel.id}`, + id: `${processGroupId}/${processModel.id}`, }); } From 90a9f23339c971f22f8eccbbce1de74fd302772c Mon Sep 17 00:00:00 2001 From: jasquat Date: Sat, 19 Nov 2022 19:44:21 -0500 Subject: [PATCH 02/17] added basic form builder. w/ burnettk --- spiffworkflow-frontend/src/interfaces.ts | 6 + .../src/routes/AdminRoutes.tsx | 5 + .../src/routes/JsonSchemaFormBuilder.tsx | 199 ++++++++++++++++++ .../src/routes/ReactFormEditor.tsx | 13 ++ 4 files changed, 223 insertions(+) create mode 100644 spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index aceca0494..3f86d9997 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -102,3 +102,9 @@ export interface PermissionCheckResult { export interface PermissionCheckResponseBody { results: PermissionCheckResult; } + +export interface FormField { + id: string; + title: string; + required: boolean; +} diff --git a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx index 56496823a..91ae7ab08 100644 --- a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx +++ b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx @@ -21,6 +21,7 @@ import ErrorContext from '../contexts/ErrorContext'; import ProcessInstanceLogList from './ProcessInstanceLogList'; import MessageInstanceList from './MessageInstanceList'; import Configuration from './Configuration'; +import JsonSchemaFormBuilder from './JsonSchemaFormBuilder'; export default function AdminRoutes() { const location = useLocation(); @@ -108,6 +109,10 @@ export default function AdminRoutes() { } /> } /> } /> + } + /> ); } diff --git a/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx b/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx new file mode 100644 index 000000000..7f0cb6745 --- /dev/null +++ b/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx @@ -0,0 +1,199 @@ +import { useEffect, useState } from 'react'; +// @ts-ignore +import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; +import { useParams } from 'react-router-dom'; +import { FormField } from '../interfaces'; +import { modifyProcessModelPath, slugifyString } from '../helpers'; +import HttpService from '../services/HttpService'; + +export default function JsonSchemaFormBuilder() { + const params = useParams(); + const [formTitle, setFormTitle] = useState(''); + const [formDescription, setFormDescription] = useState(''); + const [formId, setFormId] = useState(''); + const [formFields, setFormFields] = useState([]); + const [showNewFormField, setShowNewFormField] = useState(false); + const [formIdHasBeenUpdatedByUser, setFormIdHasBeenUpdatedByUser] = + useState(false); + const [formFieldIdHasBeenUpdatedByUser, setFormFieldIdHasBeenUpdatedByUser] = + useState(false); + + const [formFieldId, setFormFieldId] = useState(''); + const [formFieldTitle, setFormFieldTitle] = useState(''); + + const modifiedProcessModelId = modifyProcessModelPath( + `${params.process_model_id}` + ); + + useEffect(() => {}, []); + + const renderFormJson = () => { + const formJson = { + title: formTitle, + description: formDescription, + properties: {}, + required: [], + }; + formFields.forEach((formField: FormField) => { + (formJson.properties as any)[formField.id] = { + type: 'string', + title: formField.title, + }; + }); + + 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) => { + console.log('newFormFieldTitle', newFormFieldTitle); + console.log( + 'setFormFieldIdHasBeenUpdatedByUser', + formFieldIdHasBeenUpdatedByUser + ); + if (!formFieldIdHasBeenUpdatedByUser) { + setFormFieldId(slugifyString(newFormFieldTitle)); + } + setFormFieldTitle(newFormFieldTitle); + }; + + const onFormTitleChange = (newFormTitle: string) => { + if (!formIdHasBeenUpdatedByUser) { + setFormId(slugifyString(newFormTitle)); + } + setFormTitle(newFormTitle); + }; + + const createNewFormField = () => { + const newFormField: FormField = { + id: formFieldId, + title: formFieldTitle, + required: false, + }; + + setFormFieldIdHasBeenUpdatedByUser(false); + setShowNewFormField(false); + setFormFields([...formFields, newFormField]); + }; + + const newFormFieldComponent = () => { + if (showNewFormField) { + return ( + <> + { + onFormFieldTitleChange(event.srcElement.value); + }} + /> + { + setFormFieldIdHasBeenUpdatedByUser(true); + setFormFieldId(event.srcElement.value); + }} + /> + + + ); + } + return null; + }; + + const formFieldArea = () => { + if (formFields.length > 0) { + return formFields.map((formField: FormField) => { + return

Form Field: {formField.id}

; + }); + } + return null; + }; + + const handleSaveCallback = (result: any) => { + console.log('result', result); + }; + + 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 = () => { + const formJsonFileName = `${formId}-schema.json`; + const formUiJsonFileName = `${formId}-uischema.json`; + + uploadFile(new File([renderFormJson()], formJsonFileName)); + uploadFile(new File([renderFormUiJson()], formUiJsonFileName)); + }; + + const jsonFormArea = () => { + return ( + <> + + { + onFormTitleChange(event.srcElement.value); + }} + /> + { + setFormIdHasBeenUpdatedByUser(true); + setFormId(event.srcElement.value); + }} + /> + { + setFormDescription(event.srcElement.value); + }} + /> + + {formFieldArea()} + {newFormFieldComponent()} + + ); + }; + + return <>{jsonFormArea()}; +} diff --git a/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx b/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx index 8abf56fc3..92d725695 100644 --- a/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx +++ b/spiffworkflow-frontend/src/routes/ReactFormEditor.tsx @@ -175,6 +175,19 @@ export default function ReactFormEditor() { + {params.file_name ? null : ( + + )} {params.file_name ? ( Date: Sat, 19 Nov 2022 20:55:27 -0500 Subject: [PATCH 03/17] added some more widget types to the form builder. w/ burnettk --- spiffworkflow-frontend/src/interfaces.ts | 2 + .../src/routes/JsonSchemaFormBuilder.tsx | 61 +++++++++++++++++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts index 3f86d9997..f4540f4fe 100644 --- a/spiffworkflow-frontend/src/interfaces.ts +++ b/spiffworkflow-frontend/src/interfaces.ts @@ -107,4 +107,6 @@ export interface FormField { id: string; title: string; required: boolean; + type: string; + enum: string[]; } diff --git a/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx b/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx index 7f0cb6745..c97e959a8 100644 --- a/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx +++ b/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; // @ts-ignore -import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; +import { Button, Select, SelectItem, TextInput } from '@carbon/react'; import { useParams } from 'react-router-dom'; import { FormField } from '../interfaces'; import { modifyProcessModelPath, slugifyString } from '../helpers'; @@ -8,18 +8,25 @@ import HttpService from '../services/HttpService'; export default function JsonSchemaFormBuilder() { const params = useParams(); + const formFieldTypes = ['textbox', 'checkbox', 'select']; + const [formTitle, setFormTitle] = useState(''); const [formDescription, setFormDescription] = useState(''); const [formId, setFormId] = useState(''); const [formFields, setFormFields] = useState([]); const [showNewFormField, setShowNewFormField] = useState(false); + const [formFieldSelectOptions, setFormFieldSelectOptions] = + useState(''); const [formIdHasBeenUpdatedByUser, setFormIdHasBeenUpdatedByUser] = useState(false); const [formFieldIdHasBeenUpdatedByUser, setFormFieldIdHasBeenUpdatedByUser] = useState(false); + const [showFormFieldSelectTextField, setShowFormFieldSelectTextField] = + useState(false); const [formFieldId, setFormFieldId] = useState(''); const [formFieldTitle, setFormFieldTitle] = useState(''); + const [formFieldType, setFormFieldType] = useState(''); const modifiedProcessModelId = modifyProcessModelPath( `${params.process_model_id}` @@ -34,11 +41,21 @@ export default function JsonSchemaFormBuilder() { properties: {}, required: [], }; + formFields.forEach((formField: FormField) => { - (formJson.properties as any)[formField.id] = { - type: 'string', + let jsonSchemaFieldType = 'string'; + if (formField.type === 'checkbox') { + jsonSchemaFieldType = 'boolean'; + } + const formJsonObject: any = { + type: jsonSchemaFieldType, title: formField.title, }; + + if (formField.type === 'select') { + formJsonObject.enum = formField.enum; + } + (formJson.properties as any)[formField.id] = formJsonObject; }); return JSON.stringify(formJson, null, 2); @@ -70,11 +87,13 @@ export default function JsonSchemaFormBuilder() { setFormTitle(newFormTitle); }; - const createNewFormField = () => { + const addFormField = () => { const newFormField: FormField = { id: formFieldId, title: formFieldTitle, required: false, + type: formFieldType, + enum: formFieldSelectOptions.split(','), }; setFormFieldIdHasBeenUpdatedByUser(false); @@ -82,6 +101,16 @@ export default function JsonSchemaFormBuilder() { 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 ( @@ -105,7 +134,26 @@ export default function JsonSchemaFormBuilder() { setFormFieldId(event.srcElement.value); }} /> - + + {showFormFieldSelectTextField ? ( + { + setFormFieldSelectOptions(event.srcElement.value); + }} + /> + ) : null} + ); } @@ -184,6 +232,9 @@ export default function JsonSchemaFormBuilder() { onClick={() => { setFormFieldId(''); setFormFieldTitle(''); + setFormFieldType(''); + setFormFieldSelectOptions(''); + setShowFormFieldSelectTextField(false); setShowNewFormField(true); }} > From 07614abbc11f59536c7067618abcc44f2734f1f9 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Sun, 20 Nov 2022 14:53:27 -0500 Subject: [PATCH 04/17] Frontend label changes (#54) --- spiffworkflow-frontend/src/components/NavigationBar.tsx | 2 +- .../src/routes/ProcessInstanceReportList.tsx | 6 +++--- .../src/routes/ProcessInstanceReportNew.tsx | 2 +- .../src/routes/ProcessInstanceReportShow.tsx | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spiffworkflow-frontend/src/components/NavigationBar.tsx b/spiffworkflow-frontend/src/components/NavigationBar.tsx index 0cc49158a..cc7137fb8 100644 --- a/spiffworkflow-frontend/src/components/NavigationBar.tsx +++ b/spiffworkflow-frontend/src/components/NavigationBar.tsx @@ -164,7 +164,7 @@ export default function NavigationBar() { href="/admin/process-instances/reports" isCurrentPage={isActivePage('/admin/process-instances/reports')} > - Reports + Perspectives ); diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx index 2b72a9851..298008d11 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx @@ -44,9 +44,9 @@ export default function ProcessInstanceReportList() { const headerStuff = ( <> -

Process Instance Reports

+

Process Instance Perspectives

); @@ -61,7 +61,7 @@ export default function ProcessInstanceReportList() { return (
{headerStuff} -

No reports found

+

No perspectives found

); } diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceReportNew.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceReportNew.tsx index 124be4daf..eb6d2a67f 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceReportNew.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceReportNew.tsx @@ -56,7 +56,7 @@ export default function ProcessInstanceReportNew() { return ( <> -

Add Process Model

+

Add Process Instance Perspective