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