From 5c3f967128f7c91f414273cc7e413f738fb2f3d2 Mon Sep 17 00:00:00 2001 From: RaktimaNXG Date: Fri, 7 Jun 2024 19:07:56 +0530 Subject: [PATCH] fix: add validation of signature widgetat in quick send flow, update SentToOthers field when document is in inProgress , resolve resend nonexist email issue --- apps/OpenSign/src/components/BulkSendUi.js | 137 ++++++++++-------- apps/OpenSign/src/components/pdf/RenderPdf.js | 10 +- apps/OpenSign/src/constant/Utils.js | 1 + apps/OpenSign/src/pages/PdfRequestFiles.js | 44 +++--- apps/OpenSign/src/pages/PlaceHolderSign.js | 4 +- .../src/primitives/GetReportDisplay.js | 121 ++++++++++------ .../v1/routes/CreateDocumentWithTemplate.js | 1 + .../v1/routes/createDocumentwithCoordinate.js | 1 + .../cloud/parsefunction/createBatchDocs.js | 2 + 9 files changed, 194 insertions(+), 127 deletions(-) diff --git a/apps/OpenSign/src/components/BulkSendUi.js b/apps/OpenSign/src/components/BulkSendUi.js index c5445ad66..98ba5c0b8 100644 --- a/apps/OpenSign/src/components/BulkSendUi.js +++ b/apps/OpenSign/src/components/BulkSendUi.js @@ -9,7 +9,22 @@ const BulkSendUi = (props) => { const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false); const [isSubmit, setIsSubmit] = useState(false); const [allowedForm, setAllowedForm] = useState(0); + const [isSignatureExist, setIsSignatureExist] = useState(); const allowedSigners = 50; + useEffect(() => { + signatureExist(); + }, []); + + //function to check atleast one signature field exist + const signatureExist = () => { + const getPlaceholder = props.item?.Placeholders; + const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) => + placeholderObj?.placeHolder?.some((holder) => + holder?.pos?.some((posItem) => posItem?.type === "signature") + ) + ); + setIsSignatureExist(checkIsSignatureExistt); + }; useEffect(() => { if (scrollOnNextUpdate && formRef.current) { formRef.current.scrollIntoView({ @@ -165,65 +180,73 @@ const BulkSendUi = (props) => { )} {props.Placeholders?.length > 0 ? ( - <> - {props.Placeholders?.some((x) => !x.signerObjId) ? ( -
-
- {forms?.map((form, index) => ( -
+ {props.Placeholders?.some((x) => !x.signerObjId) ? ( + +
+ {forms?.map((form, index) => ( +
+ {form?.fields?.map((field, fieldIndex) => ( +
+ + + handleInputChange(index, signer, fieldIndex) + } + /> +
+ ))} + {forms?.length > 1 && ( + + )} +
+
+ ))} +
+
+ - )} -
-
- ))} + Add new + + +
+ + ) : ( +
+ All roles in this document are currently linked to contacts. To + 'quick send' copies of this template to multiple + signers, please ensure that at least one role is not linked to + any contact.
-
- - -
- - ) : ( -
- All roles in this document are currently linked to contacts. To - 'quick send' copies of this template to multiple - signers, please ensure that at least one role is not linked to any - contact. -
- )} - + )} + + ) : ( +
+ Please ensure there's at least one signature widget added for + all recipients. +
+ ) ) : (
Please add at least one role to this template in order to 'quick diff --git a/apps/OpenSign/src/components/pdf/RenderPdf.js b/apps/OpenSign/src/components/pdf/RenderPdf.js index 44f15881c..2bac6b85e 100644 --- a/apps/OpenSign/src/components/pdf/RenderPdf.js +++ b/apps/OpenSign/src/components/pdf/RenderPdf.js @@ -206,13 +206,9 @@ function RenderPdf({ const checkSignedSignes = (data) => { let checkSign = []; //condition to handle quick send flow and using normal request sign flow - if (signedSigners[0]?.Id) { - checkSign = signedSigners.filter((sign) => sign.Id === data.Id); - } else { - checkSign = signedSigners.filter( - (sign) => sign.objectId === data.signerObjId - ); - } + checkSign = signedSigners.filter( + (sign) => sign?.Id === data?.Id || sign?.objectId === data?.signerObjId + ); if (data.signerObjId === signerObjectId) { setCurrentSigner(true); } diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 3a24c4398..25a91a20c 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -669,6 +669,7 @@ export const createDocument = async (template, placeholders, signerData) => { Name: Doc.Name, URL: Doc.URL, SignedUrl: Doc.SignedUrl, + SentToOthers: Doc.SentToOthers, Description: Doc.Description, Note: Doc.Note, Placeholders: placeholdersArr, diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 97f2c71be..426a3aadf 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -403,25 +403,34 @@ function PdfRequestFiles() { let signers = []; let unSignedSigner = []; - //`emailExist` variable to handle condition for quick send flow and show unsigned signers list - const emailExist = documentData[0].Placeholders[0]?.email; + const placeholdersOrSigners = []; + for (const placeholder of documentData[0].Placeholders) { + //`emailExist` variable to handle condition for quick send flow and show unsigned signers list + const emailExist = placeholder?.email; + if (emailExist) { + placeholdersOrSigners.push(placeholder); + } else { + const getSignerData = documentData[0].Signers.filter( + (data) => data.objectId === placeholder?.signerObjId + ); + placeholdersOrSigners.push(getSignerData[0]); + } + } //condition to check already signed document by someone if (audittrailData && audittrailData.length > 0) { setIsDocId(true); - const placeholdersOrSigners = emailExist - ? documentData[0].Placeholders - : documentData[0].Signers; for (const item of placeholdersOrSigners) { + const checkEmail = item?.email; //if email exist then compare user signed by using email else signers objectId - const emailOrId = emailExist ? item.email : item.objectId; + const emailOrId = checkEmail ? item.email : item.objectId; //`isSignedSignature` variable to handle break loop whenever it get true let isSignedSignature = false; //checking the signer who signed the document by using audit trail details. //and save signedSigners and unsignedSigners details for (const doc of audittrailData) { - const signedExist = emailExist + const signedExist = checkEmail ? doc?.UserPtr.Email : doc?.UserPtr.objectId; @@ -441,12 +450,7 @@ function PdfRequestFiles() { } else { //else condition is show there are no details in audit trail then direct push all signers details //in unsignedsigners array - let unsigned = []; - const placeholdersOrSigners = emailExist - ? documentData[0].Placeholders - : documentData[0].Signers; - unsigned.push(placeholdersOrSigners); - setUnSignedSigners(unsigned[0]); + setUnSignedSigners(placeholdersOrSigners); setSignerPos(documentData[0].Placeholders); } setPdfDetails(documentData); @@ -1000,8 +1004,10 @@ function PdfRequestFiles() { } const getFirstLetter = (name) => { - const firstLetter = name.charAt(0); - return firstLetter; + if (name) { + const firstLetter = name.charAt(0); + return firstLetter; + } }; //function for image upload or update const onImageChange = (event) => { @@ -1123,8 +1129,12 @@ function PdfRequestFiles() { }; const checkUserNameColor = (obj) => { const getBackColor = checkSignerBackColor(obj); - const color = darkenColor(getBackColor, 0.4); - return color; + if (getBackColor) { + const color = darkenColor(getBackColor, 0.4); + return color; + } else { + return "#abd1d0"; + } }; //function for set decline true on press decline button diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 6e9fa46a9..3f25695f5 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -936,6 +936,7 @@ function PlaceHolderSign() { Placeholders: filterPrefill, SignedUrl: pdfUrl, Signers: signers, + SentToOthers: true, ExpiryDate: { iso: updateExpiryDate, __type: "Date" @@ -945,7 +946,8 @@ function PlaceHolderSign() { data = { Placeholders: filterPrefill, SignedUrl: pdfUrl, - Signers: signers + Signers: signers, + SentToOthers: true }; } await axios diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 0a04073bf..45011e213 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -143,6 +143,7 @@ const ReportTable = (props) => { Name: Doc.Name, URL: Doc.URL, SignedUrl: Doc.SignedUrl, + SentToOthers: Doc?.SentToOthers || false, Description: Doc.Description, Note: Doc.Note, Placeholders: placeholdersArr, @@ -338,7 +339,7 @@ const ReportTable = (props) => { const sendMail = item?.SendMail || false; const getUrl = (x) => { //encode this url value `${item.objectId}/${x.Email}/${x.objectId}` to base64 using `btoa` function - if (x.objectId) { + if (x?.signerObjId) { const encodeBase64 = btoa( `${item.objectId}/${x.signerPtr.Email}/${x.signerPtr.objectId}/${sendMail}` ); @@ -495,9 +496,9 @@ const ReportTable = (props) => { // `handleSubjectChange` is used to add or change subject of resend mail const handleSubjectChange = (subject, doc) => { - const encodeBase64 = btoa( - `${doc.objectId}/${userDetails.Email}/${userDetails.objectId}` - ); + const encodeBase64 = userDetails?.objectId + ? btoa(`${doc.objectId}/${userDetails.Email}/${userDetails.objectId}`) + : btoa(`${doc.objectId}/${userDetails.Email}`); const expireDate = doc.ExpiryDate.iso; const newDate = new Date(expireDate); const localExpireDate = newDate.toLocaleDateString("en-US", { @@ -511,8 +512,8 @@ const ReportTable = (props) => { sender_name: doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email, sender_phone: doc.ExtUserPtr?.Phone || "", - receiver_name: userDetails.Name, - receiver_email: userDetails.Email, + receiver_name: userDetails?.Name, + receiver_email: userDetails?.Email, receiver_phone: userDetails?.Phone || "", expiry_date: localExpireDate, company_name: doc.ExtUserPtr.Company, @@ -524,9 +525,9 @@ const ReportTable = (props) => { }; // `handlebodyChange` is used to add or change body of resend mail const handlebodyChange = (body, doc) => { - const encodeBase64 = btoa( - `${doc.objectId}/${userDetails.Email}/${userDetails.objectId}` - ); + const encodeBase64 = userDetails?.objectId + ? btoa(`${doc.objectId}/${userDetails.Email}/${userDetails.objectId}`) + : btoa(`${doc.objectId}/${userDetails.Email}`); const expireDate = doc.ExpiryDate.iso; const newDate = new Date(expireDate); const localExpireDate = newDate.toLocaleDateString("en-US", { @@ -540,8 +541,8 @@ const ReportTable = (props) => { sender_name: doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email, sender_phone: doc.ExtUserPtr?.Phone || "", - receiver_name: userDetails.Name, - receiver_email: userDetails.Email, + receiver_name: userDetails?.Name || "", + receiver_email: userDetails?.Email || "", receiver_phone: userDetails?.Phone || "", expiry_date: localExpireDate, company_name: doc.ExtUserPtr.Company, @@ -556,8 +557,18 @@ const ReportTable = (props) => { // `handleNextBtn` is used to open edit mail template screen in resend mail modal // as well as replace variable with original one const handleNextBtn = (user, doc) => { - setUserDetails(user); - const encodeBase64 = btoa(`${doc.objectId}/${user.Email}/${user.objectId}`); + const userdata = { + Name: user?.signerPtr?.Name, + Email: user.email ? user?.email : user.signerPtr?.Email, + Phone: user?.signerPtr?.Phone, + objectId: user?.signerPtr?.objectId + }; + setUserDetails(userdata); + const encodeBase64 = user.email + ? btoa(`${doc.objectId}/${user.email}`) + : btoa( + `${doc.objectId}/${user.signerPtr.Email}/${user.signerPtr.objectId}` + ); const expireDate = doc.ExpiryDate.iso; const newDate = new Date(expireDate); const localExpireDate = newDate.toLocaleDateString("en-US", { @@ -571,9 +582,9 @@ const ReportTable = (props) => { sender_name: doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email, sender_phone: doc.ExtUserPtr?.Phone || "", - receiver_name: user.Name, - receiver_email: user.Email, - receiver_phone: user?.Phone || "", + receiver_name: user?.signerPtr?.Name || "", + receiver_email: user?.email ? user?.email : user?.signerPtr?.Email, + receiver_phone: user?.signerPtr?.Phone || "", expiry_date: localExpireDate, company_name: doc?.ExtUserPtr?.Company || "", signing_url: `Sign here` @@ -584,14 +595,14 @@ const ReportTable = (props) => { `{{sender_name}} has requested you to sign "{{document_title}}"`; const body = doc?.RequestBody || - `

Hi {{receiver_name}},


We hope this email finds you well. {{sender_name}} has requested you to review and sign "{{document_title}}".

Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.


{{signing_url}}


If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.


Thanks

Team OpenSign™


`; + `

Hi {{receiver_name}},


We hope this email finds you well. {{sender_name}} has requested you to review and sign "{{document_title}}".

Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.


{{signing_url}}


If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.


Thanks

Team OpenSign™


`; const res = replaceMailVaribles(subject, body, variables); setMail((prev) => ({ ...prev, subject: res.subject, body: res.body })); - setIsNextStep({ [user.objectId]: true }); + setIsNextStep({ [user.Id]: true }); }; const handleResendMail = async (e, doc, user) => { e.preventDefault(); - setActLoader({ [user.objectId]: true }); + setActLoader({ [user?.Id]: true }); const url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`; const headers = { "Content-Type": "application/json", @@ -601,7 +612,7 @@ const ReportTable = (props) => { let params = { mailProvider: doc?.ExtUserPtr?.active_mail_adapter, extUserId: doc?.ExtUserPtr?.objectId, - recipient: userDetails.Email, + recipient: userDetails?.Email, subject: mail.subject, from: doc?.ExtUserPtr?.Email, html: mail.body @@ -633,9 +644,9 @@ const ReportTable = (props) => { } }; const fetchUserStatus = (user, doc) => { - const audit = doc?.AuditTrail?.find( - (x) => x.UserPtr.objectId === user.objectId - ); + const email = user.email ? user.email : user.signerPtr.Email; + const audit = doc?.AuditTrail?.find((x) => x.UserPtr.Email === email); + return (
@@ -789,23 +800,38 @@ const ReportTable = (props) => { const getPlaceholder = item?.Placeholders; //condiiton to check role is exist or not if (getPlaceholder && getPlaceholder.length > 0) { - let extendUser = JSON.parse(localStorage.getItem("Extand_Class")); - const userName = extendUser[0]?.UserName; - setIsPublicUserName(extendUser[0]?.UserName); - //condition to check user have public url or not - if (userName) { - setIsPublic((prevStates) => ({ - ...prevStates, - [item.objectId]: e.target.checked - })); - const getPlaceholder = item?.Placeholders; - if (getPlaceholder.length === 1) { - setSelectedPublicRole(getPlaceholder[0]?.Role); - } + const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) => + placeholderObj?.placeHolder?.some((holder) => + holder?.pos?.some((posItem) => posItem?.type === "signature") + ) + ); + if (checkIsSignatureExistt) { + let extendUser = JSON.parse(localStorage.getItem("Extand_Class")); + const userName = extendUser[0]?.UserName; + setIsPublicUserName(extendUser[0]?.UserName); + //condition to check user have public url or not + if (userName) { + setIsPublic((prevStates) => ({ + ...prevStates, + [item.objectId]: e.target.checked + })); + const getPlaceholder = item?.Placeholders; + if (getPlaceholder.length === 1) { + setSelectedPublicRole(getPlaceholder[0]?.Role); + } - setIsMakePublic({ [item.objectId]: true }); + setIsMakePublic({ [item.objectId]: true }); + } else { + setIsPublicProfile({ [item.objectId]: true }); + } } else { - setIsPublicProfile({ [item.objectId]: true }); + setIsAlert(true); + setAlertMsg({ + type: "danger", + message: + " Please ensure there's at least one signature widget added for all recipients." + }); + setTimeout(() => setIsAlert(false), 5000); } } else { setIsAlert(true); @@ -813,7 +839,7 @@ const ReportTable = (props) => { type: "danger", message: "Please assign at least one role to make this template public." }); - setTimeout(() => setIsAlert(false), 3000); + setTimeout(() => setIsAlert(false), 5000); } }; @@ -1240,11 +1266,11 @@ const ReportTable = (props) => { }} >
- {item?.Signers.map((user) => ( - - {isNextStep[user.objectId] && ( + {item?.Placeholders?.map((user) => ( + + {isNextStep[user.Id] && (
- {actLoader[user.objectId] && ( + {actLoader[user.Id] && (
{ >
{ {Object?.keys(isNextStep) <= 0 && (
- {user.Name} {`<${user.Email}>`} + {user?.signerPtr?.Name || "-"}{" "} + {`<${ + user?.email + ? user.email + : user.signerPtr.Email + }>`}
<>{fetchUserStatus(user, item)}
diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index aa3d327ea..758ff7971 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -179,6 +179,7 @@ export default async function createDocumentWithTemplate(request, response) { } object.set('URL', template.URL); object.set('SignedUrl', template.URL); + object.set('SentToOthers', true); if (TimeToCompleteDays) { object.set('TimeToCompleteDays', TimeToCompleteDays); } diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index 32df43ad0..f00161a2b 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -141,6 +141,7 @@ export default async function createDocumentwithCoordinate(request, response) { } object.set('URL', fileUrl); object.set('SignedUrl', fileUrl); + object.set('SentToOthers', true); object.set('CreatedBy', userPtr); object.set('ExtUserPtr', extUserPtr); if (TimeToCompleteDays) { diff --git a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js index 2f4aed714..602ddb9db 100644 --- a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js +++ b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js @@ -140,12 +140,14 @@ export default async function createBatchDocs(request) { : { ...y, signerPtr: {}, signerObjId: '' } ), SignedUrl: x.URL || x.SignedUrl, + SentToOthers: true, Signers: allSigner?.map(y => ({ __type: 'Pointer', className: 'contracts_Contactbook', objectId: y.objectId, })), ACL: Acl, + SentToOthers: true, RemindOnceInEvery: x.RemindOnceInEvery || 5, AutomaticReminders: x.AutomaticReminders || false, TimeToCompleteDays: x.TimeToCompleteDays || 15,