mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-05 17:27:39 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b202d78059 | ||
|
|
e15136ce51 | ||
|
|
39c9211167 | ||
|
|
64864e3f54 | ||
|
|
af0bef6d29 | ||
|
|
130704b32f | ||
|
|
5b139b66a9 | ||
|
|
f9c5041af1 | ||
|
|
245bf8046b | ||
|
|
94eb5f9efe | ||
|
|
044128f36a | ||
|
|
594383fe14 | ||
|
|
90a585367e | ||
|
|
0b8382df00 | ||
|
|
a54c7e7fd7 | ||
|
|
45a2c88e90 | ||
|
|
e4526f026b | ||
|
|
11a2f6b8ec | ||
|
|
3bf35166fc | ||
|
|
cd50c8cf20 | ||
|
|
055ef3f209 | ||
|
|
ec0e291604 | ||
|
|
836e952ba0 | ||
|
|
c5f39e2a24 | ||
|
|
bb69518674 | ||
|
|
dc1fab5250 | ||
|
|
3e185ef796 | ||
|
|
8cf5adb273 | ||
|
|
803faed97b | ||
|
|
b1dd4df3c0 | ||
|
|
13e2d92ce6 | ||
|
|
4b9f22a5bc | ||
|
|
77c61c89a2 |
Generated
+13
-13
@@ -33,7 +33,7 @@
|
|||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-konva": "^18.2.10",
|
"react-konva": "^18.2.10",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
"react-pdf": "^7.7.1",
|
"react-pdf": "^7.7.3",
|
||||||
"react-quill": "^2.0.0",
|
"react-quill": "^2.0.0",
|
||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.0",
|
||||||
"react-rnd": "^10.4.10",
|
"react-rnd": "^10.4.10",
|
||||||
@@ -9883,9 +9883,9 @@
|
|||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||||
},
|
},
|
||||||
"node_modules/ejs": {
|
"node_modules/ejs": {
|
||||||
"version": "3.1.9",
|
"version": "3.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||||
"integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==",
|
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jake": "^10.8.5"
|
"jake": "^10.8.5"
|
||||||
},
|
},
|
||||||
@@ -19821,9 +19821,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-pdf": {
|
"node_modules/react-pdf": {
|
||||||
"version": "7.7.1",
|
"version": "7.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-7.7.3.tgz",
|
||||||
"integrity": "sha512-cbbf/PuRtGcPPw+HLhMI1f6NSka8OJgg+j/yPWTe95Owf0fK6gmVY7OXpTxMeh92O3T3K3EzfE0ML0eXPGwR5g==",
|
"integrity": "sha512-a2VfDl8hiGjugpqezBTUzJHYLNB7IS7a2t7GD52xMI9xHg8LdVaTMsnM9ZlNmKadnStT/tvX5IfV0yLn+JvYmw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"dequal": "^2.0.3",
|
"dequal": "^2.0.3",
|
||||||
@@ -22168,9 +22168,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tar": {
|
"node_modules/tar": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||||
"integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
|
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chownr": "^2.0.0",
|
"chownr": "^2.0.0",
|
||||||
@@ -23188,9 +23188,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webpack-dev-middleware": {
|
"node_modules/webpack-dev-middleware": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
|
||||||
"integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
|
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colorette": "^2.0.10",
|
"colorette": "^2.0.10",
|
||||||
"memfs": "^3.4.3",
|
"memfs": "^3.4.3",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-konva": "^18.2.10",
|
"react-konva": "^18.2.10",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
"react-pdf": "^7.7.1",
|
"react-pdf": "^7.7.3",
|
||||||
"react-quill": "^2.0.0",
|
"react-quill": "^2.0.0",
|
||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.0",
|
||||||
"react-rnd": "^10.4.10",
|
"react-rnd": "^10.4.10",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import RSC from "react-scrollbars-custom";
|
import RSC from "react-scrollbars-custom";
|
||||||
import { Document, Page } from "react-pdf";
|
import { Document, Page } from "react-pdf";
|
||||||
import { themeColor } from "../../constant/const";
|
import { themeColor } from "../../constant/const";
|
||||||
@@ -9,13 +9,54 @@ function RenderAllPdfPage({
|
|||||||
setAllPages,
|
setAllPages,
|
||||||
setPageNumber,
|
setPageNumber,
|
||||||
setSignBtnPosition,
|
setSignBtnPosition,
|
||||||
pageNumber
|
pageNumber,
|
||||||
|
signerPos,
|
||||||
|
signerObjectId
|
||||||
}) {
|
}) {
|
||||||
|
const [signPageNumber, setSignPageNumber] = useState([]);
|
||||||
|
const [bookmarkColor, setBookmarkColor] = useState("");
|
||||||
//set all number of pages after load pdf
|
//set all number of pages after load pdf
|
||||||
function onDocumentLoad({ numPages }) {
|
function onDocumentLoad({ numPages }) {
|
||||||
setAllPages(numPages);
|
setAllPages(numPages);
|
||||||
|
//check if signerPos array exist then save page number exist in signerPos array to show bookmark icon
|
||||||
|
if (signerPos) {
|
||||||
|
const checkUser = signerPos.filter(
|
||||||
|
(data) => data.signerObjId === signerObjectId
|
||||||
|
);
|
||||||
|
setBookmarkColor(checkUser[0]?.blockColor);
|
||||||
|
let pageNumberArr = [];
|
||||||
|
if (checkUser?.length > 0) {
|
||||||
|
checkUser[0]?.placeHolder?.map((data) => {
|
||||||
|
pageNumberArr.push(data?.pageNumber);
|
||||||
|
});
|
||||||
|
|
||||||
|
setSignPageNumber(pageNumberArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//'function `addSignatureBookmark` is used to display the page where the user's signature is located.
|
||||||
|
const addSignatureBookmark = (index) => {
|
||||||
|
const ispageNumber = signPageNumber.includes(index + 1);
|
||||||
|
return (
|
||||||
|
ispageNumber && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
zIndex: 2,
|
||||||
|
top: -12,
|
||||||
|
right: -7,
|
||||||
|
transform: "translate(50% -50%)"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
style={{ color: bookmarkColor || "red" }}
|
||||||
|
className="fa-solid fa-bookmark"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="showPages">
|
<div className="showPages">
|
||||||
<div
|
<div
|
||||||
@@ -66,7 +107,8 @@ function RenderAllPdfPage({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
contain: "content"
|
|
||||||
|
position: "relative"
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPageNumber(index + 1);
|
setPageNumber(index + 1);
|
||||||
@@ -75,15 +117,25 @@ function RenderAllPdfPage({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Page
|
{signerPos && addSignatureBookmark(index)}
|
||||||
key={`page_${index + 1}`}
|
|
||||||
pageNumber={index + 1}
|
<div
|
||||||
width={100}
|
style={{
|
||||||
height={100}
|
position: "relative",
|
||||||
scale={1}
|
zIndex: 1,
|
||||||
renderAnnotationLayer={false}
|
overflow: "hidden"
|
||||||
renderTextLayer={false}
|
}}
|
||||||
/>
|
>
|
||||||
|
<Page
|
||||||
|
key={`page_${index + 1}`}
|
||||||
|
pageNumber={index + 1}
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
scale={1}
|
||||||
|
renderAnnotationLayer={false}
|
||||||
|
renderTextLayer={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Document>
|
</Document>
|
||||||
|
|||||||
@@ -169,32 +169,40 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
|||||||
{required && <span className="text-red-500 text-[13px]">*</span>}
|
{required && <span className="text-red-500 text-[13px]">*</span>}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center">
|
<div className="relative max-w-sm">
|
||||||
<div>
|
<div
|
||||||
<i
|
onClick={() => SetIsOpen(true)}
|
||||||
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
className=" cursor-pointer rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center"
|
||||||
style={{ fontSize: "40px" }}
|
>
|
||||||
aria-hidden="true"
|
<div>
|
||||||
></i>
|
<i
|
||||||
</div>
|
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
||||||
<div className="font-semibold ">
|
style={{ fontSize: "40px" }}
|
||||||
<div className="flex items-center gap-2">
|
aria-hidden="true"
|
||||||
<p>
|
></i>
|
||||||
{selectFolder && selectFolder.Name
|
</div>
|
||||||
? selectFolder.Name
|
<div className="font-semibold ">
|
||||||
: "OpenSign™ Drive"}
|
<div className="flex items-center gap-2">
|
||||||
</p>
|
<p>
|
||||||
<div className="text-black text-sm" onClick={() => SetIsOpen(true)}>
|
{selectFolder && selectFolder.Name
|
||||||
<i
|
? selectFolder.Name
|
||||||
className="fa fa-pencil"
|
: "OpenSign™ Drive"}
|
||||||
title="Select Folder"
|
</p>
|
||||||
aria-hidden="true"
|
<div
|
||||||
></i>
|
className="text-black text-sm"
|
||||||
</div>
|
// onClick={() => SetIsOpen(true)}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-pencil cursor-pointer"
|
||||||
|
title="Select Folder"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] text-gray-400">
|
||||||
|
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[10px] text-gray-400">
|
|
||||||
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-2 right-1 cursor-pointer">
|
<div className="absolute top-2 right-1 cursor-pointer">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { PDFDocument } from "pdf-lib";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { isEnableSubscription, submitBtn } from "../constant/const";
|
import { isEnableSubscription, submitBtn } from "../constant/const";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
|
|
||||||
// `Form` render all type of Form on this basis of their provided in path
|
// `Form` render all type of Form on this basis of their provided in path
|
||||||
function Form() {
|
function Form() {
|
||||||
@@ -707,12 +708,13 @@ const Forms = (props) => {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SelectFolder
|
{props.title !== "New Template" && (
|
||||||
onSuccess={handleFolder}
|
<SelectFolder
|
||||||
folderCls={props.Cls}
|
onSuccess={handleFolder}
|
||||||
isReset={isReset}
|
folderCls={props.Cls}
|
||||||
/>
|
isReset={isReset}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{props.title === "Request Signatures" && (
|
{props.title === "Request Signatures" && (
|
||||||
<div className="text-xs mt-2">
|
<div className="text-xs mt-2">
|
||||||
<label className="block">
|
<label className="block">
|
||||||
@@ -731,7 +733,66 @@ const Forms = (props) => {
|
|||||||
)}
|
)}
|
||||||
{props.title !== "Sign Yourself" && (
|
{props.title !== "Sign Yourself" && (
|
||||||
<div className="text-xs mt-2">
|
<div className="text-xs mt-2">
|
||||||
<label className="block">Send In Order</label>
|
<label className="block">
|
||||||
|
Send In Order
|
||||||
|
<a data-tooltip-id="sendInOrder-tooltip" className="ml-1">
|
||||||
|
<sup>
|
||||||
|
<i
|
||||||
|
className="fa-solid fa-question rounded-full"
|
||||||
|
style={{
|
||||||
|
borderColor: "#33bbff",
|
||||||
|
color: "#33bbff",
|
||||||
|
fontSize: 13,
|
||||||
|
borderWidth: 1.5,
|
||||||
|
padding: "1.5px 4px"
|
||||||
|
}}
|
||||||
|
></i>
|
||||||
|
</sup>
|
||||||
|
</a>
|
||||||
|
<Tooltip id="sendInOrder-tooltip" className="z-50">
|
||||||
|
<div className="max-w-[200px] md:max-w-[450px]">
|
||||||
|
<p className="font-bold">Send in Order</p>
|
||||||
|
<p>
|
||||||
|
Choose how you want the signing requests to be sent to
|
||||||
|
the document signers:
|
||||||
|
</p>
|
||||||
|
<p className="p-[5px]">
|
||||||
|
<ol className="list-disc">
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">Yes:</span>
|
||||||
|
<span>
|
||||||
|
{" "}
|
||||||
|
Selecting this option will send the signing
|
||||||
|
request to the first signer initially. Once the
|
||||||
|
first signer completes their part, the next signer
|
||||||
|
in the sequence will receive the request. This
|
||||||
|
process continues until all signers have signed
|
||||||
|
the document. This method ensures that the
|
||||||
|
document is signed in a specific order.
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">No: </span>
|
||||||
|
<span>
|
||||||
|
Selecting this option will send the signing links
|
||||||
|
to all signers simultaneously. Every signer can
|
||||||
|
sign the document at their convenience, regardless
|
||||||
|
of whether other signers have completed their
|
||||||
|
signatures. This method is faster but does not
|
||||||
|
enforce any signing order among the participants.
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Select the option that best suits the needs of your
|
||||||
|
document processing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
|
|||||||
@@ -472,36 +472,8 @@ function PdfRequestFiles() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// const checkSendInOrder = () => {
|
|
||||||
// if (sendInOrder) {
|
|
||||||
// const index = pdfDetails?.[0].Signers.findIndex(
|
|
||||||
// (x) => x.Email === jsonSender.email
|
|
||||||
// );
|
|
||||||
// const newIndex = index - 1;
|
|
||||||
// if (newIndex !== -1) {
|
|
||||||
// const user = pdfDetails?.[0].Signers[newIndex];
|
|
||||||
// const isPrevUserSigned =
|
|
||||||
// pdfDetails?.[0].AuditTrail &&
|
|
||||||
// pdfDetails?.[0].AuditTrail.some(
|
|
||||||
// (x) =>
|
|
||||||
// x.UserPtr.objectId === user.objectId && x.Activity === "Signed"
|
|
||||||
// );
|
|
||||||
// if (isPrevUserSigned) {
|
|
||||||
// return true;
|
|
||||||
// } else {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
//function for embed signature or image url in pdf
|
//function for embed signature or image url in pdf
|
||||||
async function embedWidgetsData() {
|
async function embedWidgetsData() {
|
||||||
// const validateSigning = checkSendInOrder();
|
|
||||||
// if (validateSigning) {
|
|
||||||
try {
|
try {
|
||||||
const checkUser = signerPos.filter(
|
const checkUser = signerPos.filter(
|
||||||
(data) => data.signerObjId === signerObjectId
|
(data) => data.signerObjId === signerObjectId
|
||||||
@@ -512,83 +484,99 @@ function PdfRequestFiles() {
|
|||||||
showAlert = false,
|
showAlert = false,
|
||||||
widgetKey,
|
widgetKey,
|
||||||
radioExist,
|
radioExist,
|
||||||
requiredCheckbox;
|
requiredCheckbox,
|
||||||
|
pageNumber; // `pageNumber` is used to check on which page user did not fill widget's data then change current pageNumber and show tour message on that page
|
||||||
|
|
||||||
for (let i = 0; i < checkUser[0].placeHolder.length; i++) {
|
for (let i = 0; i < checkUser[0].placeHolder.length; i++) {
|
||||||
for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) {
|
for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) {
|
||||||
|
//get current page
|
||||||
|
const updatePage = checkUser[0].placeHolder[i]?.pageNumber;
|
||||||
|
//checking checbox type widget
|
||||||
checkboxExist =
|
checkboxExist =
|
||||||
checkUser[0].placeHolder[i].pos[j].type === "checkbox";
|
checkUser[0].placeHolder[i].pos[j].type === "checkbox";
|
||||||
|
//checking radio button type widget
|
||||||
radioExist =
|
radioExist =
|
||||||
checkUser[0].placeHolder[i].pos[j].type === radioButtonWidget;
|
checkUser[0].placeHolder[i].pos[j].type === radioButtonWidget;
|
||||||
|
//condition to check checkbox widget exist or not
|
||||||
if (checkboxExist) {
|
if (checkboxExist) {
|
||||||
|
//get all required type checkbox
|
||||||
requiredCheckbox = checkUser[0].placeHolder[i].pos.filter(
|
requiredCheckbox = checkUser[0].placeHolder[i].pos.filter(
|
||||||
(position) =>
|
(position) =>
|
||||||
!position.options?.isReadOnly && position.type === "checkbox"
|
!position.options?.isReadOnly && position.type === "checkbox"
|
||||||
);
|
);
|
||||||
|
//if required type checkbox data exit then check user checked all checkbox or some checkbox remain to check
|
||||||
|
//also validate to minimum and maximum required checkbox
|
||||||
if (requiredCheckbox && requiredCheckbox.length > 0) {
|
if (requiredCheckbox && requiredCheckbox.length > 0) {
|
||||||
for (let i = 0; i < requiredCheckbox.length; i++) {
|
for (let i = 0; i < requiredCheckbox.length; i++) {
|
||||||
|
//get minimum required count if exit
|
||||||
const minCount =
|
const minCount =
|
||||||
requiredCheckbox[i].options?.validation?.minRequiredCount;
|
requiredCheckbox[i].options?.validation?.minRequiredCount;
|
||||||
const parseMin = minCount && parseInt(minCount);
|
const parseMin = minCount && parseInt(minCount);
|
||||||
|
//get maximum required count if exit
|
||||||
const maxCount =
|
const maxCount =
|
||||||
requiredCheckbox[i].options?.validation?.maxRequiredCount;
|
requiredCheckbox[i].options?.validation?.maxRequiredCount;
|
||||||
const parseMax = maxCount && parseInt(maxCount);
|
const parseMax = maxCount && parseInt(maxCount);
|
||||||
|
//in `response` variable is used to get how many checkbox checked by user
|
||||||
const response =
|
const response =
|
||||||
requiredCheckbox[i].options?.response?.length;
|
requiredCheckbox[i].options?.response?.length;
|
||||||
|
//in `defaultValue` variable is used to get how many checkbox checked by default
|
||||||
const defaultValue =
|
const defaultValue =
|
||||||
requiredCheckbox[i].options?.defaultValue?.length;
|
requiredCheckbox[i].options?.defaultValue?.length;
|
||||||
if (parseMin === 0 && parseMax === 0) {
|
//condition to check parseMin and parseMax greater than 0 then consider it as a required check box
|
||||||
if (!showAlert) {
|
if (
|
||||||
showAlert = false;
|
parseMin > 0 &&
|
||||||
setminRequiredCount(null);
|
parseMax > 0 &&
|
||||||
}
|
!response &&
|
||||||
} else if (parseMin === 0 && parseMax > 0) {
|
!defaultValue &&
|
||||||
if (!showAlert) {
|
!showAlert
|
||||||
showAlert = false;
|
) {
|
||||||
setminRequiredCount(null);
|
showAlert = true;
|
||||||
}
|
widgetKey = requiredCheckbox[i].key;
|
||||||
} else if (!response) {
|
pageNumber = updatePage;
|
||||||
if (!defaultValue) {
|
setminRequiredCount(parseMin);
|
||||||
if (!showAlert) {
|
}
|
||||||
showAlert = true;
|
//else condition to validate minimum required checkbox
|
||||||
widgetKey = requiredCheckbox[i].key;
|
else if (parseMin > 0 && (parseMin > response || !response)) {
|
||||||
setminRequiredCount(parseMin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (parseMin > 0 && parseMin > response) {
|
|
||||||
if (!showAlert) {
|
if (!showAlert) {
|
||||||
showAlert = true;
|
showAlert = true;
|
||||||
widgetKey = requiredCheckbox[i].key;
|
widgetKey = requiredCheckbox[i].key;
|
||||||
|
pageNumber = updatePage;
|
||||||
|
|
||||||
setminRequiredCount(parseMin);
|
setminRequiredCount(parseMin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (radioExist) {
|
}
|
||||||
|
//condition to check radio widget exist or not
|
||||||
|
else if (radioExist) {
|
||||||
|
//get all required type radio button
|
||||||
requiredRadio = checkUser[0].placeHolder[i].pos.filter(
|
requiredRadio = checkUser[0].placeHolder[i].pos.filter(
|
||||||
(position) =>
|
(position) =>
|
||||||
!position.options?.isReadOnly &&
|
!position.options?.isReadOnly &&
|
||||||
position.type === radioButtonWidget
|
position.type === radioButtonWidget
|
||||||
);
|
);
|
||||||
|
//if required type radio data exit then check user checked all radio button or some radio remain to check
|
||||||
if (requiredRadio && requiredRadio?.length > 0) {
|
if (requiredRadio && requiredRadio?.length > 0) {
|
||||||
let checkSigned;
|
let checkSigned;
|
||||||
for (let i = 0; i < requiredRadio?.length; i++) {
|
for (let i = 0; i < requiredRadio?.length; i++) {
|
||||||
checkSigned = requiredRadio[i]?.options.response;
|
checkSigned = requiredRadio[i]?.options?.response;
|
||||||
if (!checkSigned) {
|
if (!checkSigned) {
|
||||||
let checkDefaultSigned =
|
let checkDefaultSigned =
|
||||||
requiredRadio[i]?.options.defaultValue;
|
requiredRadio[i]?.options?.defaultValue;
|
||||||
if (!checkDefaultSigned) {
|
if (!checkDefaultSigned && !showAlert) {
|
||||||
if (!showAlert) {
|
showAlert = true;
|
||||||
showAlert = true;
|
widgetKey = requiredRadio[i].key;
|
||||||
widgetKey = requiredRadio[i].key;
|
pageNumber = updatePage;
|
||||||
setminRequiredCount(null);
|
setminRequiredCount(null);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
//else condition to check all type widget data fill or not except checkbox and radio button
|
||||||
|
else {
|
||||||
|
//get all required type widgets except checkbox and radio
|
||||||
const requiredWidgets = checkUser[0].placeHolder[i].pos.filter(
|
const requiredWidgets = checkUser[0].placeHolder[i].pos.filter(
|
||||||
(position) =>
|
(position) =>
|
||||||
position.options?.status === "required" &&
|
position.options?.status === "required" &&
|
||||||
@@ -601,16 +589,14 @@ function PdfRequestFiles() {
|
|||||||
checkSigned = requiredWidgets[i]?.options?.response;
|
checkSigned = requiredWidgets[i]?.options?.response;
|
||||||
if (!checkSigned) {
|
if (!checkSigned) {
|
||||||
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
||||||
|
|
||||||
let checkDefaultSigned =
|
|
||||||
requiredWidgets[i]?.options?.defaultValue;
|
|
||||||
if (!checkSignUrl) {
|
if (!checkSignUrl) {
|
||||||
if (!checkDefaultSigned) {
|
let checkDefaultSigned =
|
||||||
if (!showAlert) {
|
requiredWidgets[i]?.options?.defaultValue;
|
||||||
showAlert = true;
|
if (!checkDefaultSigned && !showAlert) {
|
||||||
widgetKey = requiredWidgets[i].key;
|
showAlert = true;
|
||||||
setminRequiredCount(null);
|
widgetKey = requiredWidgets[i].key;
|
||||||
}
|
pageNumber = updatePage;
|
||||||
|
setminRequiredCount(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -618,16 +604,22 @@ function PdfRequestFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//when showAlert is true then break the loop and show alert to fill required data in widgets
|
||||||
|
if (showAlert) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkboxExist && requiredCheckbox && showAlert) {
|
if (checkboxExist && requiredCheckbox && showAlert) {
|
||||||
setUnSignedWidgetId(widgetKey);
|
setUnSignedWidgetId(widgetKey);
|
||||||
|
setPageNumber(pageNumber);
|
||||||
setWidgetsTour(true);
|
setWidgetsTour(true);
|
||||||
} else if (radioExist && showAlert) {
|
} else if (radioExist && showAlert) {
|
||||||
setUnSignedWidgetId(widgetKey);
|
setUnSignedWidgetId(widgetKey);
|
||||||
|
setPageNumber(pageNumber);
|
||||||
setWidgetsTour(true);
|
setWidgetsTour(true);
|
||||||
} else if (showAlert) {
|
} else if (showAlert) {
|
||||||
setUnSignedWidgetId(widgetKey);
|
setUnSignedWidgetId(widgetKey);
|
||||||
|
setPageNumber(pageNumber);
|
||||||
setWidgetsTour(true);
|
setWidgetsTour(true);
|
||||||
} else {
|
} else {
|
||||||
setIsUiLoading(true);
|
setIsUiLoading(true);
|
||||||
@@ -882,15 +874,6 @@ function PdfRequestFiles() {
|
|||||||
alertMessage: "something went wrong, please try again later."
|
alertMessage: "something went wrong, please try again later."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// setIsAlert({
|
|
||||||
// isShow: true,
|
|
||||||
// alertMessage:
|
|
||||||
// "Please wait for your turn to sign this document, as it has been set up by the creator to be signed in a specific order; you'll be notified when it's your turn."
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//function for update TourStatus
|
//function for update TourStatus
|
||||||
@@ -1349,6 +1332,8 @@ function PdfRequestFiles() {
|
|||||||
</ModalUi>
|
</ModalUi>
|
||||||
{/* this component used to render all pdf pages in left side */}
|
{/* this component used to render all pdf pages in left side */}
|
||||||
<RenderAllPdfPage
|
<RenderAllPdfPage
|
||||||
|
signerPos={signerPos}
|
||||||
|
signerObjectId={signerObjectId}
|
||||||
signPdfUrl={
|
signPdfUrl={
|
||||||
pdfDetails[0] &&
|
pdfDetails[0] &&
|
||||||
(pdfDetails[0]?.SignedUrl || pdfDetails[0]?.URL)
|
(pdfDetails[0]?.SignedUrl || pdfDetails[0]?.URL)
|
||||||
|
|||||||
@@ -409,7 +409,10 @@ function PlaceHolderSign() {
|
|||||||
setActiveMailAdapter(res[0]?.active_mail_adapter);
|
setActiveMailAdapter(res[0]?.active_mail_adapter);
|
||||||
setSignerUserId(res[0].objectId);
|
setSignerUserId(res[0].objectId);
|
||||||
const tourstatus = res[0].TourStatus && res[0].TourStatus;
|
const tourstatus = res[0].TourStatus && res[0].TourStatus;
|
||||||
if (tourstatus && tourstatus.length > 0) {
|
const alreadyPlaceholder = documentData[0]?.SignedUrl;
|
||||||
|
if (alreadyPlaceholder) {
|
||||||
|
setCheckTourStatus(true);
|
||||||
|
} else if (tourstatus && tourstatus.length > 0) {
|
||||||
setTourStatus(tourstatus);
|
setTourStatus(tourstatus);
|
||||||
const checkTourRecipients = tourstatus.filter(
|
const checkTourRecipients = tourstatus.filter(
|
||||||
(data) => data.placeholder
|
(data) => data.placeholder
|
||||||
|
|||||||
@@ -233,29 +233,6 @@ function SignYourSelf() {
|
|||||||
setSignBtnPosition([]);
|
setSignBtnPosition([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isCompleted) {
|
|
||||||
//check current user email verified or not
|
|
||||||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
|
||||||
let isEmailVerified;
|
|
||||||
isEmailVerified = currentUser?.emailVerified;
|
|
||||||
if (isEmailVerified) {
|
|
||||||
setIsEmailVerified(isEmailVerified);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const userQuery = new Parse.Query(Parse.User);
|
|
||||||
const user = await userQuery.get(currentUser.objectId, {
|
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
|
||||||
});
|
|
||||||
if (user) {
|
|
||||||
isEmailVerified = user?.get("emailVerified");
|
|
||||||
setIsEmailVerified(isEmailVerified);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
setHandleError("Error: Something went wrong!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (
|
} else if (
|
||||||
documentData === "Error: Something went wrong!" ||
|
documentData === "Error: Something went wrong!" ||
|
||||||
(documentData.result && documentData.result.error)
|
(documentData.result && documentData.result.error)
|
||||||
@@ -603,104 +580,126 @@ function SignYourSelf() {
|
|||||||
};
|
};
|
||||||
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
||||||
async function embedWidgetsData() {
|
async function embedWidgetsData() {
|
||||||
let showAlert = false,
|
//check current user email is verified or not
|
||||||
isSignatureExist = false;
|
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||||
try {
|
let isEmailVerified;
|
||||||
for (let i = 0; i < xyPostion?.length; i++) {
|
isEmailVerified = currentUser?.emailVerified;
|
||||||
const requiredWidgets = xyPostion[i].pos.filter(
|
if (isEmailVerified) {
|
||||||
(position) => position.type !== "checkbox"
|
setIsEmailVerified(isEmailVerified);
|
||||||
);
|
} else {
|
||||||
if (requiredWidgets && requiredWidgets?.length > 0) {
|
try {
|
||||||
let checkSigned;
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
for (let i = 0; i < requiredWidgets?.length; i++) {
|
const user = await userQuery.get(currentUser.objectId, {
|
||||||
checkSigned = requiredWidgets[i]?.options?.response;
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
if (!checkSigned) {
|
});
|
||||||
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
if (user) {
|
||||||
let checkDefaultSigned =
|
isEmailVerified = user?.get("emailVerified");
|
||||||
requiredWidgets[i]?.options?.defaultValue;
|
setIsEmailVerified(isEmailVerified);
|
||||||
if (!checkSignUrl) {
|
}
|
||||||
if (!checkDefaultSigned) {
|
} catch (e) {
|
||||||
if (!showAlert) {
|
setHandleError("Error: Something went wrong!");
|
||||||
showAlert = true;
|
}
|
||||||
|
}
|
||||||
|
if (isEmailVerified) {
|
||||||
|
let showAlert = false,
|
||||||
|
isSignatureExist = false;
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < xyPostion?.length; i++) {
|
||||||
|
const requiredWidgets = xyPostion[i].pos.filter(
|
||||||
|
(position) => position.type !== "checkbox"
|
||||||
|
);
|
||||||
|
if (requiredWidgets && requiredWidgets?.length > 0) {
|
||||||
|
let checkSigned;
|
||||||
|
for (let i = 0; i < requiredWidgets?.length; i++) {
|
||||||
|
checkSigned = requiredWidgets[i]?.options?.response;
|
||||||
|
if (!checkSigned) {
|
||||||
|
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
||||||
|
let checkDefaultSigned =
|
||||||
|
requiredWidgets[i]?.options?.defaultValue;
|
||||||
|
if (!checkSignUrl) {
|
||||||
|
if (!checkDefaultSigned) {
|
||||||
|
if (!showAlert) {
|
||||||
|
showAlert = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
//condition to check exist signature widget or not
|
||||||
//condition to check exist signature widget or not
|
if (!isSignatureExist) {
|
||||||
if (!isSignatureExist) {
|
isSignatureExist = xyPostion[i].pos.some(
|
||||||
isSignatureExist = xyPostion[i].pos.some(
|
(data) => data?.type === "signature"
|
||||||
(data) => data?.type === "signature"
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (xyPostion.length === 0 || !isSignatureExist) {
|
|
||||||
setIsAlert({
|
|
||||||
header: "Fields required",
|
|
||||||
isShow: true,
|
|
||||||
alertMessage:
|
|
||||||
"Please ensure there's at least one signature widget added"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else if (showAlert) {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage:
|
|
||||||
"Please ensure all field is accurately filled and meets all requirements."
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
setIsCeleb(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsCeleb(false);
|
|
||||||
}, 3000);
|
|
||||||
setIsUiLoading(true);
|
|
||||||
const existingPdfBytes = pdfArrayBuffer;
|
|
||||||
// Load a PDFDocument from the existing PDF bytes
|
|
||||||
try {
|
|
||||||
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
|
||||||
const isSignYourSelfFlow = true;
|
|
||||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
|
||||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
|
||||||
//embed document's object id to all pages in pdf document
|
|
||||||
if (!HeaderDocId) {
|
|
||||||
await embedDocId(pdfDoc, documentId, allPages);
|
|
||||||
}
|
|
||||||
//embed multi signature in pdf
|
|
||||||
const pdfBytes = await multiSignEmbed(
|
|
||||||
xyPostion,
|
|
||||||
pdfDoc,
|
|
||||||
pdfOriginalWidth,
|
|
||||||
isSignYourSelfFlow,
|
|
||||||
containerWH
|
|
||||||
);
|
|
||||||
// console.log("pdf", pdfBytes);
|
|
||||||
//function for call to embed signature in pdf and get digital signature pdf
|
|
||||||
await signPdfFun(pdfBytes, documentId);
|
|
||||||
} catch (err) {
|
|
||||||
setIsUiLoading(false);
|
|
||||||
if (err && err.message.includes("is encrypted.")) {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: `Currently encrypted pdf files are not supported.`
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log("err in signing", err);
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: `Something went wrong.`
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (xyPostion.length === 0 || !isSignatureExist) {
|
||||||
|
setIsAlert({
|
||||||
|
header: "Fields required",
|
||||||
|
isShow: true,
|
||||||
|
alertMessage:
|
||||||
|
"Please ensure there's at least one signature widget added"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (showAlert) {
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage:
|
||||||
|
"Please ensure all field is accurately filled and meets all requirements."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
setIsCeleb(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsCeleb(false);
|
||||||
|
}, 3000);
|
||||||
|
setIsUiLoading(true);
|
||||||
|
const existingPdfBytes = pdfArrayBuffer;
|
||||||
|
// Load a PDFDocument from the existing PDF bytes
|
||||||
|
try {
|
||||||
|
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||||
|
const isSignYourSelfFlow = true;
|
||||||
|
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||||
|
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||||
|
//embed document's object id to all pages in pdf document
|
||||||
|
if (!HeaderDocId) {
|
||||||
|
await embedDocId(pdfDoc, documentId, allPages);
|
||||||
|
}
|
||||||
|
//embed multi signature in pdf
|
||||||
|
const pdfBytes = await multiSignEmbed(
|
||||||
|
xyPostion,
|
||||||
|
pdfDoc,
|
||||||
|
pdfOriginalWidth,
|
||||||
|
isSignYourSelfFlow,
|
||||||
|
containerWH
|
||||||
|
);
|
||||||
|
// console.log("pdf", pdfBytes);
|
||||||
|
//function for call to embed signature in pdf and get digital signature pdf
|
||||||
|
await signPdfFun(pdfBytes, documentId);
|
||||||
|
} catch (err) {
|
||||||
|
setIsUiLoading(false);
|
||||||
|
if (err && err.message.includes("is encrypted.")) {
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: `Currently encrypted pdf files are not supported.`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("err in signing", err);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: `Something went wrong.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in embedselfsign ", err);
|
||||||
|
setIsUiLoading(false);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong, please try again later."
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.log("err in embedselfsign ", err);
|
|
||||||
setIsUiLoading(false);
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: "something went wrong, please try again later."
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//function for get digital signature
|
//function for get digital signature
|
||||||
|
|||||||
@@ -404,7 +404,6 @@ function UserProfile() {
|
|||||||
url={
|
url={
|
||||||
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
||||||
}
|
}
|
||||||
isSubscribe={isSubscribe}
|
|
||||||
/>{" "}
|
/>{" "}
|
||||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||||
import { openInNewTab } from "../constant/Utils";
|
import { openInNewTab } from "../constant/Utils";
|
||||||
import { isEnableSubscription } from "../constant/const";
|
const Tooltip = ({ id, message, url, iconColor }) =>
|
||||||
const Tooltip = ({ id, message, url, iconColor, isSubscribe }) =>
|
|
||||||
url ? (
|
url ? (
|
||||||
<button
|
<button onClick={() => openInNewTab(url)} className={"text-center"}>
|
||||||
onClick={() => openInNewTab(url)}
|
|
||||||
className={
|
|
||||||
isSubscribe || !isEnableSubscription
|
|
||||||
? "text-center"
|
|
||||||
: "text-center opacity-20 pointer-events-none"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<sup>
|
<sup>
|
||||||
<i
|
<i
|
||||||
className="fa-solid fa-question rounded-full"
|
className="fa-solid fa-question rounded-full"
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import getTemplatetList from './routes/getTemplateList.js';
|
|||||||
import updateTemplate from './routes/updateTemplate.js';
|
import updateTemplate from './routes/updateTemplate.js';
|
||||||
import createContact from './routes/createContact.js';
|
import createContact from './routes/createContact.js';
|
||||||
import multer from 'multer';
|
import multer from 'multer';
|
||||||
// import fs from 'node:fs';
|
|
||||||
import updateDocument from './routes/updateDocument.js';
|
import updateDocument from './routes/updateDocument.js';
|
||||||
import deleteDocument from './routes/deleteDocument.js';
|
import deleteDocument from './routes/deleteDocument.js';
|
||||||
import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js';
|
import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js';
|
||||||
@@ -26,6 +25,11 @@ import deleteWebhook from './routes/deleteWebhook.js';
|
|||||||
import getWebhook from './routes/getWebhook.js';
|
import getWebhook from './routes/getWebhook.js';
|
||||||
import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js';
|
import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js';
|
||||||
import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js';
|
import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js';
|
||||||
|
import resendMail from './routes/resendMail.js';
|
||||||
|
import getFolder from './routes/getFolder.js';
|
||||||
|
import createFolder from './routes/createFolder.js';
|
||||||
|
import updateFolder from './routes/updateFolder.js';
|
||||||
|
import getFolderList from './routes/getFolderList.js';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
const storage = multer.memoryStorage();
|
const storage = multer.memoryStorage();
|
||||||
const upload = multer({ storage: storage });
|
const upload = multer({ storage: storage });
|
||||||
@@ -102,3 +106,18 @@ app.post('/webhook', saveWebhook);
|
|||||||
|
|
||||||
// set and update webhook
|
// set and update webhook
|
||||||
app.delete('/webhook', deleteWebhook);
|
app.delete('/webhook', deleteWebhook);
|
||||||
|
|
||||||
|
// resend mail
|
||||||
|
app.post('/resendmail', resendMail);
|
||||||
|
|
||||||
|
// create folder
|
||||||
|
app.post('/createfolder', createFolder);
|
||||||
|
|
||||||
|
// update folder
|
||||||
|
app.put('/folder/:folder_id', updateFolder);
|
||||||
|
|
||||||
|
// get folder
|
||||||
|
app.get('/folder/:folder_id', getFolder);
|
||||||
|
|
||||||
|
// get folder list
|
||||||
|
app.get('/folderlist', getFolderList);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
const email_subject = request.body.email_subject;
|
const email_subject = request.body.email_subject;
|
||||||
const email_body = request.body.email_body;
|
const email_body = request.body.email_body;
|
||||||
const sendInOrder = request.body.sendInOrder || false;
|
const sendInOrder = request.body.sendInOrder || false;
|
||||||
|
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
@@ -178,6 +179,9 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
}
|
}
|
||||||
object.set('URL', template.URL);
|
object.set('URL', template.URL);
|
||||||
object.set('SignedUrl', template.URL);
|
object.set('SignedUrl', template.URL);
|
||||||
|
if (TimeToCompleteDays) {
|
||||||
|
object.set('TimeToCompleteDays', TimeToCompleteDays);
|
||||||
|
}
|
||||||
object.set('CreatedBy', template.CreatedBy);
|
object.set('CreatedBy', template.CreatedBy);
|
||||||
object.set('ExtUserPtr', {
|
object.set('ExtUserPtr', {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
const email_subject = request.body.email_subject;
|
const email_subject = request.body.email_subject;
|
||||||
const email_body = request.body.email_body;
|
const email_body = request.body.email_body;
|
||||||
const sendInOrder = request.body.sendInOrder || false;
|
const sendInOrder = request.body.sendInOrder || false;
|
||||||
|
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
|
||||||
// console.log('fileData ', fileData);
|
// console.log('fileData ', fileData);
|
||||||
const protocol = customAPIurl();
|
const protocol = customAPIurl();
|
||||||
const baseUrl = new URL(process.env.SERVER_URL);
|
const baseUrl = new URL(process.env.SERVER_URL);
|
||||||
@@ -142,6 +143,9 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
object.set('SignedUrl', fileUrl);
|
object.set('SignedUrl', fileUrl);
|
||||||
object.set('CreatedBy', userPtr);
|
object.set('CreatedBy', userPtr);
|
||||||
object.set('ExtUserPtr', extUserPtr);
|
object.set('ExtUserPtr', extUserPtr);
|
||||||
|
if (TimeToCompleteDays) {
|
||||||
|
object.set('TimeToCompleteDays', TimeToCompleteDays);
|
||||||
|
}
|
||||||
object.set('IsSendMail', send_email);
|
object.set('IsSendMail', send_email);
|
||||||
let contact = [];
|
let contact = [];
|
||||||
if (signers && signers.length > 0) {
|
if (signers && signers.length > 0) {
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
export default async function createFolder(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const folderName = request.body.folderName;
|
||||||
|
const parentFolderId = request.body.parentFolderId;
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
const userQuery = new Parse.Query('contracts_Users');
|
||||||
|
userQuery.equalTo('UserId', userPtr);
|
||||||
|
const extUser = await userQuery.first({ useMasterKey: true });
|
||||||
|
|
||||||
|
const folderCls = new Parse.Object('contracts_Document');
|
||||||
|
folderCls.set('Name', folderName);
|
||||||
|
folderCls.set('CreatedBy', userPtr);
|
||||||
|
folderCls.set('Type', 'Folder');
|
||||||
|
folderCls.set('ExtUserPtr', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Users',
|
||||||
|
objectId: extUser.id,
|
||||||
|
});
|
||||||
|
if (parentFolderId) {
|
||||||
|
folderCls.set('Folder', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Document',
|
||||||
|
objectId: parentFolderId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setReadAccess(parseUser.userId.objectId, true); // Allow read access to the userPtr
|
||||||
|
acl.setWriteAccess(parseUser.userId.objectId, true); // Allow write access to the userPtr
|
||||||
|
|
||||||
|
// Set the ACL to the document
|
||||||
|
folderCls.setACL(acl);
|
||||||
|
|
||||||
|
const _resFolder = await folderCls.save(null, { useMasterKey: true });
|
||||||
|
if (_resFolder) {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_create_folder',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const parseRes = JSON.parse(JSON.stringify(_resFolder));
|
||||||
|
return response.json({
|
||||||
|
objectId: parseRes.objectId,
|
||||||
|
folderName: parseRes.Name,
|
||||||
|
parentFolderId: parseRes?.Folder?.objectId || '',
|
||||||
|
createdAt: parseRes.createdAt,
|
||||||
|
updatedAt: parseRes.updatedAt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,7 @@ export default async function getDocument(request, response) {
|
|||||||
note: document.Note || '',
|
note: document.Note || '',
|
||||||
folder: { objectId: document?.Folder?.objectId, name: document?.Folder?.Name } || '',
|
folder: { objectId: document?.Folder?.objectId, name: document?.Folder?.Name } || '',
|
||||||
file: document?.SignedUrl || document.URL,
|
file: document?.SignedUrl || document.URL,
|
||||||
|
certificate: document?.CertificateUrl || '',
|
||||||
owner: document?.ExtUserPtr?.Name,
|
owner: document?.ExtUserPtr?.Name,
|
||||||
signers:
|
signers:
|
||||||
document?.Placeholders?.map(y => ({
|
document?.Placeholders?.map(y => ({
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default async function getDocumentList(request, response) {
|
|||||||
title: x.Name,
|
title: x.Name,
|
||||||
note: x.Note || '',
|
note: x.Note || '',
|
||||||
folder: { objectId: x?.Folder?.objectId, name: x?.Folder?.Name } || '',
|
folder: { objectId: x?.Folder?.objectId, name: x?.Folder?.Name } || '',
|
||||||
file: x?.SignedUrl || x.URL,
|
// file: x?.SignedUrl || x.URL,
|
||||||
owner: x?.ExtUserPtr?.Name,
|
owner: x?.ExtUserPtr?.Name,
|
||||||
signers:
|
signers:
|
||||||
x?.Placeholders?.map(y => ({
|
x?.Placeholders?.map(y => ({
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
export default async function getFolder(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const folder_id = request.params.folder_id;
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const folderCls = new Parse.Query('contracts_Document');
|
||||||
|
folderCls.equalTo('CreatedBy', userPtr);
|
||||||
|
folderCls.equalTo('objectId', folder_id);
|
||||||
|
folderCls.equalTo('Type', 'Folder');
|
||||||
|
folderCls.notEqualTo('IsArchive', true);
|
||||||
|
folderCls.descending('createdAt');
|
||||||
|
folderCls.include('Folder');
|
||||||
|
|
||||||
|
const res = await folderCls.first({ useMasterKey: true });
|
||||||
|
if (res) {
|
||||||
|
const parseRes = JSON.parse(JSON.stringify(res));
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_get_folder',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({
|
||||||
|
objectId: parseRes.objectId,
|
||||||
|
folderName: parseRes.Name,
|
||||||
|
parentFolderId: parseRes?.Folder?.objectId || '',
|
||||||
|
parentFolderName: parseRes?.Folder?.Name || '',
|
||||||
|
createdAt: parseRes.createdAt,
|
||||||
|
updatedAt: parseRes.updatedAt,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_get_folder',
|
||||||
|
properties: { response_code: 404 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.status(404).json({ error: 'folder not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
export default async function getFolderList(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const parentFolderId = request.query?.parentFolderId || '';
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
const limit = request?.query?.limit ? parseInt(request.query.limit) : 100;
|
||||||
|
const skip = request?.query?.skip ? parseInt(request.query.skip) : 0;
|
||||||
|
const folderCls = new Parse.Query('contracts_Document');
|
||||||
|
folderCls.equalTo('CreatedBy', userPtr);
|
||||||
|
folderCls.equalTo('Type', 'Folder');
|
||||||
|
folderCls.notEqualTo('IsArchive', true);
|
||||||
|
folderCls.descending('createdAt');
|
||||||
|
folderCls.include('Folder');
|
||||||
|
if (parentFolderId) {
|
||||||
|
folderCls.equalTo('Folder', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Document',
|
||||||
|
objectId: parentFolderId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
folderCls.doesNotExist('Folder');
|
||||||
|
}
|
||||||
|
folderCls.descending('createdAt');
|
||||||
|
folderCls.limit(limit);
|
||||||
|
folderCls.skip(skip);
|
||||||
|
const res = await folderCls.find({ useMasterKey: true });
|
||||||
|
if (res && res.length > 0) {
|
||||||
|
const parseRes = JSON.parse(JSON.stringify(res));
|
||||||
|
const folderlist = parseRes.map(x => ({
|
||||||
|
objectId: x.objectId,
|
||||||
|
folderName: x.Name,
|
||||||
|
parentFolderId: x?.Folder?.objectId || '',
|
||||||
|
parentFolderName: x?.Folder?.Name || '',
|
||||||
|
createdAt: x.createdAt,
|
||||||
|
updatedAt: x.updatedAt,
|
||||||
|
}));
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_get_Folder_list',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({ result: folderlist });
|
||||||
|
} else {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_get_Folder_list',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({ result: [] });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { replaceMailVaribles } from '../../../../Utils.js';
|
||||||
|
|
||||||
|
export default async function resendMail(request, response) {
|
||||||
|
try {
|
||||||
|
const reqToken = request.headers['x-api-token'];
|
||||||
|
const docId = request.body.document_id;
|
||||||
|
const userMail = request.body.email;
|
||||||
|
const email_subject = request.body.email_subject;
|
||||||
|
const email_body = request.body.email_body;
|
||||||
|
const baseUrl = new URL(process.env.SERVER_URL);
|
||||||
|
|
||||||
|
if (!reqToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', reqToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const docQuery = new Parse.Query('contracts_Document');
|
||||||
|
docQuery.equalTo('objectId', docId);
|
||||||
|
docQuery.equalTo('CreatedBy', userPtr);
|
||||||
|
docQuery.include('Signers,ExtUserPtr');
|
||||||
|
docQuery.notEqualTo('IsCompleted', true);
|
||||||
|
docQuery.notEqualTo('IsDeclined', true);
|
||||||
|
docQuery.notEqualTo('IsArchive', true);
|
||||||
|
docQuery.lessThanOrEqualTo('ExpiryDate', new Date());
|
||||||
|
docQuery.exists('SignedUrl');
|
||||||
|
|
||||||
|
const resDoc = await docQuery.first({ useMasterKey: true });
|
||||||
|
if (resDoc) {
|
||||||
|
const _resDoc = resDoc.toJSON();
|
||||||
|
const contact = _resDoc.Signers.find(x => x.Email === userMail);
|
||||||
|
if (contact) {
|
||||||
|
try {
|
||||||
|
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
|
||||||
|
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Parse-Application-Id': process.env.APP_ID,
|
||||||
|
'X-Parse-Master-Key': process.env.MASTER_KEY,
|
||||||
|
};
|
||||||
|
|
||||||
|
const objectId = contact.objectId;
|
||||||
|
const hostUrl = baseUrl.origin;
|
||||||
|
const title = _resDoc.Name;
|
||||||
|
const receiverMail = contact.Email;
|
||||||
|
//encode this url value `${response.id}/${receiverMail}/${objectId}` to base64 using `btoa` function
|
||||||
|
const encodeBase64 = btoa(`${_resDoc.objectId}/${receiverMail}/${objectId}`);
|
||||||
|
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||||
|
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||||
|
const orgName = _resDoc.ExtUserPtr.Company ? _resDoc.ExtUserPtr.Company : '';
|
||||||
|
const newDate = new Date(_resDoc.ExpiryDate.iso);
|
||||||
|
newDate.setDate(newDate.getDate() + 15);
|
||||||
|
const sender = _resDoc.ExtUserPtr.Email;
|
||||||
|
const localExpireDate = newDate.toLocaleDateString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeBGcolor = '#47a3ad';
|
||||||
|
const email_html =
|
||||||
|
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
|
||||||
|
imgPng +
|
||||||
|
" height='50' style='padding:20px; width:170px; height:40px;' /></div> <div style='padding:2px; font-family: system-ui;background-color:" +
|
||||||
|
themeBGcolor +
|
||||||
|
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
|
||||||
|
_resDoc.ExtUserPtr.Name +
|
||||||
|
' has requested you to review and sign <strong> ' +
|
||||||
|
title +
|
||||||
|
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||||
|
sender +
|
||||||
|
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
|
||||||
|
orgName +
|
||||||
|
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||||
|
localExpireDate +
|
||||||
|
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||||
|
signPdf +
|
||||||
|
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||||
|
sender +
|
||||||
|
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
|
||||||
|
openSignUrl +
|
||||||
|
' target=_blank>here</a>.</p> </div></div></body> </html>';
|
||||||
|
let replaceVar;
|
||||||
|
const variables = {
|
||||||
|
document_title: title,
|
||||||
|
sender_name: _resDoc.ExtUserPtr.Name,
|
||||||
|
sender_mail: _resDoc.ExtUserPtr.Email,
|
||||||
|
sender_phone: _resDoc.ExtUserPtr.Phone,
|
||||||
|
receiver_name: contact.Name,
|
||||||
|
receiver_email: contact.Email,
|
||||||
|
receiver_phone: contact.Phone,
|
||||||
|
expiry_date: localExpireDate,
|
||||||
|
company_name: orgName,
|
||||||
|
signing_url: signPdf,
|
||||||
|
};
|
||||||
|
if (email_subject && email_body) {
|
||||||
|
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
|
||||||
|
} else if (email_subject) {
|
||||||
|
replaceVar = replaceMailVaribles(email_subject, '', variables);
|
||||||
|
replaceVar = { subject: replaceVar.subject, body: email_html };
|
||||||
|
} else if (email_body) {
|
||||||
|
replaceVar = replaceMailVaribles(
|
||||||
|
`${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`,
|
||||||
|
email_body,
|
||||||
|
variables
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
replaceVar = {
|
||||||
|
subject: `${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`,
|
||||||
|
body: email_html,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const subject = replaceVar.subject;
|
||||||
|
const html = replaceVar.body;
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
recipient: contact.Email,
|
||||||
|
subject: subject,
|
||||||
|
from: sender,
|
||||||
|
html: html,
|
||||||
|
extUserId: _resDoc.ExtUserPtr.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await axios.post(url, params, { headers: headers });
|
||||||
|
if (res.data.result && res.data.result.status === 'success') {
|
||||||
|
return response.json({ result: 'mail sent successfully.' });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error);
|
||||||
|
return response.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(404).json({ error: 'user not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(404).json({ error: 'document not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token.' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err in resendmail', err);
|
||||||
|
return response.status(400).json({ error: err.message || 'Something went wrong.' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
export default async function updateFolder(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const folderId = request.params.folder_id;
|
||||||
|
const name = request.body.foldername;
|
||||||
|
const parentFolderId = request.body.parentFolderId;
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const folderCls = new Parse.Query('contracts_Document');
|
||||||
|
folderCls.equalTo('CreatedBy', userPtr);
|
||||||
|
folderCls.equalTo('objectId', folderId);
|
||||||
|
folderCls.equalTo('Type', 'Folder');
|
||||||
|
folderCls.notEqualTo('IsArchive', true);
|
||||||
|
|
||||||
|
const res = await folderCls.first({ useMasterKey: true });
|
||||||
|
if (res) {
|
||||||
|
const updateFolder = new Parse.Object('contracts_Document');
|
||||||
|
updateFolder.id = res.id;
|
||||||
|
updateFolder.set('Name', name);
|
||||||
|
if (parentFolderId) {
|
||||||
|
updateFolder.set('Folder', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Document',
|
||||||
|
objectId: parentFolderId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const updateRes = await updateFolder.save(null, { useMasterKey: true });
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_update_folder',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({ objectId: updateRes.id, updatedAt: updateRes.updatedAt });
|
||||||
|
} else {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_update_folder',
|
||||||
|
properties: { response_code: 404 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.status(404).json({ error: 'folder not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user