diff --git a/app/components/Form/ProjectContactForm.tsx b/app/components/Form/ProjectContactForm.tsx index b8565497b8..4362c09244 100644 --- a/app/components/Form/ProjectContactForm.tsx +++ b/app/components/Form/ProjectContactForm.tsx @@ -10,17 +10,17 @@ import { Button } from "@button-inc/bcgov-theme"; import { mutation as addContactToRevisionMutation } from "mutations/Contact/addContactToRevision"; import { useUpdateFormChange } from "mutations/FormChange/updateFormChange"; import projectContactSchema from "data/jsonSchemaForm/projectContactSchema"; -import { ValidatingFormProps } from "./Interfaces/FormValidationTypes"; import validateFormWithErrors from "lib/helpers/validateFormWithErrors"; import { ProjectContactForm_projectRevision$key, FormChangeOperation, } from "__generated__/ProjectContactForm_projectRevision.graphql"; import useDiscardFormChange from "hooks/useDiscardFormChange"; -import useMutationWithErrorMessage from "mutations/useMutationWithErrorMessage"; +import SavingIndicator from "./SavingIndicator"; -interface Props extends ValidatingFormProps { +interface Props { query: ProjectContactForm_query$key; + onSubmit: () => void; projectRevision: ProjectContactForm_projectRevision$key; } @@ -52,6 +52,8 @@ const ProjectContactForm: React.FC = (props) => { id newFormData operation + changeStatus + updatedAt } } } @@ -59,6 +61,7 @@ const ProjectContactForm: React.FC = (props) => { `, props.projectRevision ); + const { projectContactFormChanges } = projectRevision; const { allContacts } = useFragment( graphql` @@ -76,6 +79,13 @@ const ProjectContactForm: React.FC = (props) => { props.query ); + const lastEditedDate = useMemo(() => { + const mostRecentUpdate = projectContactFormChanges.edges + .map((e) => e.node.updatedAt) + .sort((a, b) => Date.parse(b) - Date.parse(a))[0]; + return new Date(mostRecentUpdate); + }, [projectContactFormChanges]); + const contactSchema = useMemo(() => { const schema = projectContactSchema; schema.properties.contactId = { @@ -112,7 +122,7 @@ const ProjectContactForm: React.FC = (props) => { const allForms = useMemo(() => { const contactForms = [ - ...projectRevision.projectContactFormChanges.edges + ...projectContactFormChanges.edges .filter(({ node }) => node.operation !== "ARCHIVE") .map(({ node }) => node), ]; @@ -120,12 +130,12 @@ const ProjectContactForm: React.FC = (props) => { (a, b) => a.newFormData.contactIndex - b.newFormData.contactIndex ); return contactForms; - }, [projectRevision]); + }, [projectContactFormChanges]); const [primaryContactForm, ...alternateContactForms] = allForms; - const [applyUpdateFormChangeMutation] = useUpdateFormChange(); + const [applyUpdateFormChangeMutation, isUpdating] = useUpdateFormChange(); const [discardFormChange] = useDiscardFormChange( - projectRevision.projectContactFormChanges.__id + projectContactFormChanges.__id ); const deleteContact = ( @@ -145,6 +155,7 @@ const ProjectContactForm: React.FC = (props) => { readonly id: string; readonly newFormData: any; readonly operation: FormChangeOperation; + readonly changeStatus: string; }, newFormData: any ) => { @@ -154,6 +165,7 @@ const ProjectContactForm: React.FC = (props) => { id: formChange.id, formChangePatch: { newFormData, + changeStatus: formChange.changeStatus, }, }, }, @@ -162,6 +174,7 @@ const ProjectContactForm: React.FC = (props) => { formChange: { ...formChange, newFormData, + changeStatus: formChange.changeStatus, }, }, }, @@ -169,22 +182,65 @@ const ProjectContactForm: React.FC = (props) => { }); }; - props.setValidatingForm({ - selfValidate: () => { - return Object.keys(formRefs.current).reduce((agg, formId) => { - const formObject = formRefs.current[formId]; - return [...agg, ...validateFormWithErrors(formObject)]; - }, []); - }, - }); - const clearPrimaryContact = () => { const { contactId, ...newFormData } = primaryContactForm.newFormData; updateFormChange(primaryContactForm, newFormData); }; + const stageContactFormChanges = async () => { + const validationErrors = Object.keys(formRefs.current).reduce( + (agg, formId) => { + const formObject = formRefs.current[formId]; + return [...agg, ...validateFormWithErrors(formObject)]; + }, + [] + ); + + if (validationErrors.length > 0) return; + + const completedPromises: Promise[] = []; + + projectContactFormChanges.edges.forEach(({ node }) => { + if (node.changeStatus === "pending") { + const promise = new Promise((resolve) => { + applyUpdateFormChangeMutation({ + variables: { + input: { + id: node.id, + formChangePatch: { + changeStatus: "staged", + }, + }, + }, + optimisticResponse: { + updateFormChange: { + formChange: { + changeStatus: "staged", + }, + }, + }, + debounceKey: node.id, + onCompleted: () => { + resolve(); + }, + }); + }); + completedPromises.push(promise); + } + }); + + await Promise.all(completedPromises); + + props.onSubmit(); + }; + return (
+
+

Project Contacts

+ +
+ @@ -207,7 +263,10 @@ const ProjectContactForm: React.FC = (props) => { ref={(el) => (formRefs.current[primaryContactForm.id] = el)} formData={primaryContactForm.newFormData} onChange={(change) => { - updateFormChange(primaryContactForm, change.formData); + updateFormChange(primaryContactForm, { + ...change.formData, + changeStatus: "pending", + }); }} schema={contactSchema} uiSchema={uiSchema} @@ -237,7 +296,10 @@ const ProjectContactForm: React.FC = (props) => { ref={(el) => (formRefs.current[form.id] = el)} formData={form.newFormData} onChange={(change) => { - updateFormChange(form, change.formData); + updateFormChange(form, { + ...change.formData, + changeStatus: "pending", + }); }} schema={contactSchema} uiSchema={uiSchema} @@ -272,6 +334,17 @@ const ProjectContactForm: React.FC = (props) => { + + + + diff --git a/app/pages/cif/project-revision/[projectRevision]/form/contacts.tsx b/app/pages/cif/project-revision/[projectRevision]/form/contacts.tsx index c818f1fa4c..87ba3d502d 100644 --- a/app/pages/cif/project-revision/[projectRevision]/form/contacts.tsx +++ b/app/pages/cif/project-revision/[projectRevision]/form/contacts.tsx @@ -4,14 +4,8 @@ import { graphql, usePreloadedQuery } from "react-relay/hooks"; import { contactsFormQuery } from "__generated__/contactsFormQuery.graphql"; import withRelayOptions from "lib/relay/withRelayOptions"; import { useRouter } from "next/router"; -import { Button } from "@button-inc/bcgov-theme"; -import { useMemo, useRef } from "react"; -import SavingIndicator from "components/Form/SavingIndicator"; -import { mutation as updateProjectRevisionMutation } from "mutations/ProjectRevision/updateProjectRevision"; -import { useMutation } from "react-relay"; -import { getProjectsPageRoute } from "pageRoutes"; +import { getProjectRevisionPageRoute } from "pageRoutes"; import ProjectContactForm from "components/Form/ProjectContactForm"; -import { ISupportExternalValidation } from "components/Form/Interfaces/FormValidationTypes"; import TaskList from "components/TaskList"; const pageQuery = graphql` @@ -22,7 +16,6 @@ const pageQuery = graphql` } projectRevision(id: $projectRevision) { id - updatedAt ...ProjectContactForm_projectRevision ...TaskList_projectRevision } @@ -34,94 +27,25 @@ const pageQuery = graphql` export function ProjectRevision({ preloadedQuery, }: RelayProps<{}, contactsFormQuery>) { - const projectContactFormRef = useRef(null); - const router = useRouter(); - const { query } = usePreloadedQuery(pageQuery, preloadedQuery); - - const [updateProjectRevision, updatingProjectRevision] = useMutation( - updateProjectRevisionMutation - ); - const lastEditedDate = useMemo( - () => new Date(query.projectRevision.updatedAt), - [query.projectRevision.updatedAt] - ); + const { query } = usePreloadedQuery(pageQuery, preloadedQuery); if (!query.projectRevision.id) return null; - /** - * Function: approve staged change, trigger an insert on the project - * table & redirect to the project page - */ - const commitProject = async () => { - const errors = [...projectContactFormRef.current.selfValidate()]; - - if (errors.length > 0) { - console.log("Could not submit a form with errors: ", errors); - return; - } - - updateProjectRevision({ - variables: { - input: { - id: query.projectRevision.id, - projectRevisionPatch: { changeStatus: "committed" }, - }, - }, - // No need for an optimistic response - // Since we navigate away from the page after the mutation is complete - onCompleted: async () => { - await router.push(getProjectsPageRoute()); - }, - updater: (store) => { - // Invalidate the entire store,to make sure that we don't display any stale data after redirecting to the next page. - // This could be optimized to only invalidate the affected records. - store.invalidateStore(); - }, - }); + const handleSubmit = () => { + router.push(getProjectRevisionPageRoute(query.projectRevision.id)); }; const taskList = ; return ( -
-

Project Overview

- -
- - (projectContactFormRef.current = validator) - } + onSubmit={handleSubmit} /> - - - -
); }