diff --git a/apps/OpenSign/src/components/BulkSendUi.js b/apps/OpenSign/src/components/BulkSendUi.js index c5445ad66..6e4aa5e76 100644 --- a/apps/OpenSign/src/components/BulkSendUi.js +++ b/apps/OpenSign/src/components/BulkSendUi.js @@ -9,7 +9,23 @@ 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(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + //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 +181,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 716c404e2..2bac6b85e 100644 --- a/apps/OpenSign/src/components/pdf/RenderPdf.js +++ b/apps/OpenSign/src/components/pdf/RenderPdf.js @@ -204,8 +204,10 @@ function RenderPdf({ }; //function for render placeholder block over pdf document const checkSignedSignes = (data) => { - const checkSign = signedSigners.filter( - (sign) => sign.objectId === data.signerObjId + let checkSign = []; + //condition to handle quick send flow and using normal request sign flow + 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 f054f7530..426a3aadf 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -32,7 +32,8 @@ import { contactBook, handleDownloadPdf, handleToPrint, - handleDownloadCertificate + handleDownloadCertificate, + darkenColor } from "../constant/Utils"; import Loader from "../primitives/LoaderWithMsg"; import HandleError from "../primitives/HandleError"; @@ -74,7 +75,7 @@ function PdfRequestFiles() { const [selectWidgetId, setSelectWidgetId] = useState(""); const [otpLoader, setOtpLoader] = useState(false); const [isCelebration, setIsCelebration] = useState(false); - const [requestSignTour, setRequestSignTour] = useState(true); + const [requestSignTour, setRequestSignTour] = useState(false); const [tourStatus, setTourStatus] = useState([]); const [isLoading, setIsLoading] = useState({ isLoad: true, @@ -296,6 +297,10 @@ function PdfRequestFiles() { setExpiredDate(expireDateFormat); } // Check if the current signer is not a last signer and handle the complete message. else if (isNextUser) { + setIsCelebration(true); + setTimeout(() => { + setIsCelebration(false); + }, 5000); setIsCompleted({ isModal: true, message: @@ -330,7 +335,7 @@ function PdfRequestFiles() { } } } - const checkDocIdExist = + const audittrailData = documentData[0].AuditTrail && documentData[0].AuditTrail.length > 0 && documentData[0].AuditTrail.filter((data) => data.Activity === "Signed"); @@ -399,41 +404,53 @@ function PdfRequestFiles() { let signers = []; let unSignedSigner = []; - //check document is signed or not - if (checkDocIdExist && checkDocIdExist.length > 0) { + 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 signerRes = documentData[0].Signers; - //comparison auditTrail user details with signers user details - for (let i = 0; i < signerRes.length; i++) { - const signerId = signerRes[i].objectId; + for (const item of placeholdersOrSigners) { + const checkEmail = item?.email; + //if email exist then compare user signed by using email else signers objectId + const emailOrId = checkEmail ? item.email : item.objectId; + //`isSignedSignature` variable to handle break loop whenever it get true let isSignedSignature = false; - for (let j = 0; j < checkDocIdExist.length; j++) { - const signedExist = - checkDocIdExist[j] && checkDocIdExist[j].UserPtr.objectId; - //checking signerObjId and auditTrail User objId - // if match then add signed data in signer array and break loop + //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 = checkEmail + ? doc?.UserPtr.Email + : doc?.UserPtr.objectId; - if (signerId === signedExist) { - signers.push({ ...signerRes[i], ...signerRes[i] }); + if (emailOrId === signedExist) { + signers.push({ ...item }); isSignedSignature = true; break; } - // if does not match then add unsigned data in unSignedSigner array } if (!isSignedSignature) { - unSignedSigner.push({ ...signerRes[i], ...signerRes[i] }); + unSignedSigner.push({ ...item }); } } setSignedSigners(signers); setUnSignedSigners(unSignedSigner); setSignerPos(documentData[0].Placeholders); } else { - let unsigned = []; - for (let i = 0; i < documentData.length; i++) { - unsigned.push(documentData[i].Signers); - } - setUnSignedSigners(unsigned[0]); + //else condition is show there are no details in audit trail then direct push all signers details + //in unsignedsigners array + setUnSignedSigners(placeholdersOrSigners); setSignerPos(documentData[0].Placeholders); } setPdfDetails(documentData); @@ -447,7 +464,7 @@ function PdfRequestFiles() { declined || currDate > expireUpdateDate ) { - setRequestSignTour(false); + setRequestSignTour(true); } else { //else condition to check current user exist in contracts_Users class and check tour message status //if not then check user exist in contracts_Contactbook class and check tour message status @@ -463,33 +480,25 @@ function PdfRequestFiles() { setContractName("_Users"); currUserId = res[0].objectId; setSignerUserId(currUserId); - const tourstatus = res[0].TourStatus && res[0].TourStatus; - if (tourstatus && tourstatus.length > 0) { - setTourStatus(tourstatus); - const checkTourRequestSign = tourstatus.filter( - (data) => data.requestSign - ); - if (checkTourRequestSign && checkTourRequestSign.length > 0) { - setRequestSignTour(checkTourRequestSign[0].requestSign); - } + const tourData = res[0].TourStatus && res[0].TourStatus; + if (tourData && tourData.length > 0) { + setTourStatus(tourData); + setRequestSignTour(tourData[0]?.requestSign || false); } } else if (res?.length === 0) { const res = await contactBook(currUserId); + if (res === "Error: Something went wrong!") { setHandleError("Error: Something went wrong!"); } else if (res[0] && res.length) { setContractName("_Contactbook"); const objectId = res[0].objectId; setSignerUserId(objectId); - const tourstatus = res[0].TourStatus && res[0].TourStatus; - if (tourstatus && tourstatus.length > 0) { - setTourStatus(tourstatus); - const checkTourRequestSign = tourstatus.filter( - (data) => data.requestSign - ); - if (checkTourRequestSign && checkTourRequestSign.length > 0) { - setRequestSignTour(checkTourRequestSign[0].requestSign); - } + const tourData = res[0].TourStatus && res[0].TourStatus; + + if (tourData && tourData.length > 0) { + setTourStatus(tourData); + setRequestSignTour(tourData[0]?.requestSign || false); } } else if (res.length === 0) { setHandleError("Error: User does not exist!"); @@ -552,7 +561,6 @@ function PdfRequestFiles() { setIsLoading(loadObj); }); }; - //function for embed signature or image url in pdf async function embedWidgetsData() { try { @@ -996,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) => { @@ -1109,9 +1119,23 @@ function PdfRequestFiles() { }; const checkSignerBackColor = (obj) => { - const data = signerPos.filter((data) => data.signerObjId === obj.objectId); + let data = ""; + if (obj?.Id) { + data = signerPos.filter((data) => data.Id === obj.Id); + } else { + data = signerPos.filter((data) => data.signerObjId === obj.objectId); + } return data && data.length > 0 && data[0].blockColor; }; + const checkUserNameColor = (obj) => { + const getBackColor = checkSignerBackColor(obj); + if (getBackColor) { + const color = darkenColor(getBackColor, 0.4); + return color; + } else { + return "#abd1d0"; + } + }; //function for set decline true on press decline button const declineDoc = async () => { @@ -1210,7 +1234,7 @@ function PdfRequestFiles() { }; //function to close tour and save tour status const closeRequestSignTour = async () => { - setRequestSignTour(false); + setRequestSignTour(true); if (isDontShow) { let updatedTourStatus = []; if (tourStatus.length > 0) { @@ -1332,7 +1356,6 @@ function PdfRequestFiles() { /> ); }; - return ( @@ -1401,7 +1424,7 @@ function PdfRequestFiles() { }} ref={divRef} > - {requestSignTour && requestSignTourFunction()} + {!requestSignTour && requestSignTourFunction()} <ModalUi headerColor={"#dc3545"} isOpen={isAlert.isShow} @@ -1781,7 +1804,7 @@ function PdfRequestFiles() { <div className="signerStyle" style={{ - background: "#abd1d0", + background: checkUserNameColor(obj), width: 30, height: 30, display: "flex", @@ -1800,7 +1823,7 @@ function PdfRequestFiles() { textTransform: "uppercase" }} > - {getFirstLetter(obj.Name)} + {getFirstLetter(obj?.Name || obj?.Role)} </span> </div> <div @@ -1809,9 +1832,11 @@ function PdfRequestFiles() { flexDirection: "column" }} > - <span className="userName">{obj.Name}</span> + <span className="userName"> + {obj?.Name || obj?.Role} + </span> <span className="useEmail"> - {obj.Email} + {obj?.Email || obj?.email} </span> </div> </div> @@ -1850,7 +1875,7 @@ function PdfRequestFiles() { <div className="signerStyle" style={{ - background: "#abd1d0", + background: checkUserNameColor(obj), width: 30, height: 30, display: "flex", @@ -1869,7 +1894,7 @@ function PdfRequestFiles() { textTransform: "uppercase" }} > - {getFirstLetter(obj.Name)} + {getFirstLetter(obj?.Name || obj?.email)} </span> </div> <div @@ -1878,9 +1903,11 @@ function PdfRequestFiles() { flexDirection: "column" }} > - <span className="userName">{obj.Name}</span> + <span className="userName"> + {obj?.Name || obj?.Role} + </span> <span className="useEmail"> - {obj.Email} + {obj?.Email || obj?.email} </span> </div> <hr /> 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/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index cbc0d2429..a7599666e 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -1,4 +1,8 @@ -import React, { useState, useEffect, useRef } from "react"; +import React, { + useState, + useEffect + // useRef +} from "react"; import { Navigate, useNavigate } from "react-router-dom"; import Parse from "parse"; import { SaveFileSize } from "../constant/saveFileSize"; @@ -27,10 +31,10 @@ function UserProfile() { const [percentage, setpercentage] = useState(0); const [isDisableDocId, setIsDisableDocId] = useState(false); const [isSubscribe, setIsSubscribe] = useState(false); - const [publicUserName, setPublicUserName] = useState( - extendUser && extendUser?.[0]?.UserName - ); - const previousPublicUserName = useRef(publicUserName); + // const [publicUserName, setPublicUserName] = useState( + // extendUser && extendUser?.[0]?.UserName + // ); + // const previousPublicUserName = useRef(publicUserName); const [company, setCompany] = useState( extendUser && extendUser?.[0]?.Company ); @@ -41,7 +45,7 @@ function UserProfile() { const [otp, setOtp] = useState(""); const [otpLoader, setOtpLoader] = useState(false); const [isEmailVerified, setIsEmailVerified] = useState(false); - const [userNameError, setUserNameError] = useState(""); + // const [userNameError, setUserNameError] = useState(""); useEffect(() => { getUserDetail(); }, []); @@ -80,31 +84,31 @@ function UserProfile() { } }; //function to check public username already exist - const handleCheckPublicUserName = async () => { - try { - const res = await Parse.Cloud.run("getpublicusername", { - username: publicUserName - }); - if (res) { - setIsLoader(false); - setUserNameError("user name already exist"); - setTimeout(() => { - setUserNameError(""); - }, 3000); - return res; - } - } catch (e) { - console.log("error in getpublicusername cloud function"); - } - }; + // const handleCheckPublicUserName = async () => { + // try { + // const res = await Parse.Cloud.run("getpublicusername", { + // username: publicUserName + // }); + // if (res) { + // setIsLoader(false); + // setUserNameError("user name already exist"); + // setTimeout(() => { + // setUserNameError(""); + // }, 3000); + // return res; + // } + // } catch (e) { + // console.log("error in getpublicusername cloud function"); + // } + // }; const handleSubmit = async (e) => { e.preventDefault(); let phn = Phone, - res; + res = ""; //condition to call cloud function when user change publicUserName - if (previousPublicUserName.current !== publicUserName) { - res = await handleCheckPublicUserName(); - } + // if (previousPublicUserName.current !== publicUserName) { + // res = await handleCheckPublicUserName(); + // } if (!res) { setIsLoader(true); try { @@ -158,8 +162,8 @@ function UserProfile() { Name: obj.Name, HeaderDocId: isDisableDocId, JobTitle: jobTitle, - Company: company, - UserName: publicUserName || "" + Company: company + // UserName: publicUserName || "" }; await axios.put( @@ -273,23 +277,23 @@ function UserProfile() { alert("OTP sent on you email"); }; //function to handle onchange username and restrict 6-characters username for free users - const handleOnchangeUserName = (e) => { - const value = e.target.value; - if (value.length > 6 && !isSubscribe) { - setUserNameError("Please upgrade to allow more than 6 characters."); - setTimeout(() => { - setUserNameError(""); - }, 2000); - } else { - setPublicUserName(e.target.value); - } - }; + // const handleOnchangeUserName = (e) => { + // const value = e.target.value; + // if (value.length > 6 && !isSubscribe) { + // setUserNameError("Please upgrade to allow more than 6 characters."); + // setTimeout(() => { + // setUserNameError(""); + // }, 2000); + // } else { + // setPublicUserName(e.target.value); + // } + // }; const handleCancel = () => { setEditMode(false); SetName(localStorage.getItem("username")); SetPhone(UserProfile && UserProfile.phone); setImage(localStorage.getItem("profileImg")); - setPublicUserName(extendUser && extendUser?.[0]?.UserName); + // setPublicUserName(extendUser && extendUser?.[0]?.UserName); setCompany(extendUser && extendUser?.[0]?.Company); setJobTitle(extendUser?.[0]?.JobTitle); }; @@ -315,13 +319,13 @@ function UserProfile() { </div> ) : ( <div className="flex justify-center items-center w-full relative"> - {userNameError && ( + {/* {userNameError && ( <div className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`} > {userNameError} </div> - )} + )} */} <div className="bg-white flex flex-col justify-center shadow rounded w-[450px]"> <div className="flex flex-col justify-center items-center my-4"> <div className="w-[200px] h-[200px] overflow-hidden rounded-full"> @@ -449,7 +453,7 @@ function UserProfile() { )} </span> </li> - {isEnableSubscription && ( + {/* {isEnableSubscription && ( <li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all"> <span className="font-semibold"> Public profile :{" "} @@ -479,7 +483,7 @@ function UserProfile() { )} </div> </li> - )} + )} */} <li className="border-y-[1px] border-gray-300 break-all"> <div className="flex justify-between items-center py-2"> <span diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 0a04073bf..8d599aaac 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -6,8 +6,8 @@ import ModalUi from "./ModalUi"; import AddSigner from "../components/AddSigner"; import { modalSubmitBtnColor, - modalCancelBtnColor, - isEnableSubscription + modalCancelBtnColor + // isEnableSubscription } from "../constant/const"; import Alert from "./Alert"; import Tooltip from "./Tooltip"; @@ -15,8 +15,11 @@ import { RWebShare } from "react-web-share"; import Tour from "reactour"; import Parse from "parse"; import { saveAs } from "file-saver"; -import { copytoData, replaceMailVaribles } from "../constant/Utils"; -import Confetti from "react-confetti"; +import { + // copytoData, + replaceMailVaribles +} from "../constant/Utils"; +// import Confetti from "react-confetti"; import EditorToolbar, { module1, formats @@ -41,7 +44,7 @@ const ReportTable = (props) => { const [isTour, setIsTour] = useState(false); const [tourStatusArr, setTourStatusArr] = useState([]); const [isResendMail, setIsResendMail] = useState({}); - const [isMakePublic, setIsMakePublic] = useState({}); + // const [isMakePublic, setIsMakePublic] = useState({}); const [mail, setMail] = useState({ subject: "", body: "" }); const [userDetails, setUserDetails] = useState({}); const [isNextStep, setIsNextStep] = useState({}); @@ -49,12 +52,12 @@ const ReportTable = (props) => { const [templateDeatils, setTemplateDetails] = useState({}); const [placeholders, setPlaceholders] = useState([]); const [isLoader, setIsLoader] = useState({}); - const [selectedPublicRole, setSelectedPublicRole] = useState(""); - const [isCelebration, setIsCelebration] = useState(false); + // const [selectedPublicRole, setSelectedPublicRole] = useState(""); + // const [isCelebration, setIsCelebration] = useState(false); const [currentLists, setCurrentLists] = useState([]); - const [isPublic, setIsPublic] = useState({}); - const [isPublicProfile, setIsPublicProfile] = useState({}); - const [publicUserName, setIsPublicUserName] = useState(""); + // const [isPublic, setIsPublic] = useState({}); + // const [isPublicProfile, setIsPublicProfile] = useState({}); + // const [publicUserName, setIsPublicUserName] = useState(""); const [isViewShare, setIsViewShare] = useState({}); const startIndex = (currentPage - 1) * props.docPerPage; const { isMoreDocs, setIsNextRecord } = props; @@ -143,6 +146,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, @@ -250,12 +254,12 @@ const ReportTable = (props) => { // `currentLists` is total record render on current page const currentList = props.List?.slice(indexOfFirstDoc, indexOfLastDoc); //check public template and save in a object to show public and private template - setIsPublic( - currentList.reduce((acc, item) => { - acc[item.objectId] = item?.IsPublic || false; - return acc; - }, {}) - ); + // setIsPublic( + // currentList.reduce((acc, item) => { + // acc[item.objectId] = item?.IsPublic || false; + // return acc; + // }, {}) + // ); setCurrentLists(currentList); // eslint-disable-next-line react-hooks/exhaustive-deps }, [indexOfLastDoc, indexOfFirstDoc]); @@ -320,16 +324,16 @@ const ReportTable = (props) => { setActLoader({}); } }; - const handleClose = (item) => { + const handleClose = () => { setIsRevoke({}); setIsDeleteModal({}); - setIsMakePublic({}); - setSelectedPublicRole(""); - setIsPublic((prevStates) => ({ - ...prevStates, - [item.objectId]: !prevStates[item.objectId] - })); - setIsPublicProfile({}); + // setIsMakePublic({}); + // setSelectedPublicRole(""); + // setIsPublic((prevStates) => ({ + // ...prevStates, + // [item.objectId]: !prevStates[item.objectId] + // })); + // setIsPublicProfile({}); }; const handleShare = (item) => { @@ -338,7 +342,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 +499,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 +515,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 +528,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 +544,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 +560,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 +585,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: `<a href=${signPdf}>Sign here</a>` @@ -584,14 +598,14 @@ const ReportTable = (props) => { `{{sender_name}} has requested you to sign "{{document_title}}"`; const body = doc?.RequestBody || - `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign <b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p>{{signing_url}}</p><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team OpenSign™</p><br></body> </html>`; + `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign <b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p>{{signing_url}}</p><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team OpenSign™</p><br></body> </html>`; 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 +615,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 +647,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 ( <div className="flex flex-row gap-2 justify-center items-center"> <div className="flex justify-center items-center bg-gray-200 text-xs text-black shadow rounded-full w-[65px] h-[23px] cursor-default"> @@ -719,115 +733,130 @@ const ReportTable = (props) => { }; //function to make template public and set public role - const handlePublicTemplate = async (item) => { - if (selectedPublicRole || !isPublic[item.objectId]) { - setActLoader({ [item.objectId]: true }); - setIsMakePublic(false); - try { - const res = await Parse.Cloud.run("createpublictemplate", { - templateid: item.objectId, - ispublic: isPublic[item.objectId], - publicrole: [selectedPublicRole] - }); + // const handlePublicTemplate = async (item) => { + // if (selectedPublicRole || !isPublic[item.objectId]) { + // setActLoader({ [item.objectId]: true }); + // setIsMakePublic(false); + // try { + // const res = await Parse.Cloud.run("createpublictemplate", { + // templateid: item.objectId, + // ispublic: isPublic[item.objectId], + // publicrole: [selectedPublicRole] + // }); - if (res.status === "success") { - setIsAlert(true); - setTimeout(() => setIsAlert(false), 1500); - if (isPublic[item.objectId]) { - setAlertMsg({ - type: "success", - message: "You have successfully made the template public." - }); - setIsCelebration(true); - setTimeout(() => { - setIsCelebration(false); - }, 5000); - setIsPublicProfile({ [item.objectId]: isPublic[item.objectId] }); - } else { - setAlertMsg({ - type: "success", - message: "You have successfully made the template private." - }); - setSelectedPublicRole(""); - } - const updateList = props.List.map((x) => - x.objectId === item.objectId - ? { ...x, IsPublic: isPublic[item.objectId] } - : x - ); - props.setList(updateList); - setActLoader({}); - } - } catch (e) { - console.log("error in createpublictemplate", e); - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: "Something went wrong, Please try again later!" - }); - setTimeout(() => setIsAlert(false), 1500); - setIsPublic((prevStates) => ({ - ...prevStates, - [item.objectId]: !prevStates[item.objectId] - })); - } - } else { - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: "You need to select a role for the public signers." - }); - setTimeout(() => setIsAlert(false), 1500); - } - }; + // if (res.status === "success") { + // setIsAlert(true); + // setTimeout(() => setIsAlert(false), 1500); + // if (isPublic[item.objectId]) { + // setAlertMsg({ + // type: "success", + // message: "You have successfully made the template public." + // }); + // setIsCelebration(true); + // setTimeout(() => { + // setIsCelebration(false); + // }, 5000); + // setIsPublicProfile({ [item.objectId]: isPublic[item.objectId] }); + // } else { + // setAlertMsg({ + // type: "success", + // message: "You have successfully made the template private." + // }); + // setSelectedPublicRole(""); + // } + // const updateList = props.List.map((x) => + // x.objectId === item.objectId + // ? { ...x, IsPublic: isPublic[item.objectId] } + // : x + // ); + // props.setList(updateList); + // setActLoader({}); + // } + // } catch (e) { + // console.log("error in createpublictemplate", e); + // setIsAlert(true); + // setAlertMsg({ + // type: "danger", + // message: "Something went wrong, Please try again later!" + // }); + // setTimeout(() => setIsAlert(false), 1500); + // setIsPublic((prevStates) => ({ + // ...prevStates, + // [item.objectId]: !prevStates[item.objectId] + // })); + // } + // } else { + // setIsAlert(true); + // setAlertMsg({ + // type: "danger", + // message: "You need to select a role for the public signers." + // }); + // setTimeout(() => setIsAlert(false), 1500); + // } + // }; const handleViewSigners = (item) => { setIsViewShare({ [item.objectId]: true }); }; //function to handle change template status is public or private - const handlePublicChange = async (e, item) => { - 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 handlePublicChange = async (e, item) => { + // const getPlaceholder = item?.Placeholders; + // //condiiton to check role is exist or not + // if (getPlaceholder && getPlaceholder.length > 0) { + // 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 }); - } else { - setIsPublicProfile({ [item.objectId]: true }); - } - } else { - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: "Please assign at least one role to make this template public." - }); - setTimeout(() => setIsAlert(false), 3000); - } - }; + // setIsMakePublic({ [item.objectId]: true }); + // } else { + // setIsPublicProfile({ [item.objectId]: true }); + // } + // } else { + // 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); + // setAlertMsg({ + // type: "danger", + // message: "Please assign at least one role to make this template public." + // }); + // setTimeout(() => setIsAlert(false), 5000); + // } + // }; //function to copy public profile links - const copytoProfileLink = () => { - const url = `https://opensign-me.vercel.app/${publicUserName}`; - copytoData(url); - setIsAlert(true); - setAlertMsg({ - type: "success", - message: "Copied." - }); - setTimeout(() => setIsAlert(false), 1500); - }; + // const copytoProfileLink = () => { + // const url = `https://opensign-me.vercel.app/${publicUserName}`; + // copytoData(url); + // setIsAlert(true); + // setAlertMsg({ + // type: "success", + // message: "Copied." + // }); + // setTimeout(() => setIsAlert(false), 1500); + // }; return ( <div className="relative"> @@ -840,11 +869,11 @@ const ReportTable = (props) => { </div> )} <div className="p-2 overflow-x-scroll w-full bg-white rounded-md"> - {isCelebration && ( + {/* {isCelebration && ( <div style={{ position: "relative", zIndex: "1000" }}> <Confetti width={window.innerWidth} height={window.innerHeight} /> </div> - )} + )} */} {isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>} {props.tourData && props.ReportName === "Templates" && ( <Tour @@ -891,9 +920,9 @@ const ReportTable = (props) => { {props.actions?.length > 0 && ( <th className="px-4 py-2 font-thin">Action</th> )} - {props.ReportName === "Templates" && isEnableSubscription && ( + {/* {props.ReportName === "Templates" && isEnableSubscription && ( <th className="px-4 py-2 font-thin">Public</th> - )} + )} */} </tr> </thead> <tbody className="text-[12px]"> @@ -1240,11 +1269,11 @@ const ReportTable = (props) => { }} > <div className=" overflow-y-auto max-h-[340px] md:max-h-[400px]"> - {item?.Signers.map((user) => ( - <React.Fragment key={user.objectId}> - {isNextStep[user.objectId] && ( + {item?.Placeholders?.map((user) => ( + <React.Fragment key={user.Id}> + {isNextStep[user.Id] && ( <div className="relative "> - {actLoader[user.objectId] && ( + {actLoader[user.Id] && ( <div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30"> <div style={{ @@ -1264,7 +1293,7 @@ const ReportTable = (props) => { > <div className="absolute right-5 text-xs z-40"> <Tooltip - id={`${user.objectId}_help`} + id={`${user.Id}_help`} message={ "You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}." } @@ -1322,7 +1351,12 @@ const ReportTable = (props) => { {Object?.keys(isNextStep) <= 0 && ( <div className="flex justify-between items-center gap-2 my-2 px-3"> <div className="text-black"> - {user.Name} {`<${user.Email}>`} + {user?.signerPtr?.Name || "-"}{" "} + {`<${ + user?.email + ? user.email + : user.signerPtr.Email + }>`} </div> <>{fetchUserStatus(user, item)}</> </div> @@ -1333,7 +1367,7 @@ const ReportTable = (props) => { </ModalUi> )} </td> - {isEnableSubscription && ( + {/* {isEnableSubscription && ( <td className=" pl-[20px] py-2 "> {props.ReportName === "Templates" && ( <div className=" flex flex-row-"> @@ -1496,7 +1530,7 @@ const ReportTable = (props) => { </ModalUi> )} </td> - )} + )} */} </tr> ) )} 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..d04ece47b 100644 --- a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js +++ b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js @@ -35,8 +35,10 @@ async function sendMail(document, sessionToken) { const objectId = signerMail[i]?.signerObjId; const hostUrl = baseUrl.origin; let encodeBase64; + let existSigner = {}; if (objectId) { - encodeBase64 = btoa(`${document.objectId}/${signerMail[i].signerPtr.Email}/${objectId}`); + existSigner = document?.Signers?.find(user => user.objectId === objectId); + encodeBase64 = btoa(`${document.objectId}/${existSigner?.Email}/${objectId}`); } else { encodeBase64 = btoa(`${document.objectId}/${signerMail[i].email}`); } @@ -46,7 +48,7 @@ async function sendMail(document, sessionToken) { const themeBGcolor = '#47a3ad'; let params = { extUserId: document.ExtUserPtr.objectId, - recipient: objectId ? signerMail[i].signerPtr.Email : signerMail[i].email, + recipient: objectId ? existSigner?.Email : signerMail[i].email, subject: `${document.ExtUserPtr.Name} has requested you to sign "${document.Name}"`, mailProvider: document?.ExtUserPtr?.active_mail_adapter || '', from: sender, @@ -140,12 +142,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,