mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-04 16:58:05 +02:00
1602 lines
56 KiB
React
1602 lines
56 KiB
React
import { useState, useRef, useEffect, useMemo } from "react";
|
||
import { PDFDocument } from "pdf-lib";
|
||
import "../styles/signature.css";
|
||
import Parse from "parse";
|
||
import Confetti from "react-confetti";
|
||
import axios from "axios";
|
||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||
import EmailComponent from "../components/pdf/EmailComponent";
|
||
import WidgetComponent from "../components/pdf/WidgetComponent";
|
||
import {
|
||
getTenantDetails,
|
||
contractDocument,
|
||
embedDocId,
|
||
embedWidgetsToDoc,
|
||
defaultWidthHeight,
|
||
contractUsers,
|
||
contactBook,
|
||
randomId,
|
||
getDate,
|
||
textWidget,
|
||
cellsWidget,
|
||
convertPdfArrayBuffer,
|
||
textInputWidget,
|
||
fetchImageBase64,
|
||
changeImageWH,
|
||
handleSendOTP,
|
||
getContainerScale,
|
||
getDefaultSignature,
|
||
onClickZoomIn,
|
||
onClickZoomOut,
|
||
rotatePdfPage,
|
||
getBase64FromUrl,
|
||
convertBase64ToFile,
|
||
generatePdfName,
|
||
handleRemoveWidgets,
|
||
addWidgetSelfsignOptions,
|
||
getOriginalWH,
|
||
signatureTypes,
|
||
isEmptyValue,
|
||
flattenPdf,
|
||
base64ToArrayBuffer
|
||
} from "../constant/Utils";
|
||
import { useParams } from "react-router";
|
||
import Tour from "../primitives/Tour";
|
||
import Signedby from "../components/pdf/Signedby";
|
||
import Header from "../components/pdf/PdfHeader";
|
||
import RenderPdf from "../components/pdf/RenderPdf";
|
||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||
import { useDispatch, useSelector } from "react-redux";
|
||
import TextFontSetting from "../components/pdf/TextFontSetting";
|
||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||
import PdfTools from "../components/pdf/PdfTools";
|
||
import { useTranslation } from "react-i18next";
|
||
import RotateAlert from "../components/RotateAlert";
|
||
import DownloadPdfZip from "../primitives/DownloadPdfZip";
|
||
import Loader from "../primitives/Loader";
|
||
import ModalUi from "../primitives/ModalUi";
|
||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||
import HandleError from "../primitives/HandleError";
|
||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||
import {
|
||
setSaveSignCheckbox,
|
||
setMyInitial,
|
||
setDefaultSignImg,
|
||
setIsShowModal,
|
||
resetWidgetState,
|
||
setTypedSignFont,
|
||
setMyStamp
|
||
} from "../redux/reducers/widgetSlice";
|
||
import WidgetsValueModal from "../components/pdf/WidgetsValueModal";
|
||
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
||
import CellsSettingModal from "../components/pdf/CellsSettingModal";
|
||
import { useWindowSize } from "../hook/useWindowSize";
|
||
import {
|
||
applyNumberFormulasToPages,
|
||
} from "../utils";
|
||
|
||
import { useScroll } from "../context/ScrollPdfContext";
|
||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||
function SignYourSelf() {
|
||
const { t } = useTranslation();
|
||
const { docId } = useParams();
|
||
const dispatch = useDispatch();
|
||
const windowSize = useWindowSize();
|
||
const { scrollRef } = useScroll();
|
||
const isShowModal = useSelector((state) => state.widget.isShowModal);
|
||
const appName =
|
||
"OpenSign™";
|
||
const divRef = useRef(null);
|
||
const nodeRef = useRef(null);
|
||
const pdfRef = useRef();
|
||
const numPages = 1;
|
||
const [pdfDetails, setPdfDetails] = useState([]);
|
||
const [allPages, setAllPages] = useState(null);
|
||
const [pdfUrl, setPdfUrl] = useState();
|
||
const [xyPosition, setXyPosition] = useState([]);
|
||
const [pageNumber, setPageNumber] = useState(1);
|
||
const [isEmail, setIsEmail] = useState(false);
|
||
const [signBtnPosition, setSignBtnPosition] = useState([]);
|
||
const [xySignature, setXYSignature] = useState({});
|
||
const [dragKey, setDragKey] = useState();
|
||
const [fontSize, setFontSize] = useState();
|
||
const [fontColor, setFontColor] = useState();
|
||
const [pdfNewWidth, setPdfNewWidth] = useState();
|
||
const [pdfOriginalWH, setPdfOriginalWH] = useState([]);
|
||
const [successEmail, setSuccessEmail] = useState(false);
|
||
const [isUiLoading, setIsUiLoading] = useState(false);
|
||
const [isLoading, setIsLoading] = useState({
|
||
isLoad: true,
|
||
message: t("loading-mssg")
|
||
});
|
||
const [handleError, setHandleError] = useState("");
|
||
const [isDragging, setIsDragging] = useState(false);
|
||
const [signTour, setSignTour] = useState(true);
|
||
const [checkTourStatus, setCheckTourStatus] = useState(false);
|
||
const [signerUserId, setSignerUserId] = useState();
|
||
const [tourStatus, setTourStatus] = useState([]);
|
||
const [contractName, setContractName] = useState("");
|
||
const [containerWH, setContainerWH] = useState({ width: 0, height: 0 });
|
||
const [isPageCopy, setIsPageCopy] = useState(false);
|
||
const [otpLoader, setOtpLoader] = useState(false);
|
||
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
||
status: false
|
||
});
|
||
const [isTextSetting, setIsTextSetting] = useState(false);
|
||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
|
||
const [isCheckbox, setIsCheckbox] = useState(false);
|
||
const [isNameModal, setIsNameModal] = useState(false);
|
||
const [isCellsSetting, setIsCellsSetting] = useState(false);
|
||
const openNameModal = () => setIsNameModal(true);
|
||
const openCellsSettingModal = () => setIsCellsSetting(true);
|
||
const [pdfLoad, setPdfLoad] = useState(false);
|
||
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
|
||
const [isDontShow, setIsDontShow] = useState(true);
|
||
const [isCompleted, setIsCompleted] = useState(false);
|
||
const [isCelebration, setIsCelebration] = useState(false);
|
||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||
const [otp, setOtp] = useState("");
|
||
const [zoomPercent, setZoomPercent] = useState(0);
|
||
const isSidebar = useSelector((state) => state.sidebar.isOpen);
|
||
const [scale, setScale] = useState(1);
|
||
const [pdfBase64Url, setPdfBase64Url] = useState("");
|
||
const [showRotateAlert, setShowRotateAlert] = useState({
|
||
status: false,
|
||
degree: 0
|
||
});
|
||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||
const [isResize, setIsResize] = useState(false);
|
||
const [isUploadPdf, setIsUploadPdf] = useState(false);
|
||
const [unSignedWidgetId, setUnSignedWidgetId] = useState("");
|
||
const [widgetsTour, setWidgetsTour] = useState(false);
|
||
const [owner, setOwner] = useState({});
|
||
// Memoize the xyPosition index for the widget modal to avoid recomputing on every render
|
||
const widgetModalIndex = useMemo(
|
||
() =>
|
||
xyPosition?.findIndex(
|
||
(obj) =>
|
||
obj.pageNumber === (currWidgetsDetails?.pageNumber || pageNumber)
|
||
),
|
||
[xyPosition, currWidgetsDetails?.pageNumber, pageNumber]
|
||
);
|
||
const rowLevel =
|
||
localStorage.getItem("rowlevel") &&
|
||
JSON.parse(localStorage.getItem("rowlevel"));
|
||
const signObjId =
|
||
rowLevel && rowLevel?.id
|
||
? rowLevel.id
|
||
: rowLevel?.objectId && rowLevel.objectId;
|
||
const documentId = docId ? docId : signObjId && signObjId;
|
||
const senderUser =
|
||
localStorage.getItem(
|
||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||
) &&
|
||
localStorage.getItem(
|
||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||
);
|
||
const jsonSender = JSON.parse(senderUser);
|
||
|
||
useEffect(() => {
|
||
dispatch(resetWidgetState([]));
|
||
if (documentId) {
|
||
getDocumentDetails(true);
|
||
}
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, []);
|
||
|
||
|
||
useEffect(() => {
|
||
const updateSize = () => {
|
||
if (divRef.current) {
|
||
const pdfWidth = divRef.current.offsetWidth;
|
||
setPdfNewWidth(pdfWidth);
|
||
setContainerWH({
|
||
width: divRef.current.offsetWidth,
|
||
height: divRef.current.offsetHeight
|
||
});
|
||
setScale(1);
|
||
setZoomPercent(0);
|
||
}
|
||
};
|
||
|
||
// Use setTimeout to wait for the transition to complete
|
||
const timer = setTimeout(updateSize, 150); // match the transition duration
|
||
return () => clearTimeout(timer);
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [divRef.current, isSidebar, windowSize?.width]);
|
||
//function for get document details for perticular signer with signer'object id
|
||
const getDocumentDetails = async (showComplete) => {
|
||
try {
|
||
let isCompleted;
|
||
//getting document details
|
||
const documentData = await contractDocument(documentId);
|
||
|
||
if (documentData && documentData.length > 0) {
|
||
setOwner(documentData?.[0]?.ExtUserPtr);
|
||
setPdfDetails(documentData);
|
||
const placeholders =
|
||
documentData[0]?.Placeholders?.length > 0
|
||
? documentData[0]?.Placeholders
|
||
: [];
|
||
setXyPosition(placeholders);
|
||
const url =
|
||
documentData[0] &&
|
||
(documentData[0]?.SignedUrl || documentData[0]?.URL);
|
||
if (url) {
|
||
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
|
||
const arrayBuffer = await convertPdfArrayBuffer(url);
|
||
const base64Pdf = await getBase64FromUrl(url);
|
||
if (arrayBuffer === "Error") {
|
||
setHandleError(t("something-went-wrong-mssg"));
|
||
} else {
|
||
setPdfArrayBuffer(arrayBuffer);
|
||
setPdfBase64Url(base64Pdf);
|
||
}
|
||
} else {
|
||
setHandleError(t("something-went-wrong-mssg"));
|
||
}
|
||
isCompleted = documentData?.[0]?.IsCompleted;
|
||
if (isCompleted) {
|
||
setIsCelebration(true);
|
||
setTimeout(() => setIsCelebration(false), 5000);
|
||
setIsCompleted(true);
|
||
setPdfUrl(documentData[0].SignedUrl);
|
||
if (showComplete) {
|
||
setShowAlreadySignDoc({
|
||
status: true,
|
||
mssg: t("document-signed-alert")
|
||
});
|
||
} else {
|
||
setIsUiLoading(false);
|
||
setIsEmail(true);
|
||
setXyPosition([]);
|
||
setSignBtnPosition([]);
|
||
}
|
||
}
|
||
} else if (
|
||
documentData === "Error: Something went wrong!" ||
|
||
(documentData.result && documentData.result.error)
|
||
) {
|
||
if (documentData?.result?.error?.includes("deleted")) {
|
||
setHandleError(t("document-deleted"));
|
||
} else {
|
||
setHandleError(t("something-went-wrong-mssg"));
|
||
}
|
||
setIsLoading({ isLoad: false });
|
||
} else {
|
||
setHandleError(t("no-data-available"));
|
||
setIsLoading({ isLoad: false });
|
||
}
|
||
//function to get default signatur eof current user from `contracts_Signature` class
|
||
const defaultSignRes = await getDefaultSignature(jsonSender.objectId);
|
||
if (defaultSignRes?.status === "success") {
|
||
dispatch(
|
||
setSaveSignCheckbox({
|
||
isVisible: true,
|
||
signId: defaultSignRes?.res?.id
|
||
})
|
||
);
|
||
dispatch(setDefaultSignImg(defaultSignRes?.res?.defaultSignature));
|
||
dispatch(setMyInitial(defaultSignRes?.res?.defaultInitial));
|
||
dispatch(setMyStamp(defaultSignRes?.res?.defaultStamp));
|
||
} else {
|
||
dispatch(setSaveSignCheckbox({ isVisible: true }));
|
||
}
|
||
const contractUsersRes = await contractUsers();
|
||
if (contractUsersRes === "Error: Something went wrong!") {
|
||
setHandleError(t("something-went-wrong-mssg"));
|
||
setIsLoading({ isLoad: false });
|
||
} else if (contractUsersRes[0] && contractUsersRes.length > 0) {
|
||
setContractName("_Users");
|
||
setSignerUserId(contractUsersRes[0].objectId);
|
||
const tourstatus = contractUsersRes?.[0]?.TourStatus || [];
|
||
if (tourstatus && tourstatus.length > 0 && !isCompleted) {
|
||
setTourStatus(tourstatus);
|
||
const tour = tourstatus?.some((data) => data.signyourself) || false;
|
||
setSignTour(!tour);
|
||
setCheckTourStatus(tour);
|
||
} else {
|
||
setSignTour(true);
|
||
}
|
||
setIsLoading({ isLoad: false });
|
||
} else if (contractUsersRes.length === 0) {
|
||
const contractContactBook = await contactBook(jsonSender.objectId);
|
||
if (contractContactBook && contractContactBook.length > 0) {
|
||
setContractName("_Contactbook");
|
||
setSignerUserId(contractContactBook[0].objectId);
|
||
const tourstatus = contractContactBook?.[0]?.TourStatus || [];
|
||
if (tourstatus && tourstatus.length > 0 && !isCompleted) {
|
||
setTourStatus(tourstatus);
|
||
const tour = tourstatus?.some((data) => data.signyourself) || false;
|
||
setSignTour(!tour);
|
||
setCheckTourStatus(tour);
|
||
} else {
|
||
setSignTour(true);
|
||
}
|
||
} else {
|
||
setHandleError(t("no-data-available"));
|
||
}
|
||
setIsLoading({ isLoad: false });
|
||
}
|
||
} catch (err) {
|
||
console.log("Error: error in getDocumentDetails", err);
|
||
setHandleError(t("something-went-wrong-mssg"));
|
||
setIsLoading({ isLoad: false });
|
||
}
|
||
};
|
||
const getWidgetValue = (type) => {
|
||
switch (type) {
|
||
case "name":
|
||
return pdfDetails[0].ExtUserPtr.Name;
|
||
case "company":
|
||
return pdfDetails[0].ExtUserPtr.Company;
|
||
case "job title":
|
||
return pdfDetails[0].ExtUserPtr.JobTitle;
|
||
case "email":
|
||
return pdfDetails[0].ExtUserPtr.Email;
|
||
case "checkbox":
|
||
return true;
|
||
case "date":
|
||
return getDate();
|
||
default:
|
||
return "";
|
||
}
|
||
};
|
||
//function for setting position after drop signature button over pdf
|
||
const addPositionOfSignature = (item, monitor) => {
|
||
setCurrWidgetsDetails({});
|
||
const key = randomId();
|
||
let dropData = [];
|
||
let dropObj = {};
|
||
let filterDropPos = xyPosition?.filter(
|
||
(data) => data.pageNumber === pageNumber
|
||
);
|
||
const dragTypeValue = item?.text ? item.text : monitor.type;
|
||
const widgetValue = getWidgetValue(dragTypeValue);
|
||
const widgetTypeExist = [
|
||
"name",
|
||
"company",
|
||
"job title",
|
||
"email",
|
||
cellsWidget
|
||
].includes(dragTypeValue);
|
||
const containerScale = getContainerScale(
|
||
pdfOriginalWH,
|
||
pageNumber,
|
||
containerWH
|
||
);
|
||
const widgetWidth =
|
||
defaultWidthHeight(dragTypeValue).width * containerScale;
|
||
const widgetHeight =
|
||
defaultWidthHeight(dragTypeValue).height * containerScale;
|
||
//adding and updating drop position in array when user drop signature button in div
|
||
if (item === "onclick") {
|
||
// Use the current page container (id="container") so that the
|
||
// height reflects one page, not the entire multi-page document.
|
||
const containerEl =
|
||
document.getElementById("container") || divRef.current;
|
||
const divWidth = containerEl.getBoundingClientRect().width;
|
||
const divHeight = containerEl.getBoundingClientRect().height;
|
||
// Compute the pixel‐space center within the PDF viewport:
|
||
const centerX_Pixels = divWidth / 2 - widgetWidth / 2;
|
||
const xPosition_Final = centerX_Pixels / (containerScale * scale);
|
||
dropObj = {
|
||
//onclick put placeholder center on pdf
|
||
xPosition: xPosition_Final,
|
||
yPosition:
|
||
(divHeight / 2 - widgetHeight / 2) / (containerScale * scale),
|
||
isStamp:
|
||
(dragTypeValue === "stamp" || dragTypeValue === "image") && true,
|
||
key: key,
|
||
type: dragTypeValue,
|
||
scale: containerScale,
|
||
Width: widgetWidth / (containerScale * scale),
|
||
Height: widgetHeight / (containerScale * scale),
|
||
options: addWidgetSelfsignOptions(
|
||
dragTypeValue,
|
||
getWidgetValue,
|
||
owner,
|
||
xyPosition,
|
||
true
|
||
)
|
||
};
|
||
dropData.push(dropObj);
|
||
} else {
|
||
//This method returns the offset of the current pointer (mouse) position relative to the client viewport.
|
||
const offset = monitor.getClientOffset();
|
||
const containerRect = document
|
||
.getElementById("container")
|
||
.getBoundingClientRect();
|
||
//`containerRect.left`, The distance from the left of the viewport to the left side of the element.
|
||
//`containerRect.top` The distance from the top of the viewport to the top of the element.
|
||
const x = offset.x - containerRect.left;
|
||
const y = offset.y - containerRect.top;
|
||
let getXPosition = signBtnPosition[0] ? x - signBtnPosition[0].xPos : x;
|
||
let getYPosition = signBtnPosition[0] ? y - signBtnPosition[0].yPos : y;
|
||
|
||
// to avoid negative position values (half portion of widget should not be out of pdf container)
|
||
const calculateWidth = getXPosition + widgetWidth - containerRect.width;
|
||
const calculateHeight =
|
||
getYPosition + widgetHeight - containerRect.height;
|
||
|
||
// to avoid negative position values (half portion of widget should not be out of pdf container)
|
||
if (getXPosition < 0) {
|
||
getXPosition = 0;
|
||
}
|
||
if (getYPosition < 0) {
|
||
getYPosition = 0;
|
||
}
|
||
|
||
if (calculateWidth > 0) {
|
||
getXPosition = getXPosition - calculateWidth;
|
||
}
|
||
|
||
if (calculateHeight > 0) {
|
||
getYPosition = getYPosition - calculateHeight;
|
||
}
|
||
dropObj = {
|
||
xPosition: getXPosition / (containerScale * scale),
|
||
yPosition: getYPosition / (containerScale * scale),
|
||
isStamp:
|
||
(dragTypeValue === "stamp" || dragTypeValue === "image") && true,
|
||
key: key,
|
||
type: dragTypeValue,
|
||
Width: widgetWidth / (containerScale * scale),
|
||
Height: widgetHeight / (containerScale * scale),
|
||
options: addWidgetSelfsignOptions(
|
||
dragTypeValue,
|
||
getWidgetValue,
|
||
owner,
|
||
xyPosition,
|
||
pdfDetails?.[0],
|
||
true
|
||
),
|
||
scale: containerScale
|
||
};
|
||
dropData.push(dropObj);
|
||
}
|
||
if (filterDropPos?.length > 0) {
|
||
const index = xyPosition?.findIndex((object) => {
|
||
return object.pageNumber === pageNumber;
|
||
});
|
||
const updateData = filterDropPos?.[0].pos;
|
||
const newSignPos = updateData.concat(dropData);
|
||
const xyPos = { pageNumber, pos: newSignPos };
|
||
setXyPosition((prev) => {
|
||
const updated = [...prev];
|
||
updated.splice(index, 1, xyPos);
|
||
return updated;
|
||
});
|
||
} else {
|
||
const xyPos = { pageNumber: pageNumber, pos: dropData };
|
||
setXyPosition((prev) => [...prev, xyPos]);
|
||
}
|
||
if (dragTypeValue === "checkbox") {
|
||
setIsCheckbox(true);
|
||
} else if (
|
||
[
|
||
textInputWidget,
|
||
textWidget,
|
||
cellsWidget,
|
||
"name",
|
||
"company",
|
||
"job title",
|
||
"email"
|
||
].includes(dragTypeValue)
|
||
) {
|
||
setFontSize(12);
|
||
setFontColor("black");
|
||
}
|
||
dispatch(setIsShowModal({ [key]: true }));
|
||
setCurrWidgetsDetails({ ...dropObj, pageNumber: pageNumber });
|
||
};
|
||
|
||
//`handleResend` function is used to resend otp for email verification
|
||
const handleResend = async (e) => {
|
||
e.preventDefault();
|
||
setOtpLoader(true);
|
||
await handleSendOTP(Parse.User.current().getEmail());
|
||
setOtpLoader(false);
|
||
alert(t("otp-sent-alert"));
|
||
};
|
||
//`handleVerifyEmail` function is used to verify email with otp
|
||
const handleVerifyEmail = async (e) => {
|
||
e.preventDefault();
|
||
setOtpLoader(true);
|
||
try {
|
||
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||
otp: otp,
|
||
email: Parse.User.current().getEmail()
|
||
});
|
||
if (resEmail?.message === "Email is verified.") {
|
||
setIsEmailVerified(true);
|
||
alert(t("Email-verified-alert-1"));
|
||
} else if (resEmail?.message === "Email is already verified.") {
|
||
setIsEmailVerified(true);
|
||
alert(t("Email-verified-alert-2"));
|
||
}
|
||
setOtp("");
|
||
setIsVerifyModal(false);
|
||
// handleRecipientSign();
|
||
} catch (error) {
|
||
alert(error.message);
|
||
} finally {
|
||
setOtpLoader(false);
|
||
}
|
||
};
|
||
//`handleVerifyBtn` function is used to send otp on user mail
|
||
const handleVerifyBtn = async () => {
|
||
setIsVerifyModal(true);
|
||
await handleSendOTP(Parse.User.current().getEmail());
|
||
};
|
||
useEffect(() => {
|
||
const timer = setTimeout(() => {
|
||
if (
|
||
!pdfDetails?.[0]?.IsCompleted &&
|
||
pdfDetails?.[0]?.CreatedBy?.objectId === jsonSender?.objectId
|
||
) {
|
||
autosavedetails();
|
||
}
|
||
}, 2000);
|
||
return () => clearTimeout(timer);
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [xyPosition, pdfBase64Url]);
|
||
// `autosavedetails` is used to save doc details after every 2 sec when changes are happern in placeholder like drag-drop widgets, remove signers
|
||
const autosavedetails = async () => {
|
||
let pdfUrl;
|
||
if (isUploadPdf) {
|
||
const pdfName = generatePdfName(16);
|
||
pdfUrl = await convertBase64ToFile(
|
||
pdfName,
|
||
pdfBase64Url,
|
||
"",
|
||
);
|
||
}
|
||
const widgetsType = ["signature", "stamp", "image", "initials"];
|
||
let updatedXYPosition;
|
||
if (xyPosition.length > 0) {
|
||
updatedXYPosition = xyPosition.map((item) => ({
|
||
...item,
|
||
pos: item.pos.map((posItem) => {
|
||
if (widgetsType.includes(posItem.type)) {
|
||
// eslint-disable-next-line
|
||
const { SignUrl, ...rest } = posItem;
|
||
// eslint-disable-next-line
|
||
const { response, ...filteredOptions } = rest.options;
|
||
return {
|
||
...rest,
|
||
options: filteredOptions
|
||
};
|
||
} else {
|
||
return posItem;
|
||
}
|
||
})
|
||
}));
|
||
}
|
||
try {
|
||
const currentExtUser =
|
||
JSON.parse(localStorage.getItem("Extand_Class") || "[]")?.[0] || {};
|
||
const useNameAsSender = currentExtUser?.UseNameAsSender === true;
|
||
const senderName =
|
||
pdfDetails?.[0]?.SenderName ||
|
||
(useNameAsSender ? currentExtUser?.Name || "" : "");
|
||
const senderMail =
|
||
pdfDetails?.[0]?.SenderMail ||
|
||
(useNameAsSender ? currentExtUser?.Email || "" : "");
|
||
const docCls = new Parse.Object("contracts_Document");
|
||
docCls.id = documentId;
|
||
if (xyPosition?.length > 0) {
|
||
docCls.set("Placeholders", updatedXYPosition);
|
||
}
|
||
docCls.set("IsSignyourself", true);
|
||
if (pdfUrl) {
|
||
docCls.set("URL", pdfUrl);
|
||
}
|
||
if (senderName) {
|
||
docCls.set("SenderName", senderName);
|
||
}
|
||
if (senderMail) {
|
||
docCls.set("SenderMail", senderMail);
|
||
}
|
||
const res = await docCls.save();
|
||
if (res) {
|
||
if (res) {
|
||
pdfDetails[0] = { ...pdfDetails[0], URL: pdfUrl };
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.log("error", e);
|
||
alert(t("something-went-wrong-mssg"));
|
||
}
|
||
};
|
||
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
||
async function embedWidgetsData() {
|
||
//check current user email is verified or not
|
||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||
let isEmailVerified;
|
||
isEmailVerified = currentUser?.emailVerified;
|
||
const isEnableOTP = pdfDetails?.[0]?.IsEnableOTP || false;
|
||
if (isEnableOTP) {
|
||
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(t("something-went-wrong-mssg"));
|
||
}
|
||
}
|
||
}
|
||
if (!isEnableOTP || isEmailVerified) {
|
||
let showAlert = false,
|
||
widgetKey,
|
||
tourPageNumber;
|
||
try {
|
||
const isSignatureExist = xyPosition?.some((p) =>
|
||
p?.pos?.some((x) => x?.type === "signature")
|
||
);
|
||
if (!isSignatureExist) {
|
||
setIsAlert({
|
||
header: t("fields-required"),
|
||
isShow: true,
|
||
alertMessage: t("signature-widget-alert-1")
|
||
});
|
||
return;
|
||
}
|
||
|
||
for (let i = 0; i < xyPosition?.length; i++) {
|
||
tourPageNumber = xyPosition[i]?.pageNumber;
|
||
const requiredWidgets = xyPosition[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 (isEmptyValue(checkSigned)) {
|
||
const checkSignUrl = requiredWidgets[i]?.SignUrl;
|
||
let checkDefaultSigned =
|
||
requiredWidgets[i]?.options?.defaultValue;
|
||
if (
|
||
!checkSignUrl &&
|
||
isEmptyValue(checkDefaultSigned) &&
|
||
!showAlert
|
||
) {
|
||
showAlert = true;
|
||
widgetKey = requiredWidgets[i]?.key;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (showAlert) {
|
||
break;
|
||
}
|
||
}
|
||
if (showAlert) {
|
||
setUnSignedWidgetId(widgetKey);
|
||
setPageNumber(tourPageNumber);
|
||
setWidgetsTour(true);
|
||
return;
|
||
} else {
|
||
setIsUiLoading(true);
|
||
// Load a PDFDocument from the existing PDF bytes
|
||
try {
|
||
const pdfDoc = await PDFDocument.load(pdfArrayBuffer);
|
||
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) {
|
||
//pdfOriginalWH contained all pdf's pages width,height & pagenumber in array format
|
||
await embedDocId(pdfOriginalWH, pdfDoc, documentId);
|
||
}
|
||
//embed all widgets in document
|
||
const pdfBytes = await embedWidgetsToDoc(
|
||
xyPosition,
|
||
pdfDoc,
|
||
isSignYourSelfFlow,
|
||
scale
|
||
);
|
||
//function for call to embed signature in pdf and get digital signature pdf
|
||
if (!pdfBytes?.error) {
|
||
await signPdfFun(pdfBytes, documentId);
|
||
} else {
|
||
setIsUiLoading(false);
|
||
setIsAlert({
|
||
header: t("error"),
|
||
isShow: true,
|
||
alertMessage: t("pdf-uncompatible", { appName: appName })
|
||
});
|
||
}
|
||
} catch (err) {
|
||
setIsUiLoading(false);
|
||
if (err && err.message.includes("is encrypted.")) {
|
||
setIsAlert({
|
||
header: t("error"),
|
||
isShow: true,
|
||
alertMessage: t("encrypted-pdf-alert")
|
||
});
|
||
} else {
|
||
console.log("err in signing", err.message);
|
||
if (err?.message?.includes("password")) {
|
||
setIsAlert({
|
||
header: t("error"),
|
||
isShow: true,
|
||
alertMessage: t("encrypted-pdf-alert-1")
|
||
});
|
||
} else {
|
||
setIsAlert({
|
||
header: t("error"),
|
||
isShow: true,
|
||
alertMessage: t("something-went-wrong-mssg")
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.log("err in embedselfsign ", err);
|
||
setIsUiLoading(false);
|
||
setIsAlert({
|
||
header: t("error"),
|
||
isShow: true,
|
||
alertMessage: t("something-went-wrong-mssg")
|
||
});
|
||
}
|
||
}
|
||
}
|
||
//function for get digital signature
|
||
const signPdfFun = async (base64Url, documentId) => {
|
||
let isCustomCompletionMail = false;
|
||
const tenantDetails = await getTenantDetails(jsonSender.objectId);
|
||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||
alert(t("user-not-exist"));
|
||
} else {
|
||
if (
|
||
tenantDetails?.CompletionBody &&
|
||
tenantDetails?.CompletionSubject
|
||
) {
|
||
isCustomCompletionMail = true;
|
||
}
|
||
}
|
||
// below for loop is used to get first signature of user to send if to signpdf
|
||
// for adding it in completion certificate
|
||
let getSignature;
|
||
for (let item of xyPosition) {
|
||
if (!getSignature) {
|
||
const typeExist = item.pos.some((data) => data?.type);
|
||
if (typeExist) {
|
||
getSignature = item.pos.find((data) => data?.type === "signature");
|
||
} else {
|
||
getSignature = item.pos.find((data) => !data.isStamp);
|
||
}
|
||
}
|
||
}
|
||
let base64Sign = getSignature.SignUrl;
|
||
//check https type signature (default signature exist) then convert in base64
|
||
const isUrl = base64Sign.includes("https");
|
||
if (isUrl) {
|
||
try {
|
||
base64Sign = await fetchImageBase64(base64Sign);
|
||
} catch (e) {
|
||
console.log("error", e);
|
||
alert(t("something-went-wrong-mssg"));
|
||
}
|
||
}
|
||
//change image width and height to 300/120 in png base64
|
||
const imagebase64 = await changeImageWH(base64Sign);
|
||
//remove suffiix of base64 (without type)
|
||
const suffixbase64 = imagebase64 && imagebase64.split(",").pop();
|
||
const params = {
|
||
pdfFile: base64Url,
|
||
docId: documentId,
|
||
isCustomCompletionMail: isCustomCompletionMail,
|
||
signature: suffixbase64,
|
||
};
|
||
const resSignPdf = await Parse.Cloud.run("signPdf", params);
|
||
if (resSignPdf) {
|
||
const signedpdf = JSON.parse(JSON.stringify(resSignPdf));
|
||
setPdfUrl(signedpdf);
|
||
dispatch(setTypedSignFont("Fasthand"));
|
||
getDocumentDetails(false);
|
||
}
|
||
};
|
||
//function for save x and y position and show signature tab on that position
|
||
const handleTabDrag = (key) => {
|
||
setDragKey(key);
|
||
setIsDragging(true);
|
||
};
|
||
//function for set and update x and y postion after drag and drop signature tab
|
||
const handleStop = (
|
||
event,
|
||
dragElement,
|
||
_signerId,
|
||
_key,
|
||
widgetPageNumber
|
||
) => {
|
||
setFontSize();
|
||
setFontColor();
|
||
if (!isResize && isDragging && dragElement) {
|
||
event.preventDefault();
|
||
const effectivePageNumber = widgetPageNumber || pageNumber;
|
||
const containerScale = getContainerScale(
|
||
pdfOriginalWH,
|
||
effectivePageNumber,
|
||
containerWH
|
||
);
|
||
if (dragKey >= 0) {
|
||
const effectiveIndex = xyPosition?.findIndex(
|
||
(object) => object.pageNumber === effectivePageNumber
|
||
);
|
||
const filterDropPos = xyPosition?.filter(
|
||
(data) => data.pageNumber === effectivePageNumber
|
||
);
|
||
if (filterDropPos?.length > 0 && effectiveIndex >= 0) {
|
||
const addSign = xyPosition[effectiveIndex]?.pos?.map((url) => {
|
||
if (url.key === dragKey) {
|
||
return {
|
||
...url,
|
||
xPosition: dragElement.x / containerScale,
|
||
yPosition: dragElement.y / containerScale
|
||
};
|
||
}
|
||
return url;
|
||
});
|
||
|
||
const newUpdateUrl = xyPosition.map((obj, ind) => {
|
||
if (ind === effectiveIndex) {
|
||
return { ...obj, pos: addSign };
|
||
}
|
||
return obj;
|
||
});
|
||
setXyPosition(newUpdateUrl);
|
||
}
|
||
}
|
||
}
|
||
setTimeout(() => {
|
||
setIsDragging(false);
|
||
}, 200);
|
||
};
|
||
//function for get pdf page details
|
||
const pageDetails = async (pdf) => {
|
||
const pdfWHObj = await getOriginalWH(pdf);
|
||
setPdfOriginalWH(pdfWHObj);
|
||
setPdfLoad(true);
|
||
};
|
||
|
||
//function for change page numver of pdf
|
||
function changePage(offset) {
|
||
setSignBtnPosition([]);
|
||
setPageNumber((prevPageNumber) => prevPageNumber + offset);
|
||
}
|
||
|
||
//function for capture position on hover or touch widgets button
|
||
const handleDivClick = (e) => {
|
||
const isTouchEvent = e.type.startsWith("touch");
|
||
const divRect = e.currentTarget.getBoundingClientRect();
|
||
let mouseX, mouseY;
|
||
if (isTouchEvent) {
|
||
const touch = e.touches[0]; // Get the first touch point
|
||
mouseX = touch.clientX - divRect.left;
|
||
mouseY = touch.clientY - divRect.top;
|
||
setSignBtnPosition([{ xPos: mouseX, yPos: mouseY }]);
|
||
} else {
|
||
mouseX = e.clientX - divRect.left;
|
||
mouseY = e.clientY - divRect.top;
|
||
const xyPosition = { xPos: mouseX, yPos: mouseY };
|
||
setXYSignature(xyPosition);
|
||
}
|
||
};
|
||
|
||
//function for capture position of x and y on hover signature button last position
|
||
const handleMouseLeave = () => {
|
||
setSignBtnPosition([xySignature]);
|
||
};
|
||
//function for delete signature block
|
||
const handleDeleteWidget = (key, _Id, widgetPageNumber) => {
|
||
const effectivePageNumber = widgetPageNumber || pageNumber;
|
||
const effectiveIndex = xyPosition?.findIndex(
|
||
(object) => object.pageNumber === effectivePageNumber
|
||
);
|
||
setCurrWidgetsDetails({});
|
||
const updateResizeData = [];
|
||
if (effectiveIndex < 0) return;
|
||
let filterData = xyPosition[effectiveIndex].pos.filter(
|
||
(data) => data.key !== key
|
||
);
|
||
//delete and update block position
|
||
if (filterData.length > 0) {
|
||
updateResizeData.push(filterData);
|
||
const newUpdatePos = xyPosition.map((obj, ind) => {
|
||
if (ind === effectiveIndex) {
|
||
return { ...obj, pos: updateResizeData[0] };
|
||
}
|
||
return obj;
|
||
});
|
||
|
||
setXyPosition(newUpdatePos);
|
||
} else {
|
||
const getRemainPage = xyPosition.filter(
|
||
(data) => data.pageNumber !== effectivePageNumber
|
||
);
|
||
|
||
if (getRemainPage && getRemainPage.length > 0) {
|
||
setXyPosition(getRemainPage);
|
||
} else {
|
||
setXyPosition([]);
|
||
}
|
||
}
|
||
};
|
||
|
||
const handleDontShow = (isChecked) => {
|
||
setIsDontShow(isChecked);
|
||
};
|
||
const tourConfig = [
|
||
{
|
||
selector: '[data-tut="addWidgets"]',
|
||
content: () => (
|
||
<TourContentWithBtn
|
||
message={t("tour-mssg.signyour-self-1")}
|
||
isDontShowCheckbox={!checkTourStatus}
|
||
isChecked={handleDontShow}
|
||
/>
|
||
),
|
||
position: "top",
|
||
styles: { fontSize: "13px" }
|
||
},
|
||
{
|
||
selector: '[data-tut="reactourSecond"]',
|
||
content: () => (
|
||
<TourContentWithBtn
|
||
message={t("tour-mssg.signyour-self-2")}
|
||
isDontShowCheckbox={!checkTourStatus}
|
||
isChecked={handleDontShow}
|
||
/>
|
||
),
|
||
position: "top",
|
||
styles: { fontSize: "13px" }
|
||
},
|
||
{
|
||
selector: '[data-tut="pdftools"]',
|
||
content: () => (
|
||
<TourContentWithBtn
|
||
message={t("pdf-tools-tour")}
|
||
isDontShowCheckbox={!checkTourStatus}
|
||
isChecked={handleDontShow}
|
||
/>
|
||
),
|
||
position: "top",
|
||
styles: { fontSize: "13px" }
|
||
}
|
||
];
|
||
|
||
//function for update TourStatus
|
||
const closeTour = async () => {
|
||
setSignTour(false);
|
||
setIsDontShow(true);
|
||
if (!checkTourStatus && isDontShow) {
|
||
let updatedTourStatus = [];
|
||
if (tourStatus.length > 0) {
|
||
updatedTourStatus = [...tourStatus];
|
||
const signyourselfIndex = tourStatus.findIndex(
|
||
(obj) => obj["signyourself"] === false || obj["signyourself"] === true
|
||
);
|
||
if (signyourselfIndex !== -1) {
|
||
updatedTourStatus[signyourselfIndex] = { signyourself: true };
|
||
} else {
|
||
updatedTourStatus.push({ signyourself: true });
|
||
}
|
||
} else {
|
||
updatedTourStatus = [{ signyourself: true }];
|
||
}
|
||
try {
|
||
await axios.put(
|
||
`${localStorage.getItem("baseUrl")}classes/contracts${contractName}/${signerUserId}`,
|
||
{ TourStatus: updatedTourStatus },
|
||
{
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||
sessionToken: localStorage.getItem("accesstoken")
|
||
}
|
||
}
|
||
);
|
||
} catch (err) {
|
||
console.log("axois err ", err);
|
||
alert(t("something-went-wrong-mssg"));
|
||
}
|
||
}
|
||
};
|
||
|
||
const handleSaveWidgetsOptions = (
|
||
dropdownName,
|
||
dropdownOptions,
|
||
minCount,
|
||
maxCount,
|
||
isReadOnly,
|
||
addOption,
|
||
deleteOption,
|
||
status,
|
||
defaultValue,
|
||
isHideLabel,
|
||
layout
|
||
) => {
|
||
const widgetPageNumber = currWidgetsDetails?.pageNumber || pageNumber;
|
||
const getPageNumer = xyPosition.filter(
|
||
(data) => data.pageNumber === widgetPageNumber
|
||
);
|
||
if (getPageNumer.length > 0) {
|
||
const widgetLayout =
|
||
currWidgetsDetails?.type === "checkbox" ? { layout: layout } : {};
|
||
const addSignPos = getPageNumer?.[0]?.pos?.map((position) => {
|
||
if (position.key === currWidgetsDetails?.key) {
|
||
if (addOption) {
|
||
return {
|
||
...position,
|
||
Height: position.Height
|
||
? position.Height + 15
|
||
: defaultWidthHeight(currWidgetsDetails?.type).height + 15
|
||
};
|
||
} else if (deleteOption) {
|
||
return {
|
||
...position,
|
||
Height: position.Height
|
||
? position.Height - 15
|
||
: defaultWidthHeight(currWidgetsDetails?.type).height - 15
|
||
};
|
||
} else {
|
||
return {
|
||
...position,
|
||
options: {
|
||
...position.options,
|
||
name: dropdownName,
|
||
values: dropdownOptions,
|
||
...widgetLayout,
|
||
isReadOnly: isReadOnly,
|
||
isHideLabel: isHideLabel || false,
|
||
fontSize:
|
||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||
fontColor:
|
||
fontColor || currWidgetsDetails?.options?.fontColor || "black"
|
||
}
|
||
};
|
||
}
|
||
}
|
||
return position;
|
||
});
|
||
const updateXYposition = xyPosition.map((obj, ind) => {
|
||
if (obj?.pageNumber === widgetPageNumber) {
|
||
return { ...obj, pos: addSignPos };
|
||
}
|
||
return obj;
|
||
});
|
||
setXyPosition(updateXYposition);
|
||
if (!addOption && !deleteOption) {
|
||
handleCloseModal();
|
||
}
|
||
}
|
||
setFontSize();
|
||
setFontColor();
|
||
};
|
||
const handleCloseModal = () => {
|
||
setCurrWidgetsDetails({});
|
||
setIsCheckbox(false);
|
||
};
|
||
const handleTextSettingModal = (value) => {
|
||
setIsTextSetting(value);
|
||
};
|
||
const handleSaveFontSize = () => {
|
||
const widgetPageNumber = currWidgetsDetails?.pageNumber || pageNumber;
|
||
const getPageNumer = xyPosition.filter(
|
||
(data) => data.pageNumber === widgetPageNumber
|
||
);
|
||
if (getPageNumer.length > 0) {
|
||
const addSignPos = getPageNumer?.[0]?.pos?.map((position) => {
|
||
if (position.key === currWidgetsDetails?.key) {
|
||
return {
|
||
...position,
|
||
options: {
|
||
...position.options,
|
||
fontSize: fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||
fontColor:
|
||
fontColor || currWidgetsDetails?.options?.fontColor || "black"
|
||
}
|
||
};
|
||
}
|
||
return position;
|
||
});
|
||
const updateXYposition = xyPosition.map((obj, ind) => {
|
||
if (obj?.pageNumber === widgetPageNumber) {
|
||
return { ...obj, pos: addSignPos };
|
||
}
|
||
return obj;
|
||
});
|
||
setXyPosition(updateXYposition);
|
||
setFontSize();
|
||
setFontColor();
|
||
handleTextSettingModal(false);
|
||
}
|
||
};
|
||
|
||
const setCellCount = (key, newCount) => {
|
||
const widgetPageNumber = currWidgetsDetails?.pageNumber || pageNumber;
|
||
setXyPosition((prev) => {
|
||
const getPageNumer = prev.filter(
|
||
(data) => data.pageNumber === widgetPageNumber
|
||
);
|
||
if (getPageNumer.length > 0) {
|
||
const updatePos = getPageNumer[0].pos.map((p) =>
|
||
p.key === key
|
||
? { ...p, options: { ...p.options, cellCount: newCount } }
|
||
: p
|
||
);
|
||
return prev.map((obj, ind) =>
|
||
obj?.pageNumber === widgetPageNumber
|
||
? { ...obj, pos: updatePos }
|
||
: obj
|
||
);
|
||
}
|
||
return prev;
|
||
});
|
||
};
|
||
|
||
const handleWidgetdefaultdata = (defaultdata) => {
|
||
const widgetPageNumber = currWidgetsDetails?.pageNumber || pageNumber;
|
||
const newFontSize =
|
||
defaultdata?.fontSize !== undefined ? defaultdata.fontSize : fontSize;
|
||
const newFontColor =
|
||
defaultdata?.fontColor !== undefined ? defaultdata.fontColor : fontColor;
|
||
|
||
const getPageNumer = xyPosition.filter(
|
||
(data) => data.pageNumber === widgetPageNumber
|
||
);
|
||
if (getPageNumer.length > 0) {
|
||
const updatePos = getPageNumer[0].pos.map((position) => {
|
||
const textSize = newFontSize || position?.options?.fontSize;
|
||
const textColor = newFontColor || position?.options?.fontColor;
|
||
if (position.key === currWidgetsDetails?.key) {
|
||
if (position.type === cellsWidget) {
|
||
const count = parseInt(
|
||
defaultdata?.cellCount ?? position.options?.cellCount ?? 5,
|
||
10
|
||
);
|
||
return {
|
||
...position,
|
||
options: {
|
||
...position.options,
|
||
name: defaultdata?.name || position.options?.name || "Cells",
|
||
cellCount: count,
|
||
defaultValue: (defaultdata?.defaultValue || "").slice(0, count),
|
||
fontSize: textSize || 12,
|
||
fontColor: textColor || "black"
|
||
}
|
||
};
|
||
}
|
||
else {
|
||
return {
|
||
...position,
|
||
options: {
|
||
...position.options,
|
||
name: defaultdata?.name || position.options?.name,
|
||
fontSize: textSize || 12,
|
||
fontColor: textColor || "black"
|
||
}
|
||
};
|
||
}
|
||
}
|
||
return position;
|
||
});
|
||
const updateXYposition = xyPosition.map((obj, ind) =>
|
||
obj?.pageNumber === widgetPageNumber ? { ...obj, pos: updatePos } : obj
|
||
);
|
||
setXyPosition(applyNumberFormulasToPages(updateXYposition));
|
||
}
|
||
setFontSize();
|
||
setFontColor();
|
||
setCurrWidgetsDetails({});
|
||
setIsNameModal(false);
|
||
};
|
||
|
||
const handleNameModal = () => {
|
||
setIsNameModal(false);
|
||
setCurrWidgetsDetails({});
|
||
setIsCheckbox(false);
|
||
};
|
||
|
||
const handleCellsSettingSave = (data) => {
|
||
// ensure font and color are updated before applying widget changes
|
||
if (data?.fontSize !== undefined) setFontSize(data.fontSize);
|
||
if (data?.fontColor !== undefined) setFontColor(data.fontColor);
|
||
handleWidgetdefaultdata(data);
|
||
setIsCellsSetting(false);
|
||
};
|
||
|
||
const handleCellsSettingClose = () => {
|
||
setIsCellsSetting(false);
|
||
setCurrWidgetsDetails({});
|
||
};
|
||
const clickOnZoomIn = () => {
|
||
onClickZoomIn(scale, setScale, scrollRef);
|
||
};
|
||
const clickOnZoomOut = () => {
|
||
onClickZoomOut(scale, setScale, scrollRef);
|
||
};
|
||
//`handleRotationFun` function is used to roatate pdf particular page
|
||
const handleRotationFun = async (rotateDegree) => {
|
||
const isPlaceholder = handleRotateWarning();
|
||
if (isPlaceholder) {
|
||
setShowRotateAlert({ status: true, degree: rotateDegree });
|
||
} else {
|
||
setIsUploadPdf(true);
|
||
const urlDetails = await rotatePdfPage(
|
||
rotateDegree,
|
||
pageNumber - 1,
|
||
pdfArrayBuffer
|
||
);
|
||
setPdfArrayBuffer(urlDetails.arrayBuffer);
|
||
setPdfBase64Url(urlDetails.base64);
|
||
}
|
||
};
|
||
|
||
//function to show warning when user rotate page and there are some already widgets on that page
|
||
const handleRotateWarning = () => {
|
||
const placeholderData = xyPosition?.filter(
|
||
(data) => data.pageNumber === pageNumber
|
||
);
|
||
if (placeholderData && placeholderData.length > 0) {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
};
|
||
//function to use remove widgets from current page when user want to rotate page
|
||
const handleRemovePlaceholder = async () => {
|
||
handleRemoveWidgets(
|
||
setXyPosition,
|
||
xyPosition,
|
||
pageNumber,
|
||
setShowRotateAlert
|
||
);
|
||
const urlDetails = await rotatePdfPage(
|
||
showRotateAlert.degree,
|
||
pageNumber - 1,
|
||
pdfArrayBuffer
|
||
);
|
||
setPdfArrayBuffer(urlDetails.arrayBuffer);
|
||
setPdfBase64Url(urlDetails.base64);
|
||
setShowRotateAlert({ status: false, degree: 0 });
|
||
};
|
||
const WidgetTourConfig = [
|
||
{
|
||
selector: '[data-tut="IsSigned"]',
|
||
content: t("signature-validate-alert-2"),
|
||
position: "top",
|
||
styles: { fontSize: "13px" }
|
||
}
|
||
];
|
||
const closeWidgetTour = () => {
|
||
setWidgetsTour(false);
|
||
};
|
||
|
||
return (
|
||
<>
|
||
{isLoading.isLoad ? (
|
||
<LoaderWithMsg isLoading={isLoading} />
|
||
) : handleError ? (
|
||
<HandleError handleError={handleError} />
|
||
) : (
|
||
<div>
|
||
{isCelebration && (
|
||
<div className="relative z-[1000]">
|
||
<Confetti
|
||
width={window.innerWidth}
|
||
height={window.innerHeight}
|
||
recycle={false} // Prevents confetti from repeating
|
||
gravity={0.1} // Adjust the gravity to control the speed
|
||
/>
|
||
</div>
|
||
)}
|
||
<div className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300">
|
||
{isUiLoading && (
|
||
<div className="absolute h-full w-full z-[999] flex flex-col justify-center items-center bg-[#e6f2f2]/80">
|
||
<Loader />
|
||
<span className="text-[13px]">{t("loader")}</span>
|
||
</div>
|
||
)}
|
||
{!isEmailVerified && (
|
||
<VerifyEmail
|
||
isVerifyModal={isVerifyModal}
|
||
setIsVerifyModal={setIsVerifyModal}
|
||
handleVerifyEmail={handleVerifyEmail}
|
||
setOtp={setOtp}
|
||
otp={otp}
|
||
otpLoader={otpLoader}
|
||
handleVerifyBtn={handleVerifyBtn}
|
||
handleResend={handleResend}
|
||
/>
|
||
)}
|
||
{/* this component used for UI interaction and show their functionality */}
|
||
{pdfLoad && !isCompleted && (
|
||
<Tour
|
||
onRequestClose={closeTour}
|
||
steps={tourConfig}
|
||
isOpen={signTour}
|
||
/>
|
||
)}
|
||
<Tour
|
||
showNumber={false}
|
||
showNavigation={false}
|
||
showNavigationNumber={false}
|
||
onRequestClose={closeWidgetTour}
|
||
steps={WidgetTourConfig}
|
||
isOpen={widgetsTour}
|
||
/>
|
||
{/* this component used to render all pdf pages in left side */}
|
||
<RenderAllPdfPage
|
||
allPages={allPages}
|
||
setAllPages={setAllPages}
|
||
setPageNumber={setPageNumber}
|
||
setSignBtnPosition={setSignBtnPosition}
|
||
pageNumber={pageNumber}
|
||
containerWH={containerWH}
|
||
pdfBase64Url={pdfBase64Url}
|
||
setPdfArrayBuffer={setPdfArrayBuffer}
|
||
setPdfBase64Url={setPdfBase64Url}
|
||
setIsUploadPdf={setIsUploadPdf}
|
||
pdfArrayBuffer={pdfArrayBuffer}
|
||
isMergePdfBtn={!pdfDetails?.[0]?.IsCompleted}
|
||
pdfDetails={pdfDetails}
|
||
/>
|
||
<div className="w-full md:w-[57%] flex mr-4">
|
||
<PdfTools
|
||
clickOnZoomIn={clickOnZoomIn}
|
||
clickOnZoomOut={clickOnZoomOut}
|
||
handleRotationFun={handleRotationFun}
|
||
pdfArrayBuffer={pdfArrayBuffer}
|
||
pageNumber={pageNumber}
|
||
setPdfBase64Url={setPdfBase64Url}
|
||
setPdfArrayBuffer={setPdfArrayBuffer}
|
||
setIsUploadPdf={setIsUploadPdf}
|
||
setSignerPos={setXyPosition}
|
||
signerPos={xyPosition}
|
||
allPages={allPages}
|
||
setAllPages={setAllPages}
|
||
setPageNumber={setPageNumber}
|
||
isDisableEditTools={isCompleted}
|
||
pdfDetails={pdfDetails}
|
||
/>
|
||
<div className="w-full md:w-[95%]">
|
||
<ModalUi
|
||
isOpen={isAlert.isShow}
|
||
title={isAlert?.header || t("alert")}
|
||
handleClose={() =>
|
||
setIsAlert({ isShow: false, alertMessage: "" })
|
||
}
|
||
>
|
||
<div className="p-[20px] h-full">
|
||
<p>{isAlert.alertMessage}</p>
|
||
</div>
|
||
</ModalUi>
|
||
{/* this modal is used show this document is already sign */}
|
||
<ModalUi
|
||
isOpen={showAlreadySignDoc.status}
|
||
title={t("document-signed")}
|
||
handleClose={() => setShowAlreadySignDoc({ status: false })}
|
||
>
|
||
<div className="p-[20px] h-full text-base-content">
|
||
<p>{showAlreadySignDoc.mssg}</p>
|
||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||
<button
|
||
className="op-btn op-btn-ghost text-base-content shadow-md"
|
||
onClick={() => setShowAlreadySignDoc({ status: false })}
|
||
>
|
||
{t("close")}
|
||
</button>
|
||
</div>
|
||
</ModalUi>
|
||
<DropdownWidgetOption
|
||
type="checkbox"
|
||
title={t("checkbox")}
|
||
showDropdown={isCheckbox}
|
||
setShowDropdown={setIsCheckbox}
|
||
handleSaveWidgetsOptions={handleSaveWidgetsOptions}
|
||
currWidgetsDetails={currWidgetsDetails}
|
||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||
handleClose={handleCloseModal}
|
||
fontSize={fontSize}
|
||
setFontSize={setFontSize}
|
||
fontColor={fontColor}
|
||
setFontColor={setFontColor}
|
||
isShowAdvanceFeature={false}
|
||
/>
|
||
<PlaceholderCopy
|
||
isPageCopy={isPageCopy}
|
||
setIsPageCopy={setIsPageCopy}
|
||
xyPosition={xyPosition}
|
||
setXyPosition={setXyPosition}
|
||
allPages={allPages}
|
||
pageNumber={currWidgetsDetails?.pageNumber || pageNumber}
|
||
signKey={currWidgetsDetails?.key}
|
||
widgetType={currWidgetsDetails?.type}
|
||
pdfOriginalWH={pdfOriginalWH}
|
||
/>
|
||
{/*render email component to send email after finish signature on document */}
|
||
<EmailComponent
|
||
isEmail={isEmail}
|
||
setIsEmail={setIsEmail}
|
||
setSuccessEmail={setSuccessEmail}
|
||
pdfDetails={pdfDetails}
|
||
setIsAlert={setIsAlert}
|
||
setIsDownloadModal={setIsDownloadModal}
|
||
/>
|
||
{/* pdf header which contain funish back button */}
|
||
<Header
|
||
pageNumber={pageNumber}
|
||
allPages={allPages}
|
||
changePage={changePage}
|
||
embedWidgetsData={embedWidgetsData}
|
||
pdfDetails={pdfDetails}
|
||
isShowHeader={true}
|
||
currentSigner={true}
|
||
alreadySign={pdfUrl ? true : false}
|
||
isSignYourself={true}
|
||
setIsEmail={setIsEmail}
|
||
isCompleted={isCompleted}
|
||
handleRotationFun={handleRotationFun}
|
||
clickOnZoomIn={clickOnZoomIn}
|
||
clickOnZoomOut={clickOnZoomOut}
|
||
widgetsDetails={xyPosition}
|
||
setIsDownloadModal={setIsDownloadModal}
|
||
setIsUploadPdf={setIsUploadPdf}
|
||
pdfArrayBuffer={pdfArrayBuffer}
|
||
setPdfArrayBuffer={setPdfArrayBuffer}
|
||
setPdfBase64Url={setPdfBase64Url}
|
||
setSignerPos={setXyPosition}
|
||
signerPos={xyPosition}
|
||
pdfBase64={pdfBase64Url}
|
||
/>
|
||
<div ref={divRef} data-tut="reactourSecond" className="h-fit">
|
||
{containerWH?.width && (
|
||
<RenderPdf
|
||
pageNumber={pageNumber}
|
||
setPageNumber={setPageNumber}
|
||
pdfOriginalWH={pdfOriginalWH}
|
||
pdfNewWidth={pdfNewWidth}
|
||
successEmail={successEmail}
|
||
nodeRef={nodeRef}
|
||
handleTabDrag={handleTabDrag}
|
||
handleStop={handleStop}
|
||
isDragging={isDragging}
|
||
handleDeleteWidget={handleDeleteWidget}
|
||
pdfDetails={pdfDetails}
|
||
setIsDragging={setIsDragging}
|
||
xyPosition={xyPosition}
|
||
pdfRef={pdfRef}
|
||
pdfUrl={pdfUrl}
|
||
numPages={numPages}
|
||
pageDetails={pageDetails}
|
||
pdfLoad={pdfLoad}
|
||
setPdfLoad={setPdfLoad}
|
||
setXyPosition={setXyPosition}
|
||
containerWH={containerWH}
|
||
setIsPageCopy={setIsPageCopy}
|
||
setIsCheckbox={setIsCheckbox}
|
||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||
handleNameModal={openNameModal}
|
||
handleCellSettingModal={openCellsSettingModal}
|
||
handleTextSettingModal={handleTextSettingModal}
|
||
setScale={setScale}
|
||
scale={scale}
|
||
pdfBase64Url={pdfBase64Url}
|
||
fontSize={fontSize}
|
||
setFontSize={setFontSize}
|
||
setCellCount={setCellCount}
|
||
fontColor={fontColor}
|
||
setFontColor={setFontColor}
|
||
isResize={isResize}
|
||
setIsResize={setIsResize}
|
||
divRef={divRef}
|
||
currWidgetsDetails={currWidgetsDetails}
|
||
isShowModal={isShowModal}
|
||
closeWidgetTour={closeWidgetTour}
|
||
unSignedWidgetId={unSignedWidgetId}
|
||
signBtnPosition={signBtnPosition}
|
||
addPositionOfSignature={addPositionOfSignature}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="w-full md:w-[23%] bg-base-100 overflow-y-auto hide-scrollbar">
|
||
<div className="max-h-screen">
|
||
{!isCompleted ? (
|
||
<WidgetComponent
|
||
pdfUrl={pdfUrl}
|
||
handleDivClick={handleDivClick}
|
||
handleMouseLeave={handleMouseLeave}
|
||
xyPosition={xyPosition}
|
||
isSignYourself={true}
|
||
addPositionOfSignature={addPositionOfSignature}
|
||
isMailSend={false}
|
||
setIsTour={setSignTour}
|
||
/>
|
||
) : (
|
||
<Signedby pdfDetails={pdfDetails[0]} />
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
{!isCheckbox && isShowModal[currWidgetsDetails?.key] && (
|
||
<WidgetsValueModal
|
||
key={currWidgetsDetails?.key}
|
||
xyPosition={xyPosition} //placeholder details
|
||
pageNumber={pageNumber} //current page number
|
||
setXyPosition={setXyPosition} //placeholder details state
|
||
setCellCount={setCellCount}
|
||
setPageNumber={setPageNumber}
|
||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||
currWidgetsDetails={currWidgetsDetails}
|
||
index={widgetModalIndex} //current page index
|
||
isSave={true}
|
||
signatureTypes={signatureTypes}
|
||
/>
|
||
)}
|
||
<WidgetNameModal
|
||
widgetName={currWidgetsDetails?.options?.name}
|
||
defaultdata={currWidgetsDetails}
|
||
isOpen={isNameModal}
|
||
handleClose={handleNameModal}
|
||
handleData={handleWidgetdefaultdata}
|
||
fontSize={fontSize}
|
||
setFontSize={setFontSize}
|
||
fontColor={fontColor}
|
||
setFontColor={setFontColor}
|
||
widgetsSource={xyPosition}
|
||
isSelfSign={true}
|
||
/>
|
||
<CellsSettingModal
|
||
isOpen={isCellsSetting}
|
||
defaultData={currWidgetsDetails}
|
||
handleClose={handleCellsSettingClose}
|
||
handleSave={handleCellsSettingSave}
|
||
/>
|
||
<RotateAlert
|
||
showRotateAlert={showRotateAlert.status}
|
||
setShowRotateAlert={setShowRotateAlert}
|
||
handleRemoveWidgets={handleRemovePlaceholder}
|
||
/>
|
||
<DownloadPdfZip
|
||
setIsDownloadModal={setIsDownloadModal}
|
||
isDownloadModal={isDownloadModal}
|
||
pdfDetails={pdfDetails}
|
||
isDocId={true}
|
||
pdfBase64={pdfBase64Url}
|
||
/>
|
||
<TextFontSetting
|
||
isTextSetting={isTextSetting}
|
||
setIsTextSetting={setIsTextSetting}
|
||
fontSize={fontSize}
|
||
setFontSize={setFontSize}
|
||
fontColor={fontColor}
|
||
setFontColor={setFontColor}
|
||
handleSaveFontSize={handleSaveFontSize}
|
||
currWidgetsDetails={currWidgetsDetails}
|
||
/>
|
||
</>
|
||
);
|
||
}
|
||
|
||
export default SignYourSelf;
|