mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-20 14:42:37 +02:00
Merge pull request #568 from OpenSignLabs/custom_logo
feat: implement custom email template of request email and completion email
This commit is contained in:
Generated
+126
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 = () => (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<polygon className="ql-fill ql-stroke" points="6 10 4 12 2 10 6 10" />
|
||||
<path
|
||||
className="ql-stroke"
|
||||
d="M8.09,13.91A4.6,4.6,0,0,0,9,14,5,5,0,1,0,4,9"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Redo button icon component for Quill editor
|
||||
const CustomRedo = () => (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<polygon className="ql-fill ql-stroke" points="12 10 14 12 16 10 12 10" />
|
||||
<path
|
||||
className="ql-stroke"
|
||||
d="M9.91,13.91A4.6,4.6,0,0,1,9,14a5,5,0,1,1,5-5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// 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 }) => (
|
||||
<div id={containerId}>
|
||||
<span className="ql-formats">
|
||||
<select className="ql-font" defaultValue="arial">
|
||||
<option value="arial">Arial</option>
|
||||
<option value="comic-sans">Comic Sans</option>
|
||||
<option value="courier-new">Courier New</option>
|
||||
<option value="georgia">Georgia</option>
|
||||
<option value="helvetica">Helvetica</option>
|
||||
<option value="lucida">Lucida</option>
|
||||
</select>
|
||||
<select className="ql-size" defaultValue="medium">
|
||||
<option value="extra-small">Size 1</option>
|
||||
<option value="small">Size 2</option>
|
||||
<option value="medium">Size 3</option>
|
||||
<option value="large">Size 4</option>
|
||||
</select>
|
||||
<select className="ql-header" defaultValue="3">
|
||||
<option value="1">Heading</option>
|
||||
<option value="2">Subheading</option>
|
||||
<option value="3">Normal</option>
|
||||
</select>
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-bold" />
|
||||
<button className="ql-italic" />
|
||||
<button className="ql-underline" />
|
||||
<button className="ql-strike" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-list" value="ordered" />
|
||||
<button className="ql-list" value="bullet" />
|
||||
<button className="ql-indent" value="-1" />
|
||||
<button className="ql-indent" value="+1" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-script" value="super" />
|
||||
<button className="ql-script" value="sub" />
|
||||
<button className="ql-blockquote" />
|
||||
<button className="ql-direction" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<select className="ql-align" />
|
||||
<select className="ql-color" />
|
||||
<select className="ql-background" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-link" />
|
||||
<button className="ql-image" />
|
||||
<button className="ql-video" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-formula" />
|
||||
<button className="ql-code-block" />
|
||||
<button className="ql-clean" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-undo">
|
||||
<CustomUndo />
|
||||
</button>
|
||||
<button className="ql-redo">
|
||||
<CustomRedo />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default QuillToolbar;
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
|
||||
import ReactQuill from "react-quill";
|
||||
import "react-quill/dist/quill.snow.css";
|
||||
import EditorToolbar, { module1, formats } from "./EditorToolbar";
|
||||
|
||||
export function EmailBody(props) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className={`flex justify-center py-1 p-2`}>
|
||||
<div className=" flex flex-col">
|
||||
<form>
|
||||
<div className="text-lg font-normal text-black">
|
||||
<div className=" m-2 md:m-10 p-3 md:p-10 shadow-md hover:shadow-lg border-[1px] border-indigo-800 rounded-md">
|
||||
<label className="text-sm ml-2">
|
||||
Subject <Tooltip message={"email subject"} />
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
value={props.requestSubject}
|
||||
onChange={(e) => props.setRequestSubject(e.target.value)}
|
||||
placeholder="${senderName} has requested you to sign ${documentName}"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
/>
|
||||
<label className="text-sm ml-2 mt-3">
|
||||
Body <Tooltip message={"email body"} />
|
||||
</label>
|
||||
|
||||
<div className="px-3 py-2 w-full focus:outline-none text-xs">
|
||||
<EditorToolbar containerId="toolbar1" />
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={props.requestBody}
|
||||
// value={typedText}
|
||||
placeholder="add body of email "
|
||||
// readOnly={loading}
|
||||
// onChangeSelection={handleTextSelection} // Listen for text selection
|
||||
ref={props.editorRef}
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={props.handleOnchangeRequest}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -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,43 @@ 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!";
|
||||
}
|
||||
};
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}}
|
||||
>
|
||||
<div className="main_head">
|
||||
<div className="main-logo">
|
||||
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
||||
<img
|
||||
alt="sign img"
|
||||
src="https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"
|
||||
width="100%"
|
||||
src={appLogo}
|
||||
loading={true}
|
||||
className="object-contain h-full"
|
||||
alt="logo"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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) => {
|
||||
@@ -1070,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."
|
||||
/>
|
||||
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
onSaveSign,
|
||||
onSaveImage,
|
||||
addDefaultSignatureImg,
|
||||
radioButtonWidget
|
||||
radioButtonWidget,
|
||||
replaceMailVaribles
|
||||
} from "../constant/Utils";
|
||||
import Loader from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
@@ -567,14 +568,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 +590,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 +603,9 @@ 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}`;
|
||||
|
||||
try {
|
||||
const imgPng =
|
||||
@@ -625,33 +633,70 @@ function PdfRequestFiles() {
|
||||
? pdfDetails[0].ExtUserPtr.Company
|
||||
: "";
|
||||
const themeBGcolor = themeColor;
|
||||
let replaceVar;
|
||||
if (
|
||||
requestBody &&
|
||||
requestSubject &&
|
||||
(!isEnableSubscription || isSubscribed)
|
||||
) {
|
||||
const replacedRequestBody = requestBody.replace(
|
||||
/"/g,
|
||||
"'"
|
||||
);
|
||||
const htmlReqBody =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
||||
replacedRequestBody +
|
||||
"</body> </html>";
|
||||
|
||||
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: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`,
|
||||
from: sender,
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
|
||||
imgPng +
|
||||
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
|
||||
themeBGcolor +
|
||||
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
|
||||
pdfDetails?.[0].ExtUserPtr.Name +
|
||||
" has requested you to review and sign <strong> " +
|
||||
pdfDetails?.[0].Name +
|
||||
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
sender +
|
||||
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
sender +
|
||||
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
|
||||
openSignUrl +
|
||||
" target=_blank>here</a>.</p> </div></div></body> </html>"
|
||||
subject: requestSubject
|
||||
? replaceVar?.subject
|
||||
: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`,
|
||||
from: senderEmail,
|
||||
html: requestBody
|
||||
? 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:" +
|
||||
themeBGcolor +
|
||||
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
|
||||
pdfDetails?.[0].ExtUserPtr.Name +
|
||||
" has requested you to review and sign <strong> " +
|
||||
pdfDetails?.[0].Name +
|
||||
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
senderEmail +
|
||||
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
senderEmail +
|
||||
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
|
||||
openSignUrl +
|
||||
" target=_blank>here</a>.</p> </div></div></body> </html>"
|
||||
};
|
||||
await axios.post(url, params, {
|
||||
headers: headers
|
||||
@@ -689,6 +734,7 @@ function PdfRequestFiles() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//function for update TourStatus
|
||||
const closeTour = async () => {
|
||||
setWidgetsTour(false);
|
||||
@@ -1052,9 +1098,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}
|
||||
|
||||
@@ -28,7 +28,9 @@ import {
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
radioButtonWidget,
|
||||
color
|
||||
color,
|
||||
getTenantDetails,
|
||||
replaceMailVaribles
|
||||
} from "../constant/Utils";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@@ -40,8 +42,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 +88,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 +113,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 +180,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 = `<p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign {{document_title}}.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p>{{signing_url}}</p><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team OpenSign™</p><br>`;
|
||||
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 +256,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 +869,7 @@ function PlaceHolderSign() {
|
||||
};
|
||||
|
||||
const sendEmailToSigners = async () => {
|
||||
let htmlReqBody;
|
||||
setIsUiLoading(true);
|
||||
const pdfUrl = await embedPrefilllData();
|
||||
setIsSendAlert({});
|
||||
@@ -827,12 +881,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,40 +914,77 @@ function PlaceHolderSign() {
|
||||
? pdfDetails[0].ExtUserPtr.Company
|
||||
: "";
|
||||
const themeBGcolor = themeColor;
|
||||
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
|
||||
const documentName = `${pdfDetails?.[0].Name}`;
|
||||
let replaceVar;
|
||||
if (
|
||||
requestBody &&
|
||||
requestSubject &&
|
||||
isCustomize &&
|
||||
(isSubscribe || !isEnableSubscription)
|
||||
) {
|
||||
const replacedRequestBody = requestBody.replace(/"/g, "'");
|
||||
htmlReqBody =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
||||
replacedRequestBody +
|
||||
"</body> </html>";
|
||||
|
||||
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: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`,
|
||||
from: sender,
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
|
||||
imgPng +
|
||||
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
|
||||
themeBGcolor +
|
||||
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
|
||||
pdfDetails?.[0].ExtUserPtr.Name +
|
||||
" has requested you to review and sign <strong> " +
|
||||
pdfDetails?.[0].Name +
|
||||
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
sender +
|
||||
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
sender +
|
||||
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
|
||||
openSignUrl +
|
||||
" target=_blank>here</a>.</p> </div></div></body> </html>"
|
||||
subject: isCustomize
|
||||
? replaceVar?.subject
|
||||
: `${senderName} has requested you to sign ${documentName}`,
|
||||
from: senderEmail,
|
||||
html: isCustomize
|
||||
? 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:" +
|
||||
themeBGcolor +
|
||||
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
|
||||
pdfDetails?.[0].ExtUserPtr.Name +
|
||||
" has requested you to review and sign <strong> " +
|
||||
pdfDetails?.[0].Name +
|
||||
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
senderEmail +
|
||||
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
senderEmail +
|
||||
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
|
||||
openSignUrl +
|
||||
" target=_blank>here</a>.</p> </div></div></body> </html>"
|
||||
};
|
||||
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
}
|
||||
|
||||
if (sendMail.data.result.status === "success") {
|
||||
setMailStatus("success");
|
||||
const signers = signersdata?.map((x) => {
|
||||
@@ -923,21 +1017,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 +1512,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 (
|
||||
<>
|
||||
<Title title={state?.title ? state.title : "New Document"} />
|
||||
@@ -1475,20 +1602,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>
|
||||
Are you sure you want to send out this document for
|
||||
signatures?
|
||||
</p>
|
||||
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
"flex 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-30 select-none"
|
||||
}
|
||||
>
|
||||
Cutomize Email
|
||||
</span>
|
||||
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
<Upgrade message="Upgrade to customize Email" />
|
||||
)}
|
||||
</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 +1691,7 @@ function PlaceHolderSign() {
|
||||
type="button"
|
||||
className="finishBtn"
|
||||
>
|
||||
Yes
|
||||
Send
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
@@ -1852,6 +2037,7 @@ function PlaceHolderSign() {
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
<LinkUserModal
|
||||
handleAddUser={handleAddUser}
|
||||
isAddUser={isAddUser}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -529,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="">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -968,6 +968,9 @@ option {
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
@media screen and (max-width: 350px) and (min-width: 311px) {
|
||||
.signatureCanvas {
|
||||
width: 280px;
|
||||
|
||||
+33
@@ -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();
|
||||
};
|
||||
Reference in New Issue
Block a user