From 5f8affb1bbb4fa231fa0b80cf9231faad4893a5a Mon Sep 17 00:00:00 2001 From: RaktimaNXG Date: Mon, 8 Apr 2024 17:13:04 +0530 Subject: [PATCH 1/7] feat: implement custom email template of request email and completion email --- apps/OpenSign/package-lock.json | 126 ++++++++ apps/OpenSign/package.json | 1 + apps/OpenSign/src/components/Header.js | 14 +- .../src/components/pdf/EditorToolbar.js | 157 ++++++++++ apps/OpenSign/src/components/pdf/EmailBody.js | 52 ++++ apps/OpenSign/src/constant/Utils.js | 48 ++- apps/OpenSign/src/pages/GuestLogin.js | 28 +- apps/OpenSign/src/pages/Login.js | 12 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 119 ++++++-- apps/OpenSign/src/pages/PlaceHolderSign.js | 286 +++++++++++++++--- apps/OpenSign/src/pages/Signup.js | 12 +- apps/OpenSign/src/pages/SignyourselfPdf.js | 32 +- apps/OpenSign/src/primitives/ModalUi.js | 14 +- apps/OpenSign/src/styles/signature.css | 3 + 14 files changed, 788 insertions(+), 116 deletions(-) create mode 100644 apps/OpenSign/src/components/pdf/EditorToolbar.js create mode 100644 apps/OpenSign/src/components/pdf/EmailBody.js diff --git a/apps/OpenSign/package-lock.json b/apps/OpenSign/package-lock.json index c9de1b9a3..311171281 100644 --- a/apps/OpenSign/package-lock.json +++ b/apps/OpenSign/package-lock.json @@ -34,6 +34,7 @@ "react-konva": "^18.2.10", "react-modal": "^3.16.1", "react-pdf": "^7.7.1", + "react-quill": "^2.0.0", "react-redux": "^9.1.0", "react-rnd": "^10.4.1", "react-router-dom": "^6.22.3", @@ -6209,6 +6210,14 @@ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==" }, + "node_modules/@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "dependencies": { + "parchment": "^1.1.2" + } + }, "node_modules/@types/range-parser": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", @@ -9432,6 +9441,25 @@ "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -10990,6 +11018,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -12616,6 +12649,21 @@ "node": ">= 10" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -16845,6 +16893,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -17198,6 +17261,11 @@ "tslib": "^2.0.3" } }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -19138,6 +19206,50 @@ } ] }, + "node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/quill-delta/node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" + }, + "node_modules/quill/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/quill/node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, "node_modules/radix-ui": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.0.1.tgz", @@ -19749,6 +19861,20 @@ } } }, + "node_modules/react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "dependencies": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, "node_modules/react-reconciler": { "version": "0.29.0", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.0.tgz", diff --git a/apps/OpenSign/package.json b/apps/OpenSign/package.json index a90a5ea44..801361767 100644 --- a/apps/OpenSign/package.json +++ b/apps/OpenSign/package.json @@ -29,6 +29,7 @@ "react-konva": "^18.2.10", "react-modal": "^3.16.1", "react-pdf": "^7.7.1", + "react-quill": "^2.0.0", "react-redux": "^9.1.0", "react-rnd": "^10.4.1", "react-router-dom": "^6.22.3", diff --git a/apps/OpenSign/src/components/Header.js b/apps/OpenSign/src/components/Header.js index eb8416b6d..94737f8a7 100644 --- a/apps/OpenSign/src/components/Header.js +++ b/apps/OpenSign/src/components/Header.js @@ -28,13 +28,15 @@ const Header = ({ showSidebar }) => { }, []); async function checkSubscription() { if (isEnableSubscription) { - const applogo = await getAppLogo(); - if (applogo) { - setAppLogo(applogo); - } else { - setAppLogo(localStorage.getItem("appLogo") || ""); - } const getIsSubscribe = await checkIsSubscribed(); + if (getIsSubscribe) { + const applogo = await getAppLogo(); + if (applogo) { + setAppLogo(applogo); + } else { + setAppLogo(localStorage.getItem("appLogo") || ""); + } + } setIsPro(getIsSubscribe); setIsSubscribe(getIsSubscribe); } diff --git a/apps/OpenSign/src/components/pdf/EditorToolbar.js b/apps/OpenSign/src/components/pdf/EditorToolbar.js new file mode 100644 index 000000000..0dd3f90d7 --- /dev/null +++ b/apps/OpenSign/src/components/pdf/EditorToolbar.js @@ -0,0 +1,157 @@ +import React from "react"; +import { Quill } from "react-quill"; + +// Custom Undo button icon component for Quill editor. You can import it directly +// from 'quill/assets/icons/undo.svg' but I found that a number of loaders do not +// handle them correctly +const CustomUndo = () => ( + + + + +); + +// Redo button icon component for Quill editor +const CustomRedo = () => ( + + + + +); + +// Undo and redo functions for Custom Toolbar +function undoChange() { + this.quill.history.undo(); +} +function redoChange() { + this.quill.history.redo(); +} + +// Add sizes to whitelist and register them +const Size = Quill.import("formats/size"); +Size.whitelist = ["extra-small", "small", "medium", "large"]; +Quill.register(Size, true); + +// Add fonts to whitelist and register them +const Font = Quill.import("formats/font"); +Font.whitelist = [ + "arial", + "comic-sans", + "courier-new", + "georgia", + "helvetica", + "lucida" +]; +Quill.register(Font, true); + +// Modules object for setting up the Quill editor +export const module1 = { + toolbar: { + container: "#toolbar1", + handlers: { + undo: undoChange, + redo: redoChange + } + }, + history: { + delay: 500, + maxStack: 100, + userOnly: true + } +}; +// Formats objects for setting up the Quill editor +export const formats = [ + "header", + "font", + "size", + "bold", + "italic", + "underline", + "align", + "strike", + "script", + "blockquote", + "background", + "list", + "bullet", + "indent", + "link", + "image", + "color", + "code-block" +]; +// Quill Toolbar component +export const QuillToolbar = ({ containerId }) => ( +
+ + + + + + +
+ + + + + + ); +} diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 089c66149..5c220092f 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -1,6 +1,6 @@ import axios from "axios"; import moment from "moment"; -import { themeColor } from "./const"; +import { isEnableSubscription, themeColor } from "./const"; import React from "react"; import { rgb } from "pdf-lib"; import Parse from "parse"; @@ -501,14 +501,32 @@ export const signPdfFun = async ( base64Url, documentId, signerObjectId, - setIsAlert + setIsAlert, + objectId, + isSubscribed ) => { - let singleSign; + let singleSign, + isCustomCompletionMail = false; + + //get tenant details + const tenantDetails = await getTenantDetails(objectId); + if (tenantDetails && tenantDetails === "user does not exist!") { + alert("User does not exist"); + } else { + if ( + tenantDetails?.CompletionBody && + tenantDetails?.CompletionSubject && + (!isEnableSubscription || isSubscribed) + ) { + isCustomCompletionMail = true; + } + } singleSign = { pdfFile: base64Url, docId: documentId, - userId: signerObjectId + userId: signerObjectId, + isCustomCompletionMail: isCustomCompletionMail }; const response = await axios @@ -1211,8 +1229,8 @@ export const multiSignEmbed = async ( position.type === radioButtonWidget ? 10 : position.type === "checkbox" - ? 10 - : newUpdateHeight; + ? 10 + : newUpdateHeight; const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight; if (signyourself) { @@ -1804,3 +1822,21 @@ export const getAppLogo = async () => { return null; } }; + +export const getTenantDetails = async (objectId) => { + try { + const tenantCreditsQuery = new Parse.Query("partners_Tenant"); + tenantCreditsQuery.equalTo("UserId", { + __type: "Pointer", + className: "_User", + objectId: objectId + }); + const res = await tenantCreditsQuery.first(); + if (res) { + const updateRes = JSON.parse(JSON.stringify(res)); + return updateRes; + } + } catch (e) { + return "user does not exist!"; + } +}; diff --git a/apps/OpenSign/src/pages/GuestLogin.js b/apps/OpenSign/src/pages/GuestLogin.js index cce8cb208..6e0bdd831 100644 --- a/apps/OpenSign/src/pages/GuestLogin.js +++ b/apps/OpenSign/src/pages/GuestLogin.js @@ -3,8 +3,8 @@ import { useNavigate, useParams } from "react-router-dom"; import "../styles/loginPage.css"; import loader from "../assets/images/loader2.gif"; import axios from "axios"; -import { themeColor } from "../constant/const"; -import { contractUsers } from "../constant/Utils"; +import { isEnableSubscription, themeColor } from "../constant/const"; +import { contractUsers, getAppLogo } from "../constant/Utils"; function GuestLogin() { const { id, userMail, contactBookId, serverUrl } = useParams(); @@ -14,14 +14,27 @@ function GuestLogin() { const [EnterOTP, setEnterOtp] = useState(false); const [loading, setLoading] = useState(false); const [isLoading, setIsLoading] = useState(true); + const [appLogo, setAppLogo] = useState( + "https://qikinnovation.ams3.digitaloceanspaces.com/logo.png" + ); useEffect(() => { handleServerUrl(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); //function generate serverUrl and parseAppId from url and save it in local storage - const handleServerUrl = () => { + const handleServerUrl = async () => { + if (isEnableSubscription) { + const applogo = await getAppLogo(); + if (applogo) { + setAppLogo(applogo); + } else { + setAppLogo(localStorage.getItem("appLogo") || ""); + } + } + //split url in array from '&' localStorage.clear(); const checkSplit = serverUrl.split("&"); @@ -164,11 +177,12 @@ function GuestLogin() { }} >
-
+
sign img
diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index f4dad1810..8c9820061 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -58,11 +58,13 @@ function Login() { }, []); const saveLogo = async () => { - const logo = await getAppLogo(); - if (logo) { - setImage(logo); - } else { - setImage(appInfo?.applogo || undefined); + if (isEnableSubscription) { + const logo = await getAppLogo(); + if (logo) { + setImage(logo); + } else { + setImage(appInfo?.applogo || undefined); + } } }; const handleChange = (event) => { diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 1334e999f..b27f62ca8 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -567,14 +567,17 @@ function PdfRequestFiles() { flag, containerWH ); - + //get ExistUserPtr object id of user class to get tenantDetails + const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId; //function for call to embed signature in pdf and get digital signature pdf try { const res = await signPdfFun( pdfBytes, documentId, signerObjectId, - setIsAlert + setIsAlert, + objectId, + isSubscribed ); if (res && res.status === "success") { setPdfUrl(res.data); @@ -586,10 +589,12 @@ function PdfRequestFiles() { const index = pdfDetails?.[0].Signers.findIndex( (x) => x.Email === jsonSender.email ); + const requestBody = pdfDetails?.[0]?.RequestBody; + const requestSubject = pdfDetails?.[0]?.RequestSubject; + const newIndex = index + 1; const user = pdfDetails?.[0].Signers[newIndex]; if (user) { - // console.log("pdfDetails", pdfDetails); const expireDate = pdfDetails?.[0].ExpiryDate.iso; const newDate = new Date(expireDate); const localExpireDate = newDate.toLocaleDateString("en-US", { @@ -597,7 +602,10 @@ function PdfRequestFiles() { month: "long", year: "numeric" }); - let sender = pdfDetails?.[0].ExtUserPtr.Email; + let senderEmail = pdfDetails?.[0].ExtUserPtr.Email; + let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; + const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; + let emailBodyWithValue, emailSubjectWithValue; try { const imgPng = @@ -625,33 +633,81 @@ function PdfRequestFiles() { ? pdfDetails[0].ExtUserPtr.Company : ""; const themeBGcolor = themeColor; + if ( + requestBody && + requestSubject && + (!isEnableSubscription || isSubscribed) + ) { + const replacedRequestBody = requestBody.replace( + /"/g, + "'" + ); + const htmlReqBody = + "" + + replacedRequestBody + + " "; + + emailBodyWithValue = htmlReqBody + .replace(/\{{sender_name}}/g, senderName) + .replace(/\{{document_title}}/g, pdfDetails?.[0].Name) + .replace( + /\{{signing_url}}/g, + `Sign here` + ) + .replace(/\{{sender_mail}}/g, senderEmail) + .replace(/\{{sender_phone}}/g, senderPhone) + .replace(/\{{expiry_date}}}/g, localExpireDate) + .replace(/\{{company_name}}/g, orgName) + .replace(/\{{receiver_name}}/g, user.Name) + .replace(/\{{receiver_email}}/g, user.Email) + .replace(/\{{receiver_phone}}/g, user.Phone); + + emailSubjectWithValue = requestSubject + .replace(/\{{sender_name}}/g, senderName) + .replace(/\{{document_title}}/g, pdfDetails?.[0].Name) + .replace( + /\{{signing_url}}/g, + `Sign here` + ) + .replace(/\{{sender_mail}}/g, senderEmail) + .replace(/\{{sender_phone}}/g, senderPhone) + .replace(/\{{expiry_date}}}/g, localExpireDate) + .replace(/\{{company_name}}/g, orgName) + .replace(/\{{receiver_name}}/g, user.Name) + .replace(/\{{receiver_email}}/g, user.Email) + .replace(/\{{receiver_phone}}/g, user.Phone); + } + let params = { extUserId: extUserId, recipient: user.Email, - subject: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, - from: sender, - html: - "

Digital Signature Request

" + - pdfDetails?.[0].ExtUserPtr.Name + - " has requested you to review and sign " + - pdfDetails?.[0].Name + - ".

Sender " + - sender + - "
Organization " + - orgName + - "
Expires on " + - localExpireDate + - "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + - sender + - " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

" + subject: requestSubject + ? emailSubjectWithValue + : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, + from: senderEmail, + html: requestBody + ? emailBodyWithValue + : "

Digital Signature Request

" + + pdfDetails?.[0].ExtUserPtr.Name + + " has requested you to review and sign " + + pdfDetails?.[0].Name + + ".

Sender " + + senderEmail + + "
Organization " + + orgName + + "
Expires on " + + localExpireDate + + "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + + senderEmail + + " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

" }; await axios.post(url, params, { headers: headers @@ -689,6 +745,7 @@ function PdfRequestFiles() { }); } } + //function for update TourStatus const closeTour = async () => { setWidgetsTour(false); @@ -1052,9 +1109,9 @@ function PdfRequestFiles() { isDecline.currnt === "Sure" ? "Are you sure want to decline this document ?" : isDecline.currnt === "YouDeclined" - ? "You have declined this document!" - : isDecline.currnt === "another" && - "You cannot sign this document as it has been declined by one or more recipient(s)." + ? "You have declined this document!" + : isDecline.currnt === "another" && + "You cannot sign this document as it has been declined by one or more recipient(s)." } footerMessage={isDecline.currnt === "Sure"} declineDoc={declineDoc} diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 2162b3202..a5345391c 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -28,7 +28,8 @@ import { textInputWidget, textWidget, radioButtonWidget, - color + color, + getTenantDetails } from "../constant/Utils"; import RenderPdf from "../components/pdf/RenderPdf"; import { useNavigate } from "react-router-dom"; @@ -40,8 +41,11 @@ 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"; function PlaceHolderSign() { + const editorRef = useRef(); const navigate = useNavigate(); const { state } = useLocation(); const [pdfDetails, setPdfDetails] = useState([]); @@ -83,6 +87,8 @@ function PlaceHolderSign() { const [signKey, setSignKey] = useState(); const [tempSignerId, setTempSignerId] = useState(""); const [blockColor, setBlockColor] = useState(""); + const [defaultBody, setDefaultBody] = useState(""); + const [defaultSubject, setDefaultSubject] = useState(""); const [pdfLoadFail, setPdfLoadFail] = useState({ status: false, type: "load" @@ -106,11 +112,14 @@ function PlaceHolderSign() { const [mailStatus, setMailStatus] = useState(""); const [isCurrUser, setIsCurrUser] = useState(false); const [isSubscribe, setIsSubscribe] = useState(false); + const [requestSubject, setRequestSubject] = useState(""); + const [requestBody, setRequestBody] = useState(""); const [isAlreadyPlace, setIsAlreadyPlace] = useState({ status: false, message: "" }); const [extUserId, setExtUserId] = useState(""); + const [isCustomize, setIsCustomize] = useState(false); const isMobile = window.innerWidth < 767; const [, drop] = useDrop({ accept: "BOX", @@ -170,6 +179,48 @@ function PlaceHolderSign() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + //function to fetch tenant Details + const fetchTenantDetails = async () => { + const user = JSON.parse( + localStorage.getItem( + `Parse/${localStorage.getItem("parseAppId")}/currentUser` + ) + ); + + if (user) { + try { + const tenantDetails = await getTenantDetails(user?.objectId); + if (tenantDetails && tenantDetails === "user does not exist!") { + alert("User does not exist"); + } else if (tenantDetails) { + const defaultRequestBody = `

Hi {{receiver_name}},


We hope this email finds you well. {{sender_name}} has requested you to review and sign {{document_title}}.

Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.


{{signing_url}}


If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.


Thanks

Team OpenSign™


`; + if (tenantDetails?.RequestBody) { + setRequestBody(tenantDetails?.RequestBody); + setRequestSubject(tenantDetails?.RequestSubject); + + setDefaultBody(defaultRequestBody); + setDefaultSubject( + `{{senderName}} has requested you to sign {{documentName}}` + ); + } else { + setRequestBody(defaultRequestBody); + setRequestSubject( + `{{senderName}} has requested you to sign {{documentName}}` + ); + setDefaultBody(defaultRequestBody); + setDefaultSubject( + `{{senderName}} has requested you to sign {{documentName}}` + ); + } + } + } catch (e) { + alert("User does not exist"); + } + } else { + alert("User does not exist"); + } + }; + useEffect(() => { if (divRef.current) { const pdfWidth = pdfNewWidthFun(divRef); @@ -204,6 +255,7 @@ function PlaceHolderSign() { } //function for get document details const getDocumentDetails = async () => { + fetchTenantDetails(); //getting document details const documentData = await contractDocument(documentId); if (documentData && documentData.length > 0) { @@ -816,6 +868,7 @@ function PlaceHolderSign() { }; const sendEmailToSigners = async () => { + let htmlReqBody; setIsUiLoading(true); const pdfUrl = await embedPrefilllData(); setIsSendAlert({}); @@ -827,12 +880,15 @@ function PlaceHolderSign() { month: "long", year: "numeric" }); - let sender = pdfDetails?.[0].ExtUserPtr.Email; + + let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email; + let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; let signerMail = signersdata.slice(); if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) { signerMail.splice(1); } + for (let i = 0; i < signerMail.length; i++) { try { const imgPng = @@ -857,34 +913,78 @@ function PlaceHolderSign() { ? pdfDetails[0].ExtUserPtr.Company : ""; const themeBGcolor = themeColor; + const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; + const documentName = `${pdfDetails?.[0].Name}`; + let emailBodyWithValue, emailSubjectWithValue; + if ( + requestBody && + requestSubject && + isCustomize && + (isSubscribe || !isEnableSubscription) + ) { + const replacedRequestBody = requestBody.replace(/"/g, "'"); + htmlReqBody = + "" + + replacedRequestBody + + " "; + + emailBodyWithValue = htmlReqBody + .replace(/\{{sender_name}}/g, senderName) + .replace(/\{{document_title}}/g, documentName) + .replace(/\{{signing_url}}/g, `Sign here`) + .replace(/\{{sender_mail}}/g, senderEmail) + .replace(/\{{sender_phone}}/g, senderPhone) + .replace(/\{{expiry_date}}}/g, localExpireDate) + .replace(/\{{company_name}}/g, orgName) + .replace(/\{{receiver_name}}/g, signerMail[i].Name) + .replace(/\{{receiver_email}}/g, signerMail[i].Email) + .replace(/\{{receiver_phone}}/g, signerMail[i].Phone); + + emailSubjectWithValue = requestSubject + .replace(/\{{sender_name}}/g, senderName) + .replace(/\{{document_title}}/g, documentName) + .replace(/\{{signing_url}}/g, `Sign here`) + .replace(/\{{sender_mail}}/g, senderEmail) + .replace(/\{{sender_phone}}/g, senderPhone) + .replace(/\{{expiry_date}}}/g, localExpireDate) + .replace(/\{{company_name}}/g, orgName) + .replace(/\{{receiver_name}}/g, signerMail[i].Name) + .replace(/\{{receiver_email}}/g, signerMail[i].Email) + .replace(/\{{receiver_phone}}/g, signerMail[i].Phone); + } + let params = { extUserId: extUserId, recipient: signerMail[i].Email, - subject: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, - from: sender, - html: - "

Digital Signature Request

" + - pdfDetails?.[0].ExtUserPtr.Name + - " has requested you to review and sign " + - pdfDetails?.[0].Name + - ".

Sender " + - sender + - "
Organization " + - orgName + - "
Expire on " + - localExpireDate + - "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + - sender + - " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

" + subject: isCustomize + ? emailSubjectWithValue + : `${senderName} has requested you to sign ${documentName}`, + from: senderEmail, + html: isCustomize + ? emailBodyWithValue + : "

Digital Signature Request

" + + pdfDetails?.[0].ExtUserPtr.Name + + " has requested you to review and sign " + + pdfDetails?.[0].Name + + ".

Sender " + + senderEmail + + "
Organization " + + orgName + + "
Expire on " + + localExpireDate + + "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + + senderEmail + + " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

" }; + sendMail = await axios.post(url, params, { headers: headers }); } catch (error) { console.log("error", error); @@ -923,21 +1023,45 @@ function PlaceHolderSign() { try { if (updateExpiryDate) { - data = { - Placeholders: filterPrefill, - SignedUrl: pdfUrl, - Signers: signers, - ExpiryDate: { - iso: updateExpiryDate, - __type: "Date" - } - }; + if (isCustomize) { + data = { + RequestBody: htmlReqBody, + RequestSubject: requestSubject, + Placeholders: filterPrefill, + SignedUrl: pdfUrl, + Signers: signers, + ExpiryDate: { + iso: updateExpiryDate, + __type: "Date" + } + }; + } else { + data = { + Placeholders: filterPrefill, + SignedUrl: pdfUrl, + Signers: signers, + ExpiryDate: { + iso: updateExpiryDate, + __type: "Date" + } + }; + } } else { - data = { - Placeholders: filterPrefill, - SignedUrl: pdfUrl, - Signers: signers - }; + if (isCustomize) { + data = { + RequestBody: htmlReqBody, + RequestSubject: requestSubject, + Placeholders: filterPrefill, + SignedUrl: pdfUrl, + Signers: signers + }; + } else { + data = { + Placeholders: filterPrefill, + SignedUrl: pdfUrl, + Signers: signers + }; + } } await axios @@ -1394,6 +1518,15 @@ function PlaceHolderSign() { const closePopup = () => { setIsAddUser({}); }; + + //function for handle ontext change and save again text in delta in Request Email flow + const handleOnchangeRequest = () => { + if (editorRef.current) { + const html = editorRef.current.editor.root.innerHTML; + setRequestBody(html); + } + }; + return ( <> @@ -1475,20 +1608,78 @@ function PlaceHolderSign() { : isSendAlert.mssg === "confirm" && "Send Mail" } handleClose={() => setIsSendAlert({})} + showHeaderMessage={isSendAlert.mssg === "confirm"} > - <div style={{ height: "100%", padding: 20 }}> + <div + className="max-h-96 overflow-y-scroll scroll-hide" + style={{ padding: 20 }} + > {isSendAlert.mssg === "sure" ? ( - <p>Please add field for all recipients.</p> + <span>Please add field for all recipients.</span> ) : isSendAlert.mssg === textWidget ? ( <p>Please confirm that you have filled the text field.</p> ) : ( isSendAlert.mssg === "confirm" && ( - <p> - Are you sure you want to send out this document for - signatures? - </p> + <> + <p className="ml-[15px]"> + Are you sure you want to send out this document for + signatures? + </p> + + <> + <div + className={ + "flex justify-end mt-4 items-center gap-1 mt-2 underline text-blue-700 focus:outline-none " + } + onClick={() => { + isSubscribe || + (!isEnableSubscription && + setIsCustomize(!isCustomize)); + }} + > + <span + className={ + isSubscribe || !isEnableSubscription + ? "cursor-pointer" + : "opacity-20 select-none" + } + > + Cutomize Email + </span> + + {!isSubscribe && isEnableSubscription && ( + <Upgrade /> + )} + </div> + </> + + {isCustomize && + (!isEnableSubscription || isSubscribe) && ( + <> + <EmailBody + editorRef={editorRef} + requestBody={requestBody} + requestSubject={requestSubject} + handleOnchangeRequest={handleOnchangeRequest} + setRequestSubject={setRequestSubject} + /> + <div + className={ + "flex justify-end items-center gap-1 mt-2 underline text-blue-700 focus:outline-none cursor-pointer " + } + onClick={() => { + setRequestBody(defaultBody); + setRequestSubject(defaultSubject); + }} + > + <span>Reset to default</span> + </div> + </> + )} + </> ) )} + <div style={{ height: "1px", @@ -1506,7 +1697,7 @@ function PlaceHolderSign() { type="button" className="finishBtn" > - Yes + Send </button> )} <button @@ -1852,6 +2043,7 @@ function PlaceHolderSign() { </button> </div> </ModalUi> + <LinkUserModal handleAddUser={handleAddUser} isAddUser={isAddUser} diff --git a/apps/OpenSign/src/pages/Signup.js b/apps/OpenSign/src/pages/Signup.js index e26ee2cf2..41720e931 100644 --- a/apps/OpenSign/src/pages/Signup.js +++ b/apps/OpenSign/src/pages/Signup.js @@ -475,11 +475,13 @@ const Signup = () => { }, []); const saveLogo = async () => { - const logo = await getAppLogo(); - if (logo) { - setImage(logo); - } else { - setImage(appInfo?.applogo || undefined); + if (isEnableSubscription) { + const logo = await getAppLogo(); + if (logo) { + setImage(logo); + } else { + setImage(appInfo?.applogo || undefined); + } } }; diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 3777a2467..3be0dc283 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -1,7 +1,7 @@ import React, { useState, useRef, useEffect } from "react"; import { PDFDocument } from "pdf-lib"; import "../styles/signature.css"; -import { themeColor } from "../constant/const"; +import { isEnableSubscription, themeColor } from "../constant/const"; import axios from "axios"; import Loader from "../primitives/LoaderWithMsg"; import loader from "../assets/images/loader2.gif"; @@ -26,7 +26,9 @@ import { contactBook, randomId, getDate, - textWidget + textWidget, + getTenantDetails, + checkIsSubscribed } from "../constant/Utils"; import { useParams } from "react-router-dom"; import Tour from "reactour"; @@ -431,13 +433,13 @@ function SignYourSelf() { Width: widgetTypeExist ? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth : dragTypeValue === "initials" - ? defaultWidthHeight(dragTypeValue).width - : "", + ? defaultWidthHeight(dragTypeValue).width + : "", Height: widgetTypeExist ? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight : dragTypeValue === "initials" - ? defaultWidthHeight(dragTypeValue).height - : "", + ? defaultWidthHeight(dragTypeValue).height + : "", options: addWidgetOptions(dragTypeValue) }; @@ -595,9 +597,25 @@ function SignYourSelf() { // console.log("signyourself", xyPostion); //function for get digital signature const signPdfFun = async (base64Url, documentId) => { + let isCustomCompletionMail = false; + const getIsSubscribe = await checkIsSubscribed(); + const tenantDetails = await getTenantDetails(jsonSender.objectId); + if (tenantDetails && tenantDetails === "user does not exist!") { + alert("User does not exist"); + } else { + if ( + tenantDetails?.CompletionBody && + tenantDetails?.CompletionSubject && + (!isEnableSubscription || getIsSubscribe) + ) { + isCustomCompletionMail = true; + } + } + let singleSign = { pdfFile: base64Url, - docId: documentId + docId: documentId, + isCustomCompletionMail: isCustomCompletionMail }; await axios diff --git a/apps/OpenSign/src/primitives/ModalUi.js b/apps/OpenSign/src/primitives/ModalUi.js index 7a7ef9aca..01b59d94b 100644 --- a/apps/OpenSign/src/primitives/ModalUi.js +++ b/apps/OpenSign/src/primitives/ModalUi.js @@ -1,4 +1,6 @@ import React from "react"; +import { isEnableSubscription } from "../constant/const"; +import PremiumAlertHeader from "./PremiumAlertHeader"; const ModalUi = ({ children, @@ -8,12 +10,13 @@ const ModalUi = ({ handleClose, showHeader = true, showClose = true, - styleClass + styleClass, + showHeaderMessage }) => { return ( <> {isOpen && ( - <div className="fixed z-[999] top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]"> + <div className="fixed z-[999] 2 top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]"> <div className={ styleClass @@ -37,6 +40,13 @@ const ModalUi = ({ )} </div> )} + {!isEnableSubscription && showHeaderMessage && ( + <PremiumAlertHeader + message={ + "Cutomize Email is free in beta, this feature will incur a fee later." + } + /> + )} <div>{children}</div> </div> </div> diff --git a/apps/OpenSign/src/styles/signature.css b/apps/OpenSign/src/styles/signature.css index 7c901ff81..a9e7ef8e4 100644 --- a/apps/OpenSign/src/styles/signature.css +++ b/apps/OpenSign/src/styles/signature.css @@ -968,6 +968,9 @@ option { } } +.scroll-hide::-webkit-scrollbar { + display: none; +} @media screen and (max-width: 350px) and (min-width: 311px) { .signatureCanvas { width: 280px; From d230ad2655258dec3b3c2ca0c757e653a8d622a8 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 8 Apr 2024 17:17:29 +0530 Subject: [PATCH 2/7] feat: add migration script of update contracts_document class to add field RequestBody and RequestSubject --- ...08133151-update_contracts_document_cls.cjs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 apps/OpenSignServer/databases/migrations/20240408133151-update_contracts_document_cls.cjs diff --git a/apps/OpenSignServer/databases/migrations/20240408133151-update_contracts_document_cls.cjs b/apps/OpenSignServer/databases/migrations/20240408133151-update_contracts_document_cls.cjs new file mode 100644 index 000000000..3c6ec8e71 --- /dev/null +++ b/apps/OpenSignServer/databases/migrations/20240408133151-update_contracts_document_cls.cjs @@ -0,0 +1,33 @@ +/** + * + * @param {Parse} Parse + */ +exports.up = async Parse => { + // TODO: set className here + const className = 'contracts_Document'; + const schema = new Parse.Schema(className); + schema.addString('RequestBody'); + schema.addString('RequestSubject'); + // TODO: Set the schema here + // Example: + // schema.addString('name').addNumber('cash'); + + return schema.update(); +}; + +/** + * + * @param {Parse} Parse + */ +exports.down = async Parse => { + // TODO: set className here + const className = 'contracts_Document'; + const schema = new Parse.Schema(className); + schema.deleteField('RequestBody'); + schema.deleteField('RequestSubject'); + // TODO: Set the schema here + // Example: + // schema.deleteField('name').deleteField('cash'); + + return schema.update(); +}; From d30105299c2188cfb7886399ab13d1eefe9a1844 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 8 Apr 2024 17:24:14 +0530 Subject: [PATCH 3/7] fix: remove loading=true from login and signup page --- apps/OpenSign/src/pages/Login.js | 1 - apps/OpenSign/src/pages/Signup.js | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 8c9820061..f192e34c5 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -1072,7 +1072,6 @@ function Login() { <div className="w-[250px] h-[66px] inline-block overflow-hidden"> <img src={image} - loading={true} className="object-contain h-full" alt="The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly." /> diff --git a/apps/OpenSign/src/pages/Signup.js b/apps/OpenSign/src/pages/Signup.js index 41720e931..85f8725f3 100644 --- a/apps/OpenSign/src/pages/Signup.js +++ b/apps/OpenSign/src/pages/Signup.js @@ -531,12 +531,7 @@ const Signup = () => { <div> <div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 "> <div className="w-[250px] h-[66px] inline-block overflow-hidden"> - <img - src={image} - loading={true} - className="object-contain h-full" - alt="logo" - /> + <img src={image} className="object-contain h-full" alt="logo" /> </div> <div className="grid grid-cols-1 md:grid-cols-2 gap-x-2"> <div className=""> From 55465a035fd0fc8f0d02dbc7e0fcbb270c9d32de Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 8 Apr 2024 18:02:03 +0530 Subject: [PATCH 4/7] fix: add function to hcnage variable string name to variable value for email body and subject --- apps/OpenSign/src/constant/Utils.js | 22 +++++++++ apps/OpenSign/src/constant/const.js | 3 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 55 +++++++++------------- apps/OpenSign/src/pages/PlaceHolderSign.js | 50 +++++++++----------- 4 files changed, 68 insertions(+), 62 deletions(-) diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 5c220092f..e8bea0151 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -1840,3 +1840,25 @@ export const getTenantDetails = async (objectId) => { return "user does not exist!"; } }; + +//function to convert variable string name to variable value of email body and subject +export function replaceMailVaribles(subject, body, variables) { + let replacedSubject = subject; + let replacedBody = body; + + for (const variable in variables) { + const regex = new RegExp(`{{${variable}}}`, "g"); + if (subject) { + replacedSubject = replacedSubject.replace(regex, variables[variable]); + } + if (body) { + replacedBody = replacedBody.replace(regex, variables[variable]); + } + } + + const result = { + subject: replacedSubject, + body: replacedBody + }; + return result; +} diff --git a/apps/OpenSign/src/constant/const.js b/apps/OpenSign/src/constant/const.js index a046b4ae1..d74c33337 100644 --- a/apps/OpenSign/src/constant/const.js +++ b/apps/OpenSign/src/constant/const.js @@ -9,4 +9,5 @@ export const modalSubmitBtnColor = "#17a2b8"; export const modalCancelBtnColor = "white"; export const themeColor = "#47a3ad"; export const iconColor = "#686968"; -export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION; +export const isEnableSubscription = false; +// process.env.REACT_APP_ENABLE_SUBSCRIPTION; diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index b27f62ca8..bc64c7766 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -21,7 +21,8 @@ import { onSaveSign, onSaveImage, addDefaultSignatureImg, - radioButtonWidget + radioButtonWidget, + replaceMailVaribles } from "../constant/Utils"; import Loader from "../primitives/LoaderWithMsg"; import HandleError from "../primitives/HandleError"; @@ -605,7 +606,6 @@ function PdfRequestFiles() { let senderEmail = pdfDetails?.[0].ExtUserPtr.Email; let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; - let emailBodyWithValue, emailSubjectWithValue; try { const imgPng = @@ -633,6 +633,7 @@ function PdfRequestFiles() { ? pdfDetails[0].ExtUserPtr.Company : ""; const themeBGcolor = themeColor; + let replaceVar; if ( requestBody && requestSubject && @@ -647,46 +648,34 @@ function PdfRequestFiles() { replacedRequestBody + "</body> </html>"; - emailBodyWithValue = htmlReqBody - .replace(/\{{sender_name}}/g, senderName) - .replace(/\{{document_title}}/g, pdfDetails?.[0].Name) - .replace( - /\{{signing_url}}/g, - `<a href=${signPdf}>Sign here</a>` - ) - .replace(/\{{sender_mail}}/g, senderEmail) - .replace(/\{{sender_phone}}/g, senderPhone) - .replace(/\{{expiry_date}}}/g, localExpireDate) - .replace(/\{{company_name}}/g, orgName) - .replace(/\{{receiver_name}}/g, user.Name) - .replace(/\{{receiver_email}}/g, user.Email) - .replace(/\{{receiver_phone}}/g, user.Phone); - - emailSubjectWithValue = requestSubject - .replace(/\{{sender_name}}/g, senderName) - .replace(/\{{document_title}}/g, pdfDetails?.[0].Name) - .replace( - /\{{signing_url}}/g, - `<a href=${signPdf}>Sign here</a>` - ) - .replace(/\{{sender_mail}}/g, senderEmail) - .replace(/\{{sender_phone}}/g, senderPhone) - .replace(/\{{expiry_date}}}/g, localExpireDate) - .replace(/\{{company_name}}/g, orgName) - .replace(/\{{receiver_name}}/g, user.Name) - .replace(/\{{receiver_email}}/g, user.Email) - .replace(/\{{receiver_phone}}/g, user.Phone); + const variables = { + document_title: pdfDetails?.[0].Name, + sender_name: senderName, + sender_mail: senderEmail, + sender_phone: senderPhone, + receiver_name: user.Name, + receiver_email: user.Email, + receiver_phone: user.Phone, + expiry_date: localExpireDate, + company_name: orgName, + signing_url: `<a href=${signPdf}>Sign here</a>` + }; + replaceVar = replaceMailVaribles( + requestSubject, + htmlReqBody, + variables + ); } let params = { extUserId: extUserId, recipient: user.Email, subject: requestSubject - ? emailSubjectWithValue + ? replaceVar?.subject : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, from: senderEmail, html: requestBody - ? emailBodyWithValue + ? replaceVar?.body : "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" + imgPng + " height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" + diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index a5345391c..1b6315901 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -29,7 +29,8 @@ import { textWidget, radioButtonWidget, color, - getTenantDetails + getTenantDetails, + replaceMailVaribles } from "../constant/Utils"; import RenderPdf from "../components/pdf/RenderPdf"; import { useNavigate } from "react-router-dom"; @@ -915,7 +916,7 @@ function PlaceHolderSign() { const themeBGcolor = themeColor; const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; const documentName = `${pdfDetails?.[0].Name}`; - let emailBodyWithValue, emailSubjectWithValue; + let replaceVar; if ( requestBody && requestSubject && @@ -928,40 +929,34 @@ function PlaceHolderSign() { replacedRequestBody + "</body> </html>"; - emailBodyWithValue = htmlReqBody - .replace(/\{{sender_name}}/g, senderName) - .replace(/\{{document_title}}/g, documentName) - .replace(/\{{signing_url}}/g, `<a href=${signPdf}>Sign here</a>`) - .replace(/\{{sender_mail}}/g, senderEmail) - .replace(/\{{sender_phone}}/g, senderPhone) - .replace(/\{{expiry_date}}}/g, localExpireDate) - .replace(/\{{company_name}}/g, orgName) - .replace(/\{{receiver_name}}/g, signerMail[i].Name) - .replace(/\{{receiver_email}}/g, signerMail[i].Email) - .replace(/\{{receiver_phone}}/g, signerMail[i].Phone); - - emailSubjectWithValue = requestSubject - .replace(/\{{sender_name}}/g, senderName) - .replace(/\{{document_title}}/g, documentName) - .replace(/\{{signing_url}}/g, `<a href=${signPdf}>Sign here</a>`) - .replace(/\{{sender_mail}}/g, senderEmail) - .replace(/\{{sender_phone}}/g, senderPhone) - .replace(/\{{expiry_date}}}/g, localExpireDate) - .replace(/\{{company_name}}/g, orgName) - .replace(/\{{receiver_name}}/g, signerMail[i].Name) - .replace(/\{{receiver_email}}/g, signerMail[i].Email) - .replace(/\{{receiver_phone}}/g, signerMail[i].Phone); + const variables = { + document_title: documentName, + sender_name: senderName, + sender_mail: senderEmail, + sender_phone: senderPhone, + receiver_name: signerMail[i].Name, + receiver_email: signerMail[i].Email, + receiver_phone: signerMail[i].Phone, + expiry_date: localExpireDate, + company_name: orgName, + signing_url: `<a href=${signPdf}>Sign here</a>` + }; + replaceVar = replaceMailVaribles( + requestSubject, + htmlReqBody, + variables + ); } let params = { extUserId: extUserId, recipient: signerMail[i].Email, subject: isCustomize - ? emailSubjectWithValue + ? replaceVar?.subject : `${senderName} has requested you to sign ${documentName}`, from: senderEmail, html: isCustomize - ? emailBodyWithValue + ? replaceVar?.body : "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" + imgPng + " height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" + @@ -990,7 +985,6 @@ function PlaceHolderSign() { console.log("error", error); } } - if (sendMail.data.result.status === "success") { setMailStatus("success"); const signers = signersdata?.map((x) => { From f70e9e2a3fb1d3e77240bfeb789842e908094ff5 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 8 Apr 2024 19:01:13 +0530 Subject: [PATCH 5/7] fix: design of customize in email --- apps/OpenSign/src/constant/const.js | 3 +-- apps/OpenSign/src/pages/PlaceHolderSign.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/OpenSign/src/constant/const.js b/apps/OpenSign/src/constant/const.js index d74c33337..a046b4ae1 100644 --- a/apps/OpenSign/src/constant/const.js +++ b/apps/OpenSign/src/constant/const.js @@ -9,5 +9,4 @@ export const modalSubmitBtnColor = "#17a2b8"; export const modalCancelBtnColor = "white"; export const themeColor = "#47a3ad"; export const iconColor = "#686968"; -export const isEnableSubscription = false; -// process.env.REACT_APP_ENABLE_SUBSCRIPTION; +export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION; diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 1b6315901..1a784fb51 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -1615,7 +1615,7 @@ function PlaceHolderSign() { ) : ( isSendAlert.mssg === "confirm" && ( <> - <p className="ml-[15px]"> + <p> Are you sure you want to send out this document for signatures? </p> @@ -1623,7 +1623,7 @@ function PlaceHolderSign() { <> <div className={ - "flex justify-end mt-4 items-center gap-1 mt-2 underline text-blue-700 focus:outline-none " + "flex mt-4 items-center gap-1 mt-2 underline text-blue-700 focus:outline-none " } onClick={() => { isSubscribe || From 4d561ca19e1bc1d1c8b75d00f3bce007b19f1a54 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 8 Apr 2024 19:11:10 +0530 Subject: [PATCH 6/7] fix: opacity of costomize button --- apps/OpenSign/src/pages/PlaceHolderSign.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 1a784fb51..4ba652a9c 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -1635,7 +1635,7 @@ function PlaceHolderSign() { className={ isSubscribe || !isEnableSubscription ? "cursor-pointer" - : "opacity-20 select-none" + : "opacity-30 select-none" } > Cutomize Email From 04e9d13206659534168a4130bc7cb694a815b6d4 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 8 Apr 2024 19:39:57 +0530 Subject: [PATCH 7/7] fix: change costomize upgrade message --- apps/OpenSign/src/pages/PlaceHolderSign.js | 2 +- apps/OpenSign/src/primitives/Upgrade.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 4ba652a9c..44b686bcb 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -1642,7 +1642,7 @@ function PlaceHolderSign() { </span> {!isSubscribe && isEnableSubscription && ( - <Upgrade /> + <Upgrade message="Upgrade to customize Email" /> )} </div> </> diff --git a/apps/OpenSign/src/primitives/Upgrade.js b/apps/OpenSign/src/primitives/Upgrade.js index b21d43270..2df62fdef 100644 --- a/apps/OpenSign/src/primitives/Upgrade.js +++ b/apps/OpenSign/src/primitives/Upgrade.js @@ -1,7 +1,7 @@ import React from "react"; import { useNavigate } from "react-router-dom"; -function Upgrade() { +function Upgrade({ message }) { const navigation = useNavigate(); return ( @@ -10,7 +10,7 @@ function Upgrade() { onClick={() => navigation("/subscription")} className="text-blue-800 text-sm cursor-pointer hover:underline" > - Upgrade now + {message ? message : "Upgrade now"} </span> </sup> );