diff --git a/backend/applications/src/app/controllers/form.controller.ts b/backend/applications/src/app/controllers/form.controller.ts index f0838149..c4f374a6 100644 --- a/backend/applications/src/app/controllers/form.controller.ts +++ b/backend/applications/src/app/controllers/form.controller.ts @@ -24,6 +24,15 @@ export class FormController { submissionId, formId, ); + + if(!savedSubmission) + { + return Promise.reject({ + statusCode: 404, + message: 'Form data not found' + }) + } + const submissionResponse: SubmissionResponse = this.transformResult(savedSubmission); return submissionResponse; diff --git a/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/Application/ViewApplication.js b/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/Application/ViewApplication.js index 31ac51f3..0b76be0a 100644 --- a/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/Application/ViewApplication.js +++ b/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/Application/ViewApplication.js @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { useDispatch, useSelector } from "react-redux"; import startCase from "lodash/startCase"; @@ -28,6 +28,7 @@ import { setBundleSubmissionData } from "../../actions/bundleActions"; import BundleView from "../Bundle/item/submission/View"; import BundleHistory from "./BundleHistory"; import { TYPE_BUNDLE } from "../../constants/applicationConstants"; +import Nodata from "../Nodata"; const ViewApplication = React.memo(() => { const { t } = useTranslation(); @@ -49,6 +50,7 @@ const ViewApplication = React.memo(() => { const tenantKey = useSelector((state) => state.tenants?.tenantId); const dispatch = useDispatch(); const redirectUrl = MULTITENANCY_ENABLED ? `/tenant/${tenantKey}/` : "/"; + const [customSubmissionAPIFailed,setCustomSubmissionAPIFailed] = useState(false); useEffect(() => { dispatch(setApplicationDetailLoader(true)); @@ -62,9 +64,17 @@ const ViewApplication = React.memo(() => { getCustomSubmission( res.submissionId, res.formId, - (err, data) => { - if (res.formType === TYPE_BUNDLE) { - dispatch(setBundleSubmissionData({ data: data.data })); + (err, data) => { + if(err) + { + setCustomSubmissionAPIFailed(true); + dispatch(setBundleSubmissionData({ data: null })); + } + else + { + if (res.formType === TYPE_BUNDLE) { + dispatch(setBundleSubmissionData({ data: data.data })); + } } } ) @@ -149,15 +159,19 @@ const ViewApplication = React.memo(() => { } > - {applicationDetail.formType === TYPE_BUNDLE && - currentForm.isBundle ? ( - - ) : ( - - )} + { + + customSubmissionAPIFailed ? () : + applicationDetail.formType === TYPE_BUNDLE && + currentForm.isBundle ? ( + + ) : ( + + ) + } { + const { bundleId, submissionId } = useParams(); + const dispatch = useDispatch(); + // const showViewSubmissions= useSelector((state) => state.user.showViewSubmissions); + //const path = props.location.pathname; + const applicationId = useSelector( + (state) => state.bundle?.bundleSubmission?.data?.applicationId || null + ); + const userRoles = useSelector((state) => { + return selectRoot("user", state).roles; + }); + const applicationStatus = useSelector( + (state) => state.applications.applicationDetail?.applicationStatus || "" + ); + const [showSubmissionLoading, setShowSubmissionLoading] = useState(true); + const [editAllowed, setEditAllowed] = useState(false); + const [loading, setLoading] = useState(true); + const [customSubmissionAPIFailed,setCustomSubmissionAPIFailed] = useState(false); + + useEffect(() => { + dispatch(clearSubmissionError("submission")); + dispatch(resetSubmission("submission")); + dispatch(clearFormError("form")); + dispatch(resetBundleData()); + setLoading(true); + + if (CUSTOM_SUBMISSION_URL && CUSTOM_SUBMISSION_ENABLE) { + console.log("invoking here 1 "); + dispatch( + getCustomSubmission(submissionId, bundleId, (err, res) => { + console.log('here i am '); + if(err) + { + if(window.location.href.indexOf("undefined") != -1) + { + console.log("error occurred",err,res); + } + else + { + setCustomSubmissionAPIFailed(true); + //dispatch(setApiCallError({message: 'Unable to fetch form data. Please contact support.'})); + console.log("error occurred dp ",err,res); + } + } + dispatch(setBundleSubmissionData({ data: res.data })); + setLoading(false); + }) + ); + } else { + formioGetSubmission(bundleId, submissionId) + .then((res) => { + dispatch(setBundleSubmissionData({ data: res.data.data })); + setLoading(false); + }) + .catch(() => { + setLoading(false); + }); + } + }, [submissionId, bundleId, dispatch]); + + useEffect(() => { + if (applicationId) { + dispatch(setApplicationDetailLoader(true)); + dispatch(getApplicationById(applicationId)); + } + }, [applicationId, dispatch]); + + useEffect(() => { + if (getUserRolePermission(userRoles, STAFF_REVIEWER)) { + setEditAllowed(true); + } else if (applicationStatus) { + if (getUserRolePermission(userRoles, CLIENT)) { + setEditAllowed(CLIENT_EDIT_STATUS.includes(applicationStatus)); + setShowSubmissionLoading(false); + } + } + }, [applicationStatus, userRoles]); + + useEffect(() => { + if (editAllowed && applicationStatus) setShowSubmissionLoading(false); + }, [applicationStatus, editAllowed]); + + if (customSubmissionAPIFailed) + { + return ( + + ); + } + else + { + return ( +
+ + + + {showSubmissionLoading ? ( + + ) : null} + {editAllowed ? ( + + ) : null} + + +
+ ); + } +}); + +export default Item; diff --git a/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/Form/Item/Submission/Item/index.js b/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/Form/Item/Submission/Item/index.js new file mode 100644 index 00000000..12d1ef4d --- /dev/null +++ b/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/Form/Item/Submission/Item/index.js @@ -0,0 +1,155 @@ +import { Redirect, Route, Switch, useParams } from "react-router-dom"; +import React, { useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { getSubmission, selectRoot } from "react-formio"; +import View from "./View"; +import Edit from "./Edit"; +import { getApplicationById } from "../../../../../apiManager/services/applicationServices"; +import { setApplicationDetailLoader } from "../../../../../actions/applicationActions"; +import { getUserRolePermission } from "../../../../../helper/user"; +import { + BASE_ROUTE, + CLIENT, + CUSTOM_SUBMISSION_URL, + CUSTOM_SUBMISSION_ENABLE, + STAFF_REVIEWER, +} from "../../../../../constants/constants"; +import { CLIENT_EDIT_STATUS } from "../../../../../constants/applicationConstants"; +import Loading from "../../../../../containers/Loading"; +import { clearSubmissionError} from "../../../../../actions/formActions"; +import { getCustomSubmission } from "../../../../../apiManager/services/FormServices"; +import { useTranslation } from "react-i18next"; +import NotFound from "../../../../NotFound"; +import { setApiCallError } from "../../../../../actions/ErroHandling"; + +const Item = React.memo(() => { + const { formId, submissionId } = useParams(); + const dispatch = useDispatch(); + const { t } = useTranslation(); + + const applicationId = useSelector( + (state) => + state[CUSTOM_SUBMISSION_ENABLE ? "customSubmission" : "submission"]?.submission?.data?.applicationId || + null + ); + const userRoles = useSelector((state) => { + return selectRoot("user", state).roles; + }); + const applicationStatus = useSelector( + (state) => state.applications.applicationDetail?.applicationStatus || "" + ); + const [showSubmissionLoading, setShowSubmissionLoading] = useState(true); + const [editAllowed, setEditAllowed] = useState(false); + const submissionError = useSelector((state) => state.submission?.error); + + // const redirectUrl = MULTITENANCY_ENABLED ? `/tenant/${tenantKey}/` : `/` + + + + useEffect(() => { + dispatch(clearSubmissionError("submission")); + if(CUSTOM_SUBMISSION_URL && CUSTOM_SUBMISSION_ENABLE) { + + dispatch(getCustomSubmission(submissionId,formId, (err, data) => { + if(err) + { + if(window.location.href.indexOf("undefined") != -1) + { + console.log("error occurred",err,data); + } + else + { + dispatch(setApiCallError({message: 'Unable to fetch form data. Please contact support.'})); + console.log("error occurred",err,data); + } + } + })); + } else { + dispatch(getSubmission("submission", submissionId, formId)); + } + }, [submissionId, formId, dispatch]); + + + useEffect(() => { + if (applicationId) { + dispatch(setApplicationDetailLoader(true)); + dispatch(getApplicationById(applicationId)); + } + }, [applicationId, dispatch]); + + useEffect(() => { + if (getUserRolePermission(userRoles, STAFF_REVIEWER)) { + setEditAllowed(true); + } else if (applicationStatus) { + if (getUserRolePermission(userRoles, CLIENT)) { + setEditAllowed(CLIENT_EDIT_STATUS.includes(applicationStatus)); + setShowSubmissionLoading(false); + } + } + }, [applicationStatus, userRoles]); + + useEffect(() => { + if (editAllowed && applicationStatus) setShowSubmissionLoading(false); + }, [applicationStatus, editAllowed]); + + return ( +
+
    + {/* {showViewSubmissions && getUserRolePermission(userRoles, STAFF_REVIEWER) ? +
  • + + + +
  • :null} */} + {/*{(path.indexOf("edit") > 0) ? +
  • + + back View + +
  • + : + editAllowed ? (
  • + + back Edit + +
  • ) : null + }*/} +
+ + {!submissionError ? ( + + ) : } + + {showSubmissionLoading ? ( + + ) : null} + {editAllowed ? ( + + ) : null} + + +
+ ); +}); + +export default Item; diff --git a/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/ServiceFlow/details/ServiceTaskDetails.js b/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/ServiceFlow/details/ServiceTaskDetails.js new file mode 100644 index 00000000..fffca10e --- /dev/null +++ b/forms-flow-ai/forms-flow-ai-ee/forms-flow-web/src/components/ServiceFlow/details/ServiceTaskDetails.js @@ -0,0 +1,378 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { Row, Tab, Tabs } from "react-bootstrap"; +import TaskHeader from "./TaskHeader"; +import { + reloadTaskFormSubmission, + setBPMTaskDetailLoader, + setSelectedTaskID, +} from "../../../actions/bpmTaskActions"; +import { + fetchServiceTaskList, + getBPMGroups, + getBPMTaskDetail, + onBPMTaskFormSubmit, +} from "../../../apiManager/services/bpmTaskServices"; +import { useDispatch, useSelector } from "react-redux"; +import Loading from "../../../containers/Loading"; +import ProcessDiagram from "../../BPMN/ProcessDiagramHook"; +import { + getFormIdSubmissionIdFromURL, + getFormUrlWithFormIdSubmissionId, + getProcessDataObjectFromList, +} from "../../../apiManager/services/formatterService"; +import History from "../../Application/ApplicationHistory"; +import FormEdit from "../../Form/Item/Submission/Item/Edit"; +import BundleEdit from "../../Bundle/item/submission/Edit"; +import BundleVIew from "../../Bundle/item/submission/View"; +import FormView from "../../Form/Item/Submission/Item/View"; +import LoadingOverlay from "react-loading-overlay"; +import { getForm, getSubmission, Formio, resetSubmission } from "react-formio"; +import { CUSTOM_EVENT_TYPE } from "../constants/customEventTypes"; +import { getTaskSubmitFormReq } from "../../../apiManager/services/bpmServices"; +import { useParams } from "react-router-dom"; +import { push } from "connected-react-router"; +import { + resetFormData, + setFormSubmissionLoading, +} from "../../../actions/formActions"; +import { useTranslation } from "react-i18next"; +import { + CUSTOM_SUBMISSION_URL, + CUSTOM_SUBMISSION_ENABLE, + MULTITENANCY_ENABLED, +} from "../../../constants/constants"; +import { getCustomSubmission } from "../../../apiManager/services/FormServices"; +import { getFormioRoleIds } from "../../../apiManager/services/userservices"; +import { setBundleSubmissionData } from "../../../actions/bundleActions"; +import BundleHistory from "../../Application/BundleHistory"; +import { TYPE_BUNDLE } from "../../../constants/applicationConstants"; + +import { bpmActionError } from "../../../actions/bpmTaskActions"; +import { setCustomSubmission } from "../../../actions/checkListActions"; +import Nodata from "../../Nodata"; + +const ServiceFlowTaskDetails = React.memo(() => { + const { t } = useTranslation(); + const { taskId } = useParams(); + const bpmTaskId = useSelector((state) => state.bpmTasks.taskId); + const task = useSelector((state) => state.bpmTasks.taskDetail); + const processList = useSelector((state) => state.bpmTasks.processList); + const currentForm = useSelector((state) => state.form?.form || {} ); + const isTaskLoading = useSelector( + (state) => state.bpmTasks.isTaskDetailLoading + ); + const isTaskUpdating = useSelector( + (state) => state.bpmTasks.isTaskDetailUpdating + ); + const reqData = useSelector((state) => state.bpmTasks.listReqParams); + const taskFormSubmissionReload = useSelector( + (state) => state.bpmTasks.taskFormSubmissionReload + ); + const dispatch = useDispatch(); + const currentUser = useSelector( + (state) => state.user?.userDetail?.preferred_username || "" + ); + const selectedFilter = useSelector((state) => state.bpmTasks.selectedFilter); + const firstResult = useSelector((state) => state.bpmTasks.firstResult); + const [processKey, setProcessKey] = useState(""); + const [processTenant, setProcessTenant] = useState(null); + const [processInstanceId, setProcessInstanceId] = useState(""); + const tenantKey = useSelector((state) => state.tenants?.tenantId); + const redirectUrl = MULTITENANCY_ENABLED ? `/tenant/${tenantKey}/` : "/"; + const error = useSelector((state) => state.bpmTasks.error); + const [customSubmissionAPIFailed, setCustomSubmissionAPIFailed] = useState(false); + + + useEffect(() => { + if (taskId) { + dispatch(setSelectedTaskID(taskId)); + } + }, [taskId, dispatch]); + + useEffect(() => { + if (bpmTaskId) { + dispatch(setBPMTaskDetailLoader(true)); + dispatch(getBPMTaskDetail(bpmTaskId)); + dispatch(getBPMGroups(bpmTaskId)); + } + return () => { + Formio.clearCache(); + }; + }, [bpmTaskId, dispatch]); + + useEffect(() => { + if (error) { + dispatch(push('/404')); + } + return () => { + dispatch(bpmActionError('')); + }; + }, [error,dispatch]); + + useEffect(() => { + if (processList.length && task?.processDefinitionId) { + const pKey = getProcessDataObjectFromList( + processList, + task?.processDefinitionId + ); + setProcessKey(pKey["key"]); + setProcessTenant(pKey["tenantId"]); + } + }, [processList, task?.processDefinitionId]); + + useEffect(() => { + if (task?.processInstanceId) { + setProcessInstanceId(task?.processInstanceId); + } + }, [task?.processInstanceId]); + + const getFormSubmissionData = useCallback( + (formUrl, taskDetail) => { + const { formId, submissionId } = getFormIdSubmissionIdFromURL(formUrl); + Formio.clearCache(); + dispatch(resetFormData("form")); + const isBundle = taskDetail?.formType === TYPE_BUNDLE; + if(isBundle){ + dispatch(setBundleSubmissionData({})); + } + const isBundleSubmissionSaveCallBack = (err,res,formResult)=>{ + if(isBundle && formResult.isBundle){ + dispatch(setBundleSubmissionData({data:res.data})); + } + dispatch(setBPMTaskDetailLoader(false)); + }; + function fetchForm() { + dispatch( + getForm("form", formId, (err,formResult) => { + if (!err) { + if (CUSTOM_SUBMISSION_URL && CUSTOM_SUBMISSION_ENABLE) { + dispatch(setCustomSubmission({})); + dispatch(getCustomSubmission(submissionId, formId,(err,res)=>{ + + if(err) + { + setCustomSubmissionAPIFailed(true); + } + + isBundleSubmissionSaveCallBack(err,res,formResult); + + })); + } else { + dispatch(resetSubmission("submission")); + dispatch(getSubmission("submission", submissionId, formId,(err,res)=>{ + isBundleSubmissionSaveCallBack(err,res,formResult); + + })); + } + dispatch(setFormSubmissionLoading(false)); + } else { + if (err === "Bad Token" || err === "Token Expired") { + dispatch(resetFormData("form")); + dispatch( + getFormioRoleIds((err) => { + if (!err) { + fetchForm(); + } else { + dispatch(setFormSubmissionLoading(false)); + } + }) + ); + } else { + dispatch(setFormSubmissionLoading(false)); + } + } + }) + ); + } + fetchForm(); + }, + [dispatch] + ); + + useEffect(() => { + if (task?.formUrl) { + getFormSubmissionData(task?.formUrl, task); + } + }, [task?.formUrl, dispatch, getFormSubmissionData]); + + useEffect(() => { + if (task?.formUrl && taskFormSubmissionReload) { + dispatch(setFormSubmissionLoading(false)); + getFormSubmissionData(task?.formUrl, task); + dispatch(reloadTaskFormSubmission(false)); + } + }, [ + task?.formUrl, + taskFormSubmissionReload, + dispatch, + getFormSubmissionData, + ]); + + const reloadTasks = () => { + dispatch(setBPMTaskDetailLoader(true)); + dispatch(setSelectedTaskID(null)); // unSelect the Task Selected + dispatch(fetchServiceTaskList(selectedFilter.id, firstResult, reqData)); //Refreshes the Tasks + dispatch(push(`${redirectUrl}task/`)); + }; + + const reloadCurrentTask = () => { + if (selectedFilter && task?.id) { + dispatch(setBPMTaskDetailLoader(true)); + dispatch( + getBPMTaskDetail(task.id, (err, taskDetail) => { + if (!err) { + dispatch(setFormSubmissionLoading(true)); + getFormSubmissionData(taskDetail?.formUrl, taskDetail); + } + }) + ); // Refresh the Task Selected + dispatch(getBPMGroups(task.id)); + dispatch(fetchServiceTaskList(selectedFilter.id, firstResult, reqData)); //Refreshes the Tasks + } + }; + + const onCustomEventCallBack = (customEvent) => { + switch (customEvent.type) { + case CUSTOM_EVENT_TYPE.RELOAD_TASKS: + reloadTasks(); + break; + case CUSTOM_EVENT_TYPE.RELOAD_CURRENT_TASK: + reloadCurrentTask(); + break; + case CUSTOM_EVENT_TYPE.ACTION_COMPLETE: + onFormSubmitCallback(customEvent.actionType); + break; + default: + return; + } + }; + + const onFormSubmitCallback = (actionType = "") => { + if (bpmTaskId) { + dispatch(setBPMTaskDetailLoader(true)); + const { formId, submissionId } = getFormIdSubmissionIdFromURL( + task?.formUrl + ); + const formUrl = getFormUrlWithFormIdSubmissionId(formId, submissionId); + const origin = `${window.location.origin}${redirectUrl}`; + const webFormUrl = `${origin}form/${formId}/submission/${submissionId}`; + dispatch( + onBPMTaskFormSubmit( + bpmTaskId, + getTaskSubmitFormReq( + formUrl, + task?.applicationId, + actionType, + webFormUrl + ), + (err) => { + if (!err) { + reloadTasks(); + } else { + dispatch(setBPMTaskDetailLoader(false)); + } + } + ) + ); + } else { + reloadCurrentTask(); + } + }; + + if (!bpmTaskId) { + return ( + + + {t("Select a task in the list.")} + + ); + } + else if (customSubmissionAPIFailed) + { + return ( +
+ +
+ ); + } + else if (isTaskLoading) { + return ( +
+ +
+ ); + } else { + /*TODO split render*/ + return ( +
+ + + + + + { + task?.formType === TYPE_BUNDLE && currentForm.isBundle ? ( +
+ { + task?.assignee === currentUser ? ( + + ) : ( + + ) + } +
+ ) : ( + ({ + ...base, + background: "rgba(0, 0, 0, 0.2)", + cursor: "not-allowed !important", + }), + }} + > + {task?.assignee === currentUser ? ( + + ) : ( + + )} + + )} +
+ + {task?.formType === "bundle" ? ( + + ) : ( + + )} + + +
+ +
+
+
+
+
+ ); + } +}); + +export default ServiceFlowTaskDetails;