diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index b7eaca800..17afca14d 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -15,6 +15,8 @@ import { AbilityContext } from './contexts/Can'; import UserService from './services/UserService'; import ErrorDisplay from './components/ErrorDisplay'; import APIErrorProvider from './contexts/APIErrorContext'; +import ScrollToTop from "./components/ScrollToTop"; +import React from "react"; export default function App() { if (!UserService.isLoggedIn()) { @@ -32,6 +34,7 @@ export default function App() { + diff --git a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx index 401169733..67e6e18dc 100644 --- a/spiffworkflow-frontend/src/components/ErrorDisplay.tsx +++ b/spiffworkflow-frontend/src/components/ErrorDisplay.tsx @@ -109,7 +109,6 @@ export default function ErrorDisplay() { if (errorObject) { const title = 'Error:'; - window.scrollTo(0, 0); // Scroll back to the top of the page errorTag = ( removeError()} type="error"> diff --git a/spiffworkflow-frontend/src/components/ScrollToTop.tsx b/spiffworkflow-frontend/src/components/ScrollToTop.tsx new file mode 100644 index 000000000..01d0b5806 --- /dev/null +++ b/spiffworkflow-frontend/src/components/ScrollToTop.tsx @@ -0,0 +1,12 @@ +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +export default function ScrollToTop() { + const { pathname } = useLocation(); + + useEffect(() => { + window.scrollTo(0, 0); + }, [pathname]); + + return null; +} \ No newline at end of file diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index a74aa829b..6f1407d5b 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -94,6 +94,9 @@ export default function TaskShow() { const params = useParams(); const navigate = useNavigate(); const [disabled, setDisabled] = useState(false); + const [noValidate, setNoValidate] = useState(false); + + const [taskData, setTaskData] = useState(null); const { addError, removeError } = useAPIError(); @@ -108,11 +111,11 @@ export default function TaskShow() { useEffect(() => { const processResult = (result: Task) => { setTask(result); + setTaskData(result.data); setDisabled(false); if (!result.can_complete) { navigateToInterstitial(result); } - window.scrollTo(0, 0); // Scroll back to the top of the page /* Disable call to load previous tasks -- do not display menu. const url = `/v1.0/process-instances/for-me/${modifyProcessIdentifierForPathParam( @@ -160,22 +163,30 @@ export default function TaskShow() { } }; - const handleFormSubmit = (formObject: any, event: any) => { + const handleFormSubmit = (formObject: any, _event: any) => { if (disabled) { return; } + const dataToSubmit = formObject?.formData; if (!dataToSubmit) { navigate(`/tasks`); return; } let queryParams = ''; - if (event && event.submitter.id === 'close-button') { + + // if validations are turned off then save as draft + if (noValidate) { queryParams = '?save_as_draft=true'; } setDisabled(true); removeError(); delete dataToSubmit.isManualTask; + + // NOTE: rjsf sets blanks values to undefined and JSON.stringify removes keys with undefined values + // so there is no way to clear out a field that previously had a value. + // To resolve this, we could potentially go through the object that we are posting (either in here or in + // HttpService) and translate all undefined values to null. HttpService.makeCallToBackend({ path: `/tasks/${params.process_instance_id}/${params.task_id}${queryParams}`, successCallback: processSubmitResult, @@ -290,17 +301,27 @@ export default function TaskShow() { return errors; }; + // This turns off validations and then dispatches the click event after + // waiting a second to give the state time to update. + // This is to allow saving the form without validations causing issues. + const handleSaveAndCloseButton = () => { + setNoValidate(true); + setTimeout(() => { + (document.getElementById('our-very-own-form') as any).dispatchEvent( + new Event('submit', { cancelable: true, bubbles: true }) + ); + }, 1000); + }; + const formElement = () => { if (!task) { return null; } let formUiSchema; - let taskData = task.data; let jsonSchema = task.form_schema; let reactFragmentToHideSubmitButton = null; if (task.typename === 'ManualTask') { - taskData = {}; jsonSchema = { type: 'object', required: [], @@ -341,7 +362,7 @@ export default function TaskShow() { closeButton = (