diff --git a/apps/OpenSign/package-lock.json b/apps/OpenSign/package-lock.json index a4b48dc59..95ce733d0 100644 --- a/apps/OpenSign/package-lock.json +++ b/apps/OpenSign/package-lock.json @@ -33,7 +33,7 @@ "react-helmet": "^6.1.0", "react-konva": "^18.2.10", "react-modal": "^3.16.1", - "react-pdf": "^7.7.1", + "react-pdf": "^7.7.3", "react-quill": "^2.0.0", "react-redux": "^9.1.0", "react-rnd": "^10.4.10", @@ -9883,9 +9883,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dependencies": { "jake": "^10.8.5" }, @@ -19821,9 +19821,9 @@ } }, "node_modules/react-pdf": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.7.1.tgz", - "integrity": "sha512-cbbf/PuRtGcPPw+HLhMI1f6NSka8OJgg+j/yPWTe95Owf0fK6gmVY7OXpTxMeh92O3T3K3EzfE0ML0eXPGwR5g==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.7.3.tgz", + "integrity": "sha512-a2VfDl8hiGjugpqezBTUzJHYLNB7IS7a2t7GD52xMI9xHg8LdVaTMsnM9ZlNmKadnStT/tvX5IfV0yLn+JvYmw==", "dependencies": { "clsx": "^2.0.0", "dequal": "^2.0.3", @@ -22168,9 +22168,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "optional": true, "dependencies": { "chownr": "^2.0.0", @@ -23188,9 +23188,9 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.3", diff --git a/apps/OpenSign/package.json b/apps/OpenSign/package.json index cf4c66752..2b9861354 100644 --- a/apps/OpenSign/package.json +++ b/apps/OpenSign/package.json @@ -28,7 +28,7 @@ "react-helmet": "^6.1.0", "react-konva": "^18.2.10", "react-modal": "^3.16.1", - "react-pdf": "^7.7.1", + "react-pdf": "^7.7.3", "react-quill": "^2.0.0", "react-redux": "^9.1.0", "react-rnd": "^10.4.10", diff --git a/apps/OpenSign/src/components/pdf/RenderAllPdfPage.js b/apps/OpenSign/src/components/pdf/RenderAllPdfPage.js index cb1fa0700..2fae32a0d 100644 --- a/apps/OpenSign/src/components/pdf/RenderAllPdfPage.js +++ b/apps/OpenSign/src/components/pdf/RenderAllPdfPage.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import RSC from "react-scrollbars-custom"; import { Document, Page } from "react-pdf"; import { themeColor } from "../../constant/const"; @@ -9,13 +9,54 @@ function RenderAllPdfPage({ setAllPages, setPageNumber, setSignBtnPosition, - pageNumber + pageNumber, + signerPos, + signerObjectId }) { + const [signPageNumber, setSignPageNumber] = useState([]); + const [bookmarkColor, setBookmarkColor] = useState(""); //set all number of pages after load pdf function onDocumentLoad({ numPages }) { setAllPages(numPages); + //check if signerPos array exist then save page number exist in signerPos array to show bookmark icon + if (signerPos) { + const checkUser = signerPos.filter( + (data) => data.signerObjId === signerObjectId + ); + setBookmarkColor(checkUser[0]?.blockColor); + let pageNumberArr = []; + if (checkUser?.length > 0) { + checkUser[0]?.placeHolder?.map((data) => { + pageNumberArr.push(data?.pageNumber); + }); + + setSignPageNumber(pageNumberArr); + } + } } + //'function `addSignatureBookmark` is used to display the page where the user's signature is located. + const addSignatureBookmark = (index) => { + const ispageNumber = signPageNumber.includes(index + 1); + return ( + ispageNumber && ( +
+ +
+ ) + ); + }; return (
{ setPageNumber(index + 1); @@ -75,15 +117,25 @@ function RenderAllPdfPage({ } }} > - + {signerPos && addSignatureBookmark(index)} + +
+ +
))} diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index 6b04b1acb..546768adf 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -16,6 +16,7 @@ import { PDFDocument } from "pdf-lib"; import axios from "axios"; import { isEnableSubscription, submitBtn } from "../constant/const"; import ModalUi from "../primitives/ModalUi"; +import { Tooltip } from "react-tooltip"; // `Form` render all type of Form on this basis of their provided in path function Form() { @@ -708,11 +709,11 @@ const Forms = (props) => { />
{props.title !== "New Template" && ( - + )} {props.title === "Request Signatures" && (
@@ -732,7 +733,66 @@ const Forms = (props) => { )} {props.title !== "Sign Yourself" && (
- + +
{ - // if (sendInOrder) { - // const index = pdfDetails?.[0].Signers.findIndex( - // (x) => x.Email === jsonSender.email - // ); - // const newIndex = index - 1; - // if (newIndex !== -1) { - // const user = pdfDetails?.[0].Signers[newIndex]; - // const isPrevUserSigned = - // pdfDetails?.[0].AuditTrail && - // pdfDetails?.[0].AuditTrail.some( - // (x) => - // x.UserPtr.objectId === user.objectId && x.Activity === "Signed" - // ); - // if (isPrevUserSigned) { - // return true; - // } else { - // return false; - // } - // } else { - // return true; - // } - // } else { - // return true; - // } - // }; //function for embed signature or image url in pdf async function embedWidgetsData() { - // const validateSigning = checkSendInOrder(); - // if (validateSigning) { try { const checkUser = signerPos.filter( (data) => data.signerObjId === signerObjectId @@ -512,83 +484,99 @@ function PdfRequestFiles() { showAlert = false, widgetKey, radioExist, - requiredCheckbox; + requiredCheckbox, + pageNumber; // `pageNumber` is used to check on which page user did not fill widget's data then change current pageNumber and show tour message on that page for (let i = 0; i < checkUser[0].placeHolder.length; i++) { for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) { + //get current page + const updatePage = checkUser[0].placeHolder[i]?.pageNumber; + //checking checbox type widget checkboxExist = checkUser[0].placeHolder[i].pos[j].type === "checkbox"; + //checking radio button type widget radioExist = checkUser[0].placeHolder[i].pos[j].type === radioButtonWidget; + //condition to check checkbox widget exist or not if (checkboxExist) { + //get all required type checkbox requiredCheckbox = checkUser[0].placeHolder[i].pos.filter( (position) => !position.options?.isReadOnly && position.type === "checkbox" ); - + //if required type checkbox data exit then check user checked all checkbox or some checkbox remain to check + //also validate to minimum and maximum required checkbox if (requiredCheckbox && requiredCheckbox.length > 0) { for (let i = 0; i < requiredCheckbox.length; i++) { + //get minimum required count if exit const minCount = requiredCheckbox[i].options?.validation?.minRequiredCount; const parseMin = minCount && parseInt(minCount); + //get maximum required count if exit const maxCount = requiredCheckbox[i].options?.validation?.maxRequiredCount; const parseMax = maxCount && parseInt(maxCount); + //in `response` variable is used to get how many checkbox checked by user const response = requiredCheckbox[i].options?.response?.length; + //in `defaultValue` variable is used to get how many checkbox checked by default const defaultValue = requiredCheckbox[i].options?.defaultValue?.length; - if (parseMin === 0 && parseMax === 0) { - if (!showAlert) { - showAlert = false; - setminRequiredCount(null); - } - } else if (parseMin === 0 && parseMax > 0) { - if (!showAlert) { - showAlert = false; - setminRequiredCount(null); - } - } else if (!response) { - if (!defaultValue) { - if (!showAlert) { - showAlert = true; - widgetKey = requiredCheckbox[i].key; - setminRequiredCount(parseMin); - } - } - } else if (parseMin > 0 && parseMin > response) { + //condition to check parseMin and parseMax greater than 0 then consider it as a required check box + if ( + parseMin > 0 && + parseMax > 0 && + !response && + !defaultValue && + !showAlert + ) { + showAlert = true; + widgetKey = requiredCheckbox[i].key; + pageNumber = updatePage; + setminRequiredCount(parseMin); + } + //else condition to validate minimum required checkbox + else if (parseMin > 0 && (parseMin > response || !response)) { if (!showAlert) { showAlert = true; widgetKey = requiredCheckbox[i].key; + pageNumber = updatePage; + setminRequiredCount(parseMin); } } } } - } else if (radioExist) { + } + //condition to check radio widget exist or not + else if (radioExist) { + //get all required type radio button requiredRadio = checkUser[0].placeHolder[i].pos.filter( (position) => !position.options?.isReadOnly && position.type === radioButtonWidget ); + //if required type radio data exit then check user checked all radio button or some radio remain to check if (requiredRadio && requiredRadio?.length > 0) { let checkSigned; for (let i = 0; i < requiredRadio?.length; i++) { - checkSigned = requiredRadio[i]?.options.response; + checkSigned = requiredRadio[i]?.options?.response; if (!checkSigned) { let checkDefaultSigned = - requiredRadio[i]?.options.defaultValue; - if (!checkDefaultSigned) { - if (!showAlert) { - showAlert = true; - widgetKey = requiredRadio[i].key; - setminRequiredCount(null); - } + requiredRadio[i]?.options?.defaultValue; + if (!checkDefaultSigned && !showAlert) { + showAlert = true; + widgetKey = requiredRadio[i].key; + pageNumber = updatePage; + setminRequiredCount(null); } } } } - } else { + } + //else condition to check all type widget data fill or not except checkbox and radio button + else { + //get all required type widgets except checkbox and radio const requiredWidgets = checkUser[0].placeHolder[i].pos.filter( (position) => position.options?.status === "required" && @@ -601,16 +589,14 @@ function PdfRequestFiles() { checkSigned = requiredWidgets[i]?.options?.response; if (!checkSigned) { const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl; - - let checkDefaultSigned = - requiredWidgets[i]?.options?.defaultValue; if (!checkSignUrl) { - if (!checkDefaultSigned) { - if (!showAlert) { - showAlert = true; - widgetKey = requiredWidgets[i].key; - setminRequiredCount(null); - } + let checkDefaultSigned = + requiredWidgets[i]?.options?.defaultValue; + if (!checkDefaultSigned && !showAlert) { + showAlert = true; + widgetKey = requiredWidgets[i].key; + pageNumber = updatePage; + setminRequiredCount(null); } } } @@ -618,16 +604,22 @@ function PdfRequestFiles() { } } } + //when showAlert is true then break the loop and show alert to fill required data in widgets + if (showAlert) { + break; + } } - if (checkboxExist && requiredCheckbox && showAlert) { setUnSignedWidgetId(widgetKey); + setPageNumber(pageNumber); setWidgetsTour(true); } else if (radioExist && showAlert) { setUnSignedWidgetId(widgetKey); + setPageNumber(pageNumber); setWidgetsTour(true); } else if (showAlert) { setUnSignedWidgetId(widgetKey); + setPageNumber(pageNumber); setWidgetsTour(true); } else { setIsUiLoading(true); @@ -882,15 +874,6 @@ function PdfRequestFiles() { alertMessage: "something went wrong, please try again later." }); } - - // } - // else { - // setIsAlert({ - // isShow: true, - // alertMessage: - // "Please wait for your turn to sign this document, as it has been set up by the creator to be signed in a specific order; you'll be notified when it's your turn." - // }); - // } } //function for update TourStatus @@ -1349,6 +1332,8 @@ function PdfRequestFiles() { {/* this component used to render all pdf pages in left side */} 0) { + const alreadyPlaceholder = documentData[0]?.SignedUrl; + if (alreadyPlaceholder) { + setCheckTourStatus(true); + } else if (tourstatus && tourstatus.length > 0) { setTourStatus(tourstatus); const checkTourRecipients = tourstatus.filter( (data) => data.placeholder diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 027abb889..db813272f 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -233,29 +233,6 @@ function SignYourSelf() { setSignBtnPosition([]); } } - - if (!isCompleted) { - //check current user email verified or not - const currentUser = JSON.parse(JSON.stringify(Parse.User.current())); - let isEmailVerified; - isEmailVerified = currentUser?.emailVerified; - if (isEmailVerified) { - setIsEmailVerified(isEmailVerified); - } else { - try { - const userQuery = new Parse.Query(Parse.User); - const user = await userQuery.get(currentUser.objectId, { - sessionToken: localStorage.getItem("accesstoken") - }); - if (user) { - isEmailVerified = user?.get("emailVerified"); - setIsEmailVerified(isEmailVerified); - } - } catch (e) { - setHandleError("Error: Something went wrong!"); - } - } - } } else if ( documentData === "Error: Something went wrong!" || (documentData.result && documentData.result.error) @@ -603,104 +580,126 @@ function SignYourSelf() { }; //function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url async function embedWidgetsData() { - let showAlert = false, - isSignatureExist = false; - try { - for (let i = 0; i < xyPostion?.length; i++) { - const requiredWidgets = xyPostion[i].pos.filter( - (position) => position.type !== "checkbox" - ); - if (requiredWidgets && requiredWidgets?.length > 0) { - let checkSigned; - for (let i = 0; i < requiredWidgets?.length; i++) { - checkSigned = requiredWidgets[i]?.options?.response; - if (!checkSigned) { - const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl; - let checkDefaultSigned = - requiredWidgets[i]?.options?.defaultValue; - if (!checkSignUrl) { - if (!checkDefaultSigned) { - if (!showAlert) { - showAlert = true; + //check current user email is verified or not + const currentUser = JSON.parse(JSON.stringify(Parse.User.current())); + let isEmailVerified; + isEmailVerified = currentUser?.emailVerified; + if (isEmailVerified) { + setIsEmailVerified(isEmailVerified); + } else { + try { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(currentUser.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } + } catch (e) { + setHandleError("Error: Something went wrong!"); + } + } + if (isEmailVerified) { + let showAlert = false, + isSignatureExist = false; + try { + for (let i = 0; i < xyPostion?.length; i++) { + const requiredWidgets = xyPostion[i].pos.filter( + (position) => position.type !== "checkbox" + ); + if (requiredWidgets && requiredWidgets?.length > 0) { + let checkSigned; + for (let i = 0; i < requiredWidgets?.length; i++) { + checkSigned = requiredWidgets[i]?.options?.response; + if (!checkSigned) { + const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl; + let checkDefaultSigned = + requiredWidgets[i]?.options?.defaultValue; + if (!checkSignUrl) { + if (!checkDefaultSigned) { + if (!showAlert) { + showAlert = true; + } } } } } } - } - //condition to check exist signature widget or not - if (!isSignatureExist) { - isSignatureExist = xyPostion[i].pos.some( - (data) => data?.type === "signature" - ); - } - } - if (xyPostion.length === 0 || !isSignatureExist) { - setIsAlert({ - header: "Fields required", - isShow: true, - alertMessage: - "Please ensure there's at least one signature widget added" - }); - return; - } else if (showAlert) { - setIsAlert({ - isShow: true, - alertMessage: - "Please ensure all field is accurately filled and meets all requirements." - }); - return; - } else { - setIsCeleb(true); - setTimeout(() => { - setIsCeleb(false); - }, 3000); - setIsUiLoading(true); - const existingPdfBytes = pdfArrayBuffer; - // Load a PDFDocument from the existing PDF bytes - try { - const pdfDoc = await PDFDocument.load(existingPdfBytes); - const isSignYourSelfFlow = true; - const extUserPtr = pdfDetails[0].ExtUserPtr; - const HeaderDocId = extUserPtr?.HeaderDocId; - //embed document's object id to all pages in pdf document - if (!HeaderDocId) { - await embedDocId(pdfDoc, documentId, allPages); - } - //embed multi signature in pdf - const pdfBytes = await multiSignEmbed( - xyPostion, - pdfDoc, - pdfOriginalWidth, - isSignYourSelfFlow, - containerWH - ); - // console.log("pdf", pdfBytes); - //function for call to embed signature in pdf and get digital signature pdf - await signPdfFun(pdfBytes, documentId); - } catch (err) { - setIsUiLoading(false); - if (err && err.message.includes("is encrypted.")) { - setIsAlert({ - isShow: true, - alertMessage: `Currently encrypted pdf files are not supported.` - }); - } else { - console.log("err in signing", err); - setIsAlert({ - isShow: true, - alertMessage: `Something went wrong.` - }); + //condition to check exist signature widget or not + if (!isSignatureExist) { + isSignatureExist = xyPostion[i].pos.some( + (data) => data?.type === "signature" + ); } } + if (xyPostion.length === 0 || !isSignatureExist) { + setIsAlert({ + header: "Fields required", + isShow: true, + alertMessage: + "Please ensure there's at least one signature widget added" + }); + return; + } else if (showAlert) { + setIsAlert({ + isShow: true, + alertMessage: + "Please ensure all field is accurately filled and meets all requirements." + }); + return; + } else { + setIsCeleb(true); + setTimeout(() => { + setIsCeleb(false); + }, 3000); + setIsUiLoading(true); + const existingPdfBytes = pdfArrayBuffer; + // Load a PDFDocument from the existing PDF bytes + try { + const pdfDoc = await PDFDocument.load(existingPdfBytes); + const isSignYourSelfFlow = true; + const extUserPtr = pdfDetails[0].ExtUserPtr; + const HeaderDocId = extUserPtr?.HeaderDocId; + //embed document's object id to all pages in pdf document + if (!HeaderDocId) { + await embedDocId(pdfDoc, documentId, allPages); + } + //embed multi signature in pdf + const pdfBytes = await multiSignEmbed( + xyPostion, + pdfDoc, + pdfOriginalWidth, + isSignYourSelfFlow, + containerWH + ); + // console.log("pdf", pdfBytes); + //function for call to embed signature in pdf and get digital signature pdf + await signPdfFun(pdfBytes, documentId); + } catch (err) { + setIsUiLoading(false); + if (err && err.message.includes("is encrypted.")) { + setIsAlert({ + isShow: true, + alertMessage: `Currently encrypted pdf files are not supported.` + }); + } else { + console.log("err in signing", err); + setIsAlert({ + isShow: true, + alertMessage: `Something went wrong.` + }); + } + } + } + } catch (err) { + console.log("err in embedselfsign ", err); + setIsUiLoading(false); + setIsAlert({ + isShow: true, + alertMessage: "something went wrong, please try again later." + }); } - } catch (err) { - console.log("err in embedselfsign ", err); - setIsUiLoading(false); - setIsAlert({ - isShow: true, - alertMessage: "something went wrong, please try again later." - }); } } //function for get digital signature diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index 3d3f6f483..813d8ab52 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -404,7 +404,6 @@ function UserProfile() { url={ "https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid" } - isSubscribe={isSubscribe} />{" "} {!isSubscribe && isEnableSubscription && } {" "} diff --git a/apps/OpenSign/src/primitives/Tooltip.js b/apps/OpenSign/src/primitives/Tooltip.js index f49e4bce7..1264e86e1 100644 --- a/apps/OpenSign/src/primitives/Tooltip.js +++ b/apps/OpenSign/src/primitives/Tooltip.js @@ -1,17 +1,9 @@ import React from "react"; import { Tooltip as ReactTooltip } from "react-tooltip"; import { openInNewTab } from "../constant/Utils"; -import { isEnableSubscription } from "../constant/const"; -const Tooltip = ({ id, message, url, iconColor, isSubscribe }) => +const Tooltip = ({ id, message, url, iconColor }) => url ? ( -