import React, { useState, useRef, useEffect } from "react"; import { themeColor } from "../utils/ThemeColor/backColor"; import { PDFDocument } from "pdf-lib"; import "../css/signature.css"; import axios from "axios"; import loader from "../assests/loader2.gif"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import { useParams } from "react-router-dom"; import SignPad from "./component/signPad"; import RenderAllPdfPage from "./component/renderAllPdfPage"; import { useNavigate } from "react-router-dom"; import Tour from "reactour"; import { contractDocument, multiSignEmbed, embedDocId, pdfNewWidthFun, signPdfFun, onImageSelect, onSaveSign, onSaveImage, addDefaultSignatureImg, getHostUrl } from "../utils/Utils"; import Loader from "./component/loader"; import HandleError from "./component/HandleError"; import Nodata from "./component/Nodata"; import Header from "./component/header"; import RenderPdf from "./component/renderPdf"; import CustomModal from "./component/CustomModal"; import Title from "./component/Title"; import DefaultSignature from "./component/defaultSignature"; import ModalUi from "../premitives/ModalUi"; function PdfRequestFiles() { const navigate = useNavigate(); const { docId } = useParams(); const [pdfDetails, setPdfDetails] = useState([]); const [signedSigners, setSignedSigners] = useState([]); const [unsignedSigners, setUnSignedSigners] = useState([]); const [isSignPad, setIsSignPad] = useState(false); const [pdfUrl, setPdfUrl] = useState(); const [allPages, setAllPages] = useState(null); const numPages = 1; const [pageNumber, setPageNumber] = useState(1); const [image, setImage] = useState(null); const [isImageSelect, setIsImageSelect] = useState(false); const [signature, setSignature] = useState(); const [isStamp, setIsStamp] = useState(false); const [signKey, setSignKey] = useState(); const [imgWH, setImgWH] = useState({}); const imageRef = useRef(null); const [handleError, setHandleError] = useState(); const [isLoading, setIsLoading] = useState({ isLoad: true, message: "This might take some time" }); const [defaultSignImg, setDefaultSignImg] = useState(); const [isDocId, setIsDocId] = useState(false); const [pdfNewWidth, setPdfNewWidth] = useState(); const [pdfOriginalWidth, setPdfOriginalWidth] = useState(); const [signerPos, setSignerPos] = useState([]); const [noData, setNoData] = useState(false); const [signerObjectId, setSignerObjectId] = useState(); const [isUiLoading, setIsUiLoading] = useState(false); const [isDecline, setIsDecline] = useState({ isDeclined: false }); const [currentSigner, setCurrentSigner] = useState(false); const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" }); const [unSignedWidgetId, setUnSignedWidgetId] = useState(""); const [defaultSignAlert, setDefaultSignAlert] = useState({ isShow: false, alertMessage: "" }); const [isCompleted, setIsCompleted] = useState({ isCertificate: false, isModal: false }); const [myInitial, setMyInitial] = useState(""); const [isInitial, setIsInitial] = useState(false); const [pdfLoadFail, setPdfLoadFail] = useState({ status: false, type: "load" }); const [isSigned, setIsSigned] = useState(false); const [isExpired, setIsExpired] = useState(false); const [alreadySign, setAlreadySign] = useState(false); const [containerWH, setContainerWH] = useState({}); const [validateAlert, setValidateAlert] = useState(false); const [widgetsTour, setWidgetsTour] = useState(false); const [minRequiredCount, setminRequiredCount] = useState(); const [sendInOrder, setSendInOrder] = useState(false); const divRef = useRef(null); const isMobile = window.innerWidth < 767; const rowLevel = localStorage.getItem("rowlevel") && JSON.parse(localStorage.getItem("rowlevel")); const objectId = rowLevel && rowLevel.id ? rowLevel.id : rowLevel?.objectId && rowLevel.objectId; const documentId = docId ? docId : objectId && objectId; const senderUser = localStorage.getItem( `Parse/${localStorage.getItem("parseAppId")}/currentUser` ) && localStorage.getItem( `Parse/${localStorage.getItem("parseAppId")}/currentUser` ); const jsonSender = JSON.parse(senderUser); useEffect(() => { if (documentId) { getDocumentDetails(); } }, []); useEffect(() => { if (divRef.current) { const pdfWidth = pdfNewWidthFun(divRef); setPdfNewWidth(pdfWidth); setContainerWH({ width: divRef.current.offsetWidth, height: divRef.current.offsetHeight }); } }, [divRef.current]); //function for get document details for perticular signer with signer'object id const getDocumentDetails = async () => { let currUserId; //getting document details const documentData = await contractDocument(documentId); if (documentData && documentData.length > 0) { const isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted; const expireDate = documentData[0].ExpiryDate.iso; const declined = documentData[0].IsDeclined && documentData[0].IsDeclined; const expireUpdateDate = new Date(expireDate).getTime(); const currDate = new Date().getTime(); const getSigners = documentData[0].Signers; const getCurrentSigner = getSigners && getSigners.filter( (data) => data.UserId.objectId === jsonSender.objectId ); currUserId = getCurrentSigner[0] ? getCurrentSigner[0].objectId : ""; setSignerObjectId(currUserId); if (documentData[0].SignedUrl) { setPdfUrl(documentData[0].SignedUrl); } else { setPdfUrl(documentData[0].URL); } if (isCompleted) { setIsSigned(true); const data = { isCertificate: true, isModal: true }; setAlreadySign(true); setIsCompleted(data); } else if (declined) { const currentDecline = { currnt: "another", isDeclined: true }; setIsDecline(currentDecline); } else if (currDate > expireUpdateDate) { setIsExpired(true); } if (documentData.length > 0) { const checkDocIdExist = documentData[0].AuditTrail && documentData[0].AuditTrail.length > 0 && documentData[0].AuditTrail.filter( (data) => data.Activity === "Signed" ); const checkAlreadySign = documentData[0].AuditTrail && documentData[0].AuditTrail.length > 0 && documentData[0].AuditTrail.filter( (data) => data.UserPtr.objectId === currUserId && data.Activity === "Signed" ); if ( checkAlreadySign && checkAlreadySign[0] && checkAlreadySign.length > 0 ) { setAlreadySign(true); } else { const obj = documentData?.[0]; setSendInOrder(obj?.SendinOrder || false); if ( obj && obj.Signers && obj.Signers.length > 0 && obj.Placeholders && obj.Placeholders.length > 0 ) { const params = { event: "viewed", body: { objectId: documentData?.[0].objectId, file: documentData?.[0]?.SignedUrl || documentData?.[0]?.URL, name: documentData?.[0].Name, note: documentData?.[0].Note || "", description: documentData?.[0].Description || "", signers: documentData?.[0].Signers?.map((x) => ({ name: x?.Name, email: x?.Email, phone: x?.Phone })), viewedBy: jsonSender.email, viewedAt: new Date(), createdAt: documentData?.[0].createdAt } }; try { await axios.post( `${localStorage.getItem("baseUrl")}functions/callwebhook`, params, { headers: { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), sessiontoken: localStorage.getItem("accesstoken") } } ); } catch (err) { console.log("Err ", err); } } } let signers = []; let unSignedSigner = []; //check document is signed or not if (checkDocIdExist && checkDocIdExist.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; 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 if (signerId === signedExist) { signers.push({ ...signerRes[i], ...signerRes[i] }); isSignedSignature = true; break; } // if does not match then add unsigned data in unSignedSigner array } if (!isSignedSignature) { unSignedSigner.push({ ...signerRes[i], ...signerRes[i] }); } } 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]); setSignerPos(documentData[0].Placeholders); } setPdfDetails(documentData); setIsUiLoading(false); } else { alert("No data found!"); } } else if ( documentData === "Error: Something went wrong!" || (documentData.result && documentData.result.error) ) { const loadObj = { isLoad: false }; setHandleError("Error: Something went wrong!"); setIsLoading(loadObj); } else { setNoData(true); const loadObj = { isLoad: false }; setIsLoading(loadObj); setIsUiLoading(false); } await axios .get( `${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( "_appName" )}_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${ jsonSender.objectId }"}}`, { headers: { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), "X-Parse-Session-Token": localStorage.getItem("accesstoken") } } ) .then((Listdata) => { const json = Listdata.data; const res = json.results; if (res[0] && res.length > 0) { setDefaultSignImg(res[0].ImageURL); setMyInitial(res[0]?.Initials); } const loadObj = { isLoad: false }; setIsLoading(loadObj); }) .catch((err) => { const loadObj = { isLoad: false }; setHandleError("Error: Something went wrong!"); setIsLoading(loadObj); }); }; const checkSendInOrder = () => { 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) { const checkUser = signerPos.filter( (data) => data.signerObjId === signerObjectId ); if (checkUser && checkUser.length > 0) { let unSignUrlData, checkboxExist, requiredRadio, showAlert = false, widgetKey, radioExist, requiredCheckbox ; for (let i = 0; i < checkUser[0].placeHolder.length; i++) { checkboxExist = checkUser[0].placeHolder[i].pos.some( (data) => data.type === "checkbox" ); radioExist = checkUser[0].placeHolder[i].pos.some( (data) => data.type === "radio" ); if (checkboxExist) { requiredCheckbox = checkUser[0].placeHolder[i].pos.filter( (position) => !position.options?.isReadOnly && position.type ==='checkbox' ); if (requiredCheckbox && requiredCheckbox.length > 0) { for (let i = 0; i < requiredCheckbox.length; i++) { const minCount = requiredCheckbox[i].options?.validation?.minRequiredCount; const parseMin = minCount && parseInt(minCount); const maxCount = requiredCheckbox[i].options?.validation?.maxRequiredCount; const parseMax = maxCount && parseInt(maxCount); const response = requiredCheckbox[i].options?.response?.length; const defaultValue = requiredCheckbox[i].options?.defaultValue?.length; if (parseMin === 0 && parseMax === 0) { if (!showAlert) { showAlert = false; } } else if (parseMin === 0 && parseMax > 0) { if (!showAlert) { showAlert = false; } } else if (!response) { if (!defaultValue) { if (!showAlert) { showAlert = true; widgetKey = requiredCheckbox[i].key; setminRequiredCount(parseMin); } } } else if (parseMin > 0 && parseMin > response) { if (!showAlert) { showAlert = true; widgetKey = requiredCheckbox[i].key; setminRequiredCount(parseMin); } } } } } else if(radioExist) { requiredRadio = checkUser[0].placeHolder[i].pos.filter( (position) => !position.options?.isReadOnly && position.type ==='radio' ); if (requiredRadio && requiredRadio.length > 0) { let checkSigned ; for(let i=0; i < requiredRadio.length; i++){ checkSigned = requiredRadio[i]?.options.response if(!checkSigned){ let checkDefaultSigned = requiredRadio[i]?.options.defaultValue if(!checkDefaultSigned){ if (!showAlert) { showAlert = true; widgetKey = requiredRadio[i].key; } } } } } } else{ const requiredWidgets = checkUser[0].placeHolder[i].pos.filter( (position) => !position.options?.isReadOnly ); if(requiredWidgets && requiredCheckbox.length >0){ unSignUrlData = checkUser[0].placeHolder[i].pos.filter( (pos) => !pos?.SignUrl && !pos.options.response ); 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; widgetKey = requiredWidgets[i].key; } } } } } } } } if (checkboxExist && requiredCheckbox && showAlert) { setUnSignedWidgetId(widgetKey); setWidgetsTour(true); }else if(radioExist && showAlert ){ setUnSignedWidgetId(widgetKey); setWidgetsTour(true); } else if (showAlert) { setUnSignedWidgetId(widgetKey); setWidgetsTour(true); } else { setIsUiLoading(true); const pngUrl = checkUser[0].placeHolder; // Load a PDFDocument from the existing PDF bytes const existingPdfBytes = await fetch(pdfUrl).then((res) => res.arrayBuffer() ); const pdfDoc = await PDFDocument.load(existingPdfBytes, { ignoreEncryption: true }); // let pdfBase64; //checking if signature is only one then send image url in jpeg formate to server // if (pngUrl.length === 1 && pngUrl[0].pos.length === 1) { // if (isDocId) { // try { // pdfBase64 = await getBase64FromUrl(pdfUrl); // } catch (err) { // console.log(err); // } // } else { // //embed document's object id to all pages in pdf document // try { // await embedDocId(pdfDoc, documentId, allPages); // } catch (err) { // console.log(err); // } // try { // pdfBase64 = await pdfDoc.saveAsBase64({ // useObjectStreams: false // }); // } catch (err) { // console.log(err); // } // } // for (let pngData of pngUrl) { // const imgUrlList = pngData.pos; // const pageNo = pngData.pageNumber; // imgUrlList.map(async (data) => { // //cheking signUrl is defau;t signature url of custom url // let ImgUrl = data.SignUrl; // const checkUrl = urlValidator(ImgUrl); // //if default signature url then convert it in base 64 // if (checkUrl) { // ImgUrl = await getBase64FromIMG(ImgUrl + "?get"); // } // //function for called convert png signatre to jpeg in base 64 // convertPNGtoJPEG(ImgUrl) // .then((jpegBase64Data) => { // const removeBase64Fromjpeg = "data:image/jpeg;base64,"; // const newImgUrl = jpegBase64Data.replace( // removeBase64Fromjpeg, // "" // ); // //function for call to embed signature in pdf and get digital signature pdf // signPdfFun( // newImgUrl, // documentId, // signerObjectId, // pdfOriginalWidth, // pngUrl, // containerWH, // setIsAlert, // data, // pdfBase64, // pageNo // ) // .then((res) => { // if (res && res.status === "success") { // setPdfUrl(res.data); // setIsSigned(true); // setSignedSigners([]); // setUnSignedSigners([]); // getDocumentDetails(); // } else { // setIsAlert({ // isShow: true, // alertMessage: "something went wrong" // }); // } // }) // .catch((err) => { // setIsAlert({ // isShow: true, // alertMessage: "something went wrong" // }); // }); // }) // .catch((error) => { // console.error("Error:", error); // }); // }); // } // } // //else if signature is more than one then embed all sign with the use of pdf-lib // else if (pngUrl.length > 0 && pngUrl[0].pos.length > 0) { const flag = false; const extUserPtr = pdfDetails[0].ExtUserPtr; const HeaderDocId = extUserPtr?.HeaderDocId; //embed document's object id to all pages in pdf document if (!HeaderDocId) { if (!isDocId) { await embedDocId(pdfDoc, documentId, allPages); } } //embed multi signature in pdf const pdfBytes = await multiSignEmbed( pngUrl, pdfDoc, pdfOriginalWidth, flag, containerWH ); //function for call to embed signature in pdf and get digital signature pdf try { const res = await signPdfFun( pdfBytes, documentId, signerObjectId, pdfOriginalWidth, pngUrl, containerWH, setIsAlert ); if (res && res.status === "success") { setPdfUrl(res.data); setIsSigned(true); setSignedSigners([]); setUnSignedSigners([]); getDocumentDetails(); if (sendInOrder) { const index = pdfDetails?.[0].Signers.findIndex( (x) => x.Email === jsonSender.email ); const newIndex = index + 1; const user = pdfDetails?.[0].Signers[newIndex]; if (user) { let sendMail; // console.log("pdfDetails", pdfDetails); const expireDate = pdfDetails?.[0].ExpiryDate.iso; const newDate = new Date(expireDate); const localExpireDate = newDate.toLocaleDateString("en-US", { day: "numeric", month: "long", year: "numeric" }); let sender = pdfDetails?.[0].ExtUserPtr.Email; try { const imgPng = "https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"; let url = `${localStorage.getItem( "baseUrl" )}functions/sendmailv3/`; const headers = { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), sessionToken: localStorage.getItem("accesstoken") }; const serverUrl = localStorage.getItem("baseUrl"); const newServer = serverUrl.replaceAll("/", "%2F"); const objectId = user.objectId; const serverParams = `${newServer}&${localStorage.getItem( "parseAppId" )}&${localStorage.getItem("_appName")}`; const hostUrl = window.location.origin + "/loadmf/signmicroapp"; let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`; const openSignUrl = "https://www.opensignlabs.com/contact-us"; const orgName = pdfDetails[0]?.ExtUserPtr.Company ? pdfDetails[0].ExtUserPtr.Company : ""; const themeBGcolor = themeColor(); let params = { recipient: user.Email, subject: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, from: sender, html: "
Digital Signature Request
This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + sender + " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.
{isAlert.alertMessage}
You have declined this document!
) : ( isDecline.currnt === "another" && (You cannot sign this document as it has been declined by one or more person(s).
) ) } footerMessage={isDecline.currnt === "Sure"} declineDoc={declineDoc} setIsDecline={setIsDecline} /> {/* this modal is used for show expired alert */}{defaultSignAlert.alertMessage}
{defaultSignImg ? ( <> > ) : ( )}This document has been signed by all Signers.
The input does not meet the criteria set by the regular expression.