mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-03 16:28:56 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04c228cd36 | ||
|
|
915f8e7700 | ||
|
|
218cea00b5 | ||
|
|
f38f5ea670 | ||
|
|
791d8beb06 | ||
|
|
57a9f3aca0 | ||
|
|
ae8bf187c8 | ||
|
|
88f2ab1f8e | ||
|
|
76f5136073 | ||
|
|
0f84cd8949 | ||
|
|
adfdf7606f | ||
|
|
ce671d4294 | ||
|
|
2e7cf7a9df | ||
|
|
f699d93e44 | ||
|
|
b7a22d94db | ||
|
|
3d854756d1 | ||
|
|
251e7058f1 | ||
|
|
368eb24217 | ||
|
|
f89ef54b82 | ||
|
|
b9d9f8772b | ||
|
|
f2ee03e850 | ||
|
|
92e59a0072 | ||
|
|
5394c0b520 | ||
|
|
e4a9c6f8c2 | ||
|
|
e559d037c3 | ||
|
|
c9909a0869 | ||
|
|
988fc1bbfd | ||
|
|
1831bbcd70 | ||
|
|
ced9bbdbd5 | ||
|
|
15d57d937b | ||
|
|
555979869d | ||
|
|
4e6477297e | ||
|
|
e9e06fc53b | ||
|
|
ecb2818589 | ||
|
|
1a1bc41283 | ||
|
|
4188caed33 | ||
|
|
ffc742b460 | ||
|
|
0c04cc6ce6 | ||
|
|
4e6dfed838 | ||
|
|
fdc0524ad2 | ||
|
|
096d5962a3 | ||
|
|
9ea36f94a7 | ||
|
|
2b66df992f | ||
|
|
ff3c31975e | ||
|
|
beaa5eca69 | ||
|
|
b98040faac | ||
|
|
fbbff0df61 | ||
|
|
07d5f393c0 | ||
|
|
8220b15a29 | ||
|
|
7cf638b2be | ||
|
|
30fb8f31e3 | ||
|
|
ec4d875547 | ||
|
|
43eee63df2 | ||
|
|
4caf9f598a | ||
|
|
107eaca8f0 | ||
|
|
cc11848346 |
Generated
+70
@@ -19,6 +19,7 @@
|
||||
"i18next": "^23.14.0",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next-http-backend": "^2.6.1",
|
||||
"jszip": "^3.10.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"nth-check": "^2.1.1",
|
||||
@@ -12748,6 +12749,12 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/immer": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
||||
@@ -16181,6 +16188,54 @@
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jwt-decode": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||
@@ -16286,6 +16341,15 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lilconfig": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
|
||||
@@ -22606,6 +22670,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"i18next": "^23.14.0",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next-http-backend": "^2.6.1",
|
||||
"jszip": "^3.10.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"nth-check": "^2.1.1",
|
||||
|
||||
@@ -151,7 +151,8 @@
|
||||
"Name": "Name",
|
||||
"Status": "Status",
|
||||
"created-date": "Created Date",
|
||||
"Type": "Type"
|
||||
"Type": "Type",
|
||||
"Logs": "Logs"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "These are documents you have started but have not finalized for sending.",
|
||||
@@ -572,9 +573,10 @@
|
||||
"pdf-request-file-5": "You can click 'Auto Sign All' to automatically sign at all the locations meant to be signed by you. Make sure that you review the document properly before you click this button .",
|
||||
"placeholder-sign-1": "Select a recipient from this list to add a place-holder where he is supposed to sign.The placeholder will appear in the same colour as the recipient name once you drop it on the document.",
|
||||
"placeholder-sign-2": "Clicking 'Add recipients' button will allow you to add more signers.",
|
||||
"placeholder-sign-3": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-4": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
|
||||
"placeholder-sign-5": "Clicking 'Send' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
|
||||
"placeholder-sign-3": "Click this button to add more recipients/signers to the document.",
|
||||
"placeholder-sign-4": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-5": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
|
||||
"placeholder-sign-6": "Clicking 'Send' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
|
||||
"report-1": "Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like ‘HR Manager’ and ‘New Employee’. Each time you use the template, you can assign the ‘New Employee’ role to different incoming staff members, while the ‘HR Manager’ role remains constant, facilitating a seamless onboarding process for each recruit. ",
|
||||
"redirect": "Click the 'Use' button to create a new document from an existing template.",
|
||||
"bulksend": "To quickly create and send multiple documents using an existing template, click the 'Quick Send' button.",
|
||||
@@ -638,6 +640,19 @@
|
||||
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminEmail}}.",
|
||||
"sent-this-month":"Sent this month",
|
||||
"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",
|
||||
"download-files":"Download files",
|
||||
"download-pdf":"Download Pdf",
|
||||
"pdf-certificate":"Download Pdf + Certificate",
|
||||
"document-logs":"Document logs"
|
||||
|
||||
}
|
||||
|
||||
@@ -136,7 +136,8 @@
|
||||
"Name": "Nom",
|
||||
"Status": "Statut",
|
||||
"created-date": "Date de création",
|
||||
"Type": "Saisir"
|
||||
"Type": "Saisir",
|
||||
"Logs": "Journaux"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "Signer",
|
||||
@@ -571,9 +572,10 @@
|
||||
"pdf-request-file-5": "Vous pouvez cliquer sur 'Signer automatiquement tout' pour signer automatiquement à tous les emplacements que vous êtes censé signer. Assurez-vous de lire correctement le document avant de cliquer sur ce bouton.",
|
||||
"placeholder-sign-1": "Sélectionnez un destinataire dans cette liste pour ajouter un espace réservé à l'endroit où il est censé signer. L'espace réservé apparaîtra de la même couleur que le nom du destinataire une fois que vous le déposerez sur le document.",
|
||||
"placeholder-sign-2": "Cliquer sur le bouton 'Ajouter des destinataires' vous permettra d'ajouter plus de signataires.",
|
||||
"placeholder-sign-3": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-4": "La zone de contenu PDF affiche déjà les espaces réservés existants du modèle. Pour votre commodité, ces espaces réservés correspondront à la couleur du nom du destinataire, ce qui les rendra facilement identifiables.",
|
||||
"placeholder-sign-5": "Cliquer sur 'Envoyer' enregistrera le document. À l'étape suivante, vous pourrez personnaliser les e-mails à envoyer aux destinataires ou copier les liens de signature et les partager vous-même avec les destinataires.",
|
||||
"placeholder-sign-3": "Cliquez sur ce bouton pour ajouter plus de destinataires/signataires au document",
|
||||
"placeholder-sign-4": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-5": "La zone de contenu PDF affiche déjà les espaces réservés existants du modèle. Pour votre commodité, ces espaces réservés correspondront à la couleur du nom du destinataire, ce qui les rendra facilement identifiables.",
|
||||
"placeholder-sign-6": "Cliquer sur 'Envoyer' enregistrera le document. À l'étape suivante, vous pourrez personnaliser les e-mails à envoyer aux destinataires ou copier les liens de signature et les partager vous-même avec les destinataires.",
|
||||
"report-1": "Cliquez sur le bouton 'Ajouter' pour créer un nouveau modèle. Les modèles sont des documents réutilisables conçus pour générer rapidement de nouveaux documents avec la même structure et différents signataires. Par exemple, un modèle RH pour l'intégration peut avoir des rôles prédéfinis tels que 'Responsable RH' et ' Nouvel employé'. Chaque fois que vous utilisez le modèle, vous pouvez attribuer le rôle 'Nouvel employé' à différents membres du personnel entrants, tandis que le rôle 'Responsable RH' reste constant, facilitant ainsi un processus d'intégration fluide pour chaque recrue",
|
||||
"redirect": "Cliquez sur le bouton 'Utiliser' pour créer un nouveau document à partir d'un modèle existant.",
|
||||
"bulksend": "Pour créer et envoyer rapidement plusieurs documents à l'aide d'un modèle existant, cliquez sur le bouton 'Envoi rapide'.",
|
||||
@@ -637,5 +639,19 @@
|
||||
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
|
||||
"sent-this-month":"envoyé ce mois-ci",
|
||||
"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",
|
||||
"download-files" :"Télécharger des fichiers",
|
||||
"download-pdf" :"Télécharger le PDF",
|
||||
"pdf-certificate" :"Télécharger Pdf + Certificat",
|
||||
"document-logs":"Journaux de documents"
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -20,6 +20,9 @@ const Header = ({ showSidebar }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { width } = useWindowSize();
|
||||
const current_notification_version = "1"; // Update this with each new feature release
|
||||
// Get the dismissed version from local storage
|
||||
const dismissedVersion = localStorage.getItem("notificationDismissedVersion");
|
||||
const username = localStorage.getItem("username") || "";
|
||||
const image = localStorage.getItem("profileImg") || dp;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@@ -30,9 +33,15 @@ const Header = ({ showSidebar }) => {
|
||||
);
|
||||
const [emailUsed, setEmailUsed] = useState(0);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
|
||||
const [showNotification, setShowNotification] = useState(
|
||||
(!dismissedVersion || dismissedVersion !== current_notification_version) &&
|
||||
true
|
||||
);
|
||||
const toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
if (width <= 768) {
|
||||
showSidebar();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
checkSubscription();
|
||||
@@ -58,6 +67,13 @@ const Header = ({ showSidebar }) => {
|
||||
} catch (err) {
|
||||
console.log("err in while fetching monthlyfreeEmails", err);
|
||||
}
|
||||
} else {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo?.logo) {
|
||||
setAppLogo(applogo?.logo);
|
||||
} else {
|
||||
setAppLogo(localStorage.getItem("appLogo") || "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,17 +131,34 @@ const Header = ({ showSidebar }) => {
|
||||
const handleMailUsed = () => {
|
||||
setIsModal(!isModal);
|
||||
};
|
||||
// Handle dismissing the notification
|
||||
const dismissNotification = () => {
|
||||
// Store the current version as dismissed in local storage
|
||||
localStorage.setItem(
|
||||
"notificationDismissedVersion",
|
||||
current_notification_version
|
||||
);
|
||||
setShowNotification(false);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{isEnableSubscription && (
|
||||
<div className="shadow py-1 text-center bg-[#CAE4FA] text-[14px] p-1">
|
||||
{t("header-news")} —
|
||||
<span
|
||||
className="cursor-pointer font-medium underline text-blue-800"
|
||||
onClick={() => navigate("/profile")}
|
||||
{isEnableSubscription && showNotification && (
|
||||
<div className="flex justify-between items-center shadow py-1 bg-[#CAE4FA] p-1">
|
||||
<div className="text-center text-[14px] ml-auto">
|
||||
{t("header-news")} —
|
||||
<span
|
||||
className="cursor-pointer font-medium underline text-blue-800"
|
||||
onClick={() => navigate("/profile")}
|
||||
>
|
||||
{" " + t("header-news-btn") + "."}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="ml-auto mr-1 cursor-pointer"
|
||||
onClick={() => dismissNotification()}
|
||||
>
|
||||
{" " + t("header-news-btn") + "."}
|
||||
</span>
|
||||
<i className="fa-light fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="op-navbar bg-base-100 shadow">
|
||||
|
||||
@@ -3,6 +3,8 @@ import { checkIsSubscribed, getFileName } from "../../constant/Utils";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
// import SelectFolder from "../../premitives/SelectFolder";
|
||||
|
||||
const EditTemplate = ({ template, onSuccess }) => {
|
||||
@@ -14,7 +16,8 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
Description: template?.Description || "",
|
||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
|
||||
AutomaticReminders: template?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: template?.RemindOnceInEvery || 5
|
||||
RemindOnceInEvery: template?.RemindOnceInEvery || 5,
|
||||
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false"
|
||||
});
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
useEffect(() => {
|
||||
@@ -41,7 +44,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
e.stopPropagation();
|
||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||
const AutoReminder = formData?.AutomaticReminders || false;
|
||||
|
||||
const IsEnableOTP = formData.IsEnableOTP === "true" ? true : false;
|
||||
let reminderDate = {};
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
||||
@@ -49,7 +52,12 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||
reminderDate = { NextReminderDate: ReminderDate };
|
||||
}
|
||||
const data = { ...formData, SendinOrder: isChecked, ...reminderDate };
|
||||
const data = {
|
||||
...formData,
|
||||
SendinOrder: isChecked,
|
||||
IsEnableOTP: IsEnableOTP,
|
||||
...reminderDate
|
||||
};
|
||||
onSuccess(data);
|
||||
};
|
||||
const handleAutoReminder = () => {
|
||||
@@ -184,6 +192,70 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
/>
|
||||
</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">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import axios from "axios";
|
||||
import { handleDownloadPdf, handleToPrint } from "../../constant/Utils";
|
||||
import { handleToPrint } from "../../constant/Utils";
|
||||
import { themeColor, emailRegex } from "../../constant/const";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
@@ -15,7 +15,8 @@ function EmailComponent({
|
||||
sender,
|
||||
setIsAlert,
|
||||
extUserId,
|
||||
activeMailAdapter
|
||||
activeMailAdapter,
|
||||
setIsDownloadModal
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
@@ -129,6 +130,11 @@ function EmailComponent({
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleClose = () => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{/* isEmail */}
|
||||
@@ -154,7 +160,9 @@ function EmailComponent({
|
||||
<div className="flex flex-row">
|
||||
{!isAndroid && (
|
||||
<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]"
|
||||
>
|
||||
<i className="fa-light fa-print" aria-hidden="true"></i>
|
||||
@@ -163,9 +171,10 @@ function EmailComponent({
|
||||
)}
|
||||
<button
|
||||
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
setIsDownloadModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
{t("download")}
|
||||
@@ -261,11 +270,7 @@ function EmailComponent({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
onClick={() => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}}
|
||||
onClick={() => handleClose()}
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
|
||||
@@ -39,7 +39,8 @@ function Header({
|
||||
clickOnZoomOut,
|
||||
handleRotationFun,
|
||||
isDisableRotate,
|
||||
templateId
|
||||
templateId,
|
||||
setIsDownloadModal
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const filterPrefill =
|
||||
@@ -94,9 +95,13 @@ function Header({
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
@@ -142,7 +147,7 @@ function Header({
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading)
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
@@ -209,57 +214,67 @@ function Header({
|
||||
sideOffset={5}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
pdfDetails,
|
||||
pdfDetails[0]?.URL,
|
||||
setIsDownloading
|
||||
)
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">{t("download")}</span>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">
|
||||
{t("download")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{!isDisableRotate && (
|
||||
<>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleRotationFun(90)}
|
||||
>
|
||||
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-right")}
|
||||
</span>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-right")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleRotationFun(-90)}
|
||||
>
|
||||
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-left")}
|
||||
</span>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-left")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => clickOnZoomIn()}
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
|
||||
<span className="font-[500]">{t("zoom-in")}</span>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">{t("zoom-in")}</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => clickOnZoomOut()}
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i>
|
||||
<span className="font-[500]">{t("zoom-out")}</span>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("zoom-out")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
@@ -342,7 +357,9 @@ function Header({
|
||||
alreadySign ? (
|
||||
<div className="flex flex-row">
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
@@ -370,9 +387,13 @@ function Header({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-download py-[3px]"
|
||||
@@ -427,11 +448,7 @@ function Header({
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
pdfDetails,
|
||||
pdfUrl,
|
||||
setIsDownloading
|
||||
)
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
|
||||
@@ -459,7 +476,9 @@ function Header({
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
@@ -469,9 +488,9 @@ function Header({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
setIsDownloadModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">{t("download")}</span>
|
||||
|
||||
@@ -31,8 +31,8 @@ function SignPad({
|
||||
const [isTab, setIsTab] = useState("draw");
|
||||
const [isSignImg, setIsSignImg] = useState("");
|
||||
const [signValue, setSignValue] = useState("");
|
||||
const [textWidth, setTextWidth] = useState(null);
|
||||
const [textHeight, setTextHeight] = useState(null);
|
||||
const [textWidth, setTextWidth] = useState(0);
|
||||
const [textHeight, setTextHeight] = useState(0);
|
||||
const [signatureType, setSignatureType] = useState("draw");
|
||||
const fontOptions = [
|
||||
{ value: "Fasthand" },
|
||||
@@ -51,7 +51,7 @@ function SignPad({
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
const currentUserName = jsonSender && jsonSender.name;
|
||||
const currentUserName = jsonSender && jsonSender?.name;
|
||||
|
||||
//function for clear signature image
|
||||
const handleClear = () => {
|
||||
@@ -137,7 +137,7 @@ function SignPad({
|
||||
(isTab === "draw" && isSignImg) ||
|
||||
(isTab === "image" && image) ||
|
||||
(isTab === "mysignature" && isDefaultSign) ||
|
||||
(isTab === "type" && textWidth)
|
||||
(isTab === "type" && signValue)
|
||||
? false
|
||||
: image
|
||||
? false
|
||||
@@ -169,10 +169,10 @@ function SignPad({
|
||||
}
|
||||
}
|
||||
|
||||
const trimmedName = currentUserName && currentUserName.trim();
|
||||
const trimmedName = currentUserName && currentUserName?.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName);
|
||||
setSignValue(userName || "");
|
||||
setFontSelect("Fasthand");
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -200,8 +200,10 @@ function SignPad({
|
||||
canvasRef.current.fromDataURL(isSignImg);
|
||||
}
|
||||
if (isTab === "type") {
|
||||
const trimmedName = signValue ? signValue.trim() : currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const trimmedName = signValue
|
||||
? signValue?.trim()
|
||||
: currentUserName?.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : signValue;
|
||||
setSignValue(userName);
|
||||
convertToImg(fontSelect, userName);
|
||||
|
||||
@@ -708,7 +708,8 @@ export const createDocument = async (
|
||||
Signers: signers,
|
||||
SendinOrder: Doc?.SendinOrder || false,
|
||||
AutomaticReminders: Doc?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5)
|
||||
RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5),
|
||||
IsEnableOTP: Doc?.IsEnableOTP || false
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -1741,9 +1742,7 @@ export const contactBook = async (objectId) => {
|
||||
|
||||
//function for getting document details from contract_Documents class
|
||||
export const contractDocument = async (documentId) => {
|
||||
const data = {
|
||||
docId: documentId
|
||||
};
|
||||
const data = { docId: documentId };
|
||||
const documentDeatils = await axios
|
||||
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
|
||||
headers: {
|
||||
@@ -2039,28 +2038,30 @@ export const fetchUrl = async (url, pdfName) => {
|
||||
console.error("Error downloading the file:", error);
|
||||
}
|
||||
};
|
||||
//handle download signed pdf
|
||||
export const handleDownloadPdf = async (
|
||||
pdfDetails,
|
||||
pdfUrl,
|
||||
setIsDownloading
|
||||
) => {
|
||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||
setIsDownloading("pdf");
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{ url: pdfUrl },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
export const getSignedUrl = async (pdfUrl, docId) => {
|
||||
//use only axios here due to public template sign
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{ url: pdfUrl, docId: docId || "" },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
);
|
||||
const url = axiosRes.data.result;
|
||||
}
|
||||
);
|
||||
const url = axiosRes.data.result;
|
||||
return url;
|
||||
};
|
||||
//handle download signed pdf
|
||||
export const handleDownloadPdf = async (pdfDetails, setIsDownloading) => {
|
||||
const pdfName = pdfDetails[0] && pdfDetails[0]?.Name;
|
||||
const pdfUrl = pdfDetails?.[0]?.SignedUrl || pdfDetails?.[0]?.URL;
|
||||
setIsDownloading("pdf");
|
||||
const docId = !pdfDetails?.[0]?.IsEnableOTP ? pdfDetails?.[0]?.objectId : "";
|
||||
try {
|
||||
const url = await getSignedUrl(pdfUrl, docId);
|
||||
await fetchUrl(url, pdfName);
|
||||
setIsDownloading("");
|
||||
} catch (err) {
|
||||
@@ -2075,16 +2076,22 @@ export const sanitizeFileName = (pdfName) => {
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
//function for print digital sign pdf
|
||||
export const handleToPrint = async (event, pdfUrl, setIsDownloading) => {
|
||||
export const handleToPrint = async (
|
||||
event,
|
||||
pdfUrl,
|
||||
setIsDownloading,
|
||||
pdfDetails
|
||||
) => {
|
||||
event.preventDefault();
|
||||
setIsDownloading("pdf");
|
||||
const docId = !pdfDetails?.[0]?.IsEnableOTP ? pdfDetails?.[0]?.objectId : "";
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
//`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
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{ url: pdfUrl },
|
||||
{ url: pdfUrl, docId: docId },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
@@ -2124,13 +2131,18 @@ export const handleToPrint = async (event, pdfUrl, setIsDownloading) => {
|
||||
//handle download signed pdf
|
||||
export const handleDownloadCertificate = async (
|
||||
pdfDetails,
|
||||
setIsDownloading
|
||||
setIsDownloading,
|
||||
isZip
|
||||
) => {
|
||||
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
||||
try {
|
||||
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
||||
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
if (isZip) {
|
||||
return certificateUrl;
|
||||
} else {
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in download in certificate", err);
|
||||
}
|
||||
@@ -2156,7 +2168,13 @@ export const handleDownloadCertificate = async (
|
||||
if (doc?.CertificateUrl) {
|
||||
await fetch(doc?.CertificateUrl);
|
||||
const certificateUrl = doc?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
if (isZip) {
|
||||
setIsDownloading("");
|
||||
return certificateUrl;
|
||||
} else {
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
}
|
||||
|
||||
setIsDownloading("");
|
||||
} else {
|
||||
setIsDownloading("certificate");
|
||||
@@ -2456,3 +2474,45 @@ export const handleRotateWarning = (signerPos, pageNumber) => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// `generateTitleFromFilename` to generate Title of document from file name
|
||||
export function generateTitleFromFilename(filename) {
|
||||
try {
|
||||
// Step 1: Trim whitespace
|
||||
let title = filename.trim();
|
||||
|
||||
// Step 2: Remove the file extension (everything after the last '.')
|
||||
const lastDotIndex = title.lastIndexOf(".");
|
||||
if (lastDotIndex > 0) {
|
||||
title = title.substring(0, lastDotIndex);
|
||||
}
|
||||
|
||||
// Step 3: Replace special characters (except Unicode letters, digits, spaces, and hyphens)
|
||||
title = title.replace(/[^\p{L}\p{N}\s-]/gu, " ");
|
||||
|
||||
// Step 4: Replace multiple spaces with a single space
|
||||
title = title.replace(/\s+/g, " ");
|
||||
|
||||
// Step 5: Capitalize first letter of each word (Title Case), handling Unicode characters
|
||||
title = title.replace(
|
||||
/\p{L}\S*/gu,
|
||||
(txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
||||
);
|
||||
|
||||
// Step 6: Restrict length of title (optional, let's say 100 characters)
|
||||
if (title.length > 100) {
|
||||
title = title.substring(0, 100).trim();
|
||||
}
|
||||
|
||||
// Step 7: Handle empty or invalid title by falling back to "Untitled Document"
|
||||
if (!title || title.length === 0) {
|
||||
return "Untitled Document";
|
||||
}
|
||||
|
||||
return title;
|
||||
} catch (error) {
|
||||
// Handle unexpected errors gracefully by returning a default title
|
||||
console.error("Error generating title from filename:", error);
|
||||
return "Untitled Document";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,15 @@ export default function reportJson(id) {
|
||||
"Owner",
|
||||
"Signers"
|
||||
];
|
||||
const iphead = [
|
||||
"Sr.No",
|
||||
"Title",
|
||||
"Note",
|
||||
"Folder",
|
||||
"File",
|
||||
"Status",
|
||||
"Signers"
|
||||
];
|
||||
const contactbook = ["Sr.No", "Name", "Email", "Phone"];
|
||||
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
||||
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
|
||||
@@ -63,7 +72,7 @@ export default function reportJson(id) {
|
||||
case "1MwEuxLEkF":
|
||||
return {
|
||||
reportName: "In-progress documents",
|
||||
heading: head,
|
||||
heading: iphead,
|
||||
actions: [
|
||||
{
|
||||
btnId: "8901",
|
||||
|
||||
@@ -74,7 +74,7 @@ const DebugPdf = () => {
|
||||
|
||||
const inferPdfType = async (pdf) => {
|
||||
try {
|
||||
const firstPage = await pdf.getPage(2);
|
||||
const firstPage = await pdf.getPage(pdf?.numPages > 1 ? 2 : 1);
|
||||
const scale = 1;
|
||||
const { width, height } = firstPage.getViewport({ scale });
|
||||
setPdfDimension({ width: width, height: height });
|
||||
|
||||
+232
-111
@@ -9,7 +9,12 @@ import SignersInput from "../components/shared/fields/SignersInput";
|
||||
import Title from "../components/Title";
|
||||
import PageNotFound from "./PageNotFound";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import { checkIsSubscribed, getFileName, toDataUrl } from "../constant/Utils";
|
||||
import {
|
||||
checkIsSubscribed,
|
||||
generateTitleFromFilename,
|
||||
getFileName,
|
||||
toDataUrl
|
||||
} from "../constant/Utils";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import axios from "axios";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
@@ -61,7 +66,8 @@ const Forms = (props) => {
|
||||
password: "",
|
||||
file: "",
|
||||
remindOnceInEvery: 5,
|
||||
autoreminder: false
|
||||
autoreminder: false,
|
||||
IsEnableOTP: "false"
|
||||
});
|
||||
const [fileupload, setFileUpload] = useState("");
|
||||
const [fileload, setfileload] = useState(false);
|
||||
@@ -74,6 +80,7 @@ const Forms = (props) => {
|
||||
const [isDecrypting, setIsDecrypting] = useState(false);
|
||||
const [isCorrectPass, setIsCorrectPass] = useState(true);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isAdvanceOpt, setIsAdvanceOpt] = useState(false);
|
||||
const handleStrInput = (e) => {
|
||||
setIsCorrectPass(true);
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
@@ -137,12 +144,11 @@ const Forms = (props) => {
|
||||
} catch (err) {
|
||||
console.log("err in sending posthog encryptedpdf", err);
|
||||
}
|
||||
// console.log("err ", err);
|
||||
try {
|
||||
setIsDecrypting(true);
|
||||
const size = files?.[0].size;
|
||||
const name = generatePdfName(16);
|
||||
const url = "https://ai.nxglabs.in/decryptpdf"; //"https://ai.nxglabs.in/decryptpdf"; //
|
||||
const url = "https://ai.nxglabs.in/decryptpdf"; //
|
||||
let formData = new FormData();
|
||||
formData.append("file", files[0]);
|
||||
formData.append("password", "");
|
||||
@@ -185,6 +191,9 @@ const Forms = (props) => {
|
||||
if (parseFile.url()) {
|
||||
setFileUpload(parseFile.url());
|
||||
setfileload(false);
|
||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
SaveFileSize(size, response.url(), tenantId);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, parseFile.url(), tenantId);
|
||||
return parseFile.url();
|
||||
@@ -258,6 +267,8 @@ const Forms = (props) => {
|
||||
setfileload(false);
|
||||
if (response.url()) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
SaveFileSize(size, response.url(), tenantId);
|
||||
return response.url();
|
||||
}
|
||||
@@ -285,12 +296,17 @@ const Forms = (props) => {
|
||||
if (res.data) {
|
||||
setFileUpload(res.data.url);
|
||||
setfileload(false);
|
||||
const title = generateTitleFromFilename(files?.[0]?.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
}
|
||||
} catch (err) {
|
||||
e.target.value = "";
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
console.log("err in libreconverter ", err);
|
||||
alert(
|
||||
"We are currently experiencing some issues with processing DOCX files. Please upload the PDF version or contact us on support@opensignlabs.com"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,6 +345,8 @@ const Forms = (props) => {
|
||||
setfileload(false);
|
||||
if (response.url()) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const title = generateTitleFromFilename(file.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
SaveFileSize(size, response.url(), tenantId);
|
||||
return response.url();
|
||||
}
|
||||
@@ -366,9 +384,10 @@ const Forms = (props) => {
|
||||
});
|
||||
setFileUpload(response.url());
|
||||
setfileload(false);
|
||||
|
||||
if (response.url()) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
const title = generateTitleFromFilename(file.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
SaveFileSize(size, response.url(), tenantId);
|
||||
return response.url();
|
||||
}
|
||||
@@ -402,6 +421,13 @@ const Forms = (props) => {
|
||||
object.set("SendinOrder", isChecked);
|
||||
object.set("AutomaticReminders", formData.autoreminder);
|
||||
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("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
||||
@@ -428,7 +454,17 @@ const Forms = (props) => {
|
||||
setFormData({
|
||||
Name: "",
|
||||
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("");
|
||||
setpercentage(0);
|
||||
@@ -477,7 +513,8 @@ const Forms = (props) => {
|
||||
password: "",
|
||||
file: "",
|
||||
remindOnceInEvery: 5,
|
||||
autoreminder: false
|
||||
autoreminder: false,
|
||||
IsEnableOTP: "false"
|
||||
});
|
||||
setFileUpload("");
|
||||
setpercentage(0);
|
||||
@@ -529,6 +566,8 @@ const Forms = (props) => {
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
setFileUpload(parseFile.url());
|
||||
setfileload(false);
|
||||
const title = generateTitleFromFilename(formData?.file?.name);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, parseFile.url(), tenantId);
|
||||
return parseFile.url();
|
||||
@@ -641,7 +680,7 @@ const Forms = (props) => {
|
||||
)}
|
||||
<div className="text-xs">
|
||||
<label className="block">
|
||||
{`${t("report-heading.File")} (${t("file-type")} ${
|
||||
{`${t("report-heading.File")} (${t("file-type")}${
|
||||
isEnableSubscription ? ", docx)" : ")"
|
||||
}`}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
@@ -754,120 +793,73 @@ const Forms = (props) => {
|
||||
isReset={isReset}
|
||||
/>
|
||||
)}
|
||||
{props.title === "Request Signatures" && (
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
{t("time-to-complete")}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
{t("send-in-order")}
|
||||
<a data-tooltip-id="sendInOrder-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>
|
||||
<Tooltip id="sendInOrder-tooltip" className="z-50">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("send-in-order")}</p>
|
||||
<p>{t("send-in-order-help.p1")}</p>
|
||||
<p className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">{t("yes")}: </span>
|
||||
<span>{t("send-in-order-help.p2")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("send-in-order-help.p3")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>{t("send-in-order-help.p4")}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</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 className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
{t("send-in-order")}
|
||||
<a data-tooltip-id="sendInOrder-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>
|
||||
<Tooltip id="sendInOrder-tooltip" className="z-50">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("send-in-order")}</p>
|
||||
<p>{t("send-in-order-help.p1")}</p>
|
||||
<p className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">{t("yes")}:</span>
|
||||
<span>{t("send-in-order-help.p2")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("send-in-order-help.p3")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>{t("send-in-order-help.p4")}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
{isEnableSubscription && (
|
||||
<div className="text-xs mt-2">
|
||||
<span
|
||||
className={
|
||||
isSubscribe
|
||||
? "font-semibold"
|
||||
: "font-semibold text-gray-300"
|
||||
}
|
||||
>
|
||||
{t("auto-reminder")}
|
||||
{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe
|
||||
? "cursor-pointer "
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
|
||||
checked={formData.autoreminder}
|
||||
onChange={handleAutoReminder}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{formData?.autoreminder === true && (
|
||||
{isAdvanceOpt && (
|
||||
<div className="overflow-y-auto z-[500] transition-all">
|
||||
{props.title === "Request Signatures" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
{t("remind-once")}
|
||||
{t("time-to-complete")}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={formData.remindOnceInEvery}
|
||||
name="remindOnceInEvery"
|
||||
name="TimeToCompleteDays"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onChange={handleStrInput}
|
||||
value={formData.TimeToCompleteDays}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
@@ -876,7 +868,136 @@ const Forms = (props) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<>
|
||||
{isEnableSubscription && (
|
||||
<div className="text-xs mt-2">
|
||||
<span className={isSubscribe ? "" : " text-gray-300"}>
|
||||
{t("auto-reminder")}{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe
|
||||
? "cursor-pointer "
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0 mt-1.5`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
|
||||
checked={formData.autoreminder}
|
||||
onChange={handleAutoReminder}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{formData?.autoreminder === true && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
{t("remind-once")}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={formData.remindOnceInEvery}
|
||||
name="remindOnceInEvery"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onChange={handleStrInput}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
</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">
|
||||
<button
|
||||
|
||||
@@ -28,6 +28,28 @@ function GuestLogin() {
|
||||
const [contactId, setContactId] = useState(contactBookId);
|
||||
const [sendmail, setSendmail] = useState();
|
||||
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(() => {
|
||||
handleServerUrl();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -70,13 +92,14 @@ function GuestLogin() {
|
||||
"linkcontacttodoc",
|
||||
params
|
||||
);
|
||||
// console.log("linkContactRes ", linkContactRes);
|
||||
setContactId(linkContactRes?.contactId);
|
||||
await navigateToDoc(checkSplit[0], linkContactRes?.contactId);
|
||||
} catch (err) {
|
||||
console.log("Err in link ext contact", err);
|
||||
}
|
||||
} else {
|
||||
setContactId(checkSplit[2]);
|
||||
await navigateToDoc(checkSplit[0], checkSplit[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,15 +137,12 @@ function GuestLogin() {
|
||||
if (OTP) {
|
||||
setLoading(true);
|
||||
try {
|
||||
let url = `${serverUrl}functions/AuthLoginAsMail/`;
|
||||
let url = `${serverUrl}functions/AuthLoginAsMail`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseId
|
||||
};
|
||||
let body = {
|
||||
email: email,
|
||||
otp: OTP
|
||||
};
|
||||
let body = { email: email, otp: OTP };
|
||||
let user = await axios.post(url, body, { headers: headers });
|
||||
if (user.data.result === "Invalid Otp") {
|
||||
alert(t("invalid-otp"));
|
||||
@@ -140,7 +160,6 @@ function GuestLogin() {
|
||||
`Parse/${parseId}/currentUser`,
|
||||
JSON.stringify(_user)
|
||||
);
|
||||
// console.log("contractUserDetails ", contractUserDetails);
|
||||
if (contractUserDetails && contractUserDetails.length > 0) {
|
||||
localStorage.setItem(
|
||||
"Extand_Class",
|
||||
@@ -174,10 +193,15 @@ function GuestLogin() {
|
||||
try {
|
||||
setLoading(true);
|
||||
const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params);
|
||||
// console.log("linkContactRes ", linkContactRes);
|
||||
setContactId(linkContactRes.contactId);
|
||||
setEnterOtp(true);
|
||||
await SendOtp();
|
||||
const IsEnableOTP = await navigateToDoc(
|
||||
documentId,
|
||||
linkContactRes.contactId
|
||||
);
|
||||
if (!IsEnableOTP) {
|
||||
setEnterOtp(true);
|
||||
await SendOtp();
|
||||
}
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
@@ -208,7 +232,7 @@ function GuestLogin() {
|
||||
<div className="w-full md:w-[50%] text-base-content">
|
||||
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
||||
<legend className="text-[12px] text-[#878787] mt-2 mb-1">
|
||||
{t("guest-email-alert")}
|
||||
{t("get-otp-alert")}
|
||||
</legend>
|
||||
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
|
||||
<input
|
||||
@@ -236,7 +260,7 @@ function GuestLogin() {
|
||||
>
|
||||
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
||||
<legend className="text-[12px] text-[#878787] mt-2">
|
||||
{t("get-verification-code-2")}
|
||||
{t("guest-email-alert")}
|
||||
</legend>
|
||||
<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>
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
contractUsers,
|
||||
handleSendOTP,
|
||||
contactBook,
|
||||
handleDownloadPdf,
|
||||
handleToPrint,
|
||||
handleDownloadCertificate,
|
||||
getDefaultSignature,
|
||||
@@ -36,21 +35,22 @@ import {
|
||||
onClickZoomOut,
|
||||
fetchUrl
|
||||
} from "../constant/Utils";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import PdfDeclineModal from "../primitives/PdfDeclineModal";
|
||||
import Title from "../components/Title";
|
||||
import DefaultSignature from "../components/pdf/DefaultSignature";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useSelector } from "react-redux";
|
||||
import SignerListComponent from "../components/pdf/SignerListComponent";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import DownloadPdfZip from "../primitives/DownloadPdfZip";
|
||||
import Loader from "../primitives/Loader";
|
||||
import PdfDeclineModal from "../primitives/PdfDeclineModal";
|
||||
|
||||
function PdfRequestFiles(props) {
|
||||
const { t } = useTranslation();
|
||||
@@ -136,29 +136,30 @@ function PdfRequestFiles(props) {
|
||||
const [plancode, setPlanCode] = useState("");
|
||||
const isHeader = useSelector((state) => state.showHeader);
|
||||
const divRef = useRef(null);
|
||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||
|
||||
const isMobile = window.innerWidth < 767;
|
||||
|
||||
let isGuestSignFlow = false;
|
||||
let sendmail;
|
||||
let getDocId = "";
|
||||
const route = !props.templateId && window.location.pathname; //'/load/recipientSignPdf/TOAVuhXbfw/fPAKdK1qgX'
|
||||
//window.location.search = ?sendmail=false
|
||||
const getQuery =
|
||||
!props.templateId &&
|
||||
window.location?.search &&
|
||||
window.location?.search?.split("?"); //['','sendmail=false']
|
||||
|
||||
//'sendmail=false'
|
||||
let contactBookId = "";
|
||||
const route = !props.templateId && window.location.pathname;
|
||||
const getQuery = !props.templateId && window.location?.search?.split("?"); //['','sendmail=false']
|
||||
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;
|
||||
getDocId = checkSplit[3];
|
||||
getDocId = routeId[3];
|
||||
contactBookId = routeId[4];
|
||||
} 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;
|
||||
useEffect(() => {
|
||||
@@ -372,9 +373,10 @@ function PdfRequestFiles(props) {
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
// `currUserId` will be contactId or extUserId
|
||||
let currUserId;
|
||||
//getting document details
|
||||
const documentData = await contractDocument(documentId || docId);
|
||||
const documentData = await contractDocument(docId);
|
||||
if (documentData && documentData.length > 0) {
|
||||
const url =
|
||||
documentData[0] &&
|
||||
@@ -399,13 +401,13 @@ function PdfRequestFiles(props) {
|
||||
const expireUpdateDate = new Date(expireDate).getTime();
|
||||
const currDate = new Date().getTime();
|
||||
const getSigners = documentData[0].Signers;
|
||||
const getCurrentSigner =
|
||||
getSigners &&
|
||||
getSigners.filter(
|
||||
(data) => data.UserId.objectId === jsonSender?.objectId
|
||||
);
|
||||
const getCurrentSigner = getSigners?.find(
|
||||
(data) => data.UserId.objectId === jsonSender?.objectId
|
||||
);
|
||||
|
||||
currUserId = getCurrentSigner[0] ? getCurrentSigner[0].objectId : "";
|
||||
currUserId = getCurrentSigner?.objectId
|
||||
? getCurrentSigner.objectId
|
||||
: contactBookId || "";
|
||||
if (isEnableSubscription) {
|
||||
await checkIsSubscribed(
|
||||
documentData[0]?.ExtUserPtr?.objectId,
|
||||
@@ -428,10 +430,7 @@ function PdfRequestFiles(props) {
|
||||
setIsCelebration(true);
|
||||
setTimeout(() => setIsCelebration(false), 5000);
|
||||
} else if (declined) {
|
||||
const currentDecline = {
|
||||
currnt: "another",
|
||||
isDeclined: true
|
||||
};
|
||||
const currentDecline = { currnt: "another", isDeclined: true };
|
||||
setIsDecline(currentDecline);
|
||||
} else if (currDate > expireUpdateDate) {
|
||||
const expireDateFormat = moment(new Date(expireDate)).format(
|
||||
@@ -457,36 +456,24 @@ function PdfRequestFiles(props) {
|
||||
}
|
||||
}
|
||||
}
|
||||
const audittrailData =
|
||||
documentData[0].AuditTrail &&
|
||||
documentData[0].AuditTrail.length > 0 &&
|
||||
documentData[0].AuditTrail.filter(
|
||||
(data) => data.Activity === "Signed"
|
||||
);
|
||||
|
||||
const audittrailData = documentData?.[0]?.AuditTrail?.filter(
|
||||
(data) => data.Activity === "Signed"
|
||||
);
|
||||
const checkAlreadySign =
|
||||
documentData[0].AuditTrail &&
|
||||
documentData[0].AuditTrail.length > 0 &&
|
||||
documentData[0].AuditTrail.filter(
|
||||
documentData?.[0]?.AuditTrail?.some(
|
||||
(data) =>
|
||||
data.UserPtr?.objectId === currUserId &&
|
||||
data?.UserPtr?.objectId === currUserId &&
|
||||
data.Activity === "Signed"
|
||||
);
|
||||
if (
|
||||
checkAlreadySign &&
|
||||
checkAlreadySign[0] &&
|
||||
checkAlreadySign.length > 0
|
||||
) {
|
||||
) || false;
|
||||
if (checkAlreadySign) {
|
||||
setAlreadySign(true);
|
||||
} else {
|
||||
const obj = documentData?.[0];
|
||||
setSendInOrder(obj?.SendinOrder || false);
|
||||
if (
|
||||
obj &&
|
||||
obj.Signers &&
|
||||
obj.Signers.length > 0 &&
|
||||
obj.Placeholders &&
|
||||
obj.Placeholders.length > 0
|
||||
obj?.Signers?.length > 0 &&
|
||||
obj?.Placeholders?.length > 0
|
||||
) {
|
||||
const params = {
|
||||
event: "viewed",
|
||||
@@ -502,7 +489,10 @@ function PdfRequestFiles(props) {
|
||||
email: x?.Email,
|
||||
phone: x?.Phone
|
||||
})),
|
||||
viewedBy: jsonSender.email,
|
||||
viewedBy:
|
||||
documentData?.[0].Signers?.find(
|
||||
(x) => x.objectId === currUserId
|
||||
)?.Email || jsonSender?.email,
|
||||
viewedAt: new Date(),
|
||||
createdAt: documentData?.[0].createdAt
|
||||
}
|
||||
@@ -583,40 +573,51 @@ 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
|
||||
//then stop to display tour message
|
||||
if (
|
||||
(checkAlreadySign &&
|
||||
checkAlreadySign[0] &&
|
||||
checkAlreadySign.length > 0) ||
|
||||
checkAlreadySign ||
|
||||
!currUserId ||
|
||||
declined ||
|
||||
currDate > expireUpdateDate
|
||||
) {
|
||||
setRequestSignTour(true);
|
||||
} else {
|
||||
//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
|
||||
const res = await contractUsers();
|
||||
if (res === "Error: Something went wrong!") {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
} else if (res[0] && res?.length) {
|
||||
setContractName("_Users");
|
||||
currUserId = res[0].objectId;
|
||||
setSignerUserId(currUserId);
|
||||
const tourData = res[0].TourStatus && res[0].TourStatus;
|
||||
if (tourData && tourData.length > 0) {
|
||||
const checkTourRequest = tourData.filter(
|
||||
(data) => data?.requestSign
|
||||
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")
|
||||
}
|
||||
}
|
||||
);
|
||||
setTourStatus(tourData);
|
||||
setRequestSignTour(checkTourRequest[0]?.requestSign || false);
|
||||
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 if (res?.length === 0) {
|
||||
const res = await contactBook(currUserId);
|
||||
} else {
|
||||
//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 status
|
||||
const res = await contractUsers();
|
||||
if (res === "Error: Something went wrong!") {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
} else if (res[0] && res.length) {
|
||||
setContractName("_Contactbook");
|
||||
const objectId = res[0].objectId;
|
||||
setSignerUserId(objectId);
|
||||
} else if (res[0] && res?.length) {
|
||||
setContractName("_Users");
|
||||
currUserId = res[0].objectId;
|
||||
setSignerUserId(currUserId);
|
||||
const tourData = res[0].TourStatus && res[0].TourStatus;
|
||||
if (tourData && tourData.length > 0) {
|
||||
const checkTourRequest = tourData.filter(
|
||||
@@ -625,8 +626,25 @@ function PdfRequestFiles(props) {
|
||||
setTourStatus(tourData);
|
||||
setRequestSignTour(checkTourRequest[0]?.requestSign || false);
|
||||
}
|
||||
} else if (res.length === 0) {
|
||||
setHandleError(t("user-not-exist"));
|
||||
} else if (res?.length === 0) {
|
||||
const res = await contactBook(currUserId);
|
||||
if (res === "Error: Something went wrong!") {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
} else if (res[0] && res.length) {
|
||||
setContractName("_Contactbook");
|
||||
const objectId = res[0].objectId;
|
||||
setSignerUserId(objectId);
|
||||
const tourData = res[0].TourStatus && res[0].TourStatus;
|
||||
if (tourData && tourData.length > 0) {
|
||||
const checkTourRequest = tourData.filter(
|
||||
(data) => data?.requestSign
|
||||
);
|
||||
setTourStatus(tourData);
|
||||
setRequestSignTour(checkTourRequest[0]?.requestSign || false);
|
||||
}
|
||||
} else if (res.length === 0) {
|
||||
setHandleError(t("user-not-exist"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -643,12 +661,12 @@ function PdfRequestFiles(props) {
|
||||
setIsUiLoading({ isLoad: false });
|
||||
}
|
||||
//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") {
|
||||
setDefaultSignImg(defaultSignRes?.res?.defaultSignature);
|
||||
setMyInitial(defaultSignRes?.res?.defaultInitial);
|
||||
} else if (defaultSignRes?.status === "error") {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
const sign = defaultSignRes?.res?.defaultSignature || "";
|
||||
const initials = defaultSignRes?.res?.defaultInitial || "";
|
||||
setDefaultSignImg(sign);
|
||||
setMyInitial(initials);
|
||||
}
|
||||
setIsLoading({ isLoad: false });
|
||||
} catch (err) {
|
||||
@@ -664,20 +682,30 @@ function PdfRequestFiles(props) {
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
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 (!currentUser?.emailVerified) {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const getUser = await userQuery.get(currentUser?.objectId, {
|
||||
sessionToken: currentUser?.sessionToken
|
||||
});
|
||||
if (getUser) {
|
||||
currentUser = JSON.parse(JSON.stringify(getUser));
|
||||
if (isEnableOTP) {
|
||||
try {
|
||||
if (!currentUser?.emailVerified) {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const getUser = await userQuery.get(currentUser?.objectId, {
|
||||
sessionToken:
|
||||
currentUser?.sessionToken || localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (getUser) {
|
||||
currentUser = JSON.parse(JSON.stringify(getUser));
|
||||
}
|
||||
}
|
||||
isEmailVerified = currentUser?.emailVerified;
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
} catch (err) {
|
||||
console.log("err in get email verification ", err);
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
let isEmailVerified = currentUser?.emailVerified;
|
||||
//check if isEmailVerified then go on next step
|
||||
if (isEmailVerified) {
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
if (!isEnableOTP || isEmailVerified) {
|
||||
try {
|
||||
const checkUser = signerPos.filter(
|
||||
(data) => data.signerObjId === signerObjectId
|
||||
@@ -858,17 +886,9 @@ function PdfRequestFiles(props) {
|
||||
// console.log("pdfte", pdfBytes);
|
||||
//get ExistUserPtr object id of user class to get tenantDetails
|
||||
const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId;
|
||||
//get ExistUserPtr email to get userDetails
|
||||
const res = await contractUsers();
|
||||
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;
|
||||
}
|
||||
let activeMailAdapter =
|
||||
pdfDetails?.[0]?.ExtUserPtr?.active_mail_adapter;
|
||||
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
const resSign = await signPdfFun(
|
||||
pdfBytes,
|
||||
@@ -880,13 +900,13 @@ function PdfRequestFiles(props) {
|
||||
widgets
|
||||
);
|
||||
if (resSign && resSign.status === "success") {
|
||||
setPdfUrl(res.data);
|
||||
setPdfUrl(resSign.data);
|
||||
setIsSigned(true);
|
||||
setSignedSigners([]);
|
||||
setUnSignedSigners([]);
|
||||
getDocumentDetails(true);
|
||||
const index = pdfDetails?.[0].Signers.findIndex(
|
||||
(x) => x.Email === currentUser?.email
|
||||
getDocumentDetails(documentId, true);
|
||||
const index = pdfDetails?.[0]?.Signers.findIndex(
|
||||
(x) => x.objectId === signerObjectId
|
||||
);
|
||||
const newIndex = index + 1;
|
||||
const usermail = {
|
||||
@@ -1054,21 +1074,6 @@ function PdfRequestFiles(props) {
|
||||
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 +1221,26 @@ function PdfRequestFiles(props) {
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
setIsDecline({ isDeclined: false });
|
||||
const data = {
|
||||
IsDeclined: true,
|
||||
DeclineReason: reason,
|
||||
DeclineBy: {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: jsonSender?.objectId
|
||||
}
|
||||
};
|
||||
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
|
||||
.put(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Document/${documentId}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
.post(`${localStorage.getItem("baseUrl")}functions/declinedoc`, params, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
)
|
||||
})
|
||||
.then(async (result) => {
|
||||
const res = result.data;
|
||||
if (res) {
|
||||
@@ -1260,7 +1260,8 @@ function PdfRequestFiles(props) {
|
||||
email: x?.Email,
|
||||
phone: x?.Phone
|
||||
})),
|
||||
declinedBy: jsonSender.email,
|
||||
declinedBy: email,
|
||||
declinedReason: reason,
|
||||
declinedAt: new Date(),
|
||||
createdAt: pdfDetails?.[0].createdAt
|
||||
}
|
||||
@@ -1314,38 +1315,58 @@ function PdfRequestFiles(props) {
|
||||
const closeRequestSignTour = async () => {
|
||||
setRequestSignTour(true);
|
||||
if (isDontShow) {
|
||||
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 });
|
||||
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 {
|
||||
updatedTourStatus = [{ requestSign: true }];
|
||||
}
|
||||
try {
|
||||
await axios.put(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts${contractName}/${signerUserId}`,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
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 });
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
console.log("update tour messages error", e);
|
||||
} else {
|
||||
updatedTourStatus = [{ requestSign: true }];
|
||||
}
|
||||
try {
|
||||
await axios.put(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts${contractName}/${signerUserId}`,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
console.log("update tour messages error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1638,6 +1659,8 @@ function PdfRequestFiles(props) {
|
||||
<Confetti
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
recycle={false} // Prevents confetti from repeating
|
||||
gravity={0.1} // Adjust the gravity to control the speed
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -1650,7 +1673,8 @@ function PdfRequestFiles(props) {
|
||||
: "auto"
|
||||
}}
|
||||
className={`${
|
||||
props.templateId && "m-1 border-[0.5px] border-gray-300"
|
||||
(props.templateId || isGuestSignFlow) &&
|
||||
"mx-2 border-[0.5px] border-gray-300"
|
||||
} relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300`}
|
||||
>
|
||||
{!requestSignTour &&
|
||||
@@ -1691,11 +1715,7 @@ function PdfRequestFiles(props) {
|
||||
{/* this modal is used to show decline alert */}
|
||||
<PdfDeclineModal
|
||||
show={isDecline.isDeclined}
|
||||
headMsg={
|
||||
pdfDetails[0]?.IsDeclined
|
||||
? t("document-declined")
|
||||
: t("document-decline")
|
||||
}
|
||||
headMsg={t("document-declined")}
|
||||
bodyMssg={
|
||||
isDecline.currnt === "Sure"
|
||||
? t("decline-alert-1")
|
||||
@@ -1938,7 +1958,12 @@ function PdfRequestFiles(props) {
|
||||
<div className="flex mt-4 gap-1 px-[15px]">
|
||||
<button
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading)
|
||||
handleToPrint(
|
||||
e,
|
||||
pdfUrl,
|
||||
setIsDownloading,
|
||||
pdfDetails
|
||||
)
|
||||
}
|
||||
type="button"
|
||||
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral"
|
||||
@@ -1972,13 +1997,13 @@ function PdfRequestFiles(props) {
|
||||
<button
|
||||
type="button"
|
||||
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-primary"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
pdfDetails,
|
||||
pdfUrl,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
onClick={() => {
|
||||
setIsCompleted((prev) => ({
|
||||
...prev,
|
||||
isModal: false
|
||||
}));
|
||||
setIsDownloadModal(true);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-download"
|
||||
@@ -2058,6 +2083,7 @@ function PdfRequestFiles(props) {
|
||||
clickOnZoomOut={clickOnZoomOut}
|
||||
isDisableRotate={true}
|
||||
templateId={props.templateId}
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
/>
|
||||
|
||||
<div ref={divRef} data-tut="pdfArea" className="h-[95%]">
|
||||
@@ -2184,6 +2210,12 @@ function PdfRequestFiles(props) {
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<DownloadPdfZip
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
isDownloadModal={isDownloadModal}
|
||||
pdfDetails={pdfDetails}
|
||||
isDocId={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</DndProvider>
|
||||
|
||||
@@ -11,8 +11,6 @@ import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import WidgetComponent from "../components/pdf/WidgetComponent";
|
||||
import Tour from "reactour";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import SignerListPlace from "../components/pdf/SignerListPlace";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import { RWebShare } from "react-web-share";
|
||||
@@ -45,23 +43,25 @@ import {
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
import Title from "../components/Title";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import { EmailBody } from "../components/pdf/EmailBody";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useSelector } from "react-redux";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import LottieWithLoader from "../primitives/DotLottieReact";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RotateAlert from "../components/RotateAlert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
import QuotaCard from "../primitives/QuotaCard";
|
||||
import LottieWithLoader from "../primitives/DotLottieReact";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
|
||||
function PlaceHolderSign() {
|
||||
const { t } = useTranslation();
|
||||
@@ -1250,7 +1250,7 @@ function PlaceHolderSign() {
|
||||
selector: '[data-tut="addWidgets"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={t("tour-mssg.placeholder-sign-3")}
|
||||
message={t("tour-mssg.placeholder-sign-4")}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
@@ -1261,19 +1261,18 @@ function PlaceHolderSign() {
|
||||
selector: '[data-tut="pdfArea"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={t("tour-mssg.placeholder-sign-4")}
|
||||
message={t("tour-mssg.placeholder-sign-5")}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
|
||||
{
|
||||
selector: '[data-tut="headerArea"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={t("tour-mssg.placeholder-sign-5")}
|
||||
message={t("tour-mssg.placeholder-sign-6")}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
|
||||
@@ -4,7 +4,6 @@ import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
import Confetti from "react-confetti";
|
||||
import axios from "axios";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
@@ -42,21 +41,23 @@ import {
|
||||
import { useParams } from "react-router-dom";
|
||||
import Tour from "reactour";
|
||||
import Signedby from "../components/pdf/Signedby";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import Title from "../components/Title";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import { useSelector } from "react-redux";
|
||||
import TextFontSetting from "../components/pdf/TextFontSetting";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RotateAlert from "../components/RotateAlert";
|
||||
import DownloadPdfZip from "../primitives/DownloadPdfZip";
|
||||
import Loader from "../primitives/Loader";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||
function SignYourSelf() {
|
||||
const { t } = useTranslation();
|
||||
@@ -130,6 +131,7 @@ function SignYourSelf() {
|
||||
const [pdfRotateBase64, setPdfRotatese64] = useState("");
|
||||
const [isRotate, setIsRotate] = useState({ status: false, degree: 0 });
|
||||
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: true });
|
||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||
const divRef = useRef(null);
|
||||
const nodeRef = useRef(null);
|
||||
const [, drop] = useDrop({
|
||||
@@ -1190,7 +1192,12 @@ function SignYourSelf() {
|
||||
)}
|
||||
{isCelebration && (
|
||||
<div className="relative z-[1000]">
|
||||
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||
<Confetti
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
recycle={false} // Prevents confetti from repeating
|
||||
gravity={0.1} // Adjust the gravity to control the speed
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300">
|
||||
@@ -1327,6 +1334,7 @@ function SignYourSelf() {
|
||||
setIsAlert={setIsAlert}
|
||||
extUserId={extUserId}
|
||||
activeMailAdapter={activeMailAdapter}
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
/>
|
||||
{/* pdf header which contain funish back button */}
|
||||
<Header
|
||||
@@ -1346,6 +1354,7 @@ function SignYourSelf() {
|
||||
clickOnZoomIn={clickOnZoomIn}
|
||||
clickOnZoomOut={clickOnZoomOut}
|
||||
widgetsDetails={xyPostion}
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
/>
|
||||
<div ref={divRef} data-tut="reactourSecond" className="h-full">
|
||||
{containerWH?.width && containerWH?.height && (
|
||||
@@ -1450,6 +1459,12 @@ function SignYourSelf() {
|
||||
setIsRotate={setIsRotate}
|
||||
handleRemoveWidgets={handleRemoveWidgets}
|
||||
/>
|
||||
<DownloadPdfZip
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
isDownloadModal={isDownloadModal}
|
||||
pdfDetails={pdfDetails}
|
||||
isDocId={true}
|
||||
/>
|
||||
<TextFontSetting
|
||||
isTextSetting={isTextSetting}
|
||||
setIsTextSetting={setIsTextSetting}
|
||||
|
||||
@@ -9,8 +9,6 @@ import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { useDrag, useDrop } from "react-dnd";
|
||||
import WidgetComponent from "../components/pdf/WidgetComponent";
|
||||
import Tour from "reactour";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import SignerListPlace from "../components/pdf/SignerListPlace";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
||||
@@ -36,18 +34,21 @@ import {
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import "../styles/AddUser.css";
|
||||
import Title from "../components/Title";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
import EditTemplate from "../components/pdf/EditTemplate";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import AddRoleModal from "../components/pdf/AddRoleModal";
|
||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import Parse from "parse";
|
||||
import { useSelector } from "react-redux";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RotateAlert from "../components/RotateAlert";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
|
||||
const TemplatePlaceholder = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -779,6 +780,7 @@ const TemplatePlaceholder = () => {
|
||||
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
|
||||
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
|
||||
NextReminderDate: pdfDetails[0]?.NextReminderDate,
|
||||
IsEnableOTP: pdfDetails[0]?.IsEnableOTP === true ? true : false,
|
||||
URL: pdfUrl
|
||||
};
|
||||
const updateTemplate = new Parse.Object("contracts_Template");
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ModalUi from "./ModalUi";
|
||||
import {
|
||||
getSignedUrl,
|
||||
handleDownloadCertificate,
|
||||
handleDownloadPdf,
|
||||
sanitizeFileName
|
||||
} from "../constant/Utils";
|
||||
import Loader from "./Loader";
|
||||
import JSZip from "jszip";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
function DownloadPdfZip(props) {
|
||||
const { t } = useTranslation();
|
||||
const [selectType, setSelectType] = useState(1);
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const downloadType = [
|
||||
{ id: 1, label: t("download-pdf") },
|
||||
{ id: 2, label: t("pdf-certificate") }
|
||||
];
|
||||
|
||||
const handleDownload = async () => {
|
||||
if (selectType === 1) {
|
||||
await handleDownloadPdf(props.pdfDetails, setIsDownloading);
|
||||
setSelectType(1);
|
||||
props.setIsDownloadModal(false);
|
||||
} else if (selectType === 2) {
|
||||
setIsDownloading("pdf");
|
||||
const zip = new JSZip();
|
||||
const pdfDetails = props.pdfDetails;
|
||||
const pdfName = pdfDetails?.[0]?.Name || "Document";
|
||||
const pdfUrl = pdfDetails?.[0]?.SignedUrl || "";
|
||||
|
||||
try {
|
||||
// Fetch the first PDF (Signed Document)
|
||||
const docId =
|
||||
props.isDocId && !pdfDetails?.[0]?.IsEnableOTP
|
||||
? pdfDetails?.[0]?.objectId
|
||||
: "";
|
||||
const signedUrl = await getSignedUrl(pdfUrl, docId);
|
||||
const pdf1Response = await fetch(signedUrl);
|
||||
if (!pdf1Response.ok) {
|
||||
throw new Error(`Failed to fetch PDF: ${signedUrl}`);
|
||||
}
|
||||
const pdf1Blob = await pdf1Response.blob();
|
||||
const isZip = true;
|
||||
// Fetch the Certificate (or generate its URL dynamically)
|
||||
const certificateUrl = await handleDownloadCertificate(
|
||||
pdfDetails,
|
||||
setIsDownloading,
|
||||
isZip
|
||||
);
|
||||
const pdf2Response = await fetch(certificateUrl);
|
||||
if (!pdf2Response.ok) {
|
||||
throw new Error(`Failed to fetch certificate PDF: ${certificateUrl}`);
|
||||
}
|
||||
const pdf2Blob = await pdf2Response.blob();
|
||||
// Add files to ZIP
|
||||
zip.file(
|
||||
`${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`,
|
||||
pdf1Blob
|
||||
);
|
||||
zip.file("Certificate_signed_by_OpenSign™.pdf", pdf2Blob);
|
||||
|
||||
// Generate the ZIP and trigger download
|
||||
const zipBlob = await zip.generateAsync({ type: "blob" });
|
||||
saveAs(
|
||||
zipBlob,
|
||||
`${sanitizeFileName(pdfName)}_signed_by_OpenSign™.zip`
|
||||
);
|
||||
setSelectType(1);
|
||||
props.setIsDownloadModal(false);
|
||||
setIsDownloading("");
|
||||
} catch (error) {
|
||||
console.error("Error creating ZIP file:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isDownloadModal}
|
||||
title={t("download-files")}
|
||||
handleClose={() => props.setIsDownloadModal(false)}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
{downloadType.map((data, ind) => {
|
||||
return (
|
||||
<label
|
||||
key={ind}
|
||||
className="flex items-center gap-1 mb-2 cursor-pointer"
|
||||
>
|
||||
<input
|
||||
className="mr-[8px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
value={data.id}
|
||||
onChange={() => setSelectType(data.id)}
|
||||
checked={selectType === data.id}
|
||||
/>
|
||||
{data.label}
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<button
|
||||
onClick={() => handleDownload()}
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("download")}
|
||||
</button>
|
||||
</div>
|
||||
{isDownloading === "pdf" && (
|
||||
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isDownloading === "certificate"}
|
||||
title={
|
||||
isDownloading === "certificate"
|
||||
? t("generating-certificate")
|
||||
: t("pdf-download")
|
||||
}
|
||||
handleClose={() => setIsDownloading("")}
|
||||
>
|
||||
<div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
|
||||
{isDownloading === "certificate"}{" "}
|
||||
<p>{t("generate-certificate-alert")}</p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default DownloadPdfZip;
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
checkIsSubscribed,
|
||||
copytoData,
|
||||
fetchUrl,
|
||||
getSignedUrl,
|
||||
replaceMailVaribles
|
||||
} from "../constant/Utils";
|
||||
import Confetti from "react-confetti";
|
||||
@@ -30,6 +31,7 @@ import SubscribeCard from "./SubscribeCard";
|
||||
import { validplan } from "../json/plansArr";
|
||||
import { serverUrl_fn } from "../constant/appinfo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DownloadPdfZip from "./DownloadPdfZip";
|
||||
|
||||
const ReportTable = (props) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -69,7 +71,9 @@ const ReportTable = (props) => {
|
||||
const [publicUserName, setIsPublicUserName] = useState("");
|
||||
const [isViewShare, setIsViewShare] = useState({});
|
||||
const [isSubscribe, setIsSubscribe] = useState(true);
|
||||
const [isModal, setIsModal] = useState({});
|
||||
const [reason, setReason] = useState("");
|
||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||
const startIndex = (currentPage - 1) * props.docPerPage;
|
||||
@@ -266,7 +270,8 @@ const ReportTable = (props) => {
|
||||
Signers: signers,
|
||||
SendinOrder: Doc?.SendinOrder || false,
|
||||
AutomaticReminders: Doc?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: Doc?.RemindOnceInEvery || 5
|
||||
RemindOnceInEvery: Doc?.RemindOnceInEvery || 5,
|
||||
IsEnableOTP: Doc?.IsEnableOTP || false
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(
|
||||
@@ -515,7 +520,6 @@ const ReportTable = (props) => {
|
||||
.then(async (result) => {
|
||||
const res = result.data;
|
||||
if (res) {
|
||||
setReason("");
|
||||
setActLoader({});
|
||||
setIsAlert(true);
|
||||
setAlertMsg({
|
||||
@@ -527,7 +531,43 @@ const ReportTable = (props) => {
|
||||
(x) => x.objectId !== item.objectId
|
||||
);
|
||||
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) => {
|
||||
console.log("err", err);
|
||||
@@ -612,10 +652,15 @@ const ReportTable = (props) => {
|
||||
setActLoader({ [`${item.objectId}`]: true });
|
||||
const url = item?.SignedUrl || item?.URL || "";
|
||||
const pdfName = item?.Name || "exported_file";
|
||||
const isCompleted = item?.IsCompleted || false;
|
||||
if (url) {
|
||||
try {
|
||||
const signedUrl = await Parse.Cloud.run("getsignedurl", { url: url });
|
||||
await fetchUrl(signedUrl, pdfName);
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal({ [item.objectId]: true });
|
||||
} else {
|
||||
const signedUrl = await getSignedUrl(url);
|
||||
await fetchUrl(signedUrl, pdfName);
|
||||
}
|
||||
setActLoader({});
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
@@ -741,7 +786,7 @@ const ReportTable = (props) => {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
let params = {
|
||||
mailProvider: doc?.ExtUserPtr?.active_mail_adapter,
|
||||
mailProvider: doc?.ExtUserPtr?.active_mail_adapter || "",
|
||||
extUserId: doc?.ExtUserPtr?.objectId,
|
||||
recipient: userDetails?.Email,
|
||||
subject: mail.subject,
|
||||
@@ -1089,10 +1134,15 @@ const ReportTable = (props) => {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="p-2 w-full overflow-auto bg-base-100 text-base-content op-card shadow-lg">
|
||||
<div className="p-2 w-full overflow-hidden bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isCelebration && (
|
||||
<div className="relative z-[1000]">
|
||||
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||
<Confetti
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
recycle={false} // Prevents confetti from repeating
|
||||
gravity={0.1} // Adjust the gravity to control the speed
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>}
|
||||
@@ -1132,8 +1182,8 @@ const ReportTable = (props) => {
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
isDashboard && props.List?.length > 0 ? "h-[317px]" : "h-full"
|
||||
} w-full`}
|
||||
isDashboard && props.List?.length > 0 ? "min-h-[317px]" : "h-full"
|
||||
} overflow-auto w-full`}
|
||||
>
|
||||
<table className="op-table border-collapse w-full ">
|
||||
<thead className="text-[14px]">
|
||||
@@ -1169,48 +1219,50 @@ const ReportTable = (props) => {
|
||||
</td>
|
||||
<td className="px-4 py-2 ">{item?.Email || "-"}</td>
|
||||
<td className="px-4 py-2">{item?.Phone || "-"}</td>
|
||||
<td className="px-3 py-2 text-white grid grid-cols-2">
|
||||
{props.actions?.length > 0 &&
|
||||
props.actions.map((act, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handleActionBtn(act, item)}
|
||||
title={t(`btnLabel.${act.hoverLabel}`)}
|
||||
className={`${
|
||||
act?.btnColor ? act.btnColor : ""
|
||||
} op-btn op-btn-sm`}
|
||||
<td className="px-3 py-2">
|
||||
<div className="text-base-content min-w-max flex flex-row gap-x-2 gap-y-1 justify-start items-center">
|
||||
{props.actions?.length > 0 &&
|
||||
props.actions.map((act, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handleActionBtn(act, item)}
|
||||
title={t(`btnLabel.${act.hoverLabel}`)}
|
||||
className={`${
|
||||
act?.btnColor ? act.btnColor : ""
|
||||
} op-btn op-btn-sm`}
|
||||
>
|
||||
<i className={act.btnIcon}></i>
|
||||
</button>
|
||||
))}
|
||||
{isDeleteModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={"Delete Contact"}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<i className={act.btnIcon}></i>
|
||||
</button>
|
||||
))}
|
||||
{isDeleteModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={"Delete Contact"}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
{t("contact-delete-alert")}
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
{t("contact-delete-alert")}
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={() => handleDelete(item)}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={() => handleDelete(item)}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</ModalUi>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
@@ -1252,9 +1304,55 @@ const ReportTable = (props) => {
|
||||
{item?.URL ? t("download") : "-"}
|
||||
</button>
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
{formatRow(item?.ExtUserPtr)}
|
||||
</td>
|
||||
{props.ReportName === "In-progress documents" ? (
|
||||
<td className="px-4 py-2">
|
||||
<button
|
||||
onClick={() =>
|
||||
item?.AuditTrail?.length > 0 &&
|
||||
setIsModal({ [item?.objectId]: true })
|
||||
}
|
||||
className={`${
|
||||
item?.AuditTrail?.length
|
||||
? "border-green-400"
|
||||
: "cursor-default op-border-primary"
|
||||
} focus:outline-none w-[60px] border-[2px] text-[12px] rounded-full md:self-center`}
|
||||
>
|
||||
{item?.AuditTrail?.length ? "VIEWED" : "SENT"}
|
||||
</button>
|
||||
{isModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={t("document-logs")}
|
||||
handleClose={() => setIsModal({})}
|
||||
>
|
||||
{item?.AuditTrail?.map((x, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="pl-3 first:mt-2 text-sm font-medium flex flex-col md:flex-row items-start md:gap-4 border-t-[1px] border-gray-600"
|
||||
>
|
||||
<div className="py-2 break-all font-bold md:text-[12px] md:col-span-2 w-full md:w-[210px]">
|
||||
{x?.UserPtr?.Email || "-"}
|
||||
</div>
|
||||
<button className="px-2 cursor-default border-[2px] text-[12px] border-green-400 rounded-full md:self-center">
|
||||
{x?.Activity?.toUpperCase() || "-"}
|
||||
</button>
|
||||
<div className=" text-[12px] py-2">
|
||||
{x?.Activity === "Signed"
|
||||
? new Date(x?.SignedOn)?.toUTCString()
|
||||
: new Date(
|
||||
x?.ViewedOn
|
||||
)?.toUTCString() || "-"}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</ModalUi>
|
||||
)}
|
||||
</td>
|
||||
) : (
|
||||
<td className="px-4 py-2">
|
||||
{formatRow(item?.ExtUserPtr)}
|
||||
</td>
|
||||
)}
|
||||
<td className="px-4 py-2">
|
||||
{!item?.IsSignyourself && item?.Placeholders ? (
|
||||
<button
|
||||
@@ -1272,11 +1370,7 @@ const ReportTable = (props) => {
|
||||
<td className=" pl-[20px] py-2">
|
||||
{props.ReportName === "Templates" && (
|
||||
<div className="flex flex-row">
|
||||
<label
|
||||
className={
|
||||
"cursor-pointer relative inline-flex items-center mb-0"
|
||||
}
|
||||
>
|
||||
<label className="cursor-pointer relative inline-flex items-center mb-0">
|
||||
<input
|
||||
checked={props.isPublic?.[item.objectId]}
|
||||
onChange={(e) =>
|
||||
@@ -1449,7 +1543,7 @@ const ReportTable = (props) => {
|
||||
</td>
|
||||
)}
|
||||
<td className="px-2 py-2">
|
||||
<div className="text-base-content flex flex-row gap-x-2 gap-y-1 justify-start items-center">
|
||||
<div className="text-base-content min-w-max flex flex-row gap-x-2 gap-y-1 justify-start items-center">
|
||||
{props.actions?.length > 0 &&
|
||||
props.actions.map((act, index) =>
|
||||
props.ReportName === "Templates" ? (
|
||||
@@ -2023,6 +2117,14 @@ const ReportTable = (props) => {
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
{isDownloadModal[item.objectId] && (
|
||||
<DownloadPdfZip
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
isDownloadModal={isDownloadModal[item.objectId]}
|
||||
pdfDetails={[item]}
|
||||
isDocId={false}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@@ -41,8 +41,8 @@ function CustomModal(props) {
|
||||
className="op-btn op-btn-primary mr-2 px-6"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setReason("");
|
||||
props.declineDoc(reason);
|
||||
setReason("");
|
||||
}}
|
||||
>
|
||||
{t("yes")}
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function TourContentWithBtn({ message, isChecked, video }) {
|
||||
></iframe>
|
||||
</div>
|
||||
)}
|
||||
<label className="flex items-center justify-center mb-0">
|
||||
<label className="flex items-center justify-center mt-3 mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-checkbox op-checkbox-xs mr-1"
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"upgrade-now": "Upgrade now",
|
||||
"upgrade-to": "Upgrade to",
|
||||
"plan": "Plan",
|
||||
"subscribe-card-teamplan":"Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
|
||||
"subscribe-card-teamplan": "Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
|
||||
"subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
|
||||
"user-name-limit-char": "To have a username less than 8 character please subscribe",
|
||||
"tour-content": "Don't show this again",
|
||||
@@ -151,7 +151,8 @@
|
||||
"Name": "Name",
|
||||
"Status": "Status",
|
||||
"created-date": "Created Date",
|
||||
"Type": "Type"
|
||||
"Type": "Type",
|
||||
"Logs": "Logs"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "These are documents you have started but have not finalized for sending.",
|
||||
@@ -572,9 +573,10 @@
|
||||
"pdf-request-file-5": "You can click 'Auto Sign All' to automatically sign at all the locations meant to be signed by you. Make sure that you review the document properly before you click this button .",
|
||||
"placeholder-sign-1": "Select a recipient from this list to add a place-holder where he is supposed to sign.The placeholder will appear in the same colour as the recipient name once you drop it on the document.",
|
||||
"placeholder-sign-2": "Clicking 'Add recipients' button will allow you to add more signers.",
|
||||
"placeholder-sign-3": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-4": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
|
||||
"placeholder-sign-5": "Clicking 'Send' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
|
||||
"placeholder-sign-3": "Click this button to add more recipients/signers to the document.",
|
||||
"placeholder-sign-4": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-5": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
|
||||
"placeholder-sign-6": "Clicking 'Send' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
|
||||
"report-1": "Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like ‘HR Manager’ and ‘New Employee’. Each time you use the template, you can assign the ‘New Employee’ role to different incoming staff members, while the ‘HR Manager’ role remains constant, facilitating a seamless onboarding process for each recruit. ",
|
||||
"redirect": "Click the 'Use' button to create a new document from an existing template.",
|
||||
"bulksend": "To quickly create and send multiple documents using an existing template, click the 'Quick Send' button.",
|
||||
@@ -614,27 +616,42 @@
|
||||
"select-date-format": "Select a date format",
|
||||
"quantity-of-credits": "Quantity of premium credits",
|
||||
"remaining-credits": "Premium credits available:",
|
||||
"remaining-credits-help":"Use premium credits for API document signing, bulk sending, or embedding OpenSign integration on your website. You have {{allowedcredits}} included credits and {{addoncredits}} additional purchased credits remaining.",
|
||||
"remaining-credits-help": "Use premium credits for API document signing, bulk sending, or embedding OpenSign integration on your website. You have {{allowedcredits}} included credits and {{addoncredits}} additional purchased credits remaining.",
|
||||
"additional-credits": "Please purchase premium credits",
|
||||
"quota-err-quicksend": "Quota Reached, You don't have enough credits.",
|
||||
"buy-credits": "Buy Premium Credits",
|
||||
"rotate-right":"Rotate right",
|
||||
"rotate-left":"Rotate left",
|
||||
"rotate-alert-mssg":"All widgets on this page will be lost. Are you sure you want to proceed?",
|
||||
"templateid":"Template-Id",
|
||||
"bulk-send-subcription-alert":"Please upgrade to Professional or Team plan to use bulk send.",
|
||||
"generate-test-token":"Generate Test Token",
|
||||
"regenerate-test-token":"Regenerate Test Token",
|
||||
"help-test-token":"This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once you’ve completed your testing, you can upgrade to one of our paid plans to generate a production token.",
|
||||
"help-api-token":"This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.",
|
||||
"quota-mail-info-head":"Monthly request signatures email limit",
|
||||
"rotate-right": "Rotate right",
|
||||
"rotate-left": "Rotate left",
|
||||
"rotate-alert-mssg": "All widgets on this page will be lost. Are you sure you want to proceed?",
|
||||
"templateid": "Template-Id",
|
||||
"bulk-send-subcription-alert": "Please upgrade to Professional or Team plan to use bulk send.",
|
||||
"generate-test-token": "Generate Test Token",
|
||||
"regenerate-test-token": "Regenerate Test Token",
|
||||
"help-test-token": "This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once you’ve completed your testing, you can upgrade to one of our paid plans to generate a production token.",
|
||||
"help-api-token": "This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.",
|
||||
"reason": "Reason",
|
||||
"decline-by": "Declined/revoked by",
|
||||
"document-declined": "Document declined",
|
||||
"quota-mail-info-head": "Monthly request signatures email limit",
|
||||
"quota-mail-info": "You can send upto 15 signature request emails every month. Upgrade now to send unlimited signing requests directly.",
|
||||
"quota-mail": "You've reached your limit of 15 signature request emails for this month. Upgrade now to continue sending emails directly.",
|
||||
"quota-mail-tip":"Tip: You can still sign <1>unlimited documents</1> by manually sharing the signing request links.",
|
||||
"quota-mail-head":"Quota Reached",
|
||||
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminEmail}}.",
|
||||
"sent-this-month":"Sent this month",
|
||||
"available-seats":"Available seats",
|
||||
"buy-users":"Buy more users"
|
||||
|
||||
"quota-mail-tip": "Tip: You can still sign <1>unlimited documents</1> by manually sharing the signing request links.",
|
||||
"quota-mail-head": "Quota Reached",
|
||||
"unauthorized-modal": "You don't have permission to perform this action, please contact {{adminEmail}}.",
|
||||
"sent-this-month": "Sent this month",
|
||||
"available-seats": "Available seats",
|
||||
"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",
|
||||
"download-files": "Download files",
|
||||
"download-pdf": "Download Pdf",
|
||||
"pdf-certificate": "Download Pdf + Certificate",
|
||||
"document-logs": "Document logs"
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"upgrade-to": "Mettre à niveau vers",
|
||||
"pro": "PRO",
|
||||
"plan": "Offre",
|
||||
"subscribe-card-teamplan":"Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !",
|
||||
"subscribe-card-teamplan": "Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !",
|
||||
"subscribe-card-plan": "Débloquez des fonctionnalités premium à partir de seulement {{premiumPrice}}/mois. Bénéficiez de performances améliorées et de seulement {{addonPrice}} par crédit supplémentaire après vos crédits premium inclus.",
|
||||
"user-name-limit-char": "Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner",
|
||||
"tour-content": "Ne plus afficher",
|
||||
@@ -136,7 +136,8 @@
|
||||
"Name": "Nom",
|
||||
"Status": "Statut",
|
||||
"created-date": "Date de création",
|
||||
"Type": "Saisir"
|
||||
"Type": "Saisir",
|
||||
"Logs": "Journaux"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "Signer",
|
||||
@@ -571,9 +572,10 @@
|
||||
"pdf-request-file-5": "Vous pouvez cliquer sur 'Signer automatiquement tout' pour signer automatiquement à tous les emplacements que vous êtes censé signer. Assurez-vous de lire correctement le document avant de cliquer sur ce bouton.",
|
||||
"placeholder-sign-1": "Sélectionnez un destinataire dans cette liste pour ajouter un espace réservé à l'endroit où il est censé signer. L'espace réservé apparaîtra de la même couleur que le nom du destinataire une fois que vous le déposerez sur le document.",
|
||||
"placeholder-sign-2": "Cliquer sur le bouton 'Ajouter des destinataires' vous permettra d'ajouter plus de signataires.",
|
||||
"placeholder-sign-3": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-4": "La zone de contenu PDF affiche déjà les espaces réservés existants du modèle. Pour votre commodité, ces espaces réservés correspondront à la couleur du nom du destinataire, ce qui les rendra facilement identifiables.",
|
||||
"placeholder-sign-5": "Cliquer sur 'Envoyer' enregistrera le document. À l'étape suivante, vous pourrez personnaliser les e-mails à envoyer aux destinataires ou copier les liens de signature et les partager vous-même avec les destinataires.",
|
||||
"placeholder-sign-3": "Cliquez sur ce bouton pour ajouter plus de destinataires/signataires au document",
|
||||
"placeholder-sign-4": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-5": "La zone de contenu PDF affiche déjà les espaces réservés existants du modèle. Pour votre commodité, ces espaces réservés correspondront à la couleur du nom du destinataire, ce qui les rendra facilement identifiables.",
|
||||
"placeholder-sign-6": "Cliquer sur 'Envoyer' enregistrera le document. À l'étape suivante, vous pourrez personnaliser les e-mails à envoyer aux destinataires ou copier les liens de signature et les partager vous-même avec les destinataires.",
|
||||
"report-1": "Cliquez sur le bouton 'Ajouter' pour créer un nouveau modèle. Les modèles sont des documents réutilisables conçus pour générer rapidement de nouveaux documents avec la même structure et différents signataires. Par exemple, un modèle RH pour l'intégration peut avoir des rôles prédéfinis tels que 'Responsable RH' et ' Nouvel employé'. Chaque fois que vous utilisez le modèle, vous pouvez attribuer le rôle 'Nouvel employé' à différents membres du personnel entrants, tandis que le rôle 'Responsable RH' reste constant, facilitant ainsi un processus d'intégration fluide pour chaque recrue",
|
||||
"redirect": "Cliquez sur le bouton 'Utiliser' pour créer un nouveau document à partir d'un modèle existant.",
|
||||
"bulksend": "Pour créer et envoyer rapidement plusieurs documents à l'aide d'un modèle existant, cliquez sur le bouton 'Envoi rapide'.",
|
||||
@@ -611,29 +613,44 @@
|
||||
"Add-seats": "Ajouter des sièges",
|
||||
"format": "format",
|
||||
"select-date-format": "Sélectionnez un format de date",
|
||||
"quantity-of-credits":"Quantité de crédits de prime",
|
||||
"remaining-credits":"Crédits premium disponibles :",
|
||||
"additional-credits":"Veuillez acheter des crédits premium",
|
||||
"remaining-credits-help":"Utilisez des crédits premium pour la signature de documents API, l'envoi groupé ou l'intégration d'OpenSign sur votre site Web. Il vous reste {{allowedcredits}} crédits inclus et {{addoncredits}} crédits achetés supplémentaires.",
|
||||
"quantity-of-credits": "Quantité de crédits de prime",
|
||||
"remaining-credits": "Crédits premium disponibles :",
|
||||
"additional-credits": "Veuillez acheter des crédits premium",
|
||||
"remaining-credits-help": "Utilisez des crédits premium pour la signature de documents API, l'envoi groupé ou l'intégration d'OpenSign sur votre site Web. Il vous reste {{allowedcredits}} crédits inclus et {{addoncredits}} crédits achetés supplémentaires.",
|
||||
"quota-err-quicksend": "Quota atteint, vous n'avez pas assez de crédits.",
|
||||
"buy-credits": "Acheter des crédits premium",
|
||||
"rotate-right" :"Faire pivoter à droite",
|
||||
"rotate-left" :"Faire pivoter à gauche",
|
||||
"rotate-alert-mssg" :"Tous les widgets de cette page seront perdus. Êtes-vous sûr de vouloir continuer ?",
|
||||
"templateid":"ID de modèle",
|
||||
"bulk-send-subcription-alert":"Veuillez passer au forfait Professionnel ou Équipe pour utiliser Quicksend.",
|
||||
"rotate-right": "Faire pivoter à droite",
|
||||
"rotate-left": "Faire pivoter à gauche",
|
||||
"rotate-alert-mssg": "Tous les widgets de cette page seront perdus. Êtes-vous sûr de vouloir continuer ?",
|
||||
"templateid": "ID de modèle",
|
||||
"bulk-send-subcription-alert": "Veuillez passer au forfait Professionnel ou Équipe pour utiliser Quicksend.",
|
||||
"generate-test-token": "Générer jeton de test",
|
||||
"regenerate-test-token":"Régénérer le jeton de test",
|
||||
"help-test-token":"Ce jeton peut être utilisé pour tester les API au niveau du point de terminaison https://sandbox.opensignlabs.com/api/v1, vous permettant ainsi d'effectuer un nombre illimité de signatures de documents. Veuillez noter que l'API sandbox signera vos documents avec des certificats auto-signés, qui peuvent ne pas être reconnus comme valides par Adobe. Une fois vos tests terminés, vous pouvez passer à l’un de nos forfaits payants pour générer un jeton de production.",
|
||||
"help-api-token":"Ce jeton peut être utilisé pour accéder aux API de production au point de terminaison {{origin}}/api/v1. Il ne peut être généré que sur l'un de nos forfaits payants.",
|
||||
"quota-mail-info-head":"Limite mensuelle d'e-mails de signatures de demandes",
|
||||
"regenerate-test-token": "Régénérer le jeton de test",
|
||||
"help-test-token": "Ce jeton peut être utilisé pour tester les API au niveau du point de terminaison https://sandbox.opensignlabs.com/api/v1, vous permettant ainsi d'effectuer un nombre illimité de signatures de documents. Veuillez noter que l'API sandbox signera vos documents avec des certificats auto-signés, qui peuvent ne pas être reconnus comme valides par Adobe. Une fois vos tests terminés, vous pouvez passer à l’un de nos forfaits payants pour générer un jeton de production.",
|
||||
"help-api-token": "Ce jeton peut être utilisé pour accéder aux API de production au point de terminaison {{origin}}/api/v1. Il ne peut être généré que sur l'un de nos forfaits payants.",
|
||||
"reason": "Raison",
|
||||
"decline-by": "Refusé/révoqué par",
|
||||
"document-declined": "document refusé",
|
||||
"quota-mail-info-head": "Limite mensuelle d'e-mails de signatures de demandes",
|
||||
"quota-mail-info": "Vous pouvez envoyer jusqu'à 15 e-mails de demande de signature chaque mois. Mettez à niveau maintenant pour envoyer directement des demandes de signature illimitées.",
|
||||
"quota-mail": "Vous avez atteint votre limite de 15 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.",
|
||||
"quota-mail-tip":"Astuce : Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement les liens de demande de signature.",
|
||||
"quota-mail-head":"Quota atteint",
|
||||
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
|
||||
"sent-this-month":"envoyé ce mois-ci",
|
||||
"available-seats":"Disponible sièges",
|
||||
"buy-users":"Acheter plus d'utilisateurs"
|
||||
|
||||
"quota-mail-tip-tip": "Astuce : Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement les liens de demande de signature.",
|
||||
"quota-mail-head": "Quota atteint",
|
||||
"unauthorized-modal": "Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
|
||||
"sent-this-month": "envoyé ce mois-ci",
|
||||
"available-seats": "Disponible sièges",
|
||||
"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",
|
||||
"download-files": "Télécharger des fichiers",
|
||||
"download-pdf": "Télécharger le PDF",
|
||||
"pdf-certificate": "Télécharger Pdf + Certificat",
|
||||
"document-logs": "Journaux de documents"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
export const cloudServerUrl = 'http://localhost:8080/app';
|
||||
export const appName = process.env.APP_NAME || 'OpenSign™';
|
||||
export function customAPIurl() {
|
||||
const url = new URL(cloudServerUrl);
|
||||
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
|
||||
|
||||
@@ -8,6 +8,7 @@ import saveInvoice from './saveInvoice.js';
|
||||
import savePayments from './savePayments.js';
|
||||
import gooogleauth from './googleauth.js';
|
||||
import autoReminder from './autoReminder.js';
|
||||
import validateSmtp from './validateSmtp.js';
|
||||
export const app = express();
|
||||
|
||||
dotenv.config();
|
||||
@@ -22,3 +23,4 @@ app.post('/saveinvoice', saveInvoice);
|
||||
app.post('/savepayment', savePayments);
|
||||
app.post('/googleauth', gooogleauth);
|
||||
app.post('/sendreminder', autoReminder);
|
||||
app.post('/validatesmtp', validateSmtp);
|
||||
|
||||
@@ -196,6 +196,9 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
if (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('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
@@ -312,6 +315,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
from: sender,
|
||||
html: html,
|
||||
extUserId: template.ExtUserPtr.objectId,
|
||||
mailProvider: template?.ExtUserPtr?.active_mail_adapter || '',
|
||||
};
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
|
||||
@@ -63,6 +63,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
const email_body = request.body.email_body;
|
||||
const sendInOrder = request.body.sendInOrder || false;
|
||||
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
|
||||
const IsEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||
@@ -162,6 +163,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
if (TimeToCompleteDays) {
|
||||
object.set('TimeToCompleteDays', TimeToCompleteDays);
|
||||
}
|
||||
object.set('IsEnableOTP', IsEnableOTP);
|
||||
object.set('IsSendMail', send_email);
|
||||
let contact = [];
|
||||
if (signers && signers.length > 0) {
|
||||
@@ -369,6 +371,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
from: sender,
|
||||
html: html,
|
||||
extUserId: extUser.id,
|
||||
mailProvider: parseExtUser?.active_mail_adapter || '',
|
||||
};
|
||||
|
||||
await axios.post(url, params, { headers: headers });
|
||||
|
||||
@@ -9,6 +9,7 @@ export default async function createTemplate(request, response) {
|
||||
const SendinOrder = request.body.sendInOrder || false;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
|
||||
try {
|
||||
const reqToken = request.headers['x-api-token'];
|
||||
@@ -65,6 +66,7 @@ export default async function createTemplate(request, response) {
|
||||
object.set('URL', fileUrl);
|
||||
object.set('CreatedBy', userPtr);
|
||||
object.set('ExtUserPtr', extUserPtr);
|
||||
object.set('IsEnableOTP', isEnableOTP);
|
||||
if (SendinOrder) {
|
||||
object.set('SendinOrder', SendinOrder);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
const base64File = request.body.file;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const SendinOrder = request.body.sendInOrder || false;
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
|
||||
@@ -80,6 +82,7 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
object.set('URL', fileUrl);
|
||||
object.set('CreatedBy', userPtr);
|
||||
object.set('ExtUserPtr', extUserPtr);
|
||||
object.set('IsEnableOTP', isEnableOTP);
|
||||
let contact = [];
|
||||
if (signers && signers.length > 0) {
|
||||
let parseSigners;
|
||||
|
||||
@@ -12,6 +12,8 @@ export default async function draftDocument(request, response) {
|
||||
const send_email = request.body.send_email || true;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const SendinOrder = request.body.sendInOrder || false;
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
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('ExtUserPtr', extUserPtr);
|
||||
object.set('IsSendMail', send_email);
|
||||
object.set('IsEnableOTP', isEnableOTP);
|
||||
if (signers && signers.length > 0) {
|
||||
let parseSigners;
|
||||
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 })) ||
|
||||
[],
|
||||
sendInOrder: document?.SendinOrder || false,
|
||||
enableOTP: document?.IsEnableOTP || false,
|
||||
createdAt: document.createdAt,
|
||||
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 })) ||
|
||||
[],
|
||||
sendInOrder: x?.SendinOrder || false,
|
||||
enableOTP: x?.IsEnableOTP || false,
|
||||
createdAt: x.createdAt,
|
||||
updatedAt: x.updatedAt,
|
||||
}));
|
||||
|
||||
@@ -61,6 +61,7 @@ export default async function getTemplate(request, response) {
|
||||
),
|
||||
})) || [],
|
||||
sendInOrder: template?.SendinOrder || false,
|
||||
enableOTP: template?.IsEnableOTP || false,
|
||||
createdAt: template.createdAt,
|
||||
updatedAt: template.updatedAt,
|
||||
});
|
||||
|
||||
@@ -88,6 +88,7 @@ export default async function getTemplatetList(request, response) {
|
||||
),
|
||||
})) || [],
|
||||
sendInOrder: template?.SendinOrder || false,
|
||||
enableOTP: template?.IsEnableOTP || false,
|
||||
createdAt: template.createdAt,
|
||||
updatedAt: template.updatedAt,
|
||||
}));
|
||||
|
||||
@@ -41,6 +41,7 @@ export default async function resendMail(request, response) {
|
||||
if (resDoc) {
|
||||
const _resDoc = resDoc.toJSON();
|
||||
const contact = _resDoc.Signers.find(x => x.Email === userMail);
|
||||
const activeMailAdapter = _resDoc?.ExtUserPtr?.active_mail_adapter || '';
|
||||
if (contact) {
|
||||
try {
|
||||
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
|
||||
@@ -131,6 +132,7 @@ export default async function resendMail(request, response) {
|
||||
from: sender,
|
||||
html: html,
|
||||
extUserId: _resDoc.ExtUserPtr.objectId,
|
||||
mailProvider: activeMailAdapter,
|
||||
};
|
||||
|
||||
const res = await axios.post(url, params, { headers: headers });
|
||||
|
||||
@@ -10,7 +10,7 @@ export default async function updateDocument(request, response) {
|
||||
const token = await tokenQuery.first({ useMasterKey: true });
|
||||
if (token !== undefined) {
|
||||
// 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 isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
|
||||
const parseUser = JSON.parse(JSON.stringify(token));
|
||||
@@ -48,6 +48,10 @@ export default async function updateDocument(request, response) {
|
||||
objectId: request?.body?.folderId,
|
||||
});
|
||||
}
|
||||
|
||||
if (request.body?.enableOTP) {
|
||||
updateQuery.set('IsEnableOTP', request.body?.enableOTP);
|
||||
}
|
||||
const updatedRes = await updateQuery.save(null, { useMasterKey: true });
|
||||
if (updatedRes) {
|
||||
if (request.posthog) {
|
||||
|
||||
@@ -10,7 +10,7 @@ export default async function updateTemplate(request, response) {
|
||||
const token = await tokenQuery.first({ useMasterKey: true });
|
||||
if (token !== undefined) {
|
||||
// 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 isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
|
||||
const parseUser = JSON.parse(JSON.stringify(token));
|
||||
@@ -48,6 +48,9 @@ export default async function updateTemplate(request, response) {
|
||||
objectId: request?.body?.folderId,
|
||||
});
|
||||
}
|
||||
if (request.body?.enableOTP) {
|
||||
updateQuery.set('IsEnableOTP', request.body?.enableOTP);
|
||||
}
|
||||
const updatedRes = await updateQuery.save(null, { useMasterKey: true });
|
||||
if (updatedRes) {
|
||||
if (request.posthog) {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { createTransport } from 'nodemailer';
|
||||
import { appName } from '../../Utils.js';
|
||||
// Online Javascript Editor for free
|
||||
// Write, Edit and Run your Javascript code using JS Online Compiler
|
||||
|
||||
function generateOtpWithSum10() {
|
||||
let otp = [];
|
||||
let sum = 0;
|
||||
|
||||
// First, generate 3 random digits (0-9) and add them to the sum
|
||||
for (let i = 0; i < 3; i++) {
|
||||
let digit = Math.floor(Math.random() * 10);
|
||||
otp.push(digit);
|
||||
sum += digit;
|
||||
}
|
||||
|
||||
// Calculate the last digit to ensure the sum equals 10
|
||||
let lastDigit = 10 - sum;
|
||||
|
||||
// If the last digit is not valid (e.g., greater than 9), regenerate
|
||||
if (lastDigit >= 0 && lastDigit <= 9) {
|
||||
otp.push(lastDigit);
|
||||
} else {
|
||||
return generateOtpWithSum10(); // Recursively generate again
|
||||
}
|
||||
|
||||
return otp.join('');
|
||||
}
|
||||
|
||||
export default async function validateSmtp(request, response) {
|
||||
const host = request.body.host;
|
||||
const port = request.body?.port?.toString();
|
||||
const username = request.body.username;
|
||||
const password = request.body.password;
|
||||
const secure = request.body?.secure;
|
||||
const email = request.body?.email;
|
||||
const otp = generateOtpWithSum10();
|
||||
// console.log('Generated OTP:', otp);
|
||||
|
||||
try {
|
||||
const smtpsecure = secure || port !== '465' ? false : true;
|
||||
const transporterSMTP = createTransport({
|
||||
host: host,
|
||||
port: port,
|
||||
secure: smtpsecure,
|
||||
auth: { user: username, pass: password },
|
||||
});
|
||||
const from = appName;
|
||||
const mailsender = username;
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: email,
|
||||
subject: `Your ${appName} SMTP credentials verification code`,
|
||||
text: 'mail',
|
||||
html:
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>SMTP Verification Code</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your verification code is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>` +
|
||||
otp +
|
||||
'</p></div> </div> </div></body></html>',
|
||||
};
|
||||
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('custom smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
response.status(200).json({ message: 'success' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
const code = err?.responseCode || 400;
|
||||
const message = err?.response || 'failed!';
|
||||
response.status(code).json({ error: message });
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,11 @@ import ExtUserAftersave from './parsefunction/ExtUserAftersave.js';
|
||||
import ExtUserAfterdelete from './parsefunction/ExtUserAfterdelete.js';
|
||||
import AllowedCredits from './parsefunction/AllowedCredits.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';
|
||||
import addcustomsmtp from './parsefunction/addcustomsmtp.js';
|
||||
import deactivateMailAdapter from './parsefunction/deactivateMailAdapter.js';
|
||||
|
||||
// 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);
|
||||
@@ -142,3 +147,8 @@ Parse.Cloud.define('allowedusers', AllowedUsers);
|
||||
Parse.Cloud.define('buyaddonusers', BuyAddonUsers);
|
||||
Parse.Cloud.define('allowedcredits', AllowedCredits);
|
||||
Parse.Cloud.define('buycredits', BuyCredits);
|
||||
Parse.Cloud.define('getcontact', getContact);
|
||||
Parse.Cloud.define('updatecontacttour', updateContactTour);
|
||||
Parse.Cloud.define('declinedoc', declinedocument);
|
||||
Parse.Cloud.define('addsmtp', addcustomsmtp);
|
||||
Parse.Cloud.define('deactivatemailadapter', deactivateMailAdapter);
|
||||
|
||||
@@ -62,6 +62,7 @@ const createDocumentFromTemplate = async (template, existContact, index) => {
|
||||
object.set('CreatedBy', template?.CreatedBy);
|
||||
object.set('ExtUserPtr', template?.ExtUserPtr);
|
||||
object.set('OriginIp', template?.OriginIp || '');
|
||||
object.set('IsEnableOTP', template?.IsEnableOTP || false);
|
||||
let signers = template?.Signers || [];
|
||||
const signerobj = {
|
||||
__type: 'Pointer',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { smtpenable, updateMailCount } from '../../Utils.js';
|
||||
import { appName, smtpenable, updateMailCount } from '../../Utils.js';
|
||||
async function getDocument(docId) {
|
||||
try {
|
||||
const query = new Parse.Query('contracts_Document');
|
||||
@@ -28,12 +28,12 @@ async function sendMailOTPv1(request) {
|
||||
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
|
||||
try {
|
||||
await Parse.Cloud.sendEmail({
|
||||
from: 'Opensign™' + ' <' + mailsender + '>',
|
||||
from: appName + ' <' + mailsender + '>',
|
||||
recipient: recipient,
|
||||
subject: 'Your OpenSign™ OTP',
|
||||
subject: `Your ${appName} OTP`,
|
||||
text: 'This email is a test.',
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for OpenSign™ verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>" +
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for ${appName} verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>` +
|
||||
code +
|
||||
'</p></div> </div> </div></body></html>',
|
||||
});
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
export default async function addcustomsmtp(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
const host = request.params.host;
|
||||
const port = request.params.port;
|
||||
const username = request.params.username;
|
||||
const password = request.params.password;
|
||||
if (host && port && username && password) {
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const extUserCls = new Parse.Object('contracts_Users');
|
||||
extUserCls.id = extUser.id;
|
||||
extUserCls.set('SmtpConfig', { host, port, username, password });
|
||||
extUserCls.set('active_mail_adapter', 'smtp');
|
||||
const updateExtUser = await extUserCls.save(null, { useMasterKey: true });
|
||||
// console.log('updateExtUser ', updateExtUser);
|
||||
return updateExtUser.updatedAt;
|
||||
}
|
||||
return extUser;
|
||||
} catch (err) {
|
||||
console.log('Err in add custom smtp', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
|
||||
}
|
||||
}
|
||||
@@ -7,99 +7,101 @@ export default async function callWebhook(request) {
|
||||
const contactId = request.params.contactId;
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
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) {
|
||||
if (event === 'viewed' && contactId) {
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
const res = await docQuery.get(docId, { useMasterKey: true });
|
||||
if (res) {
|
||||
const _res = res.toJSON();
|
||||
const userPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: contactId,
|
||||
};
|
||||
const date = new Date().toISOString();
|
||||
const obj = {
|
||||
UserPtr: userPtr,
|
||||
SignedUrl: _res.SignedUrl,
|
||||
Activity: 'Viewed',
|
||||
ipAddress: request.headers['x-real-ip'],
|
||||
ViewedOn: date,
|
||||
};
|
||||
const isUserExist = _res?.AuditTrail?.some(
|
||||
x => x.UserPtr.objectId === contactId && x?.ViewedOn
|
||||
);
|
||||
if (!isUserExist) {
|
||||
const updateDoc = new Parse.Object('contracts_Document');
|
||||
updateDoc.id = res.id;
|
||||
if (_res?.AuditTrail && _res?.AuditTrail?.length > 0) {
|
||||
updateDoc.set('AuditTrail', [..._res?.AuditTrail, obj]);
|
||||
} else {
|
||||
updateDoc.set('AuditTrail', [obj]);
|
||||
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', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
userId = userRes.data && userRes.data.objectId;
|
||||
}
|
||||
if (!isEnableOTP || userId) {
|
||||
if (event === 'viewed' && contactId) {
|
||||
if (docRes) {
|
||||
const _docRes = docRes.toJSON();
|
||||
const userPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: contactId,
|
||||
};
|
||||
const date = new Date().toISOString();
|
||||
const obj = {
|
||||
UserPtr: userPtr,
|
||||
SignedUrl: _docRes.SignedUrl,
|
||||
Activity: 'Viewed',
|
||||
ipAddress: request.headers['x-real-ip'],
|
||||
ViewedOn: date,
|
||||
};
|
||||
const isUserExist = _docRes?.AuditTrail?.some(
|
||||
x => x.UserPtr.objectId === contactId && x?.ViewedOn
|
||||
);
|
||||
if (!isUserExist) {
|
||||
const updateDoc = new Parse.Object('contracts_Document');
|
||||
updateDoc.id = docRes.id;
|
||||
if (_docRes?.AuditTrail && _docRes?.AuditTrail?.length > 0) {
|
||||
updateDoc.set('AuditTrail', [..._docRes?.AuditTrail, obj]);
|
||||
} else {
|
||||
updateDoc.set('AuditTrail', [obj]);
|
||||
}
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
}
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
const resDoc = await docQuery.get(docId, { useMasterKey: true });
|
||||
const extendcls = new Parse.Query('contracts_Users');
|
||||
extendcls.equalTo('objectId', resDoc.get('ExtUserPtr')?.id);
|
||||
// extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
const res = await extendcls.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const extUser = JSON.parse(JSON.stringify(res));
|
||||
if (extUser?.Webhook) {
|
||||
const params = {
|
||||
event: event,
|
||||
...body,
|
||||
};
|
||||
await axios
|
||||
.post(extUser?.Webhook, params, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then(res => {
|
||||
try {
|
||||
// console.log('res ', res);
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', res?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Err send data to webhook', err.message);
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', err?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
});
|
||||
const extendcls = new Parse.Query('contracts_Users');
|
||||
extendcls.equalTo('objectId', docRes.get('ExtUserPtr')?.id);
|
||||
// extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
const resExt = await extendcls.first({ useMasterKey: true });
|
||||
if (resExt) {
|
||||
const extUser = JSON.parse(JSON.stringify(resExt));
|
||||
if (extUser?.Webhook) {
|
||||
const params = { event: event, ...body };
|
||||
await axios
|
||||
.post(extUser?.Webhook, params, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then(res => {
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', res?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Err send data to webhook', err.message);
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', err?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
return { message: 'webhook called!' };
|
||||
}
|
||||
return { message: 'webhook called!' };
|
||||
} else {
|
||||
return { message: 'User not found!' };
|
||||
}
|
||||
} else {
|
||||
return { message: 'User not found!' };
|
||||
} catch (err) {
|
||||
console.log('Err in callwebhook', err);
|
||||
return { message: 'Something went wrong!' };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ async function sendMail(document, sessionToken) {
|
||||
const orgName = document.ExtUserPtr.Company ? document.ExtUserPtr.Company : '';
|
||||
const themeBGcolor = '#47a3ad';
|
||||
let params = {
|
||||
mailProvider: document?.ExtUserPtr?.active_mail_adapter || '',
|
||||
extUserId: document.ExtUserPtr.objectId,
|
||||
recipient: objectId ? existSigner?.Email : signerMail[i].email,
|
||||
subject: `${document.ExtUserPtr.Name} has requested you to sign "${document.Name}"`,
|
||||
@@ -199,6 +200,7 @@ export default async function createBatchDocs(request) {
|
||||
TimeToCompleteDays: x.TimeToCompleteDays || 15,
|
||||
OriginIp: Ip,
|
||||
DocSentAt: { __type: 'Date', iso: isoDate },
|
||||
IsEnableOTP: x?.IsEnableOTP || false,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export default async function deactivateMailAdapter(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const extUserCls = new Parse.Object('contracts_Users');
|
||||
extUserCls.id = extUser.id;
|
||||
extUserCls.unset('active_mail_adapter');
|
||||
const updateExtUser = await extUserCls.save(null, { useMasterKey: true });
|
||||
// console.log('updateExtUser ', updateExtUser);
|
||||
return updateExtUser.updatedAt;
|
||||
}
|
||||
return extUser;
|
||||
} catch (err) {
|
||||
console.log('Err in add custom smtp', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
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;
|
||||
if (docId && userId) {
|
||||
if (docId) {
|
||||
try {
|
||||
const query = new Parse.Query('contracts_Document');
|
||||
query.equalTo('objectId', docId);
|
||||
@@ -26,11 +19,32 @@ export default async function getDocument(request) {
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const acl = res.getACL();
|
||||
if (acl && acl.getReadAccess(userId)) {
|
||||
const IsEnableOTP = res?.get('IsEnableOTP') || false;
|
||||
if (!IsEnableOTP) {
|
||||
return res;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
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();
|
||||
if (userId && acl && acl.getReadAccess(userId)) {
|
||||
return res;
|
||||
} else {
|
||||
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 {
|
||||
return { error: "You don't have access of this document!" };
|
||||
|
||||
@@ -28,15 +28,42 @@ export default function getPresignedUrl(url) {
|
||||
|
||||
export async function getSignedUrl(request) {
|
||||
try {
|
||||
const docId = request.params.docId || '';
|
||||
const url = request.params.url;
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
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 (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
} else {
|
||||
return url;
|
||||
if (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -50,37 +50,24 @@ export default async function getSubscription(request) {
|
||||
}
|
||||
} else if (contactId) {
|
||||
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 contactUser = await contactCls.get(contactId, { useMasterKey: true });
|
||||
if (contactUser) {
|
||||
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
|
||||
subscriptionCls.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: contactUser.get('TenantId').id,
|
||||
});
|
||||
subscriptionCls.descending('createdAt');
|
||||
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
|
||||
|
||||
if (subcripitions) {
|
||||
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
|
||||
if (_subcripitions.PlanCode === 'freeplan') {
|
||||
return { status: 'success', result: { isSubscribed: false, plan: 'freeplan' } };
|
||||
} else if (_subcripitions?.Next_billing_date?.iso) {
|
||||
if (new Date(_subcripitions.Next_billing_date.iso) > new Date()) {
|
||||
return { status: 'success', result: { isSubscribed: true } };
|
||||
} else {
|
||||
return { status: 'success', result: { isSubscribed: false } };
|
||||
}
|
||||
const contactCls = new Parse.Query('contracts_Contactbook');
|
||||
const contactUser = await contactCls.get(contactId, { useMasterKey: true });
|
||||
if (contactUser) {
|
||||
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
|
||||
subscriptionCls.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: contactUser.get('TenantId').id,
|
||||
});
|
||||
subscriptionCls.descending('createdAt');
|
||||
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
|
||||
if (subcripitions) {
|
||||
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
|
||||
if (_subcripitions.PlanCode === 'freeplan') {
|
||||
return { status: 'success', result: { isSubscribed: false, plan: 'freeplan' } };
|
||||
} else if (_subcripitions?.Next_billing_date?.iso) {
|
||||
if (new Date(_subcripitions.Next_billing_date.iso) > new Date()) {
|
||||
return { status: 'success', result: { isSubscribed: true } };
|
||||
} else {
|
||||
return { status: 'success', result: { isSubscribed: false } };
|
||||
}
|
||||
@@ -88,10 +75,10 @@ export default async function getSubscription(request) {
|
||||
return { status: 'success', result: { isSubscribed: false } };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'User not found!' };
|
||||
return { status: 'success', result: { isSubscribed: false } };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'Invalid session token!' };
|
||||
return { status: 'error', result: 'User not found!' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in get subscription2', err.message);
|
||||
|
||||
@@ -32,6 +32,7 @@ export default async function GenerateCertificate(docDetails) {
|
||||
const OriginIp = docDetails?.OriginIp || '';
|
||||
const company = docDetails?.ExtUserPtr?.Company || '';
|
||||
const createdAt = docDetails?.DocSentAt?.iso || docDetails.createdAt;
|
||||
const IsEnableOTP = docDetails?.IsEnableOTP || false;
|
||||
const auditTrail =
|
||||
docDetails?.Signers?.length > 0
|
||||
? docDetails.AuditTrail.map(x => {
|
||||
@@ -348,22 +349,22 @@ export default async function GenerateCertificate(docDetails) {
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
page.drawText('Security level :', {
|
||||
x: half + 55,
|
||||
y: yPosition4 + 10,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`Email, OTP Auth`, {
|
||||
x: half + 125,
|
||||
y: yPosition4 + 10,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
if (IsEnableOTP) {
|
||||
page.drawText('Security level :', {
|
||||
x: half + 55,
|
||||
y: yPosition4 + 10,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
page.drawText('Email, OTP Auth', {
|
||||
x: half + 125,
|
||||
y: yPosition4 + 10,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
}
|
||||
page.drawText('Signature :', {
|
||||
x: 30,
|
||||
y: yPosition5,
|
||||
|
||||
@@ -272,157 +272,160 @@ const sendMailsaveCertifcate = async (doc, P12Buffer, url, isCustomMail, mailPro
|
||||
*/
|
||||
async function PDF(req) {
|
||||
try {
|
||||
if (!req?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
const userIP = req.headers['x-real-ip']; // client IPaddress
|
||||
const docId = req.params.docId;
|
||||
const reqUserId = req.params.userId;
|
||||
const isCustomMail = req.params.isCustomCompletionMail || false;
|
||||
const mailProvider = req.params.mailProvider || '';
|
||||
const sign = req.params.signature || '';
|
||||
// below bode is used to get info of docId
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
docQuery.include('ExtUserPtr,Signers');
|
||||
docQuery.equalTo('objectId', docId);
|
||||
const resDoc = await docQuery.first({ useMasterKey: true });
|
||||
if (!resDoc) {
|
||||
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();
|
||||
let signUser;
|
||||
let className;
|
||||
// `reqUserId` is send throught pdfrequest signing flow
|
||||
if (reqUserId) {
|
||||
// to get contracts_Contactbook details for currentuser from reqUserId
|
||||
const _contractUser = _resDoc.Signers.find(x => x.objectId === reqUserId);
|
||||
if (_contractUser) {
|
||||
signUser = _contractUser;
|
||||
className = 'contracts_Contactbook';
|
||||
}
|
||||
} else {
|
||||
const userIP = req.headers['x-real-ip']; // client IPaddress
|
||||
const docId = req.params.docId;
|
||||
const reqUserId = req.params.userId;
|
||||
const isCustomMail = req.params.isCustomCompletionMail || false;
|
||||
const mailProvider = req.params.mailProvider || '';
|
||||
const sign = req.params.signature || '';
|
||||
// below bode is used to get info of docId
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
docQuery.include('ExtUserPtr,Signers');
|
||||
docQuery.equalTo('objectId', docId);
|
||||
const resDoc = await docQuery.first({ useMasterKey: true });
|
||||
if (!resDoc) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
|
||||
}
|
||||
const _resDoc = resDoc?.toJSON();
|
||||
let signUser;
|
||||
let className;
|
||||
// `reqUserId` is send throught pdfrequest signing flow
|
||||
if (reqUserId) {
|
||||
// to get contracts_Contactbook details for currentuser from reqUserId
|
||||
const _contractUser = _resDoc.Signers.find(x => x.objectId === reqUserId);
|
||||
if (_contractUser) {
|
||||
signUser = _contractUser;
|
||||
className = 'contracts_Contactbook';
|
||||
}
|
||||
className = 'contracts_Users';
|
||||
signUser = _resDoc.ExtUserPtr;
|
||||
}
|
||||
|
||||
const username = signUser.Name;
|
||||
const userEmail = signUser.Email;
|
||||
if (req.params.pdfFile) {
|
||||
// `PdfBuffer` used to create buffer from pdf file
|
||||
let PdfBuffer = Buffer.from(req.params.pdfFile, 'base64');
|
||||
// `P12Buffer` used to create buffer from p12 certificate
|
||||
const pfxFile = process.env.PFX_BASE64;
|
||||
// const P12Buffer = fs.readFileSync();
|
||||
const P12Buffer = Buffer.from(pfxFile, 'base64');
|
||||
const p12Cert = new P12Signer(P12Buffer, { passphrase: process.env.PASS_PHRASE || null });
|
||||
const UserPtr = { __type: 'Pointer', className: className, objectId: signUser.objectId };
|
||||
const obj = { UserPtr: UserPtr, SignedUrl: '', Activity: 'Signed', ipAddress: userIP };
|
||||
let updateAuditTrail;
|
||||
if (_resDoc.AuditTrail && _resDoc.AuditTrail.length > 0) {
|
||||
updateAuditTrail = [..._resDoc.AuditTrail, obj];
|
||||
} else {
|
||||
className = 'contracts_Users';
|
||||
signUser = _resDoc.ExtUserPtr;
|
||||
updateAuditTrail = [obj];
|
||||
}
|
||||
|
||||
const username = signUser.Name;
|
||||
const userEmail = signUser.Email;
|
||||
if (req.params.pdfFile) {
|
||||
// `PdfBuffer` used to create buffer from pdf file
|
||||
let PdfBuffer = Buffer.from(req.params.pdfFile, 'base64');
|
||||
// `P12Buffer` used to create buffer from p12 certificate
|
||||
const pfxFile = process.env.PFX_BASE64;
|
||||
// const P12Buffer = fs.readFileSync();
|
||||
const P12Buffer = Buffer.from(pfxFile, 'base64');
|
||||
const p12Cert = new P12Signer(P12Buffer, { passphrase: process.env.PASS_PHRASE || null });
|
||||
const UserPtr = { __type: 'Pointer', className: className, objectId: signUser.objectId };
|
||||
const obj = { UserPtr: UserPtr, SignedUrl: '', Activity: 'Signed', ipAddress: userIP };
|
||||
let updateAuditTrail;
|
||||
if (_resDoc.AuditTrail && _resDoc.AuditTrail.length > 0) {
|
||||
updateAuditTrail = [..._resDoc.AuditTrail, obj];
|
||||
} else {
|
||||
updateAuditTrail = [obj];
|
||||
}
|
||||
|
||||
const auditTrail = updateAuditTrail.filter(x => x.Activity === 'Signed');
|
||||
let isCompleted = false;
|
||||
if (_resDoc.Signers && _resDoc.Signers.length > 0) {
|
||||
if (auditTrail.length === _resDoc.Signers.length) {
|
||||
isCompleted = true;
|
||||
}
|
||||
} else {
|
||||
const auditTrail = updateAuditTrail.filter(x => x.Activity === 'Signed');
|
||||
let isCompleted = false;
|
||||
if (_resDoc.Signers && _resDoc.Signers.length > 0) {
|
||||
if (auditTrail.length === _resDoc.Signers.length) {
|
||||
isCompleted = true;
|
||||
}
|
||||
const randomNumber = Math.floor(Math.random() * 5000);
|
||||
const name = `exported_file_${randomNumber}.pdf`;
|
||||
const pdfName = `./exports/${name}`;
|
||||
let pdfSize = PdfBuffer.length;
|
||||
if (isCompleted) {
|
||||
const signersName = _resDoc.Signers?.map(x => x.Name + ' <' + x.Email + '>');
|
||||
if (signersName && signersName.length > 0) {
|
||||
// `pdflibAddPlaceholder` is used to add code of only digitial sign without widget
|
||||
const pdfDoc = await PDFDocument.load(PdfBuffer);
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: pdfDoc,
|
||||
reason: 'Digitally signed by OpenSign for ' + signersName?.join(', '),
|
||||
location: 'n/a',
|
||||
name: eSignName,
|
||||
contactInfo: eSigncontact,
|
||||
signatureLength: 15000,
|
||||
});
|
||||
const pdfWithPlaceholderBytes = await pdfDoc.save();
|
||||
PdfBuffer = Buffer.from(pdfWithPlaceholderBytes);
|
||||
} else {
|
||||
// `pdflibAddPlaceholder` is used to add code of only digitial sign without widget (signyourself)
|
||||
const pdfDoc = await PDFDocument.load(PdfBuffer);
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: pdfDoc,
|
||||
reason: 'Digitally signed by OpenSign for ' + username + ' <' + userEmail + '>',
|
||||
location: 'n/a',
|
||||
name: eSignName,
|
||||
contactInfo: eSigncontact,
|
||||
signatureLength: 15000,
|
||||
});
|
||||
const pdfWithPlaceholderBytes = await pdfDoc.save();
|
||||
PdfBuffer = Buffer.from(pdfWithPlaceholderBytes);
|
||||
}
|
||||
//`new signPDF` create new instance of pdfBuffer and p12Buffer
|
||||
const OBJ = new SignPdf();
|
||||
// `signedDocs` is used to signpdf digitally
|
||||
const signedDocs = await OBJ.sign(PdfBuffer, p12Cert);
|
||||
|
||||
//`saveUrl` is used to save signed pdf in exports folder
|
||||
const saveUrl = fs.writeFileSync(pdfName, signedDocs);
|
||||
pdfSize = signedDocs.length;
|
||||
} else {
|
||||
//`saveUrl` is used to save signed pdf in exports folder
|
||||
const saveUrl = fs.writeFileSync(pdfName, PdfBuffer);
|
||||
pdfSize = PdfBuffer.length;
|
||||
}
|
||||
|
||||
// `uploadFile` is used to upload pdf to aws s3 and get it's url
|
||||
const data = await uploadFile(name, pdfName);
|
||||
|
||||
if (data && data.imageUrl) {
|
||||
// `axios` is used to update signed pdf url in contracts_Document classes for given DocId
|
||||
const updatedDoc = await updateDoc(
|
||||
req.params.docId, //docId
|
||||
data.imageUrl, // url
|
||||
signUser.objectId, // userID
|
||||
userIP, // client ipAddress,
|
||||
_resDoc, // auditTrail, signers, etc data
|
||||
className, // className based on flow
|
||||
sign // sign base64
|
||||
);
|
||||
sendDoctoWebhook(_resDoc, data.imageUrl, 'signed', signUser);
|
||||
saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId);
|
||||
if (updatedDoc && updatedDoc.isCompleted) {
|
||||
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail };
|
||||
sendMailsaveCertifcate(
|
||||
doc,
|
||||
P12Buffer,
|
||||
data.imageUrl,
|
||||
isCustomMail,
|
||||
mailProvider,
|
||||
_resDoc?.CreatedBy?.objectId
|
||||
);
|
||||
}
|
||||
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
|
||||
fs.unlinkSync(pdfName);
|
||||
console.log(`New Signed PDF created called: ${pdfName}`);
|
||||
if (updatedDoc.message === 'success') {
|
||||
return { status: 'success', data: data.imageUrl };
|
||||
} else {
|
||||
const error = new Error('Please provide required parameters!');
|
||||
error.code = 400; // Set the error code (e.g., 400 for bad request)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const error = new Error('Pdf file not present!');
|
||||
error.code = 400; // Set the error code (e.g., 400 for bad request)
|
||||
throw error;
|
||||
isCompleted = true;
|
||||
}
|
||||
const randomNumber = Math.floor(Math.random() * 5000);
|
||||
const name = `exported_file_${randomNumber}.pdf`;
|
||||
const pdfName = `./exports/${name}`;
|
||||
let pdfSize = PdfBuffer.length;
|
||||
if (isCompleted) {
|
||||
const signersName = _resDoc.Signers?.map(x => x.Name + ' <' + x.Email + '>');
|
||||
if (signersName && signersName.length > 0) {
|
||||
// `pdflibAddPlaceholder` is used to add code of only digitial sign without widget
|
||||
const pdfDoc = await PDFDocument.load(PdfBuffer);
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: pdfDoc,
|
||||
reason: 'Digitally signed by OpenSign for ' + signersName?.join(', '),
|
||||
location: 'n/a',
|
||||
name: eSignName,
|
||||
contactInfo: eSigncontact,
|
||||
signatureLength: 15000,
|
||||
});
|
||||
const pdfWithPlaceholderBytes = await pdfDoc.save();
|
||||
PdfBuffer = Buffer.from(pdfWithPlaceholderBytes);
|
||||
} else {
|
||||
// `pdflibAddPlaceholder` is used to add code of only digitial sign without widget (signyourself)
|
||||
const pdfDoc = await PDFDocument.load(PdfBuffer);
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: pdfDoc,
|
||||
reason: 'Digitally signed by OpenSign for ' + username + ' <' + userEmail + '>',
|
||||
location: 'n/a',
|
||||
name: eSignName,
|
||||
contactInfo: eSigncontact,
|
||||
signatureLength: 15000,
|
||||
});
|
||||
const pdfWithPlaceholderBytes = await pdfDoc.save();
|
||||
PdfBuffer = Buffer.from(pdfWithPlaceholderBytes);
|
||||
}
|
||||
//`new signPDF` create new instance of pdfBuffer and p12Buffer
|
||||
const OBJ = new SignPdf();
|
||||
// `signedDocs` is used to signpdf digitally
|
||||
const signedDocs = await OBJ.sign(PdfBuffer, p12Cert);
|
||||
|
||||
//`saveUrl` is used to save signed pdf in exports folder
|
||||
const saveUrl = fs.writeFileSync(pdfName, signedDocs);
|
||||
pdfSize = signedDocs.length;
|
||||
} else {
|
||||
//`saveUrl` is used to save signed pdf in exports folder
|
||||
const saveUrl = fs.writeFileSync(pdfName, PdfBuffer);
|
||||
pdfSize = PdfBuffer.length;
|
||||
}
|
||||
|
||||
// `uploadFile` is used to upload pdf to aws s3 and get it's url
|
||||
const data = await uploadFile(name, pdfName);
|
||||
|
||||
if (data && data.imageUrl) {
|
||||
// `axios` is used to update signed pdf url in contracts_Document classes for given DocId
|
||||
const updatedDoc = await updateDoc(
|
||||
req.params.docId, //docId
|
||||
data.imageUrl, // url
|
||||
signUser.objectId, // userID
|
||||
userIP, // client ipAddress,
|
||||
_resDoc, // auditTrail, signers, etc data
|
||||
className, // className based on flow
|
||||
sign // sign base64
|
||||
);
|
||||
sendDoctoWebhook(_resDoc, data.imageUrl, 'signed', signUser);
|
||||
saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId);
|
||||
if (updatedDoc && updatedDoc.isCompleted) {
|
||||
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail };
|
||||
sendMailsaveCertifcate(
|
||||
doc,
|
||||
P12Buffer,
|
||||
data.imageUrl,
|
||||
isCustomMail,
|
||||
mailProvider,
|
||||
_resDoc?.CreatedBy?.objectId
|
||||
);
|
||||
}
|
||||
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
|
||||
fs.unlinkSync(pdfName);
|
||||
console.log(`New Signed PDF created called: ${pdfName}`);
|
||||
if (updatedDoc.message === 'success') {
|
||||
return { status: 'success', data: data.imageUrl };
|
||||
} else {
|
||||
const error = new Error('Please provide required parameters!');
|
||||
error.code = 400; // Set the error code (e.g., 400 for bad request)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const error = new Error('Pdf file not present!');
|
||||
error.code = 400; // Set the error code (e.g., 400 for bad request)
|
||||
throw error;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in signpdf', err);
|
||||
|
||||
@@ -65,6 +65,8 @@ export default function reportJson(id, userId) {
|
||||
'Folder.Name',
|
||||
'URL',
|
||||
'ExtUserPtr.Name',
|
||||
'ExtUserPtr.Email',
|
||||
'ExtUserPtr.active_mail_adapter',
|
||||
'Signers.Name',
|
||||
'Signers.Email',
|
||||
'Signers.Phone',
|
||||
@@ -100,6 +102,8 @@ export default function reportJson(id, userId) {
|
||||
'Folder.Name',
|
||||
'URL',
|
||||
'ExtUserPtr.Name',
|
||||
'ExtUserPtr.Email',
|
||||
'ExtUserPtr.active_mail_adapter',
|
||||
'Signers.Name',
|
||||
'Signers.Email',
|
||||
'Signers.Phone',
|
||||
@@ -139,6 +143,7 @@ export default function reportJson(id, userId) {
|
||||
'TimeToCompleteDays',
|
||||
'Placeholders',
|
||||
'IsSignyourself',
|
||||
'IsCompleted',
|
||||
],
|
||||
};
|
||||
// declined documents report
|
||||
@@ -227,6 +232,8 @@ export default function reportJson(id, userId) {
|
||||
'Folder.Name',
|
||||
'URL',
|
||||
'ExtUserPtr.Name',
|
||||
'ExtUserPtr.Email',
|
||||
'ExtUserPtr.active_mail_adapter',
|
||||
'Signers.Name',
|
||||
'Signers.Email',
|
||||
'Signers.Phone',
|
||||
@@ -268,6 +275,8 @@ export default function reportJson(id, userId) {
|
||||
'Name',
|
||||
'URL',
|
||||
'ExtUserPtr.Name',
|
||||
'ExtUserPtr.Email',
|
||||
'ExtUserPtr.active_mail_adapter',
|
||||
'Signers.Name',
|
||||
'Signers.UserId',
|
||||
'AuditTrail',
|
||||
@@ -335,6 +344,7 @@ export default function reportJson(id, userId) {
|
||||
'Folder.Name',
|
||||
'URL',
|
||||
'ExtUserPtr.Name',
|
||||
'ExtUserPtr.active_mail_adapter',
|
||||
'Signers.Name',
|
||||
'Signers.Email',
|
||||
'Signers.Phone',
|
||||
|
||||
@@ -75,15 +75,21 @@ const makeEmail = async (to, from, subject, html, url, pdfName) => {
|
||||
}
|
||||
}
|
||||
const attachmentParts = attachments.map(attachment => {
|
||||
const content = fs.readFileSync(attachment.path);
|
||||
const encodedContent = content.toString('base64');
|
||||
return [
|
||||
`Content-Type: ${attachment.type}\n`,
|
||||
'MIME-Version: 1.0\n',
|
||||
`Content-Disposition: attachment; filename="${attachment.filename}"\n`,
|
||||
`Content-Transfer-Encoding: base64\n\n`,
|
||||
`${encodedContent}\n`,
|
||||
].join('');
|
||||
if (fs.existsSync(attachment.path)) {
|
||||
try {
|
||||
const content = fs.readFileSync(attachment.path);
|
||||
const encodedContent = content.toString('base64');
|
||||
return [
|
||||
`Content-Type: ${attachment.type}\n`,
|
||||
'MIME-Version: 1.0\n',
|
||||
`Content-Disposition: attachment; filename="${attachment.filename}"\n`,
|
||||
`Content-Transfer-Encoding: base64\n\n`,
|
||||
`${encodedContent}\n`,
|
||||
].join('');
|
||||
} catch (err) {
|
||||
console.log('Err in read attachments sendmailv3', attachment.path);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const attachmentBody = attachmentParts.join(`\n--${boundary}\n`);
|
||||
|
||||
@@ -187,6 +187,111 @@ async function sendMailProvider(req, plan, monthchange) {
|
||||
}
|
||||
}
|
||||
}
|
||||
async function sendcustomsmtp(extRes, req) {
|
||||
const smtpsecure = extRes.SmtpConfig.port !== '465' ? false : true;
|
||||
const transporterSMTP = createTransport({
|
||||
host: extRes.SmtpConfig.host,
|
||||
port: extRes.SmtpConfig.port,
|
||||
secure: smtpsecure,
|
||||
auth: { user: extRes.SmtpConfig.username, pass: extRes.SmtpConfig.password },
|
||||
});
|
||||
if (req.params.url) {
|
||||
let Pdf = fs.createWriteStream('test.pdf');
|
||||
const writeToLocalDisk = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (useLocal !== 'true') {
|
||||
https.get(req.params.url, async function (response) {
|
||||
response.pipe(Pdf);
|
||||
response.on('end', () => resolve('success'));
|
||||
});
|
||||
} else {
|
||||
const path = new URL(req.params.url)?.pathname;
|
||||
const localurl = 'http://localhost:8080' + path;
|
||||
http.get(localurl, async function (response) {
|
||||
response.pipe(Pdf);
|
||||
response.on('end', () => resolve('success'));
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
// `writeToLocalDisk` is used to create pdf file from doc url
|
||||
const ress = await writeToLocalDisk();
|
||||
if (ress) {
|
||||
function readTolocal() {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
let PdfBuffer = fs.readFileSync(Pdf.path);
|
||||
resolve(PdfBuffer);
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
// `PdfBuffer` used to create buffer from pdf file
|
||||
let PdfBuffer = await readTolocal();
|
||||
const pdfName = req.params.pdfName ? `${req.params.pdfName}.pdf` : 'exported.pdf';
|
||||
const file = { filename: pdfName, content: PdfBuffer };
|
||||
let attachment;
|
||||
const certificatePath = './exports/certificate.pdf';
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
// `certificateBuffer` used to create buffer from pdf file
|
||||
const certificateBuffer = fs.readFileSync(certificatePath);
|
||||
const certificate = { filename: 'certificate.pdf', content: certificateBuffer };
|
||||
attachment = [file, certificate];
|
||||
} catch (err) {
|
||||
attachment = [file];
|
||||
console.log('Err in read certificate sendmailv3', err);
|
||||
}
|
||||
} else {
|
||||
attachment = [file];
|
||||
}
|
||||
const from = req.params.from || '';
|
||||
const mailsender = extRes.SmtpConfig.username;
|
||||
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: req.params.recipient,
|
||||
subject: req.params.subject,
|
||||
text: req.params.text || 'mail',
|
||||
html: req.params.html || '',
|
||||
attachments: attachment,
|
||||
};
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('custom smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId); //, plan, monthchange
|
||||
}
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
fs.unlinkSync(certificatePath);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink certificate sendmailv3');
|
||||
}
|
||||
}
|
||||
return { status: 'success', code: 200 };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const from = req.params.from || '';
|
||||
const mailsender = extRes.SmtpConfig.username;
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: req.params.recipient,
|
||||
subject: req.params.subject,
|
||||
text: req.params.text || 'mail',
|
||||
html: req.params.html || '',
|
||||
};
|
||||
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('custom smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId); //, plan, monthchange
|
||||
}
|
||||
return { status: 'success', code: 200 };
|
||||
}
|
||||
}
|
||||
}
|
||||
async function sendmailv3(req) {
|
||||
const mailProvider = req.params.mailProvider || 'default';
|
||||
if (mailProvider) {
|
||||
@@ -206,7 +311,11 @@ async function sendmailv3(req) {
|
||||
const extRes = await extUserQuery.get(extUserId, { useMasterKey: true });
|
||||
if (extRes) {
|
||||
const _extRes = JSON.parse(JSON.stringify(extRes));
|
||||
if (_extRes.google_refresh_token && mailProvider === 'google') {
|
||||
if (
|
||||
_extRes.active_mail_adapter === 'google' &&
|
||||
_extRes.google_refresh_token &&
|
||||
mailProvider === 'google'
|
||||
) {
|
||||
const res = await sendMailGmailProvider(_extRes, template);
|
||||
if (res.code === 200) {
|
||||
await updateMailCount(req.params.extUserId);
|
||||
@@ -214,6 +323,14 @@ async function sendmailv3(req) {
|
||||
} else {
|
||||
return { status: 'error' };
|
||||
}
|
||||
} else if (_extRes.active_mail_adapter === 'smtp' && mailProvider === 'smtp') {
|
||||
const res = await sendcustomsmtp(_extRes, req);
|
||||
if (res.code === 200) {
|
||||
await updateMailCount(req.params.extUserId);
|
||||
return { status: 'success' };
|
||||
} else {
|
||||
return { status: 'error' };
|
||||
}
|
||||
} else {
|
||||
if (Plan && Plan === 'freeplan') {
|
||||
let MonthlyFreeEmails = _extRes?.MonthlyFreeEmails || 0;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
@@ -10,7 +10,7 @@
|
||||
<br />
|
||||
<p><a href="{{{link}}}" style="background-color: lightskyblue; cursor: pointer; border-radius: 5px; padding: 10px; border-style: solid; border-width: 2px; text-decoration: none; font-weight: bolder; color:blue">Verify email</a></p>
|
||||
<br />
|
||||
<p><a href="https://www.opensignlabs.com">OpenSign™</a></p>
|
||||
<p><a href="https://www.opensignlabs.com">{{appName}}</a></p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -1 +1 @@
|
||||
OpenSign™ Email Address Verification
|
||||
{{appName}} Email Address Verification
|
||||
@@ -17,7 +17,7 @@ import { exec } from 'child_process';
|
||||
import { createTransport } from 'nodemailer';
|
||||
import { app as v1 } from './cloud/customRoute/v1/apiV1.js';
|
||||
import { PostHog } from 'posthog-node';
|
||||
import { cloudServerUrl, smtpenable, smtpsecure, useLocal } from './Utils.js';
|
||||
import { appName, cloudServerUrl, smtpenable, smtpsecure, useLocal } from './Utils.js';
|
||||
import { SSOAuth } from './auth/authadapter.js';
|
||||
let fsAdapter;
|
||||
if (useLocal !== 'true') {
|
||||
@@ -102,7 +102,7 @@ export const config = {
|
||||
verifyUserEmails: false,
|
||||
publicServerURL: process.env.SERVER_URL || cloudServerUrl,
|
||||
// Your apps name. This will appear in the subject and body of the emails that are sent.
|
||||
appName: 'Opensign',
|
||||
appName: appName,
|
||||
allowClientClassCreation: false,
|
||||
allowExpiredAuthDataToken: false,
|
||||
encodeParseObjectInCloudFunction: true,
|
||||
@@ -112,7 +112,7 @@ export const config = {
|
||||
module: 'parse-server-api-mail-adapter',
|
||||
options: {
|
||||
// The email address from which emails are sent.
|
||||
sender: 'Opensign™' + ' <' + mailsender + '>',
|
||||
sender: appName + ' <' + mailsender + '>',
|
||||
// The email templates.
|
||||
templates: {
|
||||
// The template used by Parse Server to send an email for password
|
||||
@@ -141,12 +141,7 @@ export const config = {
|
||||
}
|
||||
: {}),
|
||||
filesAdapter: fsAdapter,
|
||||
auth: {
|
||||
google: {
|
||||
enabled: true,
|
||||
},
|
||||
sso: SSOAuth,
|
||||
},
|
||||
auth: { google: { enabled: true }, sso: SSOAuth },
|
||||
};
|
||||
// Client-keys like the javascript key or the .NET key are not necessary with parse-server
|
||||
// If you wish you require them, you can set them as options in the initialization above:
|
||||
|
||||
Generated
+108
-76
@@ -1770,11 +1770,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@google-cloud/firestore": {
|
||||
"version": "7.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.9.0.tgz",
|
||||
"integrity": "sha512-c4ALHT3G08rV7Zwv8Z2KG63gZh66iKdhCBeDfCpIkLrjX6EAjTD/szMdj14M+FnQuClZLFfW5bAgoOjfNmLtJg==",
|
||||
"version": "7.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.10.0.tgz",
|
||||
"integrity": "sha512-VFNhdHvfnmqcHHs6YhmSNHHxQqaaD64GwiL0c+e1qz85S8SWZPC2XFRf8p9yHRTF40Kow424s1KBU9f0fdQa+Q==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.3.0",
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"google-gax": "^4.3.3",
|
||||
@@ -1866,9 +1867,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/merge/node_modules/@graphql-tools/utils": {
|
||||
"version": "10.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.2.tgz",
|
||||
"integrity": "sha512-VZpw7wxwmQGcCGt8epw6fDb8LkoySbTJ/MU565ibKivPqCkH96XK36Et/N0RlRCYGN6QAXn5UIaSbOYYHrnpAA==",
|
||||
"version": "10.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.4.tgz",
|
||||
"integrity": "sha512-XHnyCWSlg1ccsD8s0y6ugo5GZ5TpkTiFVNPSYms5G0s6Z/xTuSmiLBfeqgkfaCwLmLaQnRCmNDL2JRnqc2R5bQ==",
|
||||
"dependencies": {
|
||||
"@graphql-typed-document-node/core": "^3.1.1",
|
||||
"cross-inspect": "1.0.1",
|
||||
@@ -1900,9 +1901,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/schema/node_modules/@graphql-tools/utils": {
|
||||
"version": "10.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.2.tgz",
|
||||
"integrity": "sha512-VZpw7wxwmQGcCGt8epw6fDb8LkoySbTJ/MU565ibKivPqCkH96XK36Et/N0RlRCYGN6QAXn5UIaSbOYYHrnpAA==",
|
||||
"version": "10.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.4.tgz",
|
||||
"integrity": "sha512-XHnyCWSlg1ccsD8s0y6ugo5GZ5TpkTiFVNPSYms5G0s6Z/xTuSmiLBfeqgkfaCwLmLaQnRCmNDL2JRnqc2R5bQ==",
|
||||
"dependencies": {
|
||||
"@graphql-typed-document-node/core": "^3.1.1",
|
||||
"cross-inspect": "1.0.1",
|
||||
@@ -1936,9 +1937,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@grpc/grpc-js": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.1.tgz",
|
||||
"integrity": "sha512-gyt/WayZrVPH2w/UTLansS7F9Nwld472JxxaETamrM8HNlsa+jSLNyKAZmhxI2Me4c3mQHFiS1wWHDY1g1Kthw==",
|
||||
"version": "1.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.11.2.tgz",
|
||||
"integrity": "sha512-DWp92gDD7/Qkj7r8kus6/HCINeo3yPZWZ3paKgDgsbKbSpoxKg1yvN8xe2Q8uE3zOsPe3bX8FQX2+XValq2yTw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
@@ -2625,6 +2626,15 @@
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"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=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.2.0.tgz",
|
||||
"integrity": "sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==",
|
||||
"version": "22.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
|
||||
"integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.13.0"
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node-fetch": {
|
||||
@@ -3793,9 +3803,9 @@
|
||||
"integrity": "sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw=="
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
|
||||
"integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg=="
|
||||
"version": "6.9.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz",
|
||||
"integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A=="
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
"version": "1.2.7",
|
||||
@@ -4157,9 +4167,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/aws4": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.1.tgz",
|
||||
"integrity": "sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA=="
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
|
||||
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.7",
|
||||
@@ -4270,9 +4280,9 @@
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
},
|
||||
"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==",
|
||||
"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",
|
||||
@@ -4282,7 +4292,7 @@
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"qs": "6.13.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
@@ -4305,6 +4315,20 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"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": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz",
|
||||
@@ -5797,29 +5821,6 @@
|
||||
"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": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
@@ -6142,11 +6143,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/firebase-admin/node_modules/@types/node": {
|
||||
"version": "20.14.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.15.tgz",
|
||||
"integrity": "sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==",
|
||||
"version": "20.16.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz",
|
||||
"integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/firebase-admin/node_modules/long": {
|
||||
@@ -6154,11 +6155,6 @@
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
|
||||
"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": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
|
||||
@@ -6600,9 +6596,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/google-gax": {
|
||||
"version": "4.3.9",
|
||||
"resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.3.9.tgz",
|
||||
"integrity": "sha512-tcjQr7sXVGMdlvcG25wSv98ap1dtF4Z6mcV0rztGIddOcezw4YMb/uTXg72JPrLep+kXcVjaJjg6oo3KLf4itQ==",
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz",
|
||||
"integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.10.9",
|
||||
@@ -7845,9 +7841,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/loglevel": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz",
|
||||
"integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==",
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz",
|
||||
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
},
|
||||
@@ -8518,9 +8514,9 @@
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||
},
|
||||
"node_modules/node-abi": {
|
||||
"version": "3.65.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz",
|
||||
"integrity": "sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==",
|
||||
"version": "3.67.0",
|
||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.67.0.tgz",
|
||||
"integrity": "sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==",
|
||||
"dependencies": {
|
||||
"semver": "^7.3.5"
|
||||
},
|
||||
@@ -9414,6 +9410,42 @@
|
||||
"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": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz",
|
||||
@@ -10417,9 +10449,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz",
|
||||
"integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
|
||||
"integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -10475,9 +10507,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
|
||||
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
@@ -11970,9 +12002,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz",
|
||||
"integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg=="
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
|
||||
Reference in New Issue
Block a user