diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py index 7c8973aa4..9baffd258 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/tasks_controller.py @@ -428,8 +428,9 @@ def _task_submit_shared( if save_as_draft: task_model = _get_task_model_from_guid_or_raise(task_guid, process_instance_id) + ProcessInstanceService.update_form_task_data(processor, spiff_task, body, g.user) json_data_dict = TaskService.update_task_data_on_task_model_and_return_dict_if_updated( - task_model, body, "json_data_hash" + task_model, spiff_task.data, "json_data_hash" ) if json_data_dict is not None: TaskService.insert_or_update_json_data_dict(json_data_dict) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py index 39f6de15c..b8754352b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py @@ -325,6 +325,21 @@ class ProcessInstanceService: cls.replace_file_data_with_digest_references(data, models) + @staticmethod + def update_form_task_data( + processor: ProcessInstanceProcessor, + spiff_task: SpiffTask, + data: dict[str, Any], + user: UserModel, + ) -> None: + AuthorizationService.assert_user_can_complete_spiff_task(processor.process_instance_model.id, spiff_task, user) + ProcessInstanceService.save_file_data_and_replace_with_digest_references( + data, + processor.process_instance_model.id, + ) + dot_dct = ProcessInstanceService.create_dot_dict(data) + spiff_task.update_data(dot_dct) + @staticmethod def complete_form_task( processor: ProcessInstanceProcessor, @@ -338,15 +353,7 @@ class ProcessInstanceService: Abstracted here because we need to do it multiple times when completing all tasks in a multi-instance task. """ - AuthorizationService.assert_user_can_complete_spiff_task(processor.process_instance_model.id, spiff_task, user) - - ProcessInstanceService.save_file_data_and_replace_with_digest_references( - data, - processor.process_instance_model.id, - ) - - dot_dct = ProcessInstanceService.create_dot_dict(data) - spiff_task.update_data(dot_dct) + ProcessInstanceService.update_form_task_data(processor, spiff_task, data, user) # ProcessInstanceService.post_process_form(spiff_task) # some properties may update the data store. processor.complete_task(spiff_task, human_task, user=user) diff --git a/spiffworkflow-frontend/.eslintrc.js b/spiffworkflow-frontend/.eslintrc.js index b6829ff48..5ed209007 100644 --- a/spiffworkflow-frontend/.eslintrc.js +++ b/spiffworkflow-frontend/.eslintrc.js @@ -25,6 +25,10 @@ module.exports = { }, plugins: ['react', 'sonarjs', '@typescript-eslint'], rules: { + // according to https://github.com/typescript-eslint/typescript-eslint/issues/2621, You should turn off the eslint core rule and turn on the typescript-eslint rule + // but not sure which of the above "extends" statements is maybe bringing in eslint core + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': ['error'], 'jest/expect-expect': 'off', 'react/jsx-no-bind': 'off', 'jsx-a11y/no-autofocus': 'off', @@ -37,7 +41,8 @@ module.exports = { 'react/react-in-jsx-scope': 'off', 'react/require-default-props': 'off', 'import/prefer-default-export': 'off', - 'no-unused-vars': [ + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ 'error', { destructuredArrayIgnorePattern: '^_', diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index 5362cf323..d343d1a1f 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -29,6 +29,11 @@ class UnexpectedHumanTaskType extends Error { } } +enum FormSubmitType { + Default, + Draft, +} + export default function TaskShow() { const [task, setTask] = useState(null); const [userTasks] = useState(null); @@ -36,6 +41,9 @@ export default function TaskShow() { const navigate = useNavigate(); const [disabled, setDisabled] = useState(false); + // save current form data so that we can avoid validations in certain situations + const [currentFormObject, setCurrentFormObject] = useState({}); + const { addError, removeError } = useAPIError(); // eslint-disable-next-line sonarjs/no-duplicate-string @@ -87,14 +95,16 @@ export default function TaskShow() { } }; - const handleFormSubmit = (formObject: any, event: any) => { + const handleFormSubmit = ( + formObject: any, + _event: any, + submitType: FormSubmitType = FormSubmitType.Default + ) => { if (disabled) { return; } - const submitButtonId = event.nativeEvent.submitter.id; let queryParams = ''; - console.log('submitButtonId', submitButtonId); - if (submitButtonId === 'save-as-draft-button') { + if (submitType === FormSubmitType.Draft) { queryParams = '?save_as_draft=true'; } setDisabled(true); @@ -200,6 +210,11 @@ export default function TaskShow() { return errors; }; + const updateFormData = (formObject: any) => { + currentFormObject.formData = formObject.formData; + setCurrentFormObject(currentFormObject); + }; + const formElement = () => { if (!task) { return null; @@ -250,10 +265,12 @@ export default function TaskShow() { } else if (task.type === 'User Task') { saveAsDraftButton = ( @@ -287,7 +304,10 @@ export default function TaskShow() { schema={jsonSchema} uiSchema={formUiSchema} validator={validator} + onChange={updateFormData} customValidate={customValidate} + omitExtraData + liveOmit > {reactFragmentToHideSubmitButton}