mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-05 17:27:39 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93b0d00ab0 | ||
|
|
d731951159 | ||
|
|
24bf08c14f | ||
|
|
c03819f5a9 | ||
|
|
4521d16891 | ||
|
|
f77525cd8b | ||
|
|
c934f8ccd6 | ||
|
|
4e128e5a5a | ||
|
|
b202d78059 | ||
|
|
e15136ce51 | ||
|
|
39c9211167 | ||
|
|
64864e3f54 | ||
|
|
af0bef6d29 | ||
|
|
130704b32f | ||
|
|
5b139b66a9 | ||
|
|
f9c5041af1 | ||
|
|
245bf8046b | ||
|
|
94eb5f9efe | ||
|
|
044128f36a | ||
|
|
594383fe14 | ||
|
|
90a585367e | ||
|
|
0b8382df00 | ||
|
|
a54c7e7fd7 | ||
|
|
45a2c88e90 | ||
|
|
e4526f026b | ||
|
|
11a2f6b8ec | ||
|
|
3bf35166fc | ||
|
|
cd50c8cf20 | ||
|
|
055ef3f209 | ||
|
|
964ec0d422 | ||
|
|
ec0e291604 | ||
|
|
836e952ba0 | ||
|
|
c5f39e2a24 | ||
|
|
56dd060531 | ||
|
|
960fbdb0eb | ||
|
|
f8145feebc | ||
|
|
df38d15611 | ||
|
|
bb69518674 | ||
|
|
fee53fb020 | ||
|
|
d086e60ae5 | ||
|
|
51150d4e47 | ||
|
|
1d3cd02f9d | ||
|
|
d03515efc4 | ||
|
|
30e6e4b3fd | ||
|
|
cbdd729e95 | ||
|
|
ac82209d52 | ||
|
|
d5cc6f6758 | ||
|
|
3aa211dc1a | ||
|
|
33d5eecbbf | ||
|
|
1d6c108ba4 | ||
|
|
79da0f285d | ||
|
|
e8eef32958 | ||
|
|
dc1fab5250 | ||
|
|
3e185ef796 | ||
|
|
8cf5adb273 | ||
|
|
803faed97b | ||
|
|
660eaeb714 | ||
|
|
79126700c4 | ||
|
|
6355c02485 | ||
|
|
bce028877b | ||
|
|
e47211a8c0 | ||
|
|
4a4c9eaa2d | ||
|
|
0a5653f53d | ||
|
|
5a90a0f484 | ||
|
|
a3a398bd92 | ||
|
|
7d70162137 | ||
|
|
e4c62c4eeb | ||
|
|
3eadca01fa | ||
|
|
659b7fe0f4 | ||
|
|
ade6694427 | ||
|
|
f8160665a4 | ||
|
|
5778bdecae | ||
|
|
9122c71c90 | ||
|
|
2c2065aca8 | ||
|
|
b1dd4df3c0 | ||
|
|
64fd3580ac | ||
|
|
01273777b7 | ||
|
|
23eaa1ae81 | ||
|
|
3fd0613407 | ||
|
|
d45566257f | ||
|
|
e9eb59aa7a | ||
|
|
9e3ba2cf5c | ||
|
|
81afc92c14 | ||
|
|
d3bf9e9e9b | ||
|
|
18e25f6cd9 | ||
|
|
13e2d92ce6 | ||
|
|
4b9f22a5bc | ||
|
|
77c61c89a2 |
@@ -205,6 +205,33 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "ugoconsonni",
|
||||||
|
"name": "ugoconsonni",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/13661702?v=4",
|
||||||
|
"profile": "https://github.com/ugoconsonni",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "daniel-mutwiri",
|
||||||
|
"name": "Daniel Mutwiri",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/8936960?v=4",
|
||||||
|
"profile": "https://github.com/daniel-mutwiri",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Zathiel",
|
||||||
|
"name": "Zathiel",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/26553418?v=4",
|
||||||
|
"profile": "https://github.com/Zathiel",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ We would like to thank all our contributors and users for their support and feed
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ugoconsonni"><img src="https://avatars.githubusercontent.com/u/13661702?v=4?s=100" width="100px;" alt="ugoconsonni"/><br /><sub><b>ugoconsonni</b></sub></a><br /><a href="#code-ugoconsonni" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
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",
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ const AddSigner = (props) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsLoader(true);
|
setIsLoader(true);
|
||||||
|
if (localStorage.getItem("TenantId")) {
|
||||||
try {
|
try {
|
||||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||||
contactQuery.set("Name", name);
|
contactQuery.set("Name", name);
|
||||||
@@ -56,13 +57,11 @@ const AddSigner = (props) => {
|
|||||||
contactQuery.set("Email", email);
|
contactQuery.set("Email", email);
|
||||||
contactQuery.set("UserRole", "contracts_Guest");
|
contactQuery.set("UserRole", "contracts_Guest");
|
||||||
|
|
||||||
if (localStorage.getItem("TenantId")) {
|
|
||||||
contactQuery.set("TenantId", {
|
contactQuery.set("TenantId", {
|
||||||
__type: "Pointer",
|
__type: "Pointer",
|
||||||
className: "partners_Tenant",
|
className: "partners_Tenant",
|
||||||
objectId: localStorage.getItem("TenantId")
|
objectId: localStorage.getItem("TenantId")
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const _users = Parse.Object.extend("User");
|
const _users = Parse.Object.extend("User");
|
||||||
@@ -188,6 +187,9 @@ const AddSigner = (props) => {
|
|||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
alert("something went wrong!");
|
alert("something went wrong!");
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
alert('something went wrong, please try again later')
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define a function to handle the "add yourself" checkbox
|
// Define a function to handle the "add yourself" checkbox
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ const RenderDebugPdf = (props) => {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
border: "1px solid grey",
|
border: "1px solid grey",
|
||||||
overflow: "auto"
|
overflow: "auto",
|
||||||
|
cursor: "crosshair"
|
||||||
}}
|
}}
|
||||||
onMouseMove={props.handleMouseMoveDiv}
|
onMouseMove={props.handleMouseMoveDiv}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { themeColor } from "../../constant/const";
|
import { themeColor } from "../../constant/const";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import Parse from "parse";
|
import { appInfo } from "../../constant/appinfo";
|
||||||
|
|
||||||
function Header({
|
function Header({
|
||||||
isPdfRequestFiles,
|
isPdfRequestFiles,
|
||||||
@@ -64,7 +64,19 @@ function Header({
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||||
|
const axiosRes = await axios.post(
|
||||||
|
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||||
|
{ url: pdfUrl },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "Application/json",
|
||||||
|
"X-Parse-Application-Id": appInfo.appId,
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const url = axiosRes.data.result;
|
||||||
const pdf = await getBase64FromUrl(url);
|
const pdf = await getBase64FromUrl(url);
|
||||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||||
const isAppleDevice =
|
const isAppleDevice =
|
||||||
@@ -94,7 +106,19 @@ function Header({
|
|||||||
const handleDownloadPdf = async () => {
|
const handleDownloadPdf = async () => {
|
||||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||||
try {
|
try {
|
||||||
const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||||
|
const axiosRes = await axios.post(
|
||||||
|
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||||
|
{ url: pdfUrl },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "Application/json",
|
||||||
|
"X-Parse-Application-Id": appInfo.appId,
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const url = axiosRes.data.result;
|
||||||
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err in getsignedurl", err);
|
console.log("err in getsignedurl", err);
|
||||||
@@ -577,8 +601,13 @@ function Header({
|
|||||||
handleClose={() => setIsCertificate(false)}
|
handleClose={() => setIsCertificate(false)}
|
||||||
>
|
>
|
||||||
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
|
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
|
||||||
<p>Completion certificate is generating,</p>
|
<p>
|
||||||
<p>please wait for some time if not download try again later</p>
|
Your completion certificate is being generated. Please wait
|
||||||
|
momentarily.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If the download doesn't start shortly, click the button again.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -566,13 +566,13 @@ export const signPdfFun = async (
|
|||||||
// for adding it in completion certificate
|
// for adding it in completion certificate
|
||||||
let getSignature;
|
let getSignature;
|
||||||
for (let item of xyPosition) {
|
for (let item of xyPosition) {
|
||||||
const typeExist = item.pos.some((data) => data?.type);
|
if (!getSignature) {
|
||||||
if (typeExist) {
|
const typeExist = item.pos.some((data) => data?.type);
|
||||||
getSignature = item.pos.find((data) => data?.type === "signature");
|
if (typeExist) {
|
||||||
break;
|
getSignature = item.pos.find((data) => data?.type === "signature");
|
||||||
} else {
|
} else {
|
||||||
getSignature = item.pos.find((data) => !data.isStamp);
|
getSignature = item.pos.find((data) => !data.isStamp);
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,29 +28,34 @@ const HomeLayout = () => {
|
|||||||
const [tourConfigs, setTourConfigs] = useState([]);
|
const [tourConfigs, setTourConfigs] = useState([]);
|
||||||
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
|
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
|
||||||
|
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
if (!tenantId) {
|
||||||
try {
|
setIsUserValid(false);
|
||||||
// Use the session token to validate the user
|
} else {
|
||||||
const userQuery = new Parse.Query(Parse.User);
|
(async () => {
|
||||||
const user = await userQuery.get(Parse.User.current().id, {
|
try {
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
// Use the session token to validate the user
|
||||||
});
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
if (user) {
|
const user = await userQuery.get(Parse.User.current().id, {
|
||||||
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
checkIsSubscribed();
|
});
|
||||||
} else {
|
if (user) {
|
||||||
|
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
||||||
|
checkIsSubscribed();
|
||||||
|
} else {
|
||||||
|
setIsUserValid(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Session token is invalid or there was an error
|
||||||
setIsUserValid(false);
|
setIsUserValid(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})();
|
||||||
// Session token is invalid or there was an error
|
saveCookies();
|
||||||
setIsUserValid(false);
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
saveCookies();
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, [tenantId]);
|
||||||
//function to use save data in cookies storage
|
//function to use save data in cookies storage
|
||||||
const saveCookies = () => {
|
const saveCookies = () => {
|
||||||
const main_Domain = window.location.origin;
|
const main_Domain = window.location.origin;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const Dashboard = () => {
|
|||||||
getDashboard(localStorage.getItem("PageLanding"));
|
getDashboard(localStorage.getItem("PageLanding"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
navigate("/", { replace: true, state: { from: location } });
|
navigate("/", { replace: true, state: { from: "" } });
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ function PdfRequestFiles() {
|
|||||||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
|
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
|
||||||
const [isSubscriptionExpired, setIsSubscriptionExpired] = useState(false);
|
const [isSubscriptionExpired, setIsSubscriptionExpired] = useState(false);
|
||||||
const [extUserId, setExtUserId] = useState("");
|
const [extUserId, setExtUserId] = useState("");
|
||||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
|
||||||
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||||||
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||||
const [otp, setOtp] = useState("");
|
const [otp, setOtp] = useState("");
|
||||||
@@ -225,16 +224,6 @@ function PdfRequestFiles() {
|
|||||||
//getting document details
|
//getting document details
|
||||||
const documentData = await contractDocument(documentId);
|
const documentData = await contractDocument(documentId);
|
||||||
if (documentData && documentData.length > 0) {
|
if (documentData && documentData.length > 0) {
|
||||||
const url =
|
|
||||||
documentData[0] && (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("Error: Something went wrong!");
|
|
||||||
} else {
|
|
||||||
setPdfArrayBuffer(arrayBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
||||||
const isCompleted =
|
const isCompleted =
|
||||||
documentData[0].IsCompleted && documentData[0].IsCompleted;
|
documentData[0].IsCompleted && documentData[0].IsCompleted;
|
||||||
@@ -483,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
|
||||||
@@ -523,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" &&
|
||||||
@@ -612,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -629,22 +604,51 @@ 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);
|
||||||
|
|
||||||
const pngUrl = checkUser[0].placeHolder;
|
const pngUrl = 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("Error: invalid document!");
|
||||||
|
} else {
|
||||||
|
pdfArrBuffer = arrayBuffer;
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
documentData === "Error: Something went wrong!" ||
|
||||||
|
(documentData.result && documentData.result.error)
|
||||||
|
) {
|
||||||
|
setHandleError("Error: Something went wrong!");
|
||||||
|
} else {
|
||||||
|
setHandleError("Document not Found!");
|
||||||
|
}
|
||||||
|
|
||||||
// Load a PDFDocument from the existing PDF bytes
|
// Load a PDFDocument from the existing PDF bytes
|
||||||
const existingPdfBytes = pdfArrayBuffer;
|
const existingPdfBytes = pdfArrBuffer;
|
||||||
try {
|
try {
|
||||||
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||||
const isSignYourSelfFlow = false;
|
const isSignYourSelfFlow = false;
|
||||||
@@ -870,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
|
||||||
@@ -1337,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
|
||||||
@@ -830,11 +833,11 @@ function PlaceHolderSign() {
|
|||||||
return pdfDetails[0].URL;
|
return pdfDetails[0].URL;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const alertSendEmail = async () => {
|
const alertSendEmail = async () => {
|
||||||
const filterPrefill = signerPos?.filter((data) => data.Role !== "prefill");
|
const filterPrefill = signerPos?.filter((data) => data.Role !== "prefill");
|
||||||
const getPrefill = signerPos?.filter((data) => data.Role === "prefill");
|
const getPrefill = signerPos?.filter((data) => data.Role === "prefill");
|
||||||
let isLabel = false;
|
let isLabel = false;
|
||||||
|
//condition is used to check text widget data is empty or have response
|
||||||
if (getPrefill && getPrefill.length > 0) {
|
if (getPrefill && getPrefill.length > 0) {
|
||||||
const prefillPlaceholder = getPrefill[0].placeHolder;
|
const prefillPlaceholder = getPrefill[0].placeHolder;
|
||||||
if (prefillPlaceholder) {
|
if (prefillPlaceholder) {
|
||||||
@@ -845,6 +848,29 @@ function PlaceHolderSign() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let isSignatureExist = true; // variable is used to check a signature widget exit or not then execute other code
|
||||||
|
//for loop is used to check signature widget exist or not
|
||||||
|
for (let item of filterPrefill) {
|
||||||
|
let signatureExist = false; // Reset for each iteration
|
||||||
|
for (let x of item.placeHolder) {
|
||||||
|
if (!signatureExist) {
|
||||||
|
const typeExist = x.pos.some((data) => data?.type);
|
||||||
|
if (typeExist) {
|
||||||
|
signatureExist = x.pos.some((data) => data?.type === "signature");
|
||||||
|
} else {
|
||||||
|
signatureExist = x.pos.some((data) => !data.isStamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!signatureExist) {
|
||||||
|
isSignatureExist = false;
|
||||||
|
const alert = {
|
||||||
|
mssg: "sure",
|
||||||
|
alert: true
|
||||||
|
};
|
||||||
|
setIsSendAlert(alert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (getPrefill && isLabel) {
|
if (getPrefill && isLabel) {
|
||||||
const alert = {
|
const alert = {
|
||||||
@@ -852,23 +878,26 @@ function PlaceHolderSign() {
|
|||||||
alert: true
|
alert: true
|
||||||
};
|
};
|
||||||
setIsSendAlert(alert);
|
setIsSendAlert(alert);
|
||||||
} else if (filterPrefill.length === signersdata.length) {
|
} else if (isSignatureExist) {
|
||||||
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
|
if (filterPrefill.length === signersdata.length) {
|
||||||
if (IsSignerNotExist && IsSignerNotExist?.length > 0) {
|
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
|
||||||
setSignerExistModal(true);
|
if (IsSignerNotExist && IsSignerNotExist?.length > 0) {
|
||||||
setSelectWidgetId(IsSignerNotExist[0]?.placeHolder?.[0]?.pos?.[0]?.key);
|
setSignerExistModal(true);
|
||||||
|
setSelectWidgetId(
|
||||||
|
IsSignerNotExist[0]?.placeHolder?.[0]?.pos?.[0]?.key
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
saveDocumentDetails();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
saveDocumentDetails();
|
const alert = {
|
||||||
|
mssg: "sure",
|
||||||
|
alert: true
|
||||||
|
};
|
||||||
|
setIsSendAlert(alert);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const alert = {
|
|
||||||
mssg: "sure",
|
|
||||||
alert: true
|
|
||||||
};
|
|
||||||
setIsSendAlert(alert);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//function to use save placeholder details in contracts_document
|
//function to use save placeholder details in contracts_document
|
||||||
const saveDocumentDetails = async () => {
|
const saveDocumentDetails = async () => {
|
||||||
setIsUiLoading(true);
|
setIsUiLoading(true);
|
||||||
@@ -1687,8 +1716,8 @@ function PlaceHolderSign() {
|
|||||||
>
|
>
|
||||||
{isSendAlert.mssg === "sure" ? (
|
{isSendAlert.mssg === "sure" ? (
|
||||||
<span>
|
<span>
|
||||||
Please add at least one signature field for all
|
Please ensure there's at least one signature widget
|
||||||
recipients.
|
added for all recipients.
|
||||||
</span>
|
</span>
|
||||||
) : isSendAlert.mssg === textWidget ? (
|
) : isSendAlert.mssg === textWidget ? (
|
||||||
<p>Please confirm that you have filled the text field.</p>
|
<p>Please confirm that you have filled the text field.</p>
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -480,13 +457,13 @@ function SignYourSelf() {
|
|||||||
Width: widgetTypeExist
|
Width: widgetTypeExist
|
||||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
|
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
|
||||||
: dragTypeValue === "initials"
|
: dragTypeValue === "initials"
|
||||||
? defaultWidthHeight(dragTypeValue).width
|
? defaultWidthHeight(dragTypeValue).width
|
||||||
: "",
|
: "",
|
||||||
Height: widgetTypeExist
|
Height: widgetTypeExist
|
||||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
|
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
|
||||||
: dragTypeValue === "initials"
|
: dragTypeValue === "initials"
|
||||||
? defaultWidthHeight(dragTypeValue).height
|
? defaultWidthHeight(dragTypeValue).height
|
||||||
: "",
|
: "",
|
||||||
options: addWidgetOptions(dragTypeValue)
|
options: addWidgetOptions(dragTypeValue)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -603,94 +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
|
||||||
try {
|
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||||
for (let i = 0; i < xyPostion?.length; i++) {
|
let isEmailVerified;
|
||||||
const requiredWidgets = xyPostion[i].pos.filter(
|
isEmailVerified = currentUser?.emailVerified;
|
||||||
(position) => position.type !== "checkbox"
|
if (isEmailVerified) {
|
||||||
);
|
setIsEmailVerified(isEmailVerified);
|
||||||
if (requiredWidgets && requiredWidgets?.length > 0) {
|
} else {
|
||||||
let checkSigned;
|
try {
|
||||||
for (let i = 0; i < requiredWidgets?.length; i++) {
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
checkSigned = requiredWidgets[i]?.options?.response;
|
const user = await userQuery.get(currentUser.objectId, {
|
||||||
if (!checkSigned) {
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
});
|
||||||
let checkDefaultSigned =
|
if (user) {
|
||||||
requiredWidgets[i]?.options?.defaultValue;
|
isEmailVerified = user?.get("emailVerified");
|
||||||
if (!checkSignUrl) {
|
setIsEmailVerified(isEmailVerified);
|
||||||
if (!checkDefaultSigned) {
|
}
|
||||||
if (!showAlert) {
|
} catch (e) {
|
||||||
showAlert = true;
|
setHandleError("Error: Something went wrong!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isEmailVerified) {
|
||||||
|
let showAlert = false,
|
||||||
|
isSignatureExist = false;
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < xyPostion?.length; i++) {
|
||||||
|
const requiredWidgets = xyPostion[i].pos.filter(
|
||||||
|
(position) => position.type !== "checkbox"
|
||||||
|
);
|
||||||
|
if (requiredWidgets && requiredWidgets?.length > 0) {
|
||||||
|
let checkSigned;
|
||||||
|
for (let i = 0; i < requiredWidgets?.length; i++) {
|
||||||
|
checkSigned = requiredWidgets[i]?.options?.response;
|
||||||
|
if (!checkSigned) {
|
||||||
|
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
||||||
|
let checkDefaultSigned =
|
||||||
|
requiredWidgets[i]?.options?.defaultValue;
|
||||||
|
if (!checkSignUrl) {
|
||||||
|
if (!checkDefaultSigned) {
|
||||||
|
if (!showAlert) {
|
||||||
|
showAlert = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
//condition to check exist signature widget or not
|
||||||
}
|
if (!isSignatureExist) {
|
||||||
if (xyPostion.length === 0) {
|
isSignatureExist = xyPostion[i].pos.some(
|
||||||
setIsAlert({
|
(data) => data?.type === "signature"
|
||||||
isShow: true,
|
);
|
||||||
alertMessage: "Please complete your signature!"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else if (showAlert) {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: "Please complete your signature!"
|
|
||||||
});
|
|
||||||
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
|
||||||
@@ -713,13 +722,13 @@ function SignYourSelf() {
|
|||||||
// for adding it in completion certificate
|
// for adding it in completion certificate
|
||||||
let getSignature;
|
let getSignature;
|
||||||
for (let item of xyPostion) {
|
for (let item of xyPostion) {
|
||||||
const typeExist = item.pos.some((data) => data?.type);
|
if (!getSignature) {
|
||||||
if (typeExist) {
|
const typeExist = item.pos.some((data) => data?.type);
|
||||||
getSignature = item.pos.find((data) => data?.type === "signature");
|
if (typeExist) {
|
||||||
break;
|
getSignature = item.pos.find((data) => data?.type === "signature");
|
||||||
} else {
|
} else {
|
||||||
getSignature = item.pos.find((data) => !data.isStamp);
|
getSignature = item.pos.find((data) => !data.isStamp);
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let base64Sign = getSignature.SignUrl;
|
let base64Sign = getSignature.SignUrl;
|
||||||
@@ -1161,7 +1170,7 @@ function SignYourSelf() {
|
|||||||
<ModalUi
|
<ModalUi
|
||||||
headerColor={"#dc3545"}
|
headerColor={"#dc3545"}
|
||||||
isOpen={isAlert.isShow}
|
isOpen={isAlert.isShow}
|
||||||
title={"Alert"}
|
title={isAlert?.header || "Alert"}
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
setIsAlert({
|
setIsAlert({
|
||||||
isShow: false,
|
isShow: false,
|
||||||
@@ -1171,28 +1180,6 @@ function SignYourSelf() {
|
|||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div style={{ height: "100%", padding: 20 }}>
|
||||||
<p>{isAlert.alertMessage}</p>
|
<p>{isAlert.alertMessage}</p>
|
||||||
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: false,
|
|
||||||
alertMessage: ""
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
className="finishBtn cancelBtn"
|
|
||||||
>
|
|
||||||
Ok
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -50,11 +50,12 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
const folderId = request.body.folderId;
|
const folderId = request.body.folderId;
|
||||||
const templateId = request.params.template_id;
|
const templateId = request.params.template_id;
|
||||||
const protocol = customAPIurl();
|
const protocol = customAPIurl();
|
||||||
const baseUrl = new URL(process.env.SERVER_URL);
|
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||||
const send_email = request.body.send_email;
|
const send_email = request.body.send_email;
|
||||||
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'];
|
||||||
@@ -88,12 +89,20 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
const updateSigners = placeholder.every(y => signers?.some(x => x.role === y.Role));
|
const updateSigners = placeholder.every(y => signers?.some(x => x.role === y.Role));
|
||||||
// console.log('isValid ', isValid);
|
// console.log('isValid ', isValid);
|
||||||
if (isValid && updateSigners) {
|
if (isValid && updateSigners) {
|
||||||
|
//Check if every item's placeholders contain at least one placeholder with type 'signature'.
|
||||||
|
let isSignature = template?.Placeholders?.every(item =>
|
||||||
|
item?.placeHolder.some(x => x?.pos.some(data => data?.type === 'signature'))
|
||||||
|
);
|
||||||
|
if (!isSignature) {
|
||||||
|
return response
|
||||||
|
.status(400)
|
||||||
|
.json({ error: 'Please add at least one signature widget for all signers' });
|
||||||
|
}
|
||||||
const folderPtr = {
|
const folderPtr = {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
className: 'contracts_Document',
|
className: 'contracts_Document',
|
||||||
objectId: folderId,
|
objectId: folderId,
|
||||||
};
|
};
|
||||||
const template = JSON.parse(JSON.stringify(templateRes));
|
|
||||||
const object = new Parse.Object('contracts_Document');
|
const object = new Parse.Object('contracts_Document');
|
||||||
object.set('Name', template.Name);
|
object.set('Name', template.Name);
|
||||||
if (template?.Note) {
|
if (template?.Note) {
|
||||||
@@ -169,6 +178,10 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
object.set('Signers', templateSigner);
|
object.set('Signers', templateSigner);
|
||||||
}
|
}
|
||||||
object.set('URL', template.URL);
|
object.set('URL', 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,9 +66,10 @@ 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.PUBLIC_URL);
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
if (!reqToken) {
|
if (!reqToken) {
|
||||||
return response.status(400).json({ error: 'Please Provide API Token' });
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
@@ -88,6 +89,15 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
objectId: parseUser.userId.objectId,
|
objectId: parseUser.userId.objectId,
|
||||||
};
|
};
|
||||||
if (signers && signers.length > 0) {
|
if (signers && signers.length > 0) {
|
||||||
|
// Check if at least one signature exists among all items in the signers array
|
||||||
|
let isSignExist = signers.every(item =>
|
||||||
|
item.widgets.some(data => data?.type === 'signature')
|
||||||
|
);
|
||||||
|
if (!isSignExist) {
|
||||||
|
return response
|
||||||
|
.status(400)
|
||||||
|
.json({ error: 'Please add at least one signature widget for all signers' });
|
||||||
|
}
|
||||||
let fileUrl;
|
let fileUrl;
|
||||||
if (request.files?.[0]) {
|
if (request.files?.[0]) {
|
||||||
const base64 = fileData?.toString('base64');
|
const base64 = fileData?.toString('base64');
|
||||||
@@ -130,8 +140,12 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
object.set('SendinOrder', sendInOrder);
|
object.set('SendinOrder', sendInOrder);
|
||||||
}
|
}
|
||||||
object.set('URL', fileUrl);
|
object.set('URL', 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) {
|
||||||
@@ -279,7 +293,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
const objectId = contactMail[i].contactPtr.objectId;
|
const objectId = contactMail[i].contactPtr.objectId;
|
||||||
const hostUrl = baseUrl.origin;
|
const hostUrl = baseUrl.origin;
|
||||||
//encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
|
//encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
|
||||||
const encodeBase64 = btoa(`${response.id}/${contactMail[i].email}/${objectId}`);
|
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
|
||||||
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||||
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||||
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
|
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
|
||||||
@@ -332,7 +346,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
replaceVar = {
|
replaceVar = {
|
||||||
subject: `${parseExtUser.Name} has requested you to sign "${parseExtUser.Name}"`,
|
subject: `${parseExtUser.Name} has requested you to sign "${name}"`,
|
||||||
body: email_html,
|
body: email_html,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ export default async function createTemplate(request, response) {
|
|||||||
const base64File = request.body.file;
|
const base64File = request.body.file;
|
||||||
const SendinOrder = request.body.sendInOrder || false;
|
const SendinOrder = request.body.sendInOrder || false;
|
||||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||||
const baseUrl = new URL(process.env.SERVER_URL);
|
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default async function draftDocument(request, response) {
|
|||||||
const SendinOrder = request.body.sendInOrder || false;
|
const SendinOrder = request.body.sendInOrder || false;
|
||||||
// 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.PUBLIC_URL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
|
|||||||
@@ -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,157 @@
|
|||||||
|
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.greaterThanOrEqualTo('ExpiryDate', new Date());
|
||||||
|
docQuery.exists('SignedUrl');
|
||||||
|
|
||||||
|
const resDoc = await docQuery.first({ useMasterKey: true });
|
||||||
|
// console.log("resDoc ",resDoc)
|
||||||
|
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!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,8 +48,11 @@ export default async function callWebhook(request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const docQuery = new Parse.Query('contracts_Document');
|
||||||
|
const resDoc = await docQuery.get(docId, { useMasterKey: true });
|
||||||
const extendcls = new Parse.Query('contracts_Users');
|
const extendcls = new Parse.Query('contracts_Users');
|
||||||
extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
extendcls.equalTo('objectId', resDoc.get('ExtUserPtr')?.id);
|
||||||
|
// extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||||
const res = await extendcls.first({ useMasterKey: true });
|
const res = await extendcls.first({ useMasterKey: true });
|
||||||
if (res) {
|
if (res) {
|
||||||
const extUser = JSON.parse(JSON.stringify(res));
|
const extUser = JSON.parse(JSON.stringify(res));
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default async function GenerateCertificate(docDetails) {
|
|||||||
const company = docDetails?.ExtUserPtr?.Company || '';
|
const company = docDetails?.ExtUserPtr?.Company || '';
|
||||||
const createdAt = docDetails?.DocSentAt?.iso || docDetails.createdAt;
|
const createdAt = docDetails?.DocSentAt?.iso || docDetails.createdAt;
|
||||||
const auditTrail =
|
const auditTrail =
|
||||||
docDetails.AuditTrail?.length > 1
|
docDetails?.Signers?.length > 0
|
||||||
? docDetails.AuditTrail.map(x => {
|
? docDetails.AuditTrail.map(x => {
|
||||||
const data = docDetails.Signers.find(y => y.objectId === x.UserPtr.objectId);
|
const data = docDetails.Signers.find(y => y.objectId === x.UserPtr.objectId);
|
||||||
return {
|
return {
|
||||||
@@ -281,7 +281,7 @@ export default async function GenerateCertificate(docDetails) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
page.drawText('Viewed on :', {
|
page.drawText('Viewed on :', {
|
||||||
x: half +55,
|
x: half + 55,
|
||||||
y: yPosition2,
|
y: yPosition2,
|
||||||
size: timeText,
|
size: timeText,
|
||||||
font: timesRomanFont,
|
font: timesRomanFont,
|
||||||
|
|||||||
+7
-6
@@ -123,9 +123,9 @@ async function sendCompletedMail(e) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function sendDoctoWebhook(a, e, t, r) {
|
async function sendDoctoWebhook(a, e, t, r, i) {
|
||||||
let i = [];
|
let s = [];
|
||||||
(i = r
|
(s = r
|
||||||
? { name: r?.Name, email: r?.Email, phone: r?.Phone }
|
? { name: r?.Name, email: r?.Email, phone: r?.Phone }
|
||||||
: a?.Signers?.map(e => ({ name: e.Name, email: e.Email, phone: e.Phone })) || [
|
: a?.Signers?.map(e => ({ name: e.Name, email: e.Email, phone: e.Phone })) || [
|
||||||
{ name: a?.ExtUserPtr?.Name, email: a?.ExtUserPtr?.Email, phone: a?.ExtUserPtr?.Phone },
|
{ name: a?.ExtUserPtr?.Name, email: a?.ExtUserPtr?.Email, phone: a?.ExtUserPtr?.Phone },
|
||||||
@@ -133,12 +133,13 @@ async function sendDoctoWebhook(a, e, t, r) {
|
|||||||
a.ExtUserPtr?.Webhook &&
|
a.ExtUserPtr?.Webhook &&
|
||||||
((r =
|
((r =
|
||||||
'signed' === t
|
'signed' === t
|
||||||
? { signer: i, signedAt: new Date() }
|
? { signer: s, signedAt: new Date() }
|
||||||
: { signers: i, completedAt: new Date() }),
|
: { signers: s, completedAt: new Date() }),
|
||||||
(t = {
|
(t = {
|
||||||
event: t,
|
event: t,
|
||||||
objectId: a?.objectId,
|
objectId: a?.objectId,
|
||||||
file: e || '',
|
file: e || '',
|
||||||
|
...(i ? { certificate: i } : {}),
|
||||||
name: a?.Name,
|
name: a?.Name,
|
||||||
note: a?.Note || '',
|
note: a?.Note || '',
|
||||||
description: a?.Description || '',
|
description: a?.Description || '',
|
||||||
@@ -205,7 +206,7 @@ const sendMailsaveCertifcate = async (e, t, a, r, i, s) => {
|
|||||||
? console.log("don't send mail")
|
? console.log("don't send mail")
|
||||||
: sendCompletedMail({ url: a, isCustomMail: r, doc: e, mailProvider: i }),
|
: sendCompletedMail({ url: a, isCustomMail: r, doc: e, mailProvider: i }),
|
||||||
saveFileUsage(o.length, t.imageUrl, s),
|
saveFileUsage(o.length, t.imageUrl, s),
|
||||||
sendDoctoWebhook(e, a, 'completed');
|
sendDoctoWebhook(e, a, 'completed', '', t.imageUrl);
|
||||||
};
|
};
|
||||||
async function PDF(o) {
|
async function PDF(o) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user