Compare commits

...
16 Commits
Author SHA1 Message Date
AndrewandGitHub 096d5962a3 Merge pull request #1221 from OpenSignLabs/validation 2024-09-19 11:30:35 +05:30
prafull-opensignlabs 9ea36f94a7 fix: enableOTP not working properly in template 2024-09-19 11:28:13 +05:30
AndrewandGitHub 2b66df992f Merge pull request #1220 from OpenSignLabs/validation 2024-09-19 10:27:18 +05:30
prafull-opensignlabs ff3c31975e refactor: remove extra space 2024-09-19 10:26:35 +05:30
AndrewandGitHub beaa5eca69 Merge pull request #1219 from OpenSignLabs/validation 2024-09-18 21:17:16 +05:30
prafull-opensignlabs b98040faac fix: document decline event not trigger on revoke 2024-09-18 21:15:31 +05:30
AndrewandGitHub fbbff0df61 Merge pull request #1218 from OpenSignLabs/validation 2024-09-18 19:57:46 +05:30
prafull-opensignlabs 07d5f393c0 fix: document viewed event not trigger 2024-09-18 19:56:47 +05:30
AndrewandGitHub 8220b15a29 Merge pull request #1217 from OpenSignLabs/validation
refactor: add enableOTP in allowedkeys in update doc
2024-09-18 19:27:42 +05:30
prafull-opensignlabs 7cf638b2be refactor: add enableOTP in allowedkeys in update doc 2024-09-18 19:22:41 +05:30
prafull-opensignlabsandGitHub 30fb8f31e3 Merge pull request #1216 from OpenSignLabs/validation
fix: blank page appear when try to add typed signature
2024-09-18 18:10:37 +05:30
prafull-opensignlabs ec4d875547 fix: blank page appear when try to add typed signature 2024-09-18 18:09:18 +05:30
AndrewandGitHub 43eee63df2 Merge pull request #1215 from OpenSignLabs/validation 2024-09-18 14:36:30 +05:30
prafull-opensignlabs 4caf9f598a refactor: change variable from isDisableOTP to isEnableOTP 2024-09-18 14:32:45 +05:30
AndrewandGitHub 107eaca8f0 Merge pull request #1213 from OpenSignLabs/validation
feat: add disable OTP feature to directly sign document without verification
2024-09-17 18:31:15 +05:30
prafull-opensignlabs cc11848346 feat: add disable OTP feature to directly sign document without verification 2024-09-17 18:27:21 +05:30
40 changed files with 1251 additions and 739 deletions
@@ -638,6 +638,14 @@
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminEmail}}.", "unauthorized-modal":"You don't have permission to perform this action, please contact {{adminEmail}}.",
"sent-this-month":"Sent this month", "sent-this-month":"Sent this month",
"available-seats":"Available seats", "available-seats":"Available seats",
"buy-users":"Buy more users" "buy-users":"Buy more users",
"isenable-otp": "Enable OTP verification",
"isenable-otp-help": {
"p1": "Would you like to enable the verification process using a one-time password (OTP)?",
"p2": "Selecting this option will enable OTP verification. Users will receive a verification code via email, which they must enter to sign the document.",
"p3": "Selecting this option will disable OTP verification, allowing users to sign the document directly without additional steps.",
"p4": "Please choose the option that best suits your document signing requirements."
},
"advanced-options":"Advanced options",
"hide-advanced-options":"Hide Advanced options"
} }
@@ -637,5 +637,15 @@
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.", "unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
"sent-this-month":"envoyé ce mois-ci", "sent-this-month":"envoyé ce mois-ci",
"available-seats":"Disponible sièges", "available-seats":"Disponible sièges",
"buy-users":"Acheter plus d'utilisateurs" "buy-users":"Acheter plus d'utilisateurs",
"isenable-otp": "Activer la vérification OTP",
"isenable-otp-help": {
"p1": "Souhaitez-vous activer le processus de vérification à l'aide d'un mot de passe à usage unique (OTP)?",
"p2": "La sélection de cette option activera la vérification OTP. Les utilisateurs recevront un code de vérification par e-mail, qu'ils devront saisir pour signer le document.",
"p3": "La sélection de cette option désactivera la vérification OTP, permettant aux utilisateurs de signer le document directement sans étapes supplémentaires.",
"p4": "Veuillez choisir l'option qui correspond le mieux à vos exigences en matière de signature de documents."
},
"advanced-options":"Options avancées",
"hide-advanced-options": "Masquer les options avancées"
} }
@@ -3,6 +3,8 @@ import { checkIsSubscribed, getFileName } from "../../constant/Utils";
import Upgrade from "../../primitives/Upgrade"; import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const"; import { isEnableSubscription } from "../../constant/const";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Tooltip } from "react-tooltip";
// import SelectFolder from "../../premitives/SelectFolder"; // import SelectFolder from "../../premitives/SelectFolder";
const EditTemplate = ({ template, onSuccess }) => { const EditTemplate = ({ template, onSuccess }) => {
@@ -14,7 +16,8 @@ const EditTemplate = ({ template, onSuccess }) => {
Description: template?.Description || "", Description: template?.Description || "",
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false", SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
AutomaticReminders: template?.AutomaticReminders || false, AutomaticReminders: template?.AutomaticReminders || false,
RemindOnceInEvery: template?.RemindOnceInEvery || 5 RemindOnceInEvery: template?.RemindOnceInEvery || 5,
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false"
}); });
const [isSubscribe, setIsSubscribe] = useState(false); const [isSubscribe, setIsSubscribe] = useState(false);
useEffect(() => { useEffect(() => {
@@ -41,7 +44,7 @@ const EditTemplate = ({ template, onSuccess }) => {
e.stopPropagation(); e.stopPropagation();
const isChecked = formData.SendinOrder === "true" ? true : false; const isChecked = formData.SendinOrder === "true" ? true : false;
const AutoReminder = formData?.AutomaticReminders || false; const AutoReminder = formData?.AutomaticReminders || false;
const IsEnableOTP = formData.IsEnableOTP === "true" ? true : false;
let reminderDate = {}; let reminderDate = {};
if (AutoReminder) { if (AutoReminder) {
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery); const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
@@ -49,7 +52,12 @@ const EditTemplate = ({ template, onSuccess }) => {
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery); ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
reminderDate = { NextReminderDate: ReminderDate }; reminderDate = { NextReminderDate: ReminderDate };
} }
const data = { ...formData, SendinOrder: isChecked, ...reminderDate }; const data = {
...formData,
SendinOrder: isChecked,
IsEnableOTP: IsEnableOTP,
...reminderDate
};
onSuccess(data); onSuccess(data);
}; };
const handleAutoReminder = () => { const handleAutoReminder = () => {
@@ -184,6 +192,70 @@ const EditTemplate = ({ template, onSuccess }) => {
/> />
</div> </div>
)} )}
{isEnableSubscription && (
<div className="text-xs mt-2">
<label className="block">
<span className={isSubscribe ? "" : " text-gray-300"}>
{t("isenable-otp")}{" "}
<a data-tooltip-id="isenableotp-tooltip" className="ml-1">
<sup>
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
</sup>
</a>{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<Tooltip id="isenableotp-tooltip" className="z-50">
<div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">{t("isenable-otp")}</p>
<p>{t("isenable-otp-help.p1")}</p>
<p className="p-[5px]">
<ol className="list-disc">
<li>
<span className="font-bold">{t("yes")}: </span>
<span>{t("isenable-otp-help.p2")}</span>
</li>
<li>
<span className="font-bold">{t("no")}: </span>
<span>{t("isenable-otp-help.p3")}</span>
</li>
</ol>
</p>
<p>{t("isenable-otp-help.p4")}</p>
</div>
</Tooltip>
</label>
<div
className={`${
isSubscribe ? "" : "pointer-events-none opacity-50"
} flex items-center gap-2 ml-2 mb-1 `}
>
<input
type="radio"
value={"true"}
className="op-radio op-radio-xs"
name="IsEnableOTP"
checked={formData.IsEnableOTP === "true"}
onChange={handleStrInput}
/>
<div className="text-center">{t("yes")}</div>
</div>
<div
className={`${
isSubscribe ? "" : "pointer-events-none opacity-50"
} flex items-center gap-2 ml-2 mb-1 `}
>
<input
type="radio"
value={"false"}
name="IsEnableOTP"
className="op-radio op-radio-xs"
checked={formData.IsEnableOTP === "false"}
onChange={handleStrInput}
/>
<div className="text-center">{t("no")}</div>
</div>
</div>
)}
<div className="mt-[1rem] flex justify-start"> <div className="mt-[1rem] flex justify-start">
<button type="submit" className="op-btn op-btn-primary"> <button type="submit" className="op-btn op-btn-primary">
{t("submit")} {t("submit")}
@@ -154,7 +154,9 @@ function EmailComponent({
<div className="flex flex-row"> <div className="flex flex-row">
{!isAndroid && ( {!isAndroid && (
<button <button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)} onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
}
className="op-btn op-btn-neutral op-btn-sm text-[15px]" className="op-btn op-btn-neutral op-btn-sm text-[15px]"
> >
<i className="fa-light fa-print" aria-hidden="true"></i> <i className="fa-light fa-print" aria-hidden="true"></i>
@@ -142,7 +142,7 @@ function Header({
<DropdownMenu.Item <DropdownMenu.Item
className="DropdownMenuItem" className="DropdownMenuItem"
onClick={(e) => onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading) handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
} }
> >
<div className="flex flex-row"> <div className="flex flex-row">
@@ -342,7 +342,9 @@ function Header({
alreadySign ? ( alreadySign ? (
<div className="flex flex-row"> <div className="flex flex-row">
<button <button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)} onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
}
type="button" type="button"
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow" className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
> >
@@ -459,7 +461,9 @@ function Header({
</button> </button>
)} )}
<button <button
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)} onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
}
type="button" type="button"
className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow" className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
> >
+10 -8
View File
@@ -31,8 +31,8 @@ function SignPad({
const [isTab, setIsTab] = useState("draw"); const [isTab, setIsTab] = useState("draw");
const [isSignImg, setIsSignImg] = useState(""); const [isSignImg, setIsSignImg] = useState("");
const [signValue, setSignValue] = useState(""); const [signValue, setSignValue] = useState("");
const [textWidth, setTextWidth] = useState(null); const [textWidth, setTextWidth] = useState(0);
const [textHeight, setTextHeight] = useState(null); const [textHeight, setTextHeight] = useState(0);
const [signatureType, setSignatureType] = useState("draw"); const [signatureType, setSignatureType] = useState("draw");
const fontOptions = [ const fontOptions = [
{ value: "Fasthand" }, { value: "Fasthand" },
@@ -51,7 +51,7 @@ function SignPad({
`Parse/${localStorage.getItem("parseAppId")}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
); );
const jsonSender = JSON.parse(senderUser); const jsonSender = JSON.parse(senderUser);
const currentUserName = jsonSender && jsonSender.name; const currentUserName = jsonSender && jsonSender?.name;
//function for clear signature image //function for clear signature image
const handleClear = () => { const handleClear = () => {
@@ -137,7 +137,7 @@ function SignPad({
(isTab === "draw" && isSignImg) || (isTab === "draw" && isSignImg) ||
(isTab === "image" && image) || (isTab === "image" && image) ||
(isTab === "mysignature" && isDefaultSign) || (isTab === "mysignature" && isDefaultSign) ||
(isTab === "type" && textWidth) (isTab === "type" && signValue)
? false ? false
: image : image
? false ? false
@@ -169,10 +169,10 @@ function SignPad({
} }
} }
const trimmedName = currentUserName && currentUserName.trim(); const trimmedName = currentUserName && currentUserName?.trim();
const firstCharacter = trimmedName?.charAt(0); const firstCharacter = trimmedName?.charAt(0);
const userName = isInitial ? firstCharacter : currentUserName; const userName = isInitial ? firstCharacter : currentUserName;
setSignValue(userName); setSignValue(userName || "");
setFontSelect("Fasthand"); setFontSelect("Fasthand");
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -200,8 +200,10 @@ function SignPad({
canvasRef.current.fromDataURL(isSignImg); canvasRef.current.fromDataURL(isSignImg);
} }
if (isTab === "type") { if (isTab === "type") {
const trimmedName = signValue ? signValue.trim() : currentUserName.trim(); const trimmedName = signValue
const firstCharacter = trimmedName.charAt(0); ? signValue?.trim()
: currentUserName?.trim();
const firstCharacter = trimmedName?.charAt(0);
const userName = isInitial ? firstCharacter : signValue; const userName = isInitial ? firstCharacter : signValue;
setSignValue(userName); setSignValue(userName);
convertToImg(fontSelect, userName); convertToImg(fontSelect, userName);
+13 -7
View File
@@ -708,7 +708,8 @@ export const createDocument = async (
Signers: signers, Signers: signers,
SendinOrder: Doc?.SendinOrder || false, SendinOrder: Doc?.SendinOrder || false,
AutomaticReminders: Doc?.AutomaticReminders || false, AutomaticReminders: Doc?.AutomaticReminders || false,
RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5) RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5),
IsEnableOTP: Doc?.IsEnableOTP || false
}; };
try { try {
@@ -1741,9 +1742,7 @@ export const contactBook = async (objectId) => {
//function for getting document details from contract_Documents class //function for getting document details from contract_Documents class
export const contractDocument = async (documentId) => { export const contractDocument = async (documentId) => {
const data = { const data = { docId: documentId };
docId: documentId
};
const documentDeatils = await axios const documentDeatils = await axios
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, { .post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
headers: { headers: {
@@ -2047,11 +2046,12 @@ export const handleDownloadPdf = async (
) => { ) => {
const pdfName = pdfDetails[0] && pdfDetails[0].Name; const pdfName = pdfDetails[0] && pdfDetails[0].Name;
setIsDownloading("pdf"); setIsDownloading("pdf");
const docId = !pdfDetails?.[0]?.IsEnableOTP ? pdfDetails?.[0]?.objectId : "";
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( const axiosRes = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`, `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ url: pdfUrl }, { url: pdfUrl, docId: docId },
{ {
headers: { headers: {
"content-type": "Application/json", "content-type": "Application/json",
@@ -2075,16 +2075,22 @@ export const sanitizeFileName = (pdfName) => {
return pdfName.replace(/ /g, "_"); return pdfName.replace(/ /g, "_");
}; };
//function for print digital sign pdf //function for print digital sign pdf
export const handleToPrint = async (event, pdfUrl, setIsDownloading) => { export const handleToPrint = async (
event,
pdfUrl,
setIsDownloading,
pdfDetails
) => {
event.preventDefault(); event.preventDefault();
setIsDownloading("pdf"); setIsDownloading("pdf");
const docId = !pdfDetails?.[0]?.IsEnableOTP ? pdfDetails?.[0]?.objectId : "";
try { try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl }); // const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
//`localStorage.getItem("baseUrl")` is also use in public-profile flow for public-sign //`localStorage.getItem("baseUrl")` is also use in public-profile flow for public-sign
//if we give this `appInfo.baseUrl` as a base url then in public-profile it will create base url of it's window.location.origin ex- opensign.me which is not base url //if we give this `appInfo.baseUrl` as a base url then in public-profile it will create base url of it's window.location.origin ex- opensign.me which is not base url
const axiosRes = await axios.post( const axiosRes = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`, `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ url: pdfUrl }, { url: pdfUrl, docId: docId },
{ {
headers: { headers: {
"content-type": "Application/json", "content-type": "Application/json",
+136 -35
View File
@@ -61,7 +61,8 @@ const Forms = (props) => {
password: "", password: "",
file: "", file: "",
remindOnceInEvery: 5, remindOnceInEvery: 5,
autoreminder: false autoreminder: false,
IsEnableOTP: "false"
}); });
const [fileupload, setFileUpload] = useState(""); const [fileupload, setFileUpload] = useState("");
const [fileload, setfileload] = useState(false); const [fileload, setfileload] = useState(false);
@@ -74,6 +75,7 @@ const Forms = (props) => {
const [isDecrypting, setIsDecrypting] = useState(false); const [isDecrypting, setIsDecrypting] = useState(false);
const [isCorrectPass, setIsCorrectPass] = useState(true); const [isCorrectPass, setIsCorrectPass] = useState(true);
const [isSubscribe, setIsSubscribe] = useState(false); const [isSubscribe, setIsSubscribe] = useState(false);
const [isAdvanceOpt, setIsAdvanceOpt] = useState(false);
const handleStrInput = (e) => { const handleStrInput = (e) => {
setIsCorrectPass(true); setIsCorrectPass(true);
setFormData({ ...formData, [e.target.name]: e.target.value }); setFormData({ ...formData, [e.target.name]: e.target.value });
@@ -137,7 +139,6 @@ const Forms = (props) => {
} catch (err) { } catch (err) {
console.log("err in sending posthog encryptedpdf", err); console.log("err in sending posthog encryptedpdf", err);
} }
// console.log("err ", err);
try { try {
setIsDecrypting(true); setIsDecrypting(true);
const size = files?.[0].size; const size = files?.[0].size;
@@ -402,6 +403,13 @@ const Forms = (props) => {
object.set("SendinOrder", isChecked); object.set("SendinOrder", isChecked);
object.set("AutomaticReminders", formData.autoreminder); object.set("AutomaticReminders", formData.autoreminder);
object.set("RemindOnceInEvery", parseInt(formData.remindOnceInEvery)); object.set("RemindOnceInEvery", parseInt(formData.remindOnceInEvery));
if (isEnableSubscription) {
const IsEnableOTP =
formData?.IsEnableOTP === "false" ? false : true;
object.set("IsEnableOTP", IsEnableOTP);
} else {
object.set("IsEnableOTP", false);
}
} }
object.set("URL", fileupload); object.set("URL", fileupload);
object.set("CreatedBy", Parse.User.createWithoutData(currentUser.id)); object.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
@@ -428,7 +436,17 @@ const Forms = (props) => {
setFormData({ setFormData({
Name: "", Name: "",
Description: "", Description: "",
Note: "" Note:
props.title === "Sign Yourself"
? "Note to myself"
: "Please review and sign this document",
TimeToCompleteDays: 15,
SendinOrder: "true",
password: "",
file: "",
remindOnceInEvery: 5,
autoreminder: false,
IsEnableOTP: "false"
}); });
setFileUpload(""); setFileUpload("");
setpercentage(0); setpercentage(0);
@@ -477,7 +495,8 @@ const Forms = (props) => {
password: "", password: "",
file: "", file: "",
remindOnceInEvery: 5, remindOnceInEvery: 5,
autoreminder: false autoreminder: false,
IsEnableOTP: "false"
}); });
setFileUpload(""); setFileUpload("");
setpercentage(0); setpercentage(0);
@@ -754,28 +773,7 @@ const Forms = (props) => {
isReset={isReset} isReset={isReset}
/> />
)} )}
{props.title === "Request Signatures" && (
<div className="text-xs mt-2">
<label className="block">
{t("time-to-complete")}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
type="number"
name="TimeToCompleteDays"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formData.TimeToCompleteDays}
onChange={(e) => handleStrInput(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
)}
{props.title !== "Sign Yourself" && ( {props.title !== "Sign Yourself" && (
<>
<div className="text-xs mt-2"> <div className="text-xs mt-2">
<label className="block"> <label className="block">
{t("send-in-order")} {t("send-in-order")}
@@ -827,17 +825,35 @@ const Forms = (props) => {
<div className="text-center">{t("no")}</div> <div className="text-center">{t("no")}</div>
</div> </div>
</div> </div>
)}
{isAdvanceOpt && (
<div className={` overflow-y-auto z-[500] transition-all`}>
{props.title === "Request Signatures" && (
<div className="text-xs mt-2">
<label className="block">
{t("time-to-complete")}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
type="number"
name="TimeToCompleteDays"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formData.TimeToCompleteDays}
onChange={(e) => handleStrInput(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
)}
{props.title !== "Sign Yourself" && (
<>
{isEnableSubscription && ( {isEnableSubscription && (
<div className="text-xs mt-2"> <div className="text-xs mt-2">
<span <span className={isSubscribe ? "" : " text-gray-300"}>
className={ {t("auto-reminder")}{" "}
isSubscribe
? "font-semibold"
: "font-semibold text-gray-300"
}
>
{t("auto-reminder")}
{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />} {!isSubscribe && isEnableSubscription && <Upgrade />}
</span> </span>
<label <label
@@ -845,7 +861,7 @@ const Forms = (props) => {
isSubscribe isSubscribe
? "cursor-pointer " ? "cursor-pointer "
: "pointer-events-none opacity-50" : "pointer-events-none opacity-50"
} relative block items-center mb-0`} } relative block items-center mb-0 mt-1.5`}
> >
<input <input
type="checkbox" type="checkbox"
@@ -876,8 +892,93 @@ const Forms = (props) => {
/> />
</div> </div>
)} )}
{isEnableSubscription && (
<div className="text-xs mt-2">
<label className="block">
<span className={isSubscribe ? "" : " text-gray-300"}>
{t("isenable-otp")}{" "}
<a
data-tooltip-id="isenableotp-tooltip"
className="ml-1"
>
<sup>
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
</sup>
</a>{" "}
{!isSubscribe && isEnableSubscription && (
<Upgrade />
)}
</span>
<Tooltip id="isenableotp-tooltip" className="z-50">
<div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">{t("isenable-otp")}</p>
<p>{t("isenable-otp-help.p1")}</p>
<p className="p-[5px]">
<ol className="list-disc">
<li>
<span className="font-bold">
{t("yes")}:{" "}
</span>
<span>{t("isenable-otp-help.p2")}</span>
</li>
<li>
<span className="font-bold">
{t("no")}:{" "}
</span>
<span>{t("isenable-otp-help.p3")}</span>
</li>
</ol>
</p>
<p>{t("isenable-otp-help.p4")}</p>
</div>
</Tooltip>
</label>
<div
className={`${
isSubscribe ? "" : "pointer-events-none opacity-50"
} flex items-center gap-2 ml-2 mb-1 `}
>
<input
type="radio"
value={"true"}
className="op-radio op-radio-xs"
name="IsEnableOTP"
checked={formData.IsEnableOTP === "true"}
onChange={handleStrInput}
/>
<div className="text-center">{t("yes")}</div>
</div>
<div
className={`${
isSubscribe ? "" : "pointer-events-none opacity-50"
} flex items-center gap-2 ml-2 mb-1 `}
>
<input
type="radio"
value={"false"}
name="IsEnableOTP"
className="op-radio op-radio-xs"
checked={formData.IsEnableOTP === "false"}
onChange={handleStrInput}
/>
<div className="text-center">{t("no")}</div>
</div>
</div>
)}
</> </>
)} )}
</div>
)}
{props.title !== "Sign Yourself" && (
<div
onClick={() => setIsAdvanceOpt(!isAdvanceOpt)}
className={`mt-2.5 op-link op-link-primary text-sm`}
>
{isAdvanceOpt
? t("hide-advanced-options")
: t("advanced-options")}
</div>
)}
<div className="flex items-center mt-3 gap-2"> <div className="flex items-center mt-3 gap-2">
<button <button
className={`${ className={`${
+34 -10
View File
@@ -28,6 +28,28 @@ function GuestLogin() {
const [contactId, setContactId] = useState(contactBookId); const [contactId, setContactId] = useState(contactBookId);
const [sendmail, setSendmail] = useState(); const [sendmail, setSendmail] = useState();
const [contact, setContact] = useState({ name: "", phone: "", email: "" }); const [contact, setContact] = useState({ name: "", phone: "", email: "" });
const navigateToDoc = async (docId, contactId) => {
try {
const docDetails = await Parse.Cloud.run("getDocument", {
docId: docId
});
if (!docDetails.error) {
if (sendmail === "false") {
navigate(
`/load/recipientSignPdf/${docId}/${contactId}?sendmail=${sendmail}`
);
} else {
navigate(`/load/recipientSignPdf/${docId}/${contactId}`);
}
return true;
} else {
return false;
}
} catch (err) {
console.log("err while getting doc", err);
return false;
}
};
useEffect(() => { useEffect(() => {
handleServerUrl(); handleServerUrl();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -70,13 +92,14 @@ function GuestLogin() {
"linkcontacttodoc", "linkcontacttodoc",
params params
); );
// console.log("linkContactRes ", linkContactRes);
setContactId(linkContactRes?.contactId); setContactId(linkContactRes?.contactId);
await navigateToDoc(checkSplit[0], linkContactRes?.contactId);
} catch (err) { } catch (err) {
console.log("Err in link ext contact", err); console.log("Err in link ext contact", err);
} }
} else { } else {
setContactId(checkSplit[2]); setContactId(checkSplit[2]);
await navigateToDoc(checkSplit[0], checkSplit[2]);
} }
} }
@@ -114,15 +137,12 @@ function GuestLogin() {
if (OTP) { if (OTP) {
setLoading(true); setLoading(true);
try { try {
let url = `${serverUrl}functions/AuthLoginAsMail/`; let url = `${serverUrl}functions/AuthLoginAsMail`;
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": parseId "X-Parse-Application-Id": parseId
}; };
let body = { let body = { email: email, otp: OTP };
email: email,
otp: OTP
};
let user = await axios.post(url, body, { headers: headers }); let user = await axios.post(url, body, { headers: headers });
if (user.data.result === "Invalid Otp") { if (user.data.result === "Invalid Otp") {
alert(t("invalid-otp")); alert(t("invalid-otp"));
@@ -140,7 +160,6 @@ function GuestLogin() {
`Parse/${parseId}/currentUser`, `Parse/${parseId}/currentUser`,
JSON.stringify(_user) JSON.stringify(_user)
); );
// console.log("contractUserDetails ", contractUserDetails);
if (contractUserDetails && contractUserDetails.length > 0) { if (contractUserDetails && contractUserDetails.length > 0) {
localStorage.setItem( localStorage.setItem(
"Extand_Class", "Extand_Class",
@@ -174,10 +193,15 @@ function GuestLogin() {
try { try {
setLoading(true); setLoading(true);
const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params); const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params);
// console.log("linkContactRes ", linkContactRes);
setContactId(linkContactRes.contactId); setContactId(linkContactRes.contactId);
const IsEnableOTP = await navigateToDoc(
documentId,
linkContactRes.contactId
);
if (!IsEnableOTP) {
setEnterOtp(true); setEnterOtp(true);
await SendOtp(); await SendOtp();
}
} catch (err) { } catch (err) {
setLoading(false); setLoading(false);
alert(t("something-went-wrong-mssg")); alert(t("something-went-wrong-mssg"));
@@ -208,7 +232,7 @@ function GuestLogin() {
<div className="w-full md:w-[50%] text-base-content"> <div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1> <h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
<legend className="text-[12px] text-[#878787] mt-2 mb-1"> <legend className="text-[12px] text-[#878787] mt-2 mb-1">
{t("guest-email-alert")} {t("get-otp-alert")}
</legend> </legend>
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md"> <div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
<input <input
@@ -236,7 +260,7 @@ function GuestLogin() {
> >
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1> <h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
<legend className="text-[12px] text-[#878787] mt-2"> <legend className="text-[12px] text-[#878787] mt-2">
{t("get-verification-code-2")} {t("guest-email-alert")}
</legend> </legend>
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md"> <div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
<p className="text-sm">{t("enter-verification-code")}</p> <p className="text-sm">{t("enter-verification-code")}</p>
+131 -110
View File
@@ -142,23 +142,23 @@ function PdfRequestFiles(props) {
let isGuestSignFlow = false; let isGuestSignFlow = false;
let sendmail; let sendmail;
let getDocId = ""; let getDocId = "";
const route = !props.templateId && window.location.pathname; //'/load/recipientSignPdf/TOAVuhXbfw/fPAKdK1qgX' let contactBookId = "";
//window.location.search = ?sendmail=false const route = !props.templateId && window.location.pathname;
const getQuery = const getQuery = !props.templateId && window.location?.search?.split("?"); //['','sendmail=false']
!props.templateId &&
window.location?.search &&
window.location?.search?.split("?"); //['','sendmail=false']
//'sendmail=false'
if (getQuery) { if (getQuery) {
sendmail = getQuery[1].split("=")[1]; //false sendmail = getQuery?.[1]?.split("=")[1]; //false
} }
const checkSplit = route && route?.split("/"); // ['', 'load', 'recipientSignPdf', 'TOAVuhXbfw', 'fPAKdK1qgX']
if (checkSplit && checkSplit.length > 4) { const routeId = route && route?.split("/"); // ['', 'load', 'recipientSignPdf', ':docId', ':contactBookId']
if (routeId && routeId.length > 4) {
// this condition will be occur only in guest flow in which load routeId will be include
isGuestSignFlow = true; isGuestSignFlow = true;
getDocId = checkSplit[3]; getDocId = routeId[3];
contactBookId = routeId[4];
} else { } else {
getDocId = checkSplit[2]; // this condition will be occur only in if user is logged in and load routeId will be exclude
getDocId = routeId[2];
contactBookId = routeId?.[3] || "";
} }
let getDocumentId = getDocId || documentId; let getDocumentId = getDocId || documentId;
useEffect(() => { useEffect(() => {
@@ -372,9 +372,10 @@ function PdfRequestFiles(props) {
`Parse/${localStorage.getItem("parseAppId")}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
); );
const jsonSender = JSON.parse(senderUser); const jsonSender = JSON.parse(senderUser);
// `currUserId` will be contactId or extUserId
let currUserId; let currUserId;
//getting document details //getting document details
const documentData = await contractDocument(documentId || docId); const documentData = await contractDocument(docId);
if (documentData && documentData.length > 0) { if (documentData && documentData.length > 0) {
const url = const url =
documentData[0] && documentData[0] &&
@@ -399,13 +400,13 @@ function PdfRequestFiles(props) {
const expireUpdateDate = new Date(expireDate).getTime(); const expireUpdateDate = new Date(expireDate).getTime();
const currDate = new Date().getTime(); const currDate = new Date().getTime();
const getSigners = documentData[0].Signers; const getSigners = documentData[0].Signers;
const getCurrentSigner = const getCurrentSigner = getSigners?.find(
getSigners &&
getSigners.filter(
(data) => data.UserId.objectId === jsonSender?.objectId (data) => data.UserId.objectId === jsonSender?.objectId
); );
currUserId = getCurrentSigner[0] ? getCurrentSigner[0].objectId : ""; currUserId = getCurrentSigner?.objectId
? getCurrentSigner.objectId
: contactBookId || "";
if (isEnableSubscription) { if (isEnableSubscription) {
await checkIsSubscribed( await checkIsSubscribed(
documentData[0]?.ExtUserPtr?.objectId, documentData[0]?.ExtUserPtr?.objectId,
@@ -428,10 +429,7 @@ function PdfRequestFiles(props) {
setIsCelebration(true); setIsCelebration(true);
setTimeout(() => setIsCelebration(false), 5000); setTimeout(() => setIsCelebration(false), 5000);
} else if (declined) { } else if (declined) {
const currentDecline = { const currentDecline = { currnt: "another", isDeclined: true };
currnt: "another",
isDeclined: true
};
setIsDecline(currentDecline); setIsDecline(currentDecline);
} else if (currDate > expireUpdateDate) { } else if (currDate > expireUpdateDate) {
const expireDateFormat = moment(new Date(expireDate)).format( const expireDateFormat = moment(new Date(expireDate)).format(
@@ -457,36 +455,24 @@ function PdfRequestFiles(props) {
} }
} }
} }
const audittrailData = const audittrailData = documentData?.[0]?.AuditTrail?.filter(
documentData[0].AuditTrail &&
documentData[0].AuditTrail.length > 0 &&
documentData[0].AuditTrail.filter(
(data) => data.Activity === "Signed" (data) => data.Activity === "Signed"
); );
const checkAlreadySign = const checkAlreadySign =
documentData[0].AuditTrail && documentData?.[0]?.AuditTrail?.some(
documentData[0].AuditTrail.length > 0 &&
documentData[0].AuditTrail.filter(
(data) => (data) =>
data.UserPtr?.objectId === currUserId && data?.UserPtr?.objectId === currUserId &&
data.Activity === "Signed" data.Activity === "Signed"
); ) || false;
if ( if (checkAlreadySign) {
checkAlreadySign &&
checkAlreadySign[0] &&
checkAlreadySign.length > 0
) {
setAlreadySign(true); setAlreadySign(true);
} else { } else {
const obj = documentData?.[0]; const obj = documentData?.[0];
setSendInOrder(obj?.SendinOrder || false); setSendInOrder(obj?.SendinOrder || false);
if ( if (
obj && obj &&
obj.Signers && obj?.Signers?.length > 0 &&
obj.Signers.length > 0 && obj?.Placeholders?.length > 0
obj.Placeholders &&
obj.Placeholders.length > 0
) { ) {
const params = { const params = {
event: "viewed", event: "viewed",
@@ -502,7 +488,10 @@ function PdfRequestFiles(props) {
email: x?.Email, email: x?.Email,
phone: x?.Phone phone: x?.Phone
})), })),
viewedBy: jsonSender.email, viewedBy:
documentData?.[0].Signers?.find(
(x) => x.objectId === currUserId
)?.Email || jsonSender?.email,
viewedAt: new Date(), viewedAt: new Date(),
createdAt: documentData?.[0].createdAt createdAt: documentData?.[0].createdAt
} }
@@ -583,17 +572,44 @@ function PdfRequestFiles(props) {
//checking if condition current user already sign or owner does not exist as a signer or document has been declined by someone or document has been expired //checking if condition current user already sign or owner does not exist as a signer or document has been declined by someone or document has been expired
//then stop to display tour message //then stop to display tour message
if ( if (
(checkAlreadySign && checkAlreadySign ||
checkAlreadySign[0] &&
checkAlreadySign.length > 0) ||
!currUserId || !currUserId ||
declined || declined ||
currDate > expireUpdateDate currDate > expireUpdateDate
) { ) {
setRequestSignTour(true); setRequestSignTour(true);
} else {
const isEnableOTP = documentData?.[0]?.IsEnableOTP || false;
if (!isEnableOTP) {
try {
const resContact = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getcontact`,
{
contactId: currUserId
},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
}
}
);
const contact = resContact?.data?.result;
setContractName("_Contactbook");
setSignerUserId(contact?.objectId);
const tourData = contact?.TourStatus && contact?.TourStatus;
if (tourData && tourData.length > 0) {
const checkTourRequest =
tourData?.some((data) => data?.requestSign) || false;
setTourStatus(tourData);
setRequestSignTour(checkTourRequest);
}
} catch (err) {
console.log("err while getting tourstatus", err);
}
} else { } else {
//else condition to check current user exist in contracts_Users class and check tour message status //else condition to check current user exist in contracts_Users class and check tour message status
//if not then check user exist in contracts_Contactbook class and check tour message statu //if not then check user exist in contracts_Contactbook class and check tour message status
const res = await contractUsers(); const res = await contractUsers();
if (res === "Error: Something went wrong!") { if (res === "Error: Something went wrong!") {
setHandleError(t("something-went-wrong-mssg")); setHandleError(t("something-went-wrong-mssg"));
@@ -630,6 +646,7 @@ function PdfRequestFiles(props) {
} }
} }
} }
}
setIsUiLoading(false); setIsUiLoading(false);
} else if ( } else if (
documentData === "Error: Something went wrong!" || documentData === "Error: Something went wrong!" ||
@@ -643,12 +660,12 @@ function PdfRequestFiles(props) {
setIsUiLoading({ isLoad: false }); setIsUiLoading({ isLoad: false });
} }
//function to get default signatur eof current user from `contracts_Signature` class //function to get default signatur eof current user from `contracts_Signature` class
const defaultSignRes = await getDefaultSignature(jsonSender.objectId); const defaultSignRes = await getDefaultSignature(jsonSender?.objectId);
if (defaultSignRes?.status === "success") { if (defaultSignRes?.status === "success") {
setDefaultSignImg(defaultSignRes?.res?.defaultSignature); const sign = defaultSignRes?.res?.defaultSignature || "";
setMyInitial(defaultSignRes?.res?.defaultInitial); const initials = defaultSignRes?.res?.defaultInitial || "";
} else if (defaultSignRes?.status === "error") { setDefaultSignImg(sign);
setHandleError("Error: Something went wrong!"); setMyInitial(initials);
} }
setIsLoading({ isLoad: false }); setIsLoading({ isLoad: false });
} catch (err) { } catch (err) {
@@ -664,20 +681,30 @@ function PdfRequestFiles(props) {
`Parse/${localStorage.getItem("parseAppId")}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
); );
let currentUser = JSON.parse(localuser); let currentUser = JSON.parse(localuser);
let isEmailVerified = currentUser?.emailVerified;
const isEnableOTP = pdfDetails?.[0]?.IsEnableOTP || false;
//if emailVerified data is not present in local user details then fetch again in _User class //if emailVerified data is not present in local user details then fetch again in _User class
if (isEnableOTP) {
try {
if (!currentUser?.emailVerified) { if (!currentUser?.emailVerified) {
const userQuery = new Parse.Query(Parse.User); const userQuery = new Parse.Query(Parse.User);
const getUser = await userQuery.get(currentUser?.objectId, { const getUser = await userQuery.get(currentUser?.objectId, {
sessionToken: currentUser?.sessionToken sessionToken:
currentUser?.sessionToken || localStorage.getItem("accesstoken")
}); });
if (getUser) { if (getUser) {
currentUser = JSON.parse(JSON.stringify(getUser)); currentUser = JSON.parse(JSON.stringify(getUser));
} }
} }
let isEmailVerified = currentUser?.emailVerified; isEmailVerified = currentUser?.emailVerified;
//check if isEmailVerified then go on next step
if (isEmailVerified) {
setIsEmailVerified(isEmailVerified); setIsEmailVerified(isEmailVerified);
} catch (err) {
console.log("err in get email verification ", err);
setHandleError(t("something-went-wrong-mssg"));
}
}
//check if isEmailVerified then go on next step
if (!isEnableOTP || isEmailVerified) {
try { try {
const checkUser = signerPos.filter( const checkUser = signerPos.filter(
(data) => data.signerObjId === signerObjectId (data) => data.signerObjId === signerObjectId
@@ -858,17 +885,9 @@ function PdfRequestFiles(props) {
// console.log("pdfte", pdfBytes); // console.log("pdfte", pdfBytes);
//get ExistUserPtr object id of user class to get tenantDetails //get ExistUserPtr object id of user class to get tenantDetails
const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId; const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId;
//get ExistUserPtr email to get userDetails let activeMailAdapter =
const res = await contractUsers(); pdfDetails?.[0]?.ExtUserPtr?.active_mail_adapter;
let activeMailAdapter = "";
if (res === "Error: Something went wrong!") {
setHandleError(t("something-went-wrong-mssg"));
setIsLoading({ isLoad: false });
} else if (!res || res?.length === 0) {
activeMailAdapter = "";
} else if (res[0] && res.length) {
activeMailAdapter = res[0]?.active_mail_adapter;
}
//function for call to embed signature in pdf and get digital signature pdf //function for call to embed signature in pdf and get digital signature pdf
const resSign = await signPdfFun( const resSign = await signPdfFun(
pdfBytes, pdfBytes,
@@ -880,13 +899,13 @@ function PdfRequestFiles(props) {
widgets widgets
); );
if (resSign && resSign.status === "success") { if (resSign && resSign.status === "success") {
setPdfUrl(res.data); setPdfUrl(resSign.data);
setIsSigned(true); setIsSigned(true);
setSignedSigners([]); setSignedSigners([]);
setUnSignedSigners([]); setUnSignedSigners([]);
getDocumentDetails(true); getDocumentDetails(documentId, true);
const index = pdfDetails?.[0].Signers.findIndex( const index = pdfDetails?.[0]?.Signers.findIndex(
(x) => x.Email === currentUser?.email (x) => x.objectId === signerObjectId
); );
const newIndex = index + 1; const newIndex = index + 1;
const usermail = { const usermail = {
@@ -1054,21 +1073,6 @@ function PdfRequestFiles(props) {
alertMessage: t("something-went-wrong-mssg") alertMessage: t("something-went-wrong-mssg")
}); });
} }
} else {
//else verify users email
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) {
console.log("error in save user's emailVerified in user class");
setHandleError(t("something-went-wrong-mssg"));
}
} }
} }
@@ -1216,31 +1220,26 @@ function PdfRequestFiles(props) {
); );
const jsonSender = JSON.parse(senderUser); const jsonSender = JSON.parse(senderUser);
setIsDecline({ isDeclined: false }); setIsDecline({ isDeclined: false });
const data = {
IsDeclined: true,
DeclineReason: reason,
DeclineBy: {
__type: "Pointer",
className: "_User",
objectId: jsonSender?.objectId
}
};
setIsUiLoading(true); setIsUiLoading(true);
const email =
pdfDetails?.[0].Signers?.find((x) => x.objectId === signerObjectId)
?.Email || jsonSender?.email;
const userId =
pdfDetails?.[0].Signers?.find((x) => x.objectId === signerObjectId)
?.UserId?.objectId || jsonSender?.objectId;
const params = {
docId: pdfDetails?.[0].objectId,
reason: reason,
userId: userId
};
await axios await axios
.put( .post(`${localStorage.getItem("baseUrl")}functions/declinedoc`, params, {
`${localStorage.getItem(
"baseUrl"
)}classes/contracts_Document/${documentId}`,
data,
{
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"), "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken") "X-Parse-Session-Token": localStorage.getItem("accesstoken")
} }
} })
)
.then(async (result) => { .then(async (result) => {
const res = result.data; const res = result.data;
if (res) { if (res) {
@@ -1260,7 +1259,8 @@ function PdfRequestFiles(props) {
email: x?.Email, email: x?.Email,
phone: x?.Phone phone: x?.Phone
})), })),
declinedBy: jsonSender.email, declinedBy: email,
declinedReason: reason,
declinedAt: new Date(), declinedAt: new Date(),
createdAt: pdfDetails?.[0].createdAt createdAt: pdfDetails?.[0].createdAt
} }
@@ -1314,6 +1314,25 @@ function PdfRequestFiles(props) {
const closeRequestSignTour = async () => { const closeRequestSignTour = async () => {
setRequestSignTour(true); setRequestSignTour(true);
if (isDontShow) { if (isDontShow) {
const isEnableOTP = pdfDetails?.[0]?.IsEnableOTP || false;
if (!isEnableOTP) {
try {
await axios.post(
`${localStorage.getItem("baseUrl")}functions/updatecontacttour`,
{
contactId: signerObjectId
},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
}
}
);
} catch (e) {
console.log("update tour messages error", e);
}
} else {
let updatedTourStatus = []; let updatedTourStatus = [];
if (tourStatus.length > 0) { if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus]; updatedTourStatus = [...tourStatus];
@@ -1348,6 +1367,7 @@ function PdfRequestFiles(props) {
console.log("update tour messages error", e); console.log("update tour messages error", e);
} }
} }
}
}; };
const requestSignTourFunction = () => { const requestSignTourFunction = () => {
const tourConfig = [ const tourConfig = [
@@ -1691,11 +1711,7 @@ function PdfRequestFiles(props) {
{/* this modal is used to show decline alert */} {/* this modal is used to show decline alert */}
<PdfDeclineModal <PdfDeclineModal
show={isDecline.isDeclined} show={isDecline.isDeclined}
headMsg={ headMsg={t("document-declined")}
pdfDetails[0]?.IsDeclined
? t("document-declined")
: t("document-decline")
}
bodyMssg={ bodyMssg={
isDecline.currnt === "Sure" isDecline.currnt === "Sure"
? t("decline-alert-1") ? t("decline-alert-1")
@@ -1938,7 +1954,12 @@ function PdfRequestFiles(props) {
<div className="flex mt-4 gap-1 px-[15px]"> <div className="flex mt-4 gap-1 px-[15px]">
<button <button
onClick={(e) => onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading) handleToPrint(
e,
pdfUrl,
setIsDownloading,
pdfDetails
)
} }
type="button" type="button"
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral" className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral"
@@ -779,6 +779,7 @@ const TemplatePlaceholder = () => {
AutomaticReminders: pdfDetails[0]?.AutomaticReminders, AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery), RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
NextReminderDate: pdfDetails[0]?.NextReminderDate, NextReminderDate: pdfDetails[0]?.NextReminderDate,
IsEnableOTP: pdfDetails[0]?.IsEnableOTP === true ? true : false,
URL: pdfUrl URL: pdfUrl
}; };
const updateTemplate = new Parse.Object("contracts_Template"); const updateTemplate = new Parse.Object("contracts_Template");
@@ -266,7 +266,8 @@ const ReportTable = (props) => {
Signers: signers, Signers: signers,
SendinOrder: Doc?.SendinOrder || false, SendinOrder: Doc?.SendinOrder || false,
AutomaticReminders: Doc?.AutomaticReminders || false, AutomaticReminders: Doc?.AutomaticReminders || false,
RemindOnceInEvery: Doc?.RemindOnceInEvery || 5 RemindOnceInEvery: Doc?.RemindOnceInEvery || 5,
IsEnableOTP: Doc?.IsEnableOTP || false
}; };
try { try {
const res = await axios.post( const res = await axios.post(
@@ -515,7 +516,6 @@ const ReportTable = (props) => {
.then(async (result) => { .then(async (result) => {
const res = result.data; const res = result.data;
if (res) { if (res) {
setReason("");
setActLoader({}); setActLoader({});
setIsAlert(true); setIsAlert(true);
setAlertMsg({ setAlertMsg({
@@ -527,7 +527,43 @@ const ReportTable = (props) => {
(x) => x.objectId !== item.objectId (x) => x.objectId !== item.objectId
); );
props.setList(upldatedList); props.setList(upldatedList);
const params = {
event: "declined",
body: {
objectId: item.objectId,
file: item?.SignedUrl || item?.URL,
name: item?.Name,
note: item?.Note || "",
description: item?.Description || "",
signers: item?.Signers?.map((x) => ({
name: x?.Name,
email: x?.Email,
phone: x?.Phone
})),
declinedBy: jsonSender?.email,
declinedReason: reason,
declinedAt: new Date(),
createdAt: item?.createdAt
} }
};
try {
await axios.post(
`${localStorage.getItem("baseUrl")}functions/callwebhook`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessiontoken: localStorage.getItem("accesstoken")
}
}
);
} catch (err) {
console.log("Err ", err);
}
}
setReason("");
}) })
.catch((err) => { .catch((err) => {
console.log("err", err); console.log("err", err);
@@ -41,8 +41,8 @@ function CustomModal(props) {
className="op-btn op-btn-primary mr-2 px-6" className="op-btn op-btn-primary mr-2 px-6"
type="button" type="button"
onClick={() => { onClick={() => {
setReason("");
props.declineDoc(reason); props.declineDoc(reason);
setReason("");
}} }}
> >
{t("yes")} {t("yes")}
@@ -635,6 +635,14 @@
"unauthorized-modal": "You don't have permission to perform this action, please contact {{adminEmail}}.", "unauthorized-modal": "You don't have permission to perform this action, please contact {{adminEmail}}.",
"sent-this-month": "Sent this month", "sent-this-month": "Sent this month",
"available-seats": "Available seats", "available-seats": "Available seats",
"buy-users":"Buy more users" "buy-users": "Buy more users",
"isenable-otp": "Enable OTP verification",
"isenable-otp-help": {
"p1": "Would you like to enable the verification process using a one-time password (OTP)?",
"p2": "Selecting this option will enable OTP verification. Users will receive a verification code via email, which they must enter to sign the document.",
"p3": "Selecting this option will disable OTP verification, allowing users to sign the document directly without additional steps.",
"p4": "Please choose the option that best suits your document signing requirements."
},
"advanced-options": "Advanced options",
"hide-advanced-options": "Hide Advanced options"
} }
@@ -634,6 +634,14 @@
"unauthorized-modal": "Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.", "unauthorized-modal": "Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
"sent-this-month": "envoyé ce mois-ci", "sent-this-month": "envoyé ce mois-ci",
"available-seats": "Disponible sièges", "available-seats": "Disponible sièges",
"buy-users":"Acheter plus d'utilisateurs" "buy-users": "Acheter plus d'utilisateurs",
"isenable-otp": "Activer la vérification OTP",
"isenable-otp-help": {
"p1": "Souhaitez-vous activer le processus de vérification à l'aide d'un mot de passe à usage unique (OTP)?",
"p2": "La sélection de cette option activera la vérification OTP. Les utilisateurs recevront un code de vérification par e-mail, qu'ils devront saisir pour signer le document.",
"p3": "La sélection de cette option désactivera la vérification OTP, permettant aux utilisateurs de signer le document directement sans étapes supplémentaires.",
"p4": "Veuillez choisir l'option qui correspond le mieux à vos exigences en matière de signature de documents."
},
"advanced-options": "Options avancées",
"hide-advanced-options": "Masquer les options avancées"
} }
@@ -196,6 +196,9 @@ export default async function createDocumentWithTemplate(request, response) {
if (TimeToCompleteDays) { if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays); object.set('TimeToCompleteDays', TimeToCompleteDays);
} }
const enableOTP = request.body?.enableOTP === true ? true : false;
const IsEnableOTP = enableOTP || template?.IsEnableOTP || false;
object.set('IsEnableOTP', IsEnableOTP);
object.set('CreatedBy', template.CreatedBy); object.set('CreatedBy', template.CreatedBy);
object.set('ExtUserPtr', { object.set('ExtUserPtr', {
__type: 'Pointer', __type: 'Pointer',
@@ -63,6 +63,7 @@ export default async function createDocumentwithCoordinate(request, response) {
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; const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
const IsEnableOTP = request.body?.enableOTP === true ? true : false;
// console.log('fileData ', fileData); // console.log('fileData ', fileData);
const protocol = customAPIurl(); const protocol = customAPIurl();
const baseUrl = new URL(process.env.PUBLIC_URL); const baseUrl = new URL(process.env.PUBLIC_URL);
@@ -162,6 +163,7 @@ export default async function createDocumentwithCoordinate(request, response) {
if (TimeToCompleteDays) { if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays); object.set('TimeToCompleteDays', TimeToCompleteDays);
} }
object.set('IsEnableOTP', IsEnableOTP);
object.set('IsSendMail', send_email); object.set('IsSendMail', send_email);
let contact = []; let contact = [];
if (signers && signers.length > 0) { if (signers && signers.length > 0) {
@@ -9,6 +9,7 @@ export default async function createTemplate(request, response) {
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.PUBLIC_URL); const baseUrl = new URL(process.env.PUBLIC_URL);
const isEnableOTP = request.body?.enableOTP === true ? true : false;
try { try {
const reqToken = request.headers['x-api-token']; const reqToken = request.headers['x-api-token'];
@@ -65,6 +66,7 @@ export default async function createTemplate(request, response) {
object.set('URL', fileUrl); object.set('URL', fileUrl);
object.set('CreatedBy', userPtr); object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr); object.set('ExtUserPtr', extUserPtr);
object.set('IsEnableOTP', isEnableOTP);
if (SendinOrder) { if (SendinOrder) {
object.set('SendinOrder', SendinOrder); object.set('SendinOrder', SendinOrder);
} }
@@ -17,6 +17,8 @@ export default async function createTemplatewithCoordinate(request, response) {
const base64File = request.body.file; const base64File = request.body.file;
const fileData = request.files?.[0] ? request.files[0].buffer : null; const fileData = request.files?.[0] ? request.files[0].buffer : null;
const SendinOrder = request.body.sendInOrder || false; const SendinOrder = request.body.sendInOrder || false;
const isEnableOTP = request.body?.enableOTP === true ? true : false;
// console.log('fileData ', fileData); // console.log('fileData ', fileData);
const protocol = customAPIurl(); const protocol = customAPIurl();
@@ -80,6 +82,7 @@ export default async function createTemplatewithCoordinate(request, response) {
object.set('URL', fileUrl); object.set('URL', fileUrl);
object.set('CreatedBy', userPtr); object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr); object.set('ExtUserPtr', extUserPtr);
object.set('IsEnableOTP', isEnableOTP);
let contact = []; let contact = [];
if (signers && signers.length > 0) { if (signers && signers.length > 0) {
let parseSigners; let parseSigners;
@@ -12,6 +12,8 @@ export default async function draftDocument(request, response) {
const send_email = request.body.send_email || true; const send_email = request.body.send_email || true;
const fileData = request.files?.[0] ? request.files[0].buffer : null; const fileData = request.files?.[0] ? request.files[0].buffer : null;
const SendinOrder = request.body.sendInOrder || false; const SendinOrder = request.body.sendInOrder || false;
const isEnableOTP = request.body?.enableOTP === true ? true : false;
// console.log('fileData ', fileData); // console.log('fileData ', fileData);
const protocol = customAPIurl(); const protocol = customAPIurl();
const baseUrl = new URL(process.env.PUBLIC_URL); const baseUrl = new URL(process.env.PUBLIC_URL);
@@ -83,6 +85,7 @@ export default async function draftDocument(request, response) {
object.set('CreatedBy', userPtr); object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr); object.set('ExtUserPtr', extUserPtr);
object.set('IsSendMail', send_email); object.set('IsSendMail', send_email);
object.set('IsEnableOTP', isEnableOTP);
if (signers && signers.length > 0) { if (signers && signers.length > 0) {
let parseSigners; let parseSigners;
if (base64File) { if (base64File) {
@@ -62,6 +62,7 @@ export default async function getDocument(request, response) {
document?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) || document?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) ||
[], [],
sendInOrder: document?.SendinOrder || false, sendInOrder: document?.SendinOrder || false,
enableOTP: document?.IsEnableOTP || false,
createdAt: document.createdAt, createdAt: document.createdAt,
updatedAt: document.updatedAt, updatedAt: document.updatedAt,
}); });
@@ -100,6 +100,7 @@ export default async function getDocumentList(request, response) {
x?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) || x?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) ||
[], [],
sendInOrder: x?.SendinOrder || false, sendInOrder: x?.SendinOrder || false,
enableOTP: x?.IsEnableOTP || false,
createdAt: x.createdAt, createdAt: x.createdAt,
updatedAt: x.updatedAt, updatedAt: x.updatedAt,
})); }));
@@ -61,6 +61,7 @@ export default async function getTemplate(request, response) {
), ),
})) || [], })) || [],
sendInOrder: template?.SendinOrder || false, sendInOrder: template?.SendinOrder || false,
enableOTP: template?.IsEnableOTP || false,
createdAt: template.createdAt, createdAt: template.createdAt,
updatedAt: template.updatedAt, updatedAt: template.updatedAt,
}); });
@@ -88,6 +88,7 @@ export default async function getTemplatetList(request, response) {
), ),
})) || [], })) || [],
sendInOrder: template?.SendinOrder || false, sendInOrder: template?.SendinOrder || false,
enableOTP: template?.IsEnableOTP || false,
createdAt: template.createdAt, createdAt: template.createdAt,
updatedAt: template.updatedAt, updatedAt: template.updatedAt,
})); }));
@@ -10,7 +10,7 @@ export default async function updateDocument(request, response) {
const token = await tokenQuery.first({ useMasterKey: true }); const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) { if (token !== undefined) {
// Valid Token then proceed request // Valid Token then proceed request
const allowedKeys = ['name', 'note', 'description', 'folderId']; const allowedKeys = ['name', 'note', 'description', 'folderId', 'enableOTP'];
const objectKeys = Object.keys(request.body); const objectKeys = Object.keys(request.body);
const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0; const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
const parseUser = JSON.parse(JSON.stringify(token)); const parseUser = JSON.parse(JSON.stringify(token));
@@ -48,6 +48,10 @@ export default async function updateDocument(request, response) {
objectId: request?.body?.folderId, objectId: request?.body?.folderId,
}); });
} }
if (request.body?.enableOTP) {
updateQuery.set('IsEnableOTP', request.body?.enableOTP);
}
const updatedRes = await updateQuery.save(null, { useMasterKey: true }); const updatedRes = await updateQuery.save(null, { useMasterKey: true });
if (updatedRes) { if (updatedRes) {
if (request.posthog) { if (request.posthog) {
@@ -10,7 +10,7 @@ export default async function updateTemplate(request, response) {
const token = await tokenQuery.first({ useMasterKey: true }); const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) { if (token !== undefined) {
// Valid Token then proceed request // Valid Token then proceed request
const allowedKeys = ['name', 'note', 'description', 'folderId']; const allowedKeys = ['name', 'note', 'description', 'folderId', 'enableOTP'];
const objectKeys = Object.keys(request.body); const objectKeys = Object.keys(request.body);
const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0; const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
const parseUser = JSON.parse(JSON.stringify(token)); const parseUser = JSON.parse(JSON.stringify(token));
@@ -48,6 +48,9 @@ export default async function updateTemplate(request, response) {
objectId: request?.body?.folderId, objectId: request?.body?.folderId,
}); });
} }
if (request.body?.enableOTP) {
updateQuery.set('IsEnableOTP', request.body?.enableOTP);
}
const updatedRes = await updateQuery.save(null, { useMasterKey: true }); const updatedRes = await updateQuery.save(null, { useMasterKey: true });
if (updatedRes) { if (updatedRes) {
if (request.posthog) { if (request.posthog) {
+6
View File
@@ -65,6 +65,9 @@ import ExtUserAftersave from './parsefunction/ExtUserAftersave.js';
import ExtUserAfterdelete from './parsefunction/ExtUserAfterdelete.js'; import ExtUserAfterdelete from './parsefunction/ExtUserAfterdelete.js';
import AllowedCredits from './parsefunction/AllowedCredits.js'; import AllowedCredits from './parsefunction/AllowedCredits.js';
import BuyCredits from './parsefunction/BuyCredits.js'; import BuyCredits from './parsefunction/BuyCredits.js';
import getContact from './parsefunction/getContact.js';
import updateContactTour from './parsefunction/updateContactTour.js';
import declinedocument from './parsefunction/declinedocument.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic. // This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
@@ -142,3 +145,6 @@ Parse.Cloud.define('allowedusers', AllowedUsers);
Parse.Cloud.define('buyaddonusers', BuyAddonUsers); Parse.Cloud.define('buyaddonusers', BuyAddonUsers);
Parse.Cloud.define('allowedcredits', AllowedCredits); Parse.Cloud.define('allowedcredits', AllowedCredits);
Parse.Cloud.define('buycredits', BuyCredits); Parse.Cloud.define('buycredits', BuyCredits);
Parse.Cloud.define('getcontact', getContact);
Parse.Cloud.define('updatecontacttour', updateContactTour);
Parse.Cloud.define('declinedoc', declinedocument);
@@ -62,6 +62,7 @@ const createDocumentFromTemplate = async (template, existContact, index) => {
object.set('CreatedBy', template?.CreatedBy); object.set('CreatedBy', template?.CreatedBy);
object.set('ExtUserPtr', template?.ExtUserPtr); object.set('ExtUserPtr', template?.ExtUserPtr);
object.set('OriginIp', template?.OriginIp || ''); object.set('OriginIp', template?.OriginIp || '');
object.set('IsEnableOTP', template?.IsEnableOTP || false);
let signers = template?.Signers || []; let signers = template?.Signers || [];
const signerobj = { const signerobj = {
__type: 'Pointer', __type: 'Pointer',
@@ -7,20 +7,24 @@ export default async function callWebhook(request) {
const contactId = request.params.contactId; const contactId = request.params.contactId;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID; const appId = process.env.APP_ID;
try {
const docQuery = new Parse.Query('contracts_Document');
const docRes = await docQuery.get(docId, { useMasterKey: true });
const isEnableOTP = docRes?.get('IsEnableOTP') || false;
let userId;
if (isEnableOTP) {
const userRes = await axios.get(serverUrl + '/users/me', { const userRes = await axios.get(serverUrl + '/users/me', {
headers: { headers: {
'X-Parse-Application-Id': appId, 'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'], 'X-Parse-Session-Token': request.headers['sessiontoken'],
}, },
}); });
userId = userRes.data && userRes.data.objectId;
const userId = userRes.data && userRes.data.objectId; }
if (userId) { if (!isEnableOTP || userId) {
if (event === 'viewed' && contactId) { if (event === 'viewed' && contactId) {
const docQuery = new Parse.Query('contracts_Document'); if (docRes) {
const res = await docQuery.get(docId, { useMasterKey: true }); const _docRes = docRes.toJSON();
if (res) {
const _res = res.toJSON();
const userPtr = { const userPtr = {
__type: 'Pointer', __type: 'Pointer',
className: 'contracts_Contactbook', className: 'contracts_Contactbook',
@@ -29,19 +33,19 @@ export default async function callWebhook(request) {
const date = new Date().toISOString(); const date = new Date().toISOString();
const obj = { const obj = {
UserPtr: userPtr, UserPtr: userPtr,
SignedUrl: _res.SignedUrl, SignedUrl: _docRes.SignedUrl,
Activity: 'Viewed', Activity: 'Viewed',
ipAddress: request.headers['x-real-ip'], ipAddress: request.headers['x-real-ip'],
ViewedOn: date, ViewedOn: date,
}; };
const isUserExist = _res?.AuditTrail?.some( const isUserExist = _docRes?.AuditTrail?.some(
x => x.UserPtr.objectId === contactId && x?.ViewedOn x => x.UserPtr.objectId === contactId && x?.ViewedOn
); );
if (!isUserExist) { if (!isUserExist) {
const updateDoc = new Parse.Object('contracts_Document'); const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = res.id; updateDoc.id = docRes.id;
if (_res?.AuditTrail && _res?.AuditTrail?.length > 0) { if (_docRes?.AuditTrail && _docRes?.AuditTrail?.length > 0) {
updateDoc.set('AuditTrail', [..._res?.AuditTrail, obj]); updateDoc.set('AuditTrail', [..._docRes?.AuditTrail, obj]);
} else { } else {
updateDoc.set('AuditTrail', [obj]); updateDoc.set('AuditTrail', [obj]);
} }
@@ -49,26 +53,20 @@ 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('objectId', resDoc.get('ExtUserPtr')?.id); extendcls.equalTo('objectId', docRes.get('ExtUserPtr')?.id);
// extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId }); // extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
const res = await extendcls.first({ useMasterKey: true }); const resExt = await extendcls.first({ useMasterKey: true });
if (res) { if (resExt) {
const extUser = JSON.parse(JSON.stringify(res)); const extUser = JSON.parse(JSON.stringify(resExt));
if (extUser?.Webhook) { if (extUser?.Webhook) {
const params = { const params = { event: event, ...body };
event: event,
...body,
};
await axios await axios
.post(extUser?.Webhook, params, { .post(extUser?.Webhook, params, {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
}) })
.then(res => { .then(res => {
try { try {
// console.log('res ', res);
const webhook = new Parse.Object('contracts_Webhook'); const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status); webhook.set('Log', res?.status);
webhook.set('UserId', { webhook.set('UserId', {
@@ -102,4 +100,8 @@ export default async function callWebhook(request) {
} else { } else {
return { message: 'User not found!' }; return { message: 'User not found!' };
} }
} catch (err) {
console.log('Err in callwebhook', err);
return { message: 'Something went wrong!' };
}
} }
@@ -199,6 +199,7 @@ export default async function createBatchDocs(request) {
TimeToCompleteDays: x.TimeToCompleteDays || 15, TimeToCompleteDays: x.TimeToCompleteDays || 15,
OriginIp: Ip, OriginIp: Ip,
DocSentAt: { __type: 'Date', iso: isoDate }, DocSentAt: { __type: 'Date', iso: isoDate },
IsEnableOTP: x?.IsEnableOTP || false,
}, },
}; };
}); });
@@ -0,0 +1,38 @@
export default async function declinedocument(request) {
const docId = request.params.docId;
const reason = request.params?.reason || '';
const declineBy = {
__type: 'Pointer',
className: '_User',
objectId: request.params?.userId,
};
try {
const docCls = new Parse.Query('contracts_Document');
const updateDoc = await docCls.get(docId, { useMasterKey: true });
if (updateDoc) {
const isEnableOTP = updateDoc?.get('IsEnableOTP') || false;
if (!isEnableOTP) {
updateDoc.set('IsDeclined', true);
updateDoc.set('DeclineReason', reason);
updateDoc.set('DeclineBy', declineBy);
await updateDoc.save(null, { useMasterKey: true });
return 'document declined';
} else {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
updateDoc.set('IsDeclined', true);
updateDoc.set('DeclineReason', reason);
updateDoc.set('DeclineBy', declineBy);
await updateDoc.save(null, { useMasterKey: true });
return 'document declined';
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
}
} catch (err) {
console.log('err while decling doc', err);
throw err;
}
}
@@ -0,0 +1,11 @@
export default async function getContact(request) {
const contactId = request.params.contactId;
try {
const contactCls = new Parse.Query('contracts_Contactbook');
const contactRes = await contactCls.get(contactId, { useMasterKey: true });
return contactRes;
} catch (err) {
console.log('Err in contracts_Contactbook class ', err);
throw err;
}
}
@@ -6,14 +6,7 @@ export default async function getDocument(request) {
const docId = request.params.docId; const docId = request.params.docId;
try { try {
const userRes = await axios.get(serverUrl + '/users/me', { if (docId) {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (docId && userId) {
try { try {
const query = new Parse.Query('contracts_Document'); const query = new Parse.Query('contracts_Document');
query.equalTo('objectId', docId); query.equalTo('objectId', docId);
@@ -26,12 +19,33 @@ export default async function getDocument(request) {
query.notEqualTo('IsArchive', true); query.notEqualTo('IsArchive', true);
const res = await query.first({ useMasterKey: true }); const res = await query.first({ useMasterKey: true });
if (res) { if (res) {
const IsEnableOTP = res?.get('IsEnableOTP') || false;
if (!IsEnableOTP) {
return res;
} else {
if (request?.headers?.['sessiontoken']) {
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data?.objectId;
const acl = res.getACL(); const acl = res.getACL();
if (acl && acl.getReadAccess(userId)) { if (userId && acl && acl.getReadAccess(userId)) {
return res; return res;
} else { } else {
return { error: "You don't have access of this document!" }; return { error: "You don't have access of this document!" };
} }
} catch (err) {
console.log('err user in not authenticated', err);
return { error: "You don't have access of this document!" };
}
} else {
return { error: "You don't have access of this document!" };
}
}
} else { } else {
return { error: "You don't have access of this document!" }; return { error: "You don't have access of this document!" };
} }
@@ -28,7 +28,33 @@ export default function getPresignedUrl(url) {
export async function getSignedUrl(request) { export async function getSignedUrl(request) {
try { try {
const docId = request.params.docId || '';
const url = request.params.url; const url = request.params.url;
if (docId) {
try {
const query = new Parse.Query('contracts_Document');
query.equalTo('objectId', docId);
query.notEqualTo('IsEnableOTP', true);
query.include('CreatedBy');
query.include('Signers');
query.include('AuditTrail.UserPtr');
query.include('Placeholders');
query.include('DeclineBy');
query.notEqualTo('IsArchive', true);
const res = await query.first({ useMasterKey: true });
if (res) {
if (useLocal !== 'true') {
const presignedUrl = getPresignedUrl(url);
return presignedUrl;
} else {
return url;
}
}
} catch (err) {
console.log('Err in presigned url', err);
throw err;
}
} else {
if (!request?.user) { if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else { } else {
@@ -39,6 +65,7 @@ export async function getSignedUrl(request) {
return url; return url;
} }
} }
}
} catch (err) { } catch (err) {
console.log('error in getsignedurl', err); console.log('error in getsignedurl', err);
const code = err.code || 400; const code = err.code || 400;
@@ -50,15 +50,6 @@ export default async function getSubscription(request) {
} }
} else if (contactId) { } else if (contactId) {
try { try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const contactCls = new Parse.Query('contracts_Contactbook'); const contactCls = new Parse.Query('contracts_Contactbook');
const contactUser = await contactCls.get(contactId, { useMasterKey: true }); const contactUser = await contactCls.get(contactId, { useMasterKey: true });
if (contactUser) { if (contactUser) {
@@ -70,7 +61,6 @@ export default async function getSubscription(request) {
}); });
subscriptionCls.descending('createdAt'); subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true }); const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions) { if (subcripitions) {
const _subcripitions = JSON.parse(JSON.stringify(subcripitions)); const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
if (_subcripitions.PlanCode === 'freeplan') { if (_subcripitions.PlanCode === 'freeplan') {
@@ -90,9 +80,6 @@ export default async function getSubscription(request) {
} else { } else {
return { status: 'error', result: 'User not found!' }; return { status: 'error', result: 'User not found!' };
} }
} else {
return { status: 'error', result: 'Invalid session token!' };
}
} catch (err) { } catch (err) {
console.log('Err in get subscription2', err.message); console.log('Err in get subscription2', err.message);
return { status: 'error', result: err.message }; return { status: 'error', result: err.message };
@@ -32,6 +32,7 @@ export default async function GenerateCertificate(docDetails) {
const OriginIp = docDetails?.OriginIp || ''; const OriginIp = docDetails?.OriginIp || '';
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 IsEnableOTP = docDetails?.IsEnableOTP || false;
const auditTrail = const auditTrail =
docDetails?.Signers?.length > 0 docDetails?.Signers?.length > 0
? docDetails.AuditTrail.map(x => { ? docDetails.AuditTrail.map(x => {
@@ -348,6 +349,7 @@ export default async function GenerateCertificate(docDetails) {
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
if (IsEnableOTP) {
page.drawText('Security level :', { page.drawText('Security level :', {
x: half + 55, x: half + 55,
y: yPosition4 + 10, y: yPosition4 + 10,
@@ -355,15 +357,14 @@ export default async function GenerateCertificate(docDetails) {
font: timesRomanFont, font: timesRomanFont,
color: textKeyColor, color: textKeyColor,
}); });
page.drawText('Email, OTP Auth', {
page.drawText(`Email, OTP Auth`, {
x: half + 125, x: half + 125,
y: yPosition4 + 10, y: yPosition4 + 10,
size: timeText, size: timeText,
font: timesRomanFont, font: timesRomanFont,
color: textValueColor, color: textValueColor,
}); });
}
page.drawText('Signature :', { page.drawText('Signature :', {
x: 30, x: 30,
y: yPosition5, y: yPosition5,
@@ -272,9 +272,6 @@ const sendMailsaveCertifcate = async (doc, P12Buffer, url, isCustomMail, mailPro
*/ */
async function PDF(req) { async function PDF(req) {
try { try {
if (!req?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
const userIP = req.headers['x-real-ip']; // client IPaddress const userIP = req.headers['x-real-ip']; // client IPaddress
const docId = req.params.docId; const docId = req.params.docId;
const reqUserId = req.params.userId; const reqUserId = req.params.userId;
@@ -289,6 +286,13 @@ async function PDF(req) {
if (!resDoc) { if (!resDoc) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.'); throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
} }
const IsEnableOTP = resDoc?.get('IsEnableOTP') || false;
// if `IsEnableOTP` is false then we don't have to check authentication
if (IsEnableOTP) {
if (!req?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
}
const _resDoc = resDoc?.toJSON(); const _resDoc = resDoc?.toJSON();
let signUser; let signUser;
let className; let className;
@@ -423,7 +427,6 @@ async function PDF(req) {
error.code = 400; // Set the error code (e.g., 400 for bad request) error.code = 400; // Set the error code (e.g., 400 for bad request)
throw error; throw error;
} }
}
} catch (err) { } catch (err) {
console.log('Err in signpdf', err); console.log('Err in signpdf', err);
throw err; throw err;
@@ -0,0 +1,33 @@
export default async function updateContactTour(request) {
const contactId = request.params.contactId;
try {
const contactCls = new Parse.Query('contracts_Contactbook');
const contactRes = await contactCls.get(contactId, { useMasterKey: true });
if (contactRes) {
const _contactRes = JSON.parse(JSON.stringify(contactRes));
const tourStatus = _contactRes?.TourStatus?.length > 0 ? _contactRes.TourStatus : [];
let updatedTourStatus = [];
if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus];
const requestSignIndex = tourStatus.findIndex(
obj => obj['requestSign'] === false || obj['requestSign'] === true
);
if (requestSignIndex !== -1) {
updatedTourStatus[requestSignIndex] = { requestSign: true };
} else {
updatedTourStatus.push({ requestSign: true });
}
} else {
updatedTourStatus = [{ requestSign: true }];
}
contactRes.set('TourStatus', updatedTourStatus);
const updateRes = await contactRes.save(null, { useMasterKey: true });
return updateRes;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'contact not found.');
}
} catch (err) {
console.log('Err in contracts_Contactbook class ', err);
throw err;
}
}
@@ -0,0 +1,29 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
const templateSchema = new Parse.Schema('contracts_Template');
templateSchema.addBoolean('IsEnableOTP');
await templateSchema.update();
const className = 'contracts_Document';
const schema = new Parse.Schema(className);
schema.addBoolean('IsEnableOTP');
return schema.update();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
const templateSchema = new Parse.Schema('contracts_Template');
templateSchema.deleteField('IsEnableOTP');
await templateSchema.update();
const className = 'contracts_Document';
const schema = new Parse.Schema(className);
schema.deleteField('IsEnableOTP');
return schema.update();
};
+108 -76
View File
@@ -1770,11 +1770,12 @@
} }
}, },
"node_modules/@google-cloud/firestore": { "node_modules/@google-cloud/firestore": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.10.0.tgz",
"integrity": "sha512-c4ALHT3G08rV7Zwv8Z2KG63gZh66iKdhCBeDfCpIkLrjX6EAjTD/szMdj14M+FnQuClZLFfW5bAgoOjfNmLtJg==", "integrity": "sha512-VFNhdHvfnmqcHHs6YhmSNHHxQqaaD64GwiL0c+e1qz85S8SWZPC2XFRf8p9yHRTF40Kow424s1KBU9f0fdQa+Q==",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"@opentelemetry/api": "^1.3.0",
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"google-gax": "^4.3.3", "google-gax": "^4.3.3",
@@ -1866,9 +1867,9 @@
} }
}, },
"node_modules/@graphql-tools/merge/node_modules/@graphql-tools/utils": { "node_modules/@graphql-tools/merge/node_modules/@graphql-tools/utils": {
"version": "10.5.2", "version": "10.5.4",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.2.tgz", "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.4.tgz",
"integrity": "sha512-VZpw7wxwmQGcCGt8epw6fDb8LkoySbTJ/MU565ibKivPqCkH96XK36Et/N0RlRCYGN6QAXn5UIaSbOYYHrnpAA==", "integrity": "sha512-XHnyCWSlg1ccsD8s0y6ugo5GZ5TpkTiFVNPSYms5G0s6Z/xTuSmiLBfeqgkfaCwLmLaQnRCmNDL2JRnqc2R5bQ==",
"dependencies": { "dependencies": {
"@graphql-typed-document-node/core": "^3.1.1", "@graphql-typed-document-node/core": "^3.1.1",
"cross-inspect": "1.0.1", "cross-inspect": "1.0.1",
@@ -1900,9 +1901,9 @@
} }
}, },
"node_modules/@graphql-tools/schema/node_modules/@graphql-tools/utils": { "node_modules/@graphql-tools/schema/node_modules/@graphql-tools/utils": {
"version": "10.5.2", "version": "10.5.4",
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.2.tgz", "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.4.tgz",
"integrity": "sha512-VZpw7wxwmQGcCGt8epw6fDb8LkoySbTJ/MU565ibKivPqCkH96XK36Et/N0RlRCYGN6QAXn5UIaSbOYYHrnpAA==", "integrity": "sha512-XHnyCWSlg1ccsD8s0y6ugo5GZ5TpkTiFVNPSYms5G0s6Z/xTuSmiLBfeqgkfaCwLmLaQnRCmNDL2JRnqc2R5bQ==",
"dependencies": { "dependencies": {
"@graphql-typed-document-node/core": "^3.1.1", "@graphql-typed-document-node/core": "^3.1.1",
"cross-inspect": "1.0.1", "cross-inspect": "1.0.1",
@@ -1936,9 +1937,9 @@
} }
}, },
"node_modules/@grpc/grpc-js": { "node_modules/@grpc/grpc-js": {
"version": "1.11.1", "version": "1.11.2",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.1.tgz", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.2.tgz",
"integrity": "sha512-gyt/WayZrVPH2w/UTLansS7F9Nwld472JxxaETamrM8HNlsa+jSLNyKAZmhxI2Me4c3mQHFiS1wWHDY1g1Kthw==", "integrity": "sha512-DWp92gDD7/Qkj7r8kus6/HCINeo3yPZWZ3paKgDgsbKbSpoxKg1yvN8xe2Q8uE3zOsPe3bX8FQX2+XValq2yTw==",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"@grpc/proto-loader": "^0.7.13", "@grpc/proto-loader": "^0.7.13",
@@ -2625,6 +2626,15 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@opentelemetry/api": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"optional": true,
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/@parse/fs-files-adapter": { "node_modules/@parse/fs-files-adapter": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@parse/fs-files-adapter/-/fs-files-adapter-3.0.0.tgz",
@@ -3771,11 +3781,11 @@
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.2.0", "version": "22.5.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
"integrity": "sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==", "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
"dependencies": { "dependencies": {
"undici-types": "~6.13.0" "undici-types": "~6.19.2"
} }
}, },
"node_modules/@types/node-fetch": { "node_modules/@types/node-fetch": {
@@ -3793,9 +3803,9 @@
"integrity": "sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw==" "integrity": "sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw=="
}, },
"node_modules/@types/qs": { "node_modules/@types/qs": {
"version": "6.9.15", "version": "6.9.16",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz",
"integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A=="
}, },
"node_modules/@types/range-parser": { "node_modules/@types/range-parser": {
"version": "1.2.7", "version": "1.2.7",
@@ -4157,9 +4167,9 @@
} }
}, },
"node_modules/aws4": { "node_modules/aws4": {
"version": "1.13.1", "version": "1.13.2",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.1.tgz", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
"integrity": "sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA==" "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.7.7", "version": "1.7.7",
@@ -4270,9 +4280,9 @@
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
}, },
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.2", "version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
"content-type": "~1.0.5", "content-type": "~1.0.5",
@@ -4282,7 +4292,7 @@
"http-errors": "2.0.0", "http-errors": "2.0.0",
"iconv-lite": "0.4.24", "iconv-lite": "0.4.24",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"qs": "6.11.0", "qs": "6.13.0",
"raw-body": "2.5.2", "raw-body": "2.5.2",
"type-is": "~1.6.18", "type-is": "~1.6.18",
"unpipe": "1.0.0" "unpipe": "1.0.0"
@@ -4305,6 +4315,20 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
}, },
"node_modules/body-parser/node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dependencies": {
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/bowser": { "node_modules/bowser": {
"version": "2.11.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
@@ -5797,29 +5821,6 @@
"express": "^4 || ^5" "express": "^4 || ^5"
} }
}, },
"node_modules/express/node_modules/body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/express/node_modules/debug": { "node_modules/express/node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -6142,11 +6143,11 @@
} }
}, },
"node_modules/firebase-admin/node_modules/@types/node": { "node_modules/firebase-admin/node_modules/@types/node": {
"version": "20.14.15", "version": "20.16.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.15.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz",
"integrity": "sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==", "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==",
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~6.19.2"
} }
}, },
"node_modules/firebase-admin/node_modules/long": { "node_modules/firebase-admin/node_modules/long": {
@@ -6154,11 +6155,6 @@
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
}, },
"node_modules/firebase-admin/node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/flat-cache": { "node_modules/flat-cache": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
@@ -6600,9 +6596,9 @@
} }
}, },
"node_modules/google-gax": { "node_modules/google-gax": {
"version": "4.3.9", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.3.9.tgz", "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz",
"integrity": "sha512-tcjQr7sXVGMdlvcG25wSv98ap1dtF4Z6mcV0rztGIddOcezw4YMb/uTXg72JPrLep+kXcVjaJjg6oo3KLf4itQ==", "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"@grpc/grpc-js": "^1.10.9", "@grpc/grpc-js": "^1.10.9",
@@ -7845,9 +7841,9 @@
} }
}, },
"node_modules/loglevel": { "node_modules/loglevel": {
"version": "1.9.1", "version": "1.9.2",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
"integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
"engines": { "engines": {
"node": ">= 0.6.0" "node": ">= 0.6.0"
}, },
@@ -8518,9 +8514,9 @@
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
}, },
"node_modules/node-abi": { "node_modules/node-abi": {
"version": "3.65.0", "version": "3.67.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.67.0.tgz",
"integrity": "sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==", "integrity": "sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==",
"dependencies": { "dependencies": {
"semver": "^7.3.5" "semver": "^7.3.5"
}, },
@@ -9414,6 +9410,42 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/parse-server/node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/parse-server/node_modules/body-parser/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/parse-server/node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/parse-server/node_modules/bson": { "node_modules/parse-server/node_modules/bson": {
"version": "5.5.1", "version": "5.5.1",
"resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz", "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz",
@@ -10417,9 +10449,9 @@
} }
}, },
"node_modules/protobufjs": { "node_modules/protobufjs": {
"version": "7.3.2", "version": "7.4.0",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
"integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==", "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
"hasInstallScript": true, "hasInstallScript": true,
"optional": true, "optional": true,
"dependencies": { "dependencies": {
@@ -10475,9 +10507,9 @@
"dev": true "dev": true
}, },
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.0", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
"dependencies": { "dependencies": {
"end-of-stream": "^1.1.0", "end-of-stream": "^1.1.0",
"once": "^1.3.1" "once": "^1.3.1"
@@ -11970,9 +12002,9 @@
"dev": true "dev": true
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.13.0", "version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
}, },
"node_modules/unpipe": { "node_modules/unpipe": {
"version": "1.0.0", "version": "1.0.0",