Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation

This commit is contained in:
prafull-opensignlabs
2024-08-21 18:14:05 +05:30
19 changed files with 938 additions and 470 deletions
@@ -369,7 +369,8 @@
"copy-type": {
"All pages": "All pages",
"All pages but last": "All pages but last",
"All pages but first": "All pages but first"
"All pages but first": "All pages but first",
"Beside to widget":"Beside to widget"
},
"options": "Options",
"minimun-check": "Minimun check",
@@ -612,5 +613,8 @@
"additional-quicksend": "Please purchase add-on quick send",
"quantityofquicksend": "Quantity of Quick send",
"quotaerrquicksend": "Quota Reached, You don't have enough credits.",
"buycredits": "Buy credits"
}
"buycredits": "Buy credits",
"rotate-right":"Rotate right",
"rotate-left":"Rotate left",
"rotate-alert-mssg":"All widgets on this page will be lost. Are you sure you want to proceed?"
}
@@ -369,7 +369,8 @@
"copy-type": {
"All pages": "Toutes les pages",
"All pages but last": "Toutes les pages sauf la dernière",
"All pages but first": "Toutes les pages sauf la première"
"All pages but first": "Toutes les pages sauf la première",
"Beside to widget": "À côté du widget"
},
"options": "Possibilités",
"minimun-check": "Vérification minimale",
@@ -612,6 +613,8 @@
"additional-quicksend":"Veuillez acheter le module complémentaire quick send",
"quantityofquicksend": "Quantité de Quick send",
"quotaerrquicksend": "Quota atteint, vous n'avez pas assez de crédits.",
"buycredits": "Acheter des crédits"
}
"buycredits": "Acheter des crédits",
"rotate-right" :"Faire pivoter à droite",
"rotate-left" :"Faire pivoter à gauche",
"rotate-alert-mssg" :"Tous les widgets de cette page seront perdus. Êtes-vous sûr de vouloir continuer ?"
}
+1 -5
View File
@@ -227,11 +227,7 @@ const Header = ({ showSidebar }) => {
<i className="fa-light fa-lock"></i> {t("change-password")}
</span>
</li>
<li
onClick={() => {
handleConsoleRedirect();
}}
>
<li onClick={() => handleConsoleRedirect()}>
<span>
<i className="fa-light fa-id-card"></i> Console
</span>
@@ -0,0 +1,38 @@
import React from "react";
import ModalUi from "../primitives/ModalUi";
import { useTranslation } from "react-i18next";
function RotateAlert(props) {
const { t } = useTranslation();
return (
<ModalUi
isOpen={props.isRotate}
title={t("Rotation-alert")}
handleClose={() => props.setIsRotate({ status: false, degree: 0 })}
>
{" "}
<div className="p-[20px] h-full">
<p>{t("rotate-alert-mssg")}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<div className="flex gap-1">
<button
onClick={() => props.setIsRotate({ status: false, degree: 0 })}
type="button"
className="op-btn op-btn-ghost shadow-md"
>
{t("no")}
</button>
<button
onClick={() => props.handleRemoveWidgets()}
type="button"
className="op-btn op-btn-primary"
>
{t("yes")}
</button>
</div>
</div>
</ModalUi>
);
}
export default RotateAlert;
+48 -19
View File
@@ -34,7 +34,11 @@ function Header({
setIsEmail,
completeBtnTitle,
setIsEditTemplate,
isPublicTemplate
isPublicTemplate,
clickOnZoomIn,
clickOnZoomOut,
handleRotationFun,
isDisableRotate
}) {
const { t } = useTranslation();
const filterPrefill =
@@ -73,7 +77,7 @@ function Header({
allPages={allPages}
changePage={changePage}
/>
{pdfUrl && alreadySign ? (
{isCompleted && alreadySign ? (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[5px]">
@@ -184,13 +188,7 @@ function Header({
) : (
<div
data-tut="reactourThird"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
} else if (isPdfRequestFiles) {
embedWidgetsData();
}
}}
onClick={() => embedWidgetsData()}
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
>
{t("finish")}
@@ -207,7 +205,7 @@ function Header({
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="bg-white shadow-md rounded-full px-3 py-2"
className="bg-white shadow-md rounded-md px-3 py-2"
sideOffset={5}
>
<DropdownMenu.Item
@@ -215,7 +213,7 @@ function Header({
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
pdfDetails[0]?.URL,
setIsDownloading
)
}
@@ -226,6 +224,43 @@ function Header({
></i>
<span className="font-[500]">{t("download")}</span>
</DropdownMenu.Item>
{!isDisableRotate && (
<>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() => handleRotationFun(90)}
>
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px] mr-[3px]"></i>
<span className="font-[500]">
{t("rotate-right")}
</span>
</DropdownMenu.Item>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() => handleRotationFun(-90)}
>
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px] mr-[3px]"></i>
<span className="font-[500]">
{t("rotate-left")}
</span>
</DropdownMenu.Item>
</>
)}
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() => clickOnZoomIn()}
>
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
<span className="font-[500]">{t("zoom-in")}</span>
</DropdownMenu.Item>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() => clickOnZoomOut()}
>
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i>
<span className="font-[500]">{t("zoom-out")}</span>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
@@ -423,9 +458,7 @@ function Header({
<button
type="button"
className="op-btn op-btn-primary op-btn-sm shadow"
onClick={() => {
embedWidgetsData();
}}
onClick={() => embedWidgetsData()}
>
{t("sign-now")}
</button>
@@ -442,11 +475,7 @@ function Header({
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
}
}}
onClick={() => embedWidgetsData()}
>
{t("finish")}
</button>
+22 -29
View File
@@ -3,44 +3,37 @@ import { useTranslation } from "react-i18next";
function PdfZoom(props) {
const { t } = useTranslation();
const onClickZoomIn = () => {
props.setScale(props.scale + 0.1 * props.scale);
props.setZoomPercent(props.zoomPercent + 10);
};
const onClickZoomOut = () => {
if (props.zoomPercent > 0) {
if (props.zoomPercent === 10) {
props.setScale(1);
} else {
props.setScale(props.scale - 0.1 * props.scale);
}
props.setZoomPercent(props.zoomPercent - 10);
}
};
const handleReset = () => {
props.setScale(1);
props.setZoomPercent(0);
};
return (
// md:mt-[41px] xl:mt-[63px]mt=[]
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[50px] 2xl:mt-[63px]">
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[43px] 2xl:mt-[63px]">
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={onClickZoomIn}
onClick={() => props.clickOnZoomIn()}
title={t("zoom-in")}
>
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
</span>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={handleReset}
title={t("reset")}
>
<i className="fa-light fa-magnifying-glass-arrows-rotate text-gray-500 2xl:text-[30px]"></i>
</span>
{!props?.isDisableRotate && (
<>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={() => props.handleRotationFun(90)}
title={t("rotate-right")}
>
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px]"></i>
</span>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
title={t("rotate-left")}
onClick={() => props.handleRotationFun(-90)}
>
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px]"></i>
</span>
</>
)}
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px]"
onClick={onClickZoomOut}
onClick={() => props.clickOnZoomOut()}
style={{
cursor: props.zoomPercent > 0 ? "pointer" : "default"
}}
@@ -1,12 +1,21 @@
import React, { useState } from "react";
import ModalUi from "../../primitives/ModalUi";
import { randomId, textWidget } from "../../constant/Utils";
import {
handleCopyNextToWidget,
randomId,
textWidget
} from "../../constant/Utils";
import { useTranslation } from "react-i18next";
function PlaceholderCopy(props) {
const { t } = useTranslation();
const copyType = ["All pages", "All pages but last", "All pages but first"];
const [selectCopyType, setSelectCopyType] = useState("");
const copyType = [
{ id: 1, type: "All pages" },
{ id: 2, type: "All pages but last" },
{ id: 3, type: "All pages but first" },
{ id: 4, type: "Beside to widget" }
];
const [selectCopyType, setSelectCopyType] = useState(1);
//function for get copy placeholder position
const getCopyPlaceholderPosition = (
type,
@@ -20,7 +29,7 @@ function PlaceholderCopy(props) {
(data) => data.xPosition !== currentPlaceholder.xPosition
);
//copy all page placeholder at requested position except first page
if (newPageNumber === 1 && type === "first") {
if (newPageNumber === 1 && type === 3) {
if (filterPosition && filterPosition.length > 0) {
return {
pageNumber: newPageNumber,
@@ -29,7 +38,7 @@ function PlaceholderCopy(props) {
}
}
//copy all page placeholder at requested position except last page
else if (newPageNumber === props.allPages && type === "last") {
else if (newPageNumber === props.allPages && type === 2) {
if (existPlaceholderPosition) {
return {
pageNumber: newPageNumber,
@@ -172,20 +181,60 @@ function PlaceholderCopy(props) {
//function for getting selected type placeholder copy
const handleApplyCopy = () => {
if (selectCopyType === "All pages") {
copyPlaceholder("all");
} else if (selectCopyType === "All pages but last") {
copyPlaceholder("last");
} else if (selectCopyType === "All pages but first") {
copyPlaceholder("first");
if (selectCopyType === 4) {
const signerPosition = props.xyPostion;
let currentXYposition;
const signerId = props.signerObjId ? props.signerObjId : props.Id;
if (signerId) {
const filterSignerPosition = signerPosition.filter(
(item) => item?.Id === signerId
);
currentXYposition = filterSignerPosition[0].placeHolder.filter(
(data) => data.pageNumber === props?.pageNumber
);
//get current placeholder position data which user want to copy
currentXYposition = currentXYposition[0].pos.find(
(position) => position.key === props.signKey
);
//function to create new widget next to just widget
handleCopyNextToWidget(
currentXYposition,
props.widgetType,
props.xyPostion,
props.pageNumber,
props.setXyPostion,
props?.Id
);
} else {
const getIndex = props?.xyPostion.findIndex(
(data) => data.pageNumber === props.pageNumber
);
const placeholderPosition = props?.xyPostion[getIndex];
//get current placeholder position data which user want to copy
currentXYposition = placeholderPosition.pos.find(
(pos) => pos.key === props.signKey
);
//function to create new widget next to just widget
handleCopyNextToWidget(
currentXYposition,
props.widgetType,
props.xyPostion,
getIndex,
props.setXyPostion
);
}
} else {
copyPlaceholder(selectCopyType);
}
};
const handleUniqueId = () => {
if (props.widgetType === textWidget) {
const signerId = props.signerObjId ? props.signerObjId : props.Id;
if (signerId && props.widgetType === textWidget) {
props.setUniqueId(props?.tempSignerId);
props.setTempSignerId("");
}
props.setIsPageCopy(false);
setSelectCopyType(1);
};
return (
<ModalUi
@@ -201,11 +250,11 @@ function PlaceholderCopy(props) {
<input
className="mr-[8px] op-radio op-radio-xs"
type="radio"
value={data}
onChange={() => setSelectCopyType(data)}
checked={selectCopyType === data}
value={data.id}
onChange={() => setSelectCopyType(data.id)}
checked={selectCopyType === data.id}
/>
{t(`copy-type.${data}`)}
{t(`copy-type.${data.type}`)}
</label>
</div>
);
@@ -12,7 +12,8 @@ function RenderAllPdfPage({
pageNumber,
signerPos,
signerObjectId,
containerWH
containerWH,
pdfRotateBase64
}) {
const { t } = useTranslation();
const [signPageNumber, setSignPageNumber] = useState([]);
@@ -66,6 +67,7 @@ function RenderAllPdfPage({
)
);
};
const pdfDataBase64 = `data:application/pdf;base64,${pdfRotateBase64}`;
return (
<div ref={pageContainer} className="hidden w-[20%] bg-base-100 md:block">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
@@ -78,7 +80,7 @@ function RenderAllPdfPage({
<Document
loading={t("loading-doc")}
onLoadSuccess={onDocumentLoad}
file={signPdfUrl}
file={(pdfRotateBase64 && pdfDataBase64) || signPdfUrl}
>
{Array.from(new Array(allPages), (el, index) => (
<div
+170 -147
View File
@@ -62,6 +62,7 @@ function RenderPdf({
setIsSelectId,
ispublicTemplate,
handleUserDetails,
pdfRotateBase64,
fontSize,
setFontSize,
fontColor,
@@ -69,6 +70,7 @@ function RenderPdf({
}) {
const { t } = useTranslation();
const isMobile = window.innerWidth < 767;
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
const isGuestSigner = localStorage.getItem("isGuestSigner");
@@ -252,163 +254,177 @@ function RenderPdf({
);
}
};
const pdfDataBase64 = `data:application/pdf;base64,${pdfRotateBase64}`;
return (
<>
{successEmail && (
<Alert type={"success"}>{t("success-email-alert")}</Alert>
)}
{isMobile ? (
<div
data-tut="reactourForth"
className={`${
isGuestSigner ? "30px" : ""
} border-[0.1px] border-[#ebe8e8]`}
ref={drop}
id="container"
<RSC
style={{
position: "relative",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
height: window.innerHeight - 200,
zIndex: 0
}}
className="h-full"
noScrollY={false}
noScrollX={scale === 1 ? true : false}
>
{containerWH?.width &&
pdfOriginalWH.length > 0 &&
(pdfRequest
? signerPos.map((data, key) => {
return (
<React.Fragment key={key}>
{checkSignedSigners(data)}
</React.Fragment>
);
})
: placeholder // placeholder mobile
? signerPos.map((data, ind) => {
<div
data-tut="reactourForth"
className={`${
isGuestSigner ? "30px" : ""
} border-[0.1px] border-[#ebe8e8] overflow-x-auto`}
style={{
width: containerWH?.width && containerWH?.width * scale
}}
ref={drop}
id="container"
>
{containerWH?.width &&
pdfOriginalWH.length > 0 &&
(pdfRequest
? signerPos.map((data, key) => {
return (
<React.Fragment key={ind}>
{data.placeHolder.map((placeData, index) => {
return (
<React.Fragment key={index}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleImageResize
}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
setSignerObjId={setSignerObjId}
data={data}
setIsResize={setIsResize}
setShowDropdown={setShowDropdown}
isShowBorder={true}
isPlaceholder={true}
setUniqueId={setUniqueId}
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
setIsValidate={setIsValidate}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
setIsSelectId={setIsSelectId}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
</React.Fragment>
);
})}
</React.Fragment>
);
})}
<React.Fragment key={key}>
{checkSignedSigners(data)}
</React.Fragment>
);
})
: xyPostion.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.pageNumber === pageNumber &&
data.pos.map((pos) => {
: placeholder // placeholder mobile
? signerPos.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.placeHolder.map((placeData, index) => {
return (
pos && (
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleSignYourselfImageResize
}
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
containerWH={containerWH}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
isDragging={isDragging}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleUserName={handleUserName}
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
)
<React.Fragment key={index}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleImageResize
}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
setSignerObjId={setSignerObjId}
data={data}
setIsResize={setIsResize}
setShowDropdown={setShowDropdown}
isShowBorder={true}
isPlaceholder={true}
setUniqueId={setUniqueId}
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
setIsValidate={setIsValidate}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
setIsSelectId={setIsSelectId}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
</React.Fragment>
);
})}
</React.Fragment>
);
})}
</React.Fragment>
);
}))}
</React.Fragment>
);
})
: xyPostion.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.pageNumber === pageNumber &&
data.pos.map((pos) => {
return (
pos && (
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleSignYourselfImageResize
}
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
containerWH={containerWH}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
isDragging={isDragging}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleUserName={handleUserName}
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
)
);
})}
</React.Fragment>
);
}))}
{/* this component for render pdf document is in middle of the component */}
<div className="flex items-center justify-center">
<Document
onLoadError={() => {
setPdfLoad(false);
}}
onLoadError={() => setPdfLoad(false)}
loading={t("loading-doc")}
onLoadSuccess={pageDetails}
// ref={pdfRef}'
@@ -417,9 +433,14 @@ function RenderPdf({
setSelectWidgetId("");
}
}}
file={pdfDetails[0]?.SignedUrl || pdfDetails[0]?.URL}
file={
(pdfRotateBase64 && pdfDataBase64) ||
pdfDetails[0]?.SignedUrl ||
pdfDetails[0].URL
}
>
<Page
scale={scale || 1}
key={index}
pageNumber={pageNumber}
width={containerWH.width}
@@ -431,7 +452,7 @@ function RenderPdf({
/>
</Document>
</div>
</div>
</RSC>
) : (
<RSC
style={{
@@ -595,9 +616,7 @@ function RenderPdf({
{/* this component for render pdf document is in middle of the component */}
<Document
onLoadError={() => {
setPdfLoad(false);
}}
onLoadError={() => setPdfLoad(false)}
loading={t("loading-doc")}
onLoadSuccess={pageDetails}
onClick={() => {
@@ -606,7 +625,11 @@ function RenderPdf({
}
}}
// ref={pdfRef}
file={pdfDetails[0]?.SignedUrl || pdfDetails[0].URL}
file={
(pdfRotateBase64 && pdfDataBase64) ||
pdfDetails[0]?.SignedUrl ||
pdfDetails[0].URL
}
>
<Page
key={index}
@@ -56,9 +56,7 @@ function VerifyEmail(props) {
<button
className="op-btn op-btn-primary"
type="submit"
onClick={() => {
props.handleVerifyBtn();
}}
onClick={() => props.handleVerifyBtn()}
>
{t("send-otp")}
</button>
+147 -28
View File
@@ -1,7 +1,7 @@
import axios from "axios";
import moment from "moment";
import React from "react";
import { rgb } from "pdf-lib";
import { PDFDocument, rgb, degrees } from "pdf-lib";
import Parse from "parse";
import { appInfo } from "./appinfo";
import { saveAs } from "file-saver";
@@ -658,7 +658,12 @@ export const randomId = () => {
return randomDigit;
};
export const createDocument = async (template, placeholders, signerData) => {
export const createDocument = async (
template,
placeholders,
signerData,
pdfUrl
) => {
if (template && template.length > 0) {
const Doc = template[0];
@@ -681,7 +686,7 @@ export const createDocument = async (template, placeholders, signerData) => {
}
const data = {
Name: Doc.Name,
URL: Doc.URL,
URL: pdfUrl,
SignedUrl: Doc.SignedUrl,
SentToOthers: Doc.SentToOthers,
Description: Doc.Description,
@@ -867,7 +872,6 @@ export const onChangeInput = (
}
} else {
let getXYdata = xyPostion[index].pos;
const updatePosition = getXYdata.map((positionData) => {
if (positionData.key === signKey) {
if (dateFormat) {
@@ -1077,13 +1081,18 @@ export const embedDocId = async (pdfDoc, documentId, allPages) => {
const textContent = documentId && `OpenSign™ DocumentId: ${documentId} `;
const pages = pdfDoc.getPages();
const page = pages[i];
page.drawText(textContent, {
x: 10,
y: page.getHeight() - 10,
size: fontSize,
const getObj = compensateRotation(
page.getRotation().angle,
10,
5,
1,
page.getSize(),
fontSize,
rgb(0.5, 0.5, 0.5),
font,
color: rgb(0.5, 0.5, 0.5)
});
page
);
page.drawText(textContent, getObj);
}
};
@@ -1849,29 +1858,26 @@ export const handleCopyNextToWidget = (
setXyPostion,
userId
) => {
const isSigners = xyPostion.some((data) => data.signerPtr);
let filterSignerPos;
//get position of previous widget and create new widget next to that widget on same data except
// xPosition and key
let newpos = position;
let newposition = position;
const calculateXPosition =
parseInt(position.xPosition) +
defaultWidthHeight(widgetType).width +
resizeBorderExtraWidth();
const newId = randomId();
newpos = { ...newpos, xPosition: calculateXPosition, key: newId };
newposition = { ...newposition, xPosition: calculateXPosition, key: newId };
//if condition to create widget in request-sign flow
if (isSigners) {
if (userId) {
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
}
const getPlaceHolder = filterSignerPos[0]?.placeHolder;
const getPageNumer = getPlaceHolder.filter(
if (userId) {
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
const getPlaceHolder = filterSignerPos && filterSignerPos[0]?.placeHolder;
const getPageNumer = getPlaceHolder?.filter(
(data) => data.pageNumber === index
);
const getXYdata = getPageNumer[0].pos;
getXYdata.push(newpos);
if (getPageNumer.length > 0) {
const getXYdata = getPageNumer && getPageNumer[0]?.pos;
getXYdata.push(newposition);
if (getPageNumer && getPageNumer.length > 0) {
const newUpdateSignPos = getPlaceHolder.map((obj) => {
if (obj.pageNumber === index) {
return { ...obj, pos: getXYdata };
@@ -1889,9 +1895,8 @@ export const handleCopyNextToWidget = (
setXyPostion(newUpdateSigner);
}
} else {
// else condition to create widget in sign-yourself flow
let getXYdata = xyPostion[index].pos;
getXYdata.push(newpos);
let getXYdata = xyPostion[index]?.pos || [];
getXYdata.push(newposition);
const updatePlaceholder = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: getXYdata };
@@ -2192,9 +2197,11 @@ function compensateRotation(
font,
page
) {
// Ensure pageRotation is between 0 and 360 degrees
pageRotation = ((pageRotation % 360) + 360) % 360;
let rotationRads = (pageRotation * Math.PI) / 180;
//These coords are now from bottom/left
// Coordinates are from bottom-left
let coordsFromBottomLeft = { x: x / scale };
if (pageRotation === 90 || pageRotation === 270) {
coordsFromBottomLeft.y = dimensions.width - (y + fontSize) / scale;
@@ -2204,6 +2211,7 @@ function compensateRotation(
let drawX = null;
let drawY = null;
if (pageRotation === 90) {
drawX =
coordsFromBottomLeft.x * Math.cos(rotationRads) -
@@ -2229,8 +2237,8 @@ function compensateRotation(
coordsFromBottomLeft.x * Math.sin(rotationRads) +
coordsFromBottomLeft.y * Math.cos(rotationRads) +
dimensions.height;
} else {
//no rotation
} else if (pageRotation === 0 || pageRotation === 360) {
// No rotation or full rotation
drawX = coordsFromBottomLeft.x;
drawY = coordsFromBottomLeft.y;
}
@@ -2334,3 +2342,114 @@ export const getDefaultSignature = async (objectId) => {
};
}
};
//function to rotate pdf page
export async function rotatePdfPage(
url,
rotateDegree,
pageNumber,
pdfRotateBase64
) {
let file;
//condition to handle pdf base64 in arrayBuffer format
if (pdfRotateBase64) {
file = base64ToArrayBuffer(pdfRotateBase64);
} else {
//condition to handle pdf url in arrayBuffer format
file = await convertPdfArrayBuffer(url);
}
// Load the existing PDF
const pdfDoc = await PDFDocument.load(file);
// Get the page according to page number
const page = pdfDoc.getPage(pageNumber);
//get current page rotation angle
const currentRotation = page.getRotation().angle;
// Apply the rotation in the counterclockwise direction
let newRotation = (currentRotation + rotateDegree) % 360;
// Adjust for negative angles to keep within 0-359 range
if (newRotation < 0) {
newRotation += 360;
}
page.setRotation(degrees(newRotation));
const pdfbase64 = await pdfDoc.saveAsBase64({ useObjectStreams: false });
//convert base64 to arraybuffer is used in pdf-lib
//pdfbase64 is used to show pdf rotated format
const arrayBuffer = base64ToArrayBuffer(pdfbase64);
//`base64` is used to show pdf
return { arrayBuffer: arrayBuffer, base64: pdfbase64 };
}
function base64ToArrayBuffer(base64) {
// Decode the base64 string to a binary string
const binaryString = atob(base64);
// Create a new ArrayBuffer with the same length as the binary string
const len = binaryString.length;
const bytes = new Uint8Array(len);
// Convert the binary string to a byte array
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Return the ArrayBuffer
return bytes.buffer;
}
export const convertBase64ToFile = async (pdfName, pdfBase64) => {
const fileName = sanitizeFileName(pdfName) + ".pdf";
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
// Save the Parse File if needed
const pdfData = await pdfFile.save();
const pdfUrl = pdfData.url();
return pdfUrl;
};
export const onClickZoomIn = (scale, zoomPercent, setScale, setZoomPercent) => {
setScale(scale + 0.1 * scale);
setZoomPercent(zoomPercent + 10);
};
export const onClickZoomOut = (
zoomPercent,
scale,
setZoomPercent,
setScale
) => {
if (zoomPercent > 0) {
if (zoomPercent === 10) {
setScale(1);
} else {
setScale(scale - 0.1 * scale);
}
setZoomPercent(zoomPercent - 10);
}
};
//function to use remove widgets from current page when user want to rotate page
export const handleRemoveWidgets = (
setSignerPos,
signerPos,
pageNumber,
setIsRotate
) => {
const updatedSignerPos = signerPos.map((placeholderObj) => {
return {
...placeholderObj,
placeHolder: placeholderObj?.placeHolder?.filter(
(data) => data?.pageNumber !== pageNumber
)
};
});
setSignerPos(updatedSignerPos);
setIsRotate({
status: false,
degree: 0
});
};
//function to show warning when user rotate page and there are some already widgets on that page
export const handleRotateWarning = (signerPos, pageNumber) => {
const placeholderExist = signerPos?.some((placeholderObj) =>
placeholderObj?.placeHolder?.some((data) => data?.pageNumber === pageNumber)
);
if (placeholderExist) {
return true;
} else {
return false;
}
};
+58 -58
View File
@@ -32,7 +32,9 @@ import {
handleToPrint,
handleDownloadCertificate,
openInNewTab,
getDefaultSignature
getDefaultSignature,
onClickZoomIn,
onClickZoomOut
} from "../constant/Utils";
import LoaderWithMsg from "../primitives/LoaderWithMsg";
import HandleError from "../primitives/HandleError";
@@ -122,7 +124,6 @@ function PdfRequestFiles(props) {
const [otp, setOtp] = useState("");
const [contractName, setContractName] = useState("");
const [zoomPercent, setZoomPercent] = useState(0);
const [totalZoomPercent, setTotalZoomPercent] = useState();
const [scale, setScale] = useState(1);
const [uniqueId, setUniqueId] = useState("");
const [isPublicTemplate, setIsPublicTemplate] = useState(false);
@@ -132,6 +133,7 @@ function PdfRequestFiles(props) {
const [res, setRes] = useState({});
const [documentId, setDocumentId] = useState("");
const [isPublicContact, setIsPublicContact] = useState(false);
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
const isHeader = useSelector((state) => state.showHeader);
const divRef = useRef(null);
@@ -140,17 +142,18 @@ function PdfRequestFiles(props) {
let isGuestSignFlow = false;
let sendmail;
let getDocId = "";
const route = !props.templateId && window.location.pathname;
const route = !props.templateId && window.location.pathname; //'/load/recipientSignPdf/TOAVuhXbfw/fPAKdK1qgX'
//window.location.search = ?sendmail=false
const getQuery =
!props.templateId &&
window.location?.search &&
window.location?.search?.split("?");
window.location?.search?.split("?"); //['','sendmail=false']
//'sendmail=false'
if (getQuery) {
sendmail = getQuery[1].split("=")[1];
sendmail = getQuery[1].split("=")[1]; //false
}
const checkSplit = route && route?.split("/");
const checkSplit = route && route?.split("/"); // ['', 'load', 'recipientSignPdf', 'TOAVuhXbfw', 'fPAKdK1qgX']
if (checkSplit && checkSplit.length > 4) {
isGuestSignFlow = true;
getDocId = checkSplit[3];
@@ -298,6 +301,20 @@ function PdfRequestFiles(props) {
: [];
if (documentData && documentData.length > 0) {
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);
if (arrayBuffer === "Error") {
setHandleError(t("something-went-wrong-mssg"));
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError(t("something-went-wrong-mssg"));
}
setIsPublicTemplate(true);
const getPublicRole = documentData[0]?.PublicRole[0];
const getUniqueIdDetails = documentData[0]?.Placeholders.find(
@@ -361,6 +378,20 @@ function PdfRequestFiles(props) {
//getting document details
const documentData = await contractDocument(documentId || docId);
if (documentData && documentData.length > 0) {
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);
if (arrayBuffer === "Error") {
setHandleError(t("something-went-wrong-mssg"));
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError(t("something-went-wrong-mssg"));
}
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const isCompleted =
documentData[0].IsCompleted && documentData[0].IsCompleted;
@@ -817,30 +848,9 @@ function PdfRequestFiles(props) {
setIsUiLoading(true);
// `widgets` is Used to return widgets details with page number of current user
const widgets = checkUser?.[0]?.placeHolder;
let pdfArrBuffer;
//`contractDocument` function used to get updated SignedUrl
//resolved issue of sign document by multiple signers simultaneously
const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) {
const url = documentData[0]?.SignedUrl || documentData[0]?.URL;
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError(t("invalid-document"));
} else {
pdfArrBuffer = arrayBuffer;
}
} else if (
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
setHandleError(t("something-went-wrong-mssg"));
} else {
setHandleError(t("document-not-found"));
}
// Load a PDFDocument from the existing PDF bytes
const existingPdfBytes = pdfArrBuffer;
const existingPdfBytes = pdfArrayBuffer;
try {
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const isSignYourSelfFlow = false;
@@ -1316,7 +1326,6 @@ function PdfRequestFiles(props) {
const handleDontShow = (isChecked) => {
setIsDontShow(isChecked);
};
// console.log("signerpos", signerPos);
//function to close tour and save tour status
const closeRequestSignTour = async () => {
setRequestSignTour(true);
@@ -1591,6 +1600,12 @@ function PdfRequestFiles(props) {
});
};
const clickOnZoomIn = () => {
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
};
const clickOnZoomOut = () => {
onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale);
};
return (
<DndProvider backend={HTML5Backend}>
<Title title={props.templateId ? "Public Sign" : "Request Sign"} />
@@ -1712,9 +1727,7 @@ function PdfRequestFiles(props) {
<ModalUi
isOpen={isPublicContact}
title={isOtp ? t("verify-email-1") : t("contact-details")}
handleClose={() => {
handleCloseOtp();
}}
handleClose={() => handleCloseOtp()}
>
<div className="h-full p-[20px]">
{isOtp ? (
@@ -1742,9 +1755,7 @@ function PdfRequestFiles(props) {
<div className="flex gap-2">
<button
className="op-btn op-btn-ghost"
onClick={() => {
handleCloseOtp();
}}
onClick={() => handleCloseOtp()}
>
{t("cancel")}
</button>
@@ -1834,9 +1845,7 @@ function PdfRequestFiles(props) {
<div className="flex gap-2">
<button
className="op-btn op-btn-ghost"
onClick={() => {
handleCloseOtp();
}}
onClick={() => handleCloseOtp()}
>
{t("close")}
</button>
@@ -1914,20 +1923,18 @@ function PdfRequestFiles(props) {
{/* pdf render view */}
<div className=" w-full md:w-[57%] flex mr-4">
<PdfZoom
setScale={setScale}
scale={scale}
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
isDisableRotate={true}
/>
<div className=" w-full md:w-[95%] ">
{/* this modal is used show this document is already sign */}
<ModalUi
isOpen={isCompleted.isModal}
title={t("document-signed")}
handleClose={() => {
setIsCompleted((prev) => ({ ...prev, isModal: false }));
}}
handleClose={() =>
setIsCompleted((prev) => ({ ...prev, isModal: false }))
}
reduceWidth={
!isCompleted?.message &&
"md:min-w-[440px] md:max-w-[400px]"
@@ -2059,15 +2066,11 @@ function PdfRequestFiles(props) {
currentSigner={currentSigner}
pdfUrl={pdfUrl}
alreadySign={alreadySign}
totalZoomPercent={totalZoomPercent}
setTotalZoomPercent={setTotalZoomPercent}
setScale={setScale}
scale={scale}
pdfOriginalWH={pdfOriginalWH}
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
isPublicTemplate={isPublicTemplate}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
isDisableRotate={true}
/>
<div ref={divRef} data-tut="pdfArea" className="h-[95%]">
@@ -2101,7 +2104,6 @@ function PdfRequestFiles(props) {
divRef={divRef}
setIsResize={setIsResize}
isResize={isResize}
setTotalZoomPercent={setTotalZoomPercent}
setScale={setScale}
scale={scale}
uniqueId={uniqueId}
@@ -2181,9 +2183,7 @@ function PdfRequestFiles(props) {
<ModalUi
isOpen={validateAlert}
title={t("validation-alert")}
handleClose={() => {
setValidateAlert(false);
}}
handleClose={() => setValidateAlert(false)}
>
<div className="h-[100%] p-[20px]">
<p>{t("validation-alert-1")}</p>
+85 -31
View File
@@ -35,7 +35,13 @@ import {
fetchSubscription,
convertPdfArrayBuffer,
getContainerScale,
openInNewTab
openInNewTab,
convertBase64ToFile,
onClickZoomIn,
onClickZoomOut,
rotatePdfPage,
handleRemoveWidgets,
handleRotateWarning
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import { useNavigate } from "react-router-dom";
@@ -56,6 +62,7 @@ import PdfZoom from "../components/pdf/PdfZoom";
import LottieWithLoader from "../primitives/DotLottieReact";
import { paidUrl } from "../json/plansArr";
import { useTranslation } from "react-i18next";
import RotateAlert from "../components/RotateAlert";
function PlaceHolderSign() {
const { t } = useTranslation();
@@ -133,6 +140,10 @@ function PlaceHolderSign() {
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
const isHeader = useSelector((state) => state.showHeader);
const [activeMailAdapter, setActiveMailAdapter] = useState("");
const [isRotate, setIsRotate] = useState({
status: false,
degree: 0
});
const [isAlreadyPlace, setIsAlreadyPlace] = useState({
status: false,
message: ""
@@ -141,6 +152,8 @@ function PlaceHolderSign() {
const [isCustomize, setIsCustomize] = useState(false);
const [zoomPercent, setZoomPercent] = useState(0);
const [scale, setScale] = useState(1);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const isMobile = window.innerWidth < 767;
const [, drop] = useDrop({
accept: "BOX",
@@ -837,11 +850,6 @@ function PlaceHolderSign() {
const handleMouseLeave = () => {
setSignBtnPosition([xySignature]);
};
function sanitizeFileName(fileName) {
// Remove spaces and invalid characters
return fileName.replace(/[^a-zA-Z0-9._-]/g, "");
}
//embed prefill label widget data
const embedPrefilllData = async () => {
const prefillExist = signerPos.filter((data) => data.Role === "prefill");
@@ -853,7 +861,7 @@ function PlaceHolderSign() {
});
const isSignYourSelfFlow = false;
try {
const pdfBytes = await multiSignEmbed(
const pdfBase64 = await multiSignEmbed(
placeholder,
pdfDoc,
isSignYourSelfFlow,
@@ -861,19 +869,26 @@ function PlaceHolderSign() {
pdfOriginalWH,
containerWH
);
const fileName = sanitizeFileName(pdfDetails[0].Name) + ".pdf";
const pdfFile = new Parse.File(fileName, { base64: pdfBytes });
// Save the Parse File if needed
const pdfData = await pdfFile.save();
const pdfUrl = pdfData.url();
const pdfUrl = await convertBase64ToFile(pdfDetails[0].Name, pdfBase64);
const tenantId = localStorage.getItem("TenantId");
const buffer = atob(pdfBytes);
const buffer = atob(pdfBase64);
SaveFileSize(buffer.length, pdfUrl, tenantId);
return pdfUrl;
} catch (e) {
console.log("error", e);
console.log("error to convertBase64ToFile in placeholder flow", e);
}
} else if (pdfRotateBase64) {
try {
const pdfUrl = await convertBase64ToFile(
pdfDetails[0].Name,
pdfRotateBase64
);
const tenantId = localStorage.getItem("TenantId");
const buffer = atob(pdfRotateBase64);
SaveFileSize(buffer.length, pdfUrl, tenantId);
return pdfUrl;
} catch (e) {
console.log("error to convertBase64ToFile in placeholder flow", e);
}
} else {
return pdfDetails[0].URL;
@@ -1710,6 +1725,39 @@ function PlaceHolderSign() {
});
}
};
const clickOnZoomIn = () => {
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
};
const clickOnZoomOut = () => {
onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale);
};
//`handleRotationFun` function is used to roatate pdf particular page
const handleRotationFun = async (rotateDegree) => {
const isRotate = handleRotateWarning(signerPos, pageNumber);
if (isRotate) {
setIsRotate({ status: true, degree: rotateDegree });
} else {
const urlDetails = await rotatePdfPage(
pdfDetails[0].URL,
rotateDegree,
pageNumber - 1,
pdfRotateBase64
);
setPdfArrayBuffer && setPdfArrayBuffer(urlDetails.arrayBuffer);
setPdfRotatese64(urlDetails.base64);
}
};
const handleRemovePlaceholder = async () => {
handleRemoveWidgets(setSignerPos, signerPos, pageNumber, setIsRotate);
const urlDetails = await rotatePdfPage(
pdfDetails[0].URL,
isRotate.degree,
pageNumber - 1,
pdfRotateBase64
);
setPdfArrayBuffer && setPdfArrayBuffer(urlDetails.arrayBuffer);
setPdfRotatese64(urlDetails.base64);
};
return (
<>
<Title title={state?.title ? state.title : "New Document"} />
@@ -1758,15 +1806,14 @@ function PlaceHolderSign() {
setPageNumber={setPageNumber}
setSignBtnPosition={setSignBtnPosition}
pageNumber={pageNumber}
pdfRotateBase64={pdfRotateBase64}
/>
{/* pdf render view */}
<div className=" w-full md:w-[57%] flex mr-4">
<PdfZoom
setScale={setScale}
scale={scale}
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
handleRotationFun={handleRotationFun}
/>
<div className=" w-full md:w-[95%] ">
{/* this modal is used show alert set placeholder for all signers before send mail */}
@@ -1894,9 +1941,7 @@ function PlaceHolderSign() {
>
{isCurrUser && (
<button
onClick={() => {
handleRecipientSign();
}}
onClick={() => handleRecipientSign()}
type="button"
className="op-btn op-btn-primary mr-1"
>
@@ -1921,17 +1966,13 @@ function PlaceHolderSign() {
<ModalUi
isOpen={isShowEmail}
title={t("signers-alert")}
handleClose={() => {
setIsShowEmail(false);
}}
handleClose={() => setIsShowEmail(false)}
>
<div className="h-[100%] p-[20px]">
<p>{t("placeholder-alert-7")}</p>
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
<button
onClick={() => {
setIsShowEmail(false);
}}
onClick={() => setIsShowEmail(false)}
type="button"
className="op-btn op-btn-primary"
>
@@ -2012,9 +2053,16 @@ function PlaceHolderSign() {
alertSendEmail={alertSendEmail}
isShowHeader={true}
currentSigner={true}
handleRotationFun={handleRotationFun}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
/>
<div ref={divRef} data-tut="pdfArea" className="h-[95%]">
<div
ref={divRef}
data-tut="pdfArea"
className="h-full md:h-[95%]"
>
{containerWH && (
<RenderPdf
pageNumber={pageNumber}
@@ -2056,6 +2104,7 @@ function PlaceHolderSign() {
setScale={setScale}
scale={scale}
setIsSelectId={setIsSelectId}
pdfRotateBase64={pdfRotateBase64}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
@@ -2202,6 +2251,11 @@ function PlaceHolderSign() {
fontColor={fontColor}
setFontColor={setFontColor}
/>
<RotateAlert
isRotate={isRotate.status}
setIsRotate={setIsRotate}
handleRemoveWidgets={handleRemovePlaceholder}
/>
</DndProvider>
</>
);
+80 -17
View File
@@ -34,7 +34,10 @@ import {
changeImageWH,
handleSendOTP,
getContainerScale,
getDefaultSignature
getDefaultSignature,
onClickZoomIn,
onClickZoomOut,
rotatePdfPage
} from "../constant/Utils";
import { useParams } from "react-router-dom";
import Tour from "reactour";
@@ -53,6 +56,7 @@ import VerifyEmail from "../components/pdf/VerifyEmail";
import PdfZoom from "../components/pdf/PdfZoom";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import RotateAlert from "../components/RotateAlert";
//For signYourself inProgress section signer can add sign and complete doc sign.
function SignYourSelf() {
const { t } = useTranslation();
@@ -123,6 +127,11 @@ function SignYourSelf() {
const [zoomPercent, setZoomPercent] = useState(0);
const isHeader = useSelector((state) => state.showHeader);
const [scale, setScale] = useState(1);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const [isRotate, setIsRotate] = useState({
status: false,
degree: 0
});
const divRef = useRef(null);
const nodeRef = useRef(null);
const [, drop] = useDrop({
@@ -206,6 +215,7 @@ function SignYourSelf() {
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]);
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async (showComplete) => {
try {
@@ -794,6 +804,7 @@ function SignYourSelf() {
const signedpdf = JSON.parse(JSON.stringify(resSignPdf));
setPdfUrl(signedpdf);
getDocumentDetails(false);
setPdfRotatese64("");
}
};
@@ -1160,6 +1171,56 @@ function SignYourSelf() {
handleTextSettingModal(false);
}
};
const clickOnZoomIn = () => {
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
};
const clickOnZoomOut = () => {
onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale);
};
//`handleRotationFun` function is used to roatate pdf particular page
const handleRotationFun = async (rotateDegree) => {
const isPlaceholder = handleRotateWarning();
if (isPlaceholder) {
setIsRotate({ status: true, degree: rotateDegree });
} else {
const urlDetails = await rotatePdfPage(
pdfUrl || pdfDetails[0].URL,
rotateDegree,
pageNumber - 1,
pdfRotateBase64
);
setPdfArrayBuffer && setPdfArrayBuffer(urlDetails.arrayBuffer);
setPdfRotatese64(urlDetails.base64);
}
};
//function to show warning when user rotate page and there are some already widgets on that page
const handleRotateWarning = () => {
const placeholderData = xyPostion?.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 handleRemoveWidgets = async () => {
const updatePlaceholder = xyPostion.filter(
(data) => data.pageNumber !== pageNumber
);
const urlDetails = await rotatePdfPage(
pdfUrl || pdfDetails[0].URL,
isRotate.degree,
pageNumber - 1,
pdfRotateBase64
);
setPdfArrayBuffer && setPdfArrayBuffer(urlDetails.arrayBuffer);
setPdfRotatese64(urlDetails.base64);
setXyPostion(updatePlaceholder);
setIsRotate({ status: false, degree: 0 });
};
return (
<DndProvider backend={HTML5Backend}>
<Title title={"Self Sign"} />
@@ -1219,14 +1280,13 @@ function SignYourSelf() {
setSignBtnPosition={setSignBtnPosition}
pageNumber={pageNumber}
containerWH={containerWH}
pdfRotateBase64={pdfRotateBase64}
/>
<div className=" w-full md:w-[57%] flex mr-4">
<PdfZoom
setScale={setScale}
scale={scale}
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
handleRotationFun={handleRotationFun}
/>
<div className="w-full md:w-[95%]">
<ModalUi
@@ -1245,9 +1305,7 @@ function SignYourSelf() {
<ModalUi
isOpen={showAlreadySignDoc.status}
title={t("document-signed")}
handleClose={() => {
setShowAlreadySignDoc({ status: false });
}}
handleClose={() => setShowAlreadySignDoc({ status: false })}
>
<div className="p-[20px] h-full">
<p>{showAlreadySignDoc.mssg}</p>
@@ -1284,6 +1342,7 @@ function SignYourSelf() {
allPages={allPages}
pageNumber={pageNumber}
signKey={signKey}
widgetType={widgetType}
/>
{/* this is modal of signature pad */}
<SignPad
@@ -1334,12 +1393,12 @@ function SignYourSelf() {
isSignYourself={true}
setIsEmail={setIsEmail}
isCompleted={isCompleted}
handleRotationFun={handleRotationFun}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
widgetsDetails={xyPostion}
/>
<div
ref={divRef}
data-tut="reactourSecond"
className="h-full md:h-[95%]"
>
<div ref={divRef} data-tut="reactourSecond" className="h-full">
{containerWH?.width && containerWH?.height && (
<RenderPdf
pageNumber={pageNumber}
@@ -1378,6 +1437,7 @@ function SignYourSelf() {
handleTextSettingModal={handleTextSettingModal}
setScale={setScale}
scale={scale}
pdfRotateBase64={pdfRotateBase64}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
@@ -1421,9 +1481,7 @@ function SignYourSelf() {
<ModalUi
isOpen={validateAlert}
title={t("validation-alert")}
handleClose={() => {
setValidateAlert(false);
}}
handleClose={() => setValidateAlert(false)}
>
<div className="p-[20px] h-full">
<p>{t("validate-alert-mssg")}</p>
@@ -1438,6 +1496,11 @@ function SignYourSelf() {
</button>
</div>
</ModalUi>
<RotateAlert
isRotate={isRotate.status}
setIsRotate={setIsRotate}
handleRemoveWidgets={handleRemoveWidgets}
/>
<TextFontSetting
isTextSetting={isTextSetting}
setIsTextSetting={setIsTextSetting}
+85 -8
View File
@@ -26,7 +26,13 @@ import {
radioButtonWidget,
fetchSubscription,
getContainerScale,
openInNewTab
openInNewTab,
convertBase64ToFile,
rotatePdfPage,
onClickZoomOut,
onClickZoomIn,
handleRemoveWidgets,
handleRotateWarning
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import "../styles/AddUser.css";
@@ -43,6 +49,7 @@ import { useSelector } from "react-redux";
import PdfZoom from "../components/pdf/PdfZoom";
import { paidUrl } from "../json/plansArr";
import { useTranslation } from "react-i18next";
import RotateAlert from "../components/RotateAlert";
const TemplatePlaceholder = () => {
const { t } = useTranslation();
const navigate = useNavigate();
@@ -63,6 +70,10 @@ const TemplatePlaceholder = () => {
const [isSendAlert, setIsSendAlert] = useState(false);
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isRotate, setIsRotate] = useState({
status: false,
degree: 0
});
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: t("loading-mssg")
@@ -93,6 +104,7 @@ const TemplatePlaceholder = () => {
const [isNameModal, setIsNameModal] = useState(false);
const [isTextSetting, setIsTextSetting] = useState(false);
const [pdfLoad, setPdfLoad] = useState(false);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const color = [
"#93a3db",
"#e6c3db",
@@ -731,6 +743,7 @@ const TemplatePlaceholder = () => {
handleSaveTemplate();
}
};
const handleSaveTemplate = async () => {
if (signersdata?.length) {
const loadObj = {
@@ -752,6 +765,17 @@ const TemplatePlaceholder = () => {
}
});
}
let pdfUrl = pdfDetails[0]?.URL;
if (pdfRotateBase64) {
try {
pdfUrl = await convertBase64ToFile(
pdfDetails[0].Name,
pdfRotateBase64
);
} catch (e) {
console.log("error to convertBase64ToFile in placeholder flow", e);
}
}
try {
const data = {
Placeholders: signerPos,
@@ -762,7 +786,8 @@ const TemplatePlaceholder = () => {
SendinOrder: pdfDetails[0]?.SendinOrder || false,
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
NextReminderDate: pdfDetails[0]?.NextReminderDate
NextReminderDate: pdfDetails[0]?.NextReminderDate,
URL: pdfUrl
};
const updateTemplate = new Parse.Object("contracts_Template");
updateTemplate.id = templateId;
@@ -902,8 +927,21 @@ const TemplatePlaceholder = () => {
const handleCreateDocModal = async () => {
setIsCreateDocModal(false);
setIsCreateDoc(true);
let pdfUrl = pdfDetails[0]?.URL;
if (pdfRotateBase64) {
try {
pdfUrl = await convertBase64ToFile(pdfDetails[0].Name, pdfRotateBase64);
} catch (e) {
console.log("error to convertBase64ToFile in placeholder flow", e);
}
}
// handle create document
const res = await createDocument(pdfDetails, signerPos, signersdata);
const res = await createDocument(
pdfDetails,
signerPos,
signersdata,
pdfUrl
);
if (res.status === "success") {
navigate(`/placeHolderSign/${res.id}`, {
state: { title: "Use Template" }
@@ -1289,6 +1327,38 @@ const TemplatePlaceholder = () => {
setIsCheckbox(false);
};
const clickOnZoomIn = () => {
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
};
const clickOnZoomOut = () => {
onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale);
};
//`handleRotationFun` function is used to roatate pdf particular page
const handleRotationFun = async (rotateDegree) => {
const isRotate = handleRotateWarning(signerPos, pageNumber);
if (isRotate) {
setIsRotate({ status: true, degree: rotateDegree });
} else {
const urlDetails = await rotatePdfPage(
pdfDetails[0].URL,
rotateDegree,
pageNumber - 1,
pdfRotateBase64
);
setPdfRotatese64(urlDetails.base64);
}
};
const handleRemovePlaceholder = async () => {
handleRemoveWidgets(setSignerPos, signerPos, pageNumber, setIsRotate);
const urlDetails = await rotatePdfPage(
pdfDetails[0].URL,
isRotate.degree,
pageNumber - 1,
pdfRotateBase64
);
setPdfRotatese64(urlDetails.base64);
};
return (
<>
<Title title={"Template"} />
@@ -1336,11 +1406,9 @@ const TemplatePlaceholder = () => {
{/* pdf render view */}
<div className=" w-full md:w-[57%] flex mr-4">
<PdfZoom
setScale={setScale}
scale={scale}
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
handleRotationFun={handleRotationFun}
/>
<div className="w-full md:w-[95%]">
{/* this modal is used show alert set placeholder for all signers before send mail */}
@@ -1483,6 +1551,9 @@ const TemplatePlaceholder = () => {
currentSigner={true}
setIsEditTemplate={handleEditTemplateModal}
dataTut4="reactourFour"
handleRotationFun={handleRotationFun}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
/>
<div
ref={divRef}
@@ -1528,6 +1599,7 @@ const TemplatePlaceholder = () => {
setScale={setScale}
scale={scale}
setIsSelectId={setIsSelectId}
pdfRotateBase64={pdfRotateBase64}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
@@ -1671,6 +1743,11 @@ const TemplatePlaceholder = () => {
fontColor={fontColor}
setFontColor={setFontColor}
/>
<RotateAlert
isRotate={isRotate.status}
setIsRotate={setIsRotate}
handleRemoveWidgets={handleRemovePlaceholder}
/>
</DndProvider>
</>
);
+111 -95
View File
@@ -46,109 +46,125 @@ const AddContact = (props) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
try {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
if (phone) {
contactQuery.set("Phone", phone);
}
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
if (localStorage.getItem("TenantId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
}
if (localStorage.getItem("TenantId")) {
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
const user = Parse.User.current();
const query = new Parse.Query("contracts_Contactbook");
query.equalTo("CreatedBy", user);
query.notEqualTo("IsDeleted", true);
query.equalTo("Email", email);
const res = await query.first();
if (!res) {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
if (phone) {
contactQuery.set("Phone", phone);
}
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
const user = await _user.save();
if (user) {
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
props.details(parseData);
if (props.closePopup) {
props.closePopup();
if (localStorage.getItem("TenantId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
}
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
const user = await _user.save();
if (user) {
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
props.details(parseData);
if (props.closePopup) {
props.closePopup();
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params);
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details(parseData);
}
if (props.closePopup) {
props.closePopup();
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
}
} else {
alert(t("add-signer-alert"));
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params);
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details(parseData);
}
if (props.closePopup) {
props.closePopup();
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
// console.log("err", err);
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
}
} catch (err) {
// console.log("err", err);
} else {
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
}
@@ -1306,9 +1306,7 @@ const ReportTable = (props) => {
<ModalUi
isOpen
title={t("public-url")}
handleClose={() => {
setIsPublicProfile({});
}}
handleClose={() => setIsPublicProfile({})}
>
<div className="m-[20px]">
{publicUserName ? (
@@ -604,5 +604,8 @@
"Add-seats": "Add Seats",
"user-name-exist": "user name already exist",
"format": "format",
"select-date-format": "Select a date format"
"select-date-format": "Select a date format",
"rotate-right": "Rotate right",
"rotate-left": "Rotate left",
"rotate-alert-mssg": "All widgets on this page will be lost. Are you sure you want to proceed?"
}
@@ -603,5 +603,8 @@
"Select": "sélectionner",
"Add-seats": "Ajouter des sièges",
"format": "format",
"select-date-format": "Sélectionnez un format de date"
"select-date-format": "Sélectionnez un format de date",
"rotate-right": "Faire pivoter à droite",
"rotate-left": "Faire pivoter à gauche",
"rotate-alert-mssg" :"Tous les widgets de cette page seront perdus. Êtes-vous sûr de vouloir continuer ?"
}