implement share button option to share recipient's sign url after finishing a doc creation using request signature flow

This commit is contained in:
RaktimaNXG
2024-04-09 18:48:32 +05:30
parent 0fc12c957e
commit d779720f26
8 changed files with 614 additions and 533 deletions
+10
View File
@@ -43,6 +43,7 @@
"react-select": "^5.8.0", "react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6", "react-signature-canvas": "^1.0.6",
"react-tooltip": "^5.26.3", "react-tooltip": "^5.26.3",
"react-web-share": "^2.0.2",
"reactour": "^1.19.2", "reactour": "^1.19.2",
"redux": "^5.0.1", "redux": "^5.0.1",
"redux-thunk": "^3.1.0", "redux-thunk": "^3.1.0",
@@ -20225,6 +20226,15 @@
"react-dom": ">=16.6.0" "react-dom": ">=16.6.0"
} }
}, },
"node_modules/react-web-share": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/react-web-share/-/react-web-share-2.0.2.tgz",
"integrity": "sha512-bGm1TJc6xtC0MhAnYFsrzT4En7l2oAdLTgCK7nk7b4xGTg3L5gOldiPrmYQ8a/dEXNydgekvOj/tfjduIlIRVA==",
"peerDependencies": {
"react": "^16 || ^17 || ^18",
"react-dom": "^16 || ^17 || ^18"
}
},
"node_modules/reactour": { "node_modules/reactour": {
"version": "1.19.2", "version": "1.19.2",
"resolved": "https://registry.npmjs.org/reactour/-/reactour-1.19.2.tgz", "resolved": "https://registry.npmjs.org/reactour/-/reactour-1.19.2.tgz",
+1
View File
@@ -38,6 +38,7 @@
"react-select": "^5.8.0", "react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6", "react-signature-canvas": "^1.0.6",
"react-tooltip": "^5.26.3", "react-tooltip": "^5.26.3",
"react-web-share": "^2.0.2",
"reactour": "^1.19.2", "reactour": "^1.19.2",
"redux": "^5.0.1", "redux": "^5.0.1",
"redux-thunk": "^3.1.0", "redux-thunk": "^3.1.0",
+15
View File
@@ -1862,3 +1862,18 @@ export function replaceMailVaribles(subject, body, variables) {
}; };
return result; return result;
} }
export const copytoData = (text) => {
// navigator.clipboard.writeText(text);
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
} else {
// Fallback for browsers that don't support navigator.clipboard
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
}
};
+2 -2
View File
@@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const"; import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
import { checkIsSubscribed, openInNewTab } from "../constant/Utils"; import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader"; import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip"; import Tooltip from "../primitives/Tooltip";
@@ -92,7 +92,7 @@ function GenerateToken() {
}; };
const copytoclipboard = (text) => { const copytoclipboard = (text) => {
navigator.clipboard.writeText(text); copytoData(text);
setCopied(true); setCopied(true);
setTimeout(() => { setTimeout(() => {
setCopied(false); setCopied(false);
+3 -1
View File
@@ -44,7 +44,7 @@ function Login() {
Destination: "" Destination: ""
}); });
const [isModal, setIsModal] = useState(false); const [isModal, setIsModal] = useState(false);
const [image, setImage] = useState(appInfo?.applogo); const [image, setImage] = useState();
useEffect(() => { useEffect(() => {
if (localStorage.getItem("accesstoken")) { if (localStorage.getItem("accesstoken")) {
@@ -65,6 +65,8 @@ function Login() {
} else { } else {
setImage(appInfo?.applogo || undefined); setImage(appInfo?.applogo || undefined);
} }
} else {
setImage(appInfo?.applogo || undefined);
} }
}; };
const handleChange = (event) => { const handleChange = (event) => {
+299 -302
View File
@@ -385,354 +385,351 @@ function PdfRequestFiles() {
}); });
}; };
const checkSendInOrder = () => { // const checkSendInOrder = () => {
if (sendInOrder) { // if (sendInOrder) {
const index = pdfDetails?.[0].Signers.findIndex( // const index = pdfDetails?.[0].Signers.findIndex(
(x) => x.Email === jsonSender.email // (x) => x.Email === jsonSender.email
); // );
const newIndex = index - 1; // const newIndex = index - 1;
if (newIndex !== -1) { // if (newIndex !== -1) {
const user = pdfDetails?.[0].Signers[newIndex]; // const user = pdfDetails?.[0].Signers[newIndex];
const isPrevUserSigned = // const isPrevUserSigned =
pdfDetails?.[0].AuditTrail && // pdfDetails?.[0].AuditTrail &&
pdfDetails?.[0].AuditTrail.some( // pdfDetails?.[0].AuditTrail.some(
(x) => // (x) =>
x.UserPtr.objectId === user.objectId && x.Activity === "Signed" // x.UserPtr.objectId === user.objectId && x.Activity === "Signed"
); // );
if (isPrevUserSigned) { // if (isPrevUserSigned) {
return true; // return true;
} else { // } else {
return false; // return false;
} // }
} else { // } else {
return true; // return true;
} // }
} else { // } else {
return true; // return true;
} // }
}; // };
//function for embed signature or image url in pdf //function for embed signature or image url in pdf
async function embedWidgetsData() { async function embedWidgetsData() {
const validateSigning = checkSendInOrder(); // const validateSigning = checkSendInOrder();
if (validateSigning) { // if (validateSigning) {
const checkUser = signerPos.filter( const checkUser = signerPos.filter(
(data) => data.signerObjId === signerObjectId (data) => data.signerObjId === signerObjectId
); );
if (checkUser && checkUser.length > 0) { if (checkUser && checkUser.length > 0) {
let checkboxExist, let checkboxExist,
requiredRadio, requiredRadio,
showAlert = false, showAlert = false,
widgetKey, widgetKey,
radioExist, radioExist,
requiredCheckbox; requiredCheckbox;
for (let i = 0; i < checkUser[0].placeHolder.length; i++) { for (let i = 0; i < checkUser[0].placeHolder.length; i++) {
for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) { for (let j = 0; j < checkUser[0].placeHolder[i].pos.length; j++) {
checkboxExist = checkboxExist =
checkUser[0].placeHolder[i].pos[j].type === "checkbox"; checkUser[0].placeHolder[i].pos[j].type === "checkbox";
radioExist = radioExist =
checkUser[0].placeHolder[i].pos[j].type === radioButtonWidget; checkUser[0].placeHolder[i].pos[j].type === radioButtonWidget;
if (checkboxExist) { if (checkboxExist) {
requiredCheckbox = checkUser[0].placeHolder[i].pos.filter( requiredCheckbox = checkUser[0].placeHolder[i].pos.filter(
(position) => (position) =>
!position.options?.isReadOnly && position.type === "checkbox" !position.options?.isReadOnly && position.type === "checkbox"
); );
if (requiredCheckbox && requiredCheckbox.length > 0) { if (requiredCheckbox && requiredCheckbox.length > 0) {
for (let i = 0; i < requiredCheckbox.length; i++) { for (let i = 0; i < requiredCheckbox.length; i++) {
const minCount = const minCount =
requiredCheckbox[i].options?.validation?.minRequiredCount; requiredCheckbox[i].options?.validation?.minRequiredCount;
const parseMin = minCount && parseInt(minCount); const parseMin = minCount && parseInt(minCount);
const maxCount = const maxCount =
requiredCheckbox[i].options?.validation?.maxRequiredCount; requiredCheckbox[i].options?.validation?.maxRequiredCount;
const parseMax = maxCount && parseInt(maxCount); const parseMax = maxCount && parseInt(maxCount);
const response = const response = requiredCheckbox[i].options?.response?.length;
requiredCheckbox[i].options?.response?.length; const defaultValue =
const defaultValue = requiredCheckbox[i].options?.defaultValue?.length;
requiredCheckbox[i].options?.defaultValue?.length; if (parseMin === 0 && parseMax === 0) {
if (parseMin === 0 && parseMax === 0) { if (!showAlert) {
if (!showAlert) { showAlert = false;
showAlert = false; setminRequiredCount(null);
setminRequiredCount(null); }
} } else if (parseMin === 0 && parseMax > 0) {
} else if (parseMin === 0 && parseMax > 0) { if (!showAlert) {
if (!showAlert) { showAlert = false;
showAlert = false; setminRequiredCount(null);
setminRequiredCount(null); }
} } else if (!response) {
} else if (!response) { if (!defaultValue) {
if (!defaultValue) {
if (!showAlert) {
showAlert = true;
widgetKey = requiredCheckbox[i].key;
setminRequiredCount(parseMin);
}
}
} else if (parseMin > 0 && parseMin > response) {
if (!showAlert) { if (!showAlert) {
showAlert = true; showAlert = true;
widgetKey = requiredCheckbox[i].key; widgetKey = requiredCheckbox[i].key;
setminRequiredCount(parseMin); setminRequiredCount(parseMin);
} }
} }
} else if (parseMin > 0 && parseMin > response) {
if (!showAlert) {
showAlert = true;
widgetKey = requiredCheckbox[i].key;
setminRequiredCount(parseMin);
}
} }
} }
} else if (radioExist) { }
requiredRadio = checkUser[0].placeHolder[i].pos.filter( } else if (radioExist) {
(position) => requiredRadio = checkUser[0].placeHolder[i].pos.filter(
!position.options?.isReadOnly && (position) =>
position.type === radioButtonWidget !position.options?.isReadOnly &&
); position.type === radioButtonWidget
if (requiredRadio && requiredRadio?.length > 0) { );
let checkSigned; if (requiredRadio && requiredRadio?.length > 0) {
for (let i = 0; i < requiredRadio?.length; i++) { let checkSigned;
checkSigned = requiredRadio[i]?.options.response; for (let i = 0; i < requiredRadio?.length; i++) {
if (!checkSigned) { checkSigned = requiredRadio[i]?.options.response;
let checkDefaultSigned = if (!checkSigned) {
requiredRadio[i]?.options.defaultValue; let checkDefaultSigned =
requiredRadio[i]?.options.defaultValue;
if (!checkDefaultSigned) {
if (!showAlert) {
showAlert = true;
widgetKey = requiredRadio[i].key;
setminRequiredCount(null);
}
}
}
}
}
} else {
const requiredWidgets = checkUser[0].placeHolder[i].pos.filter(
(position) =>
position.options?.status === "required" &&
position.type !== radioButtonWidget &&
position.type !== "checkbox"
);
if (requiredWidgets && requiredWidgets?.length > 0) {
let checkSigned;
for (let i = 0; i < requiredWidgets?.length; i++) {
checkSigned = requiredWidgets[i]?.options?.response;
if (!checkSigned) {
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
let checkDefaultSigned =
requiredWidgets[i]?.options?.defaultValue;
if (!checkSignUrl) {
if (!checkDefaultSigned) { if (!checkDefaultSigned) {
if (!showAlert) { if (!showAlert) {
showAlert = true; showAlert = true;
widgetKey = requiredRadio[i].key; widgetKey = requiredWidgets[i].key;
setminRequiredCount(null); setminRequiredCount(null);
} }
} }
} }
} }
} }
} else {
const requiredWidgets = checkUser[0].placeHolder[i].pos.filter(
(position) =>
position.options?.status === "required" &&
position.type !== radioButtonWidget &&
position.type !== "checkbox"
);
if (requiredWidgets && requiredWidgets?.length > 0) {
let checkSigned;
for (let i = 0; i < requiredWidgets?.length; i++) {
checkSigned = requiredWidgets[i]?.options?.response;
if (!checkSigned) {
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
let checkDefaultSigned =
requiredWidgets[i]?.options?.defaultValue;
if (!checkSignUrl) {
if (!checkDefaultSigned) {
if (!showAlert) {
showAlert = true;
widgetKey = requiredWidgets[i].key;
setminRequiredCount(null);
}
}
}
}
}
}
} }
} }
} }
}
if (checkboxExist && requiredCheckbox && showAlert) { if (checkboxExist && requiredCheckbox && showAlert) {
setUnSignedWidgetId(widgetKey); setUnSignedWidgetId(widgetKey);
setWidgetsTour(true); setWidgetsTour(true);
} else if (radioExist && showAlert) { } else if (radioExist && showAlert) {
setUnSignedWidgetId(widgetKey); setUnSignedWidgetId(widgetKey);
setWidgetsTour(true); setWidgetsTour(true);
} else if (showAlert) { } else if (showAlert) {
setUnSignedWidgetId(widgetKey); setUnSignedWidgetId(widgetKey);
setWidgetsTour(true); setWidgetsTour(true);
} else { } else {
setIsUiLoading(true); setIsUiLoading(true);
const pngUrl = checkUser[0].placeHolder; const pngUrl = checkUser[0].placeHolder;
// Load a PDFDocument from the existing PDF bytes // Load a PDFDocument from the existing PDF bytes
const existingPdfBytes = await fetch(pdfUrl).then((res) => const existingPdfBytes = await fetch(pdfUrl).then((res) =>
res.arrayBuffer() res.arrayBuffer()
); );
const pdfDoc = await PDFDocument.load(existingPdfBytes, { const pdfDoc = await PDFDocument.load(existingPdfBytes, {
ignoreEncryption: true ignoreEncryption: true
}); });
const flag = false; const flag = false;
const extUserPtr = pdfDetails[0].ExtUserPtr; const extUserPtr = pdfDetails[0].ExtUserPtr;
const HeaderDocId = extUserPtr?.HeaderDocId; const HeaderDocId = extUserPtr?.HeaderDocId;
//embed document's object id to all pages in pdf document //embed document's object id to all pages in pdf document
if (!HeaderDocId) { if (!HeaderDocId) {
if (!isDocId) { if (!isDocId) {
await embedDocId(pdfDoc, documentId, allPages); await embedDocId(pdfDoc, documentId, allPages);
}
} }
//embed multi signature in pdf }
const pdfBytes = await multiSignEmbed( //embed multi signature in pdf
pngUrl, const pdfBytes = await multiSignEmbed(
pdfDoc, pngUrl,
pdfOriginalWidth, pdfDoc,
flag, pdfOriginalWidth,
containerWH 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,
objectId,
isSubscribed
); );
//get ExistUserPtr object id of user class to get tenantDetails if (res && res.status === "success") {
const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId; setPdfUrl(res.data);
//function for call to embed signature in pdf and get digital signature pdf setIsSigned(true);
try { setSignedSigners([]);
const res = await signPdfFun( setUnSignedSigners([]);
pdfBytes, getDocumentDetails();
documentId, if (sendInOrder) {
signerObjectId, const index = pdfDetails?.[0].Signers.findIndex(
setIsAlert, (x) => x.Email === jsonSender.email
objectId, );
isSubscribed const requestBody = pdfDetails?.[0]?.RequestBody;
); const requestSubject = pdfDetails?.[0]?.RequestSubject;
if (res && res.status === "success") {
setPdfUrl(res.data);
setIsSigned(true);
setSignedSigners([]);
setUnSignedSigners([]);
getDocumentDetails();
if (sendInOrder) {
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 newIndex = index + 1;
const user = pdfDetails?.[0].Signers[newIndex]; const user = pdfDetails?.[0].Signers[newIndex];
if (user) { if (user) {
const expireDate = pdfDetails?.[0].ExpiryDate.iso; const expireDate = pdfDetails?.[0].ExpiryDate.iso;
const newDate = new Date(expireDate); const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString("en-US", { const localExpireDate = newDate.toLocaleDateString("en-US", {
day: "numeric", day: "numeric",
month: "long", month: "long",
year: "numeric" year: "numeric"
}); });
let senderEmail = pdfDetails?.[0].ExtUserPtr.Email; let senderEmail = pdfDetails?.[0].ExtUserPtr.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
try { try {
const imgPng = const imgPng =
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"; "https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
let url = `${localStorage.getItem( let url = `${localStorage.getItem(
"baseUrl" "baseUrl"
)}functions/sendmailv3/`; )}functions/sendmailv3/`;
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Parse-Application-Id": "X-Parse-Application-Id":
localStorage.getItem("parseAppId"), localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken") sessionToken: localStorage.getItem("accesstoken")
};
const serverUrl = localStorage.getItem("baseUrl");
const newServer = serverUrl.replaceAll("/", "%2F");
const objectId = user.objectId;
const serverParams = `${newServer}&${localStorage.getItem(
"parseAppId"
)}&${localStorage.getItem("_appName")}`;
const hostUrl = window.location.origin;
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`;
const openSignUrl = "https://www.opensignlabs.com/contact-us";
const orgName = pdfDetails[0]?.ExtUserPtr.Company
? 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>`
}; };
const serverUrl = localStorage.getItem("baseUrl"); replaceVar = replaceMailVaribles(
const newServer = serverUrl.replaceAll("/", "%2F"); requestSubject,
const objectId = user.objectId; htmlReqBody,
const serverParams = `${newServer}&${localStorage.getItem( variables
"parseAppId" );
)}&${localStorage.getItem("_appName")}`;
const hostUrl = window.location.origin;
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`;
const openSignUrl =
"https://www.opensignlabs.com/contact-us";
const orgName = pdfDetails[0]?.ExtUserPtr.Company
? 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: 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
});
} catch (error) {
console.log("error", error);
} }
let params = {
extUserId: extUserId,
recipient: user.Email,
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
});
} catch (error) {
console.log("error", error);
} }
} }
} else {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
} }
} catch (err) { } else {
setIsAlert({ setIsAlert({
isShow: true, isShow: true,
alertMessage: "something went wrong" alertMessage: "something went wrong"
}); });
} }
} catch (err) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
} }
setIsSignPad(false);
} else {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
} }
setIsSignPad(false);
} else { } else {
setIsAlert({ setIsAlert({
isShow: true, isShow: true,
alertMessage: alertMessage: "something went wrong"
"Please wait for your turn to sign this document, as it has been set up by the creator to be signed in a specific order; you'll be notified when it's your turn."
}); });
} }
// }
// else {
// setIsAlert({
// isShow: true,
// alertMessage:
// "Please wait for your turn to sign this document, as it has been set up by the creator to be signed in a specific order; you'll be notified when it's your turn."
// });
// }
} }
//function for update TourStatus //function for update TourStatus
+281 -227
View File
@@ -16,6 +16,7 @@ import Loader from "../primitives/LoaderWithMsg";
import HandleError from "../primitives/HandleError"; import HandleError from "../primitives/HandleError";
import SignerListPlace from "../components/pdf/SignerListPlace"; import SignerListPlace from "../components/pdf/SignerListPlace";
import Header from "../components/pdf/PdfHeader"; import Header from "../components/pdf/PdfHeader";
import { RWebShare } from "react-web-share";
import { import {
pdfNewWidthFun, pdfNewWidthFun,
contractDocument, contractDocument,
@@ -30,7 +31,8 @@ import {
radioButtonWidget, radioButtonWidget,
color, color,
getTenantDetails, getTenantDetails,
replaceMailVaribles replaceMailVaribles,
copytoData
} from "../constant/Utils"; } from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf"; import RenderPdf from "../components/pdf/RenderPdf";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@@ -44,6 +46,7 @@ import WidgetNameModal from "../components/pdf/WidgetNameModal";
import { SaveFileSize } from "../constant/saveFileSize"; import { SaveFileSize } from "../constant/saveFileSize";
import { EmailBody } from "../components/pdf/EmailBody"; import { EmailBody } from "../components/pdf/EmailBody";
import Upgrade from "../primitives/Upgrade"; import Upgrade from "../primitives/Upgrade";
import Alert from "../primitives/Alert";
function PlaceHolderSign() { function PlaceHolderSign() {
const editorRef = useRef(); const editorRef = useRef();
@@ -63,6 +66,7 @@ function PlaceHolderSign() {
const [isSelectListId, setIsSelectId] = useState(); const [isSelectListId, setIsSelectId] = useState();
const [isSendAlert, setIsSendAlert] = useState({}); const [isSendAlert, setIsSendAlert] = useState({});
const [isSend, setIsSend] = useState(false); const [isSend, setIsSend] = useState(false);
const [copied, setCopied] = useState(false);
const [isLoading, setIsLoading] = useState({ const [isLoading, setIsLoading] = useState({
isLoad: true, isLoad: true,
message: "This might take some time" message: "This might take some time"
@@ -857,6 +861,7 @@ function PlaceHolderSign() {
mssg: "confirm", mssg: "confirm",
alert: true alert: true
}; };
saveDocumentDetails();
setIsSendAlert(alert); setIsSendAlert(alert);
} }
} else { } else {
@@ -868,10 +873,146 @@ function PlaceHolderSign() {
} }
}; };
//function to use save placeholder details in contracts_document
const saveDocumentDetails = async () => {
let signerMail = signersdata.slice();
if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) {
signerMail.splice(1);
}
const pdfUrl = await embedPrefilllData();
const signers = signersdata?.map((x) => {
return {
__type: "Pointer",
className: "contracts_Contactbook",
objectId: x.objectId
};
});
const addExtraDays = pdfDetails[0]?.TimeToCompleteDays
? pdfDetails[0].TimeToCompleteDays
: 15;
const currentUser = signersdata.find((x) => x.Email === currentId);
setCurrentId(currentUser?.objectId);
if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) {
const currentUserMail = Parse.User.current()?.getEmail();
const isCurrentUser = signerMail?.[0]?.Email === currentUserMail;
setIsCurrUser(isCurrentUser);
} else {
setIsCurrUser(currentUser?.objectId ? true : false);
}
let updateExpiryDate, data;
updateExpiryDate = new Date();
updateExpiryDate.setDate(updateExpiryDate.getDate() + addExtraDays);
//filter label widgets after add label widgets data on pdf
const filterPrefill = signerPos.filter((data) => data.Role !== "prefill");
try {
if (updateExpiryDate) {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers,
ExpiryDate: {
iso: updateExpiryDate,
__type: "Date"
}
};
} else {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers
};
}
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document/${documentId}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then(() => {
setIsMailSend(true);
setSignerPos([]);
})
.catch((err) => {
console.log("axois err ", err);
alert("something went wrong");
});
} catch (e) {
console.log("error", e);
alert("something went wrong");
}
};
const copytoclipboard = (text) => {
copytoData(text);
setCopied(true);
setTimeout(() => {
setCopied(false);
}, 1500); // Reset copied state after 1.5 seconds
};
//function show signer list and share link to share signUrl
const handleShareList = () => {
const shareLinkList = [];
let signerMail = signersdata.slice();
if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) {
signerMail.splice(1);
}
for (let i = 0; i < signerMail.length; i++) {
const serverUrl = localStorage.getItem("baseUrl");
const newServer = serverUrl.replaceAll("/", "%2F");
const objectId = signerMail[i].objectId;
const serverParams = `${newServer}&${localStorage.getItem(
"parseAppId"
)}&${localStorage.getItem("_appName")}`;
const hostUrl = window.location.origin;
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`;
shareLinkList.push({ signerEmail: signerMail[i].Email, url: signPdf });
}
return shareLinkList.map((data, ind) => {
return (
<div
className="flex flex-col md:flex-row justify-between mb-1"
key={ind}
>
{copied && <Alert type="success">Copied</Alert>}
<span>{data.signerEmail}</span>
<div className=" ">
<span
className="mr-3 underline text-blue-700 cursor-pointer "
onClick={() => copytoclipboard(data.url)}
>
<i className="fa-solid fa-link "></i> copy link
</span>
<RWebShare
data={{
// text: "Like humans, flamingos make friends for life",
url: data.url,
title: "Sign url"
}}
// onClick={() => console.log("shared successfully!")}
>
<i
className="fa-solid fa-share-from-square cursor-pointer "
style={{ color: themeColor }}
></i>
</RWebShare>
</div>
</div>
);
});
};
const sendEmailToSigners = async () => { const sendEmailToSigners = async () => {
let htmlReqBody; let htmlReqBody;
setIsUiLoading(true); setIsUiLoading(true);
const pdfUrl = await embedPrefilllData();
setIsSendAlert({}); setIsSendAlert({});
let sendMail; let sendMail;
const expireDate = pdfDetails?.[0].ExpiryDate.iso; const expireDate = pdfDetails?.[0].ExpiryDate.iso;
@@ -987,170 +1128,62 @@ function PlaceHolderSign() {
} }
if (sendMail.data.result.status === "success") { if (sendMail.data.result.status === "success") {
setMailStatus("success"); setMailStatus("success");
const signers = signersdata?.map((x) => {
return {
__type: "Pointer",
className: "contracts_Contactbook",
objectId: x.objectId
};
});
const addExtraDays = pdfDetails[0]?.TimeToCompleteDays
? pdfDetails[0].TimeToCompleteDays
: 15;
const currentUser = signersdata.find((x) => x.Email === currentId);
setCurrentId(currentUser?.objectId);
if ( if (
pdfDetails?.[0]?.SendinOrder && requestBody &&
pdfDetails?.[0]?.SendinOrder === true requestSubject &&
isCustomize &&
(isSubscribe || !isEnableSubscription)
) { ) {
const currentUserMail = Parse.User.current()?.getEmail(); try {
const isCurrentUser = signerMail?.[0]?.Email === currentUserMail; const data = {
setIsCurrUser(isCurrentUser); RequestBody: htmlReqBody,
} else { RequestSubject: requestSubject
setIsCurrUser(currentUser?.objectId ? true : false); };
}
let updateExpiryDate, data;
updateExpiryDate = new Date();
updateExpiryDate.setDate(updateExpiryDate.getDate() + addExtraDays);
//filter label widgets after add label widgets data on pdf
const filterPrefill = signerPos.filter((data) => data.Role !== "prefill");
try { await axios
if (updateExpiryDate) { .put(
if (isCustomize) { `${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
data = { "_appName"
RequestBody: htmlReqBody, )}_Document/${documentId}`,
RequestSubject: requestSubject, data,
Placeholders: filterPrefill, {
SignedUrl: pdfUrl, headers: {
Signers: signers, "Content-Type": "application/json",
ExpiryDate: { "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
iso: updateExpiryDate, "X-Parse-Session-Token": localStorage.getItem("accesstoken")
__type: "Date" }
} }
}; )
} else { .then(() => {
data = { setIsSend(true);
Placeholders: filterPrefill, setIsMailSend(true);
SignedUrl: pdfUrl, const loadObj = {
Signers: signers, isLoad: false
ExpiryDate: { };
iso: updateExpiryDate, setIsLoading(loadObj);
__type: "Date" setIsUiLoading(false);
} })
}; .catch((err) => {
} console.log("axois err ", err);
} else { });
if (isCustomize) { } catch (e) {
data = { console.log("error", e);
RequestBody: htmlReqBody,
RequestSubject: requestSubject,
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers
};
} else {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers
};
}
} }
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document/${documentId}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then(() => {
setIsSend(true);
setIsMailSend(true);
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
setIsUiLoading(false);
})
.catch((err) => {
console.log("axois err ", err);
});
} catch (e) {
console.log("error", e);
} }
setIsSend(true);
setIsMailSend(true);
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
setIsUiLoading(false);
} else { } else {
setMailStatus("failed"); setMailStatus("failed");
const signers = signersdata?.map((x) => { setIsSend(true);
return { setIsMailSend(true);
__type: "Pointer", setIsUiLoading(false);
className: "contracts_Contactbook",
objectId: x.objectId
};
});
const addExtraDays = pdfDetails[0]?.TimeToCompleteDays
? pdfDetails[0].TimeToCompleteDays
: 15;
const currentUser = signersdata.find((x) => x.Email === currentId);
setIsCurrUser(currentUser?.objectId ? true : false);
setCurrentId(currentUser?.objectId);
let updateExpiryDate, data;
updateExpiryDate = new Date();
updateExpiryDate.setDate(updateExpiryDate.getDate() + addExtraDays);
const filterPrefill = signerPos.filter((data) => data.Role !== "prefill");
try {
if (updateExpiryDate) {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers,
ExpiryDate: {
iso: updateExpiryDate,
__type: "Date"
}
};
} else {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers
};
}
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document/${documentId}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then(() => {
setIsSend(true);
setIsMailSend(true);
setIsUiLoading(false);
})
.catch((err) => {
console.log("axois err ", err);
});
} catch (e) {
console.log("error", e);
}
} }
}; };
const handleDontShow = (isChecked) => { const handleDontShow = (isChecked) => {
@@ -1615,92 +1648,112 @@ function PlaceHolderSign() {
) : ( ) : (
isSendAlert.mssg === "confirm" && ( isSendAlert.mssg === "confirm" && (
<> <>
<p>
Are you sure you want to send out this document for
signatures?
</p>
<> <>
{!isCustomize && (
<span>
Are you sure you want to send out this document
for signatures?
</span>
)}
{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 <div
className={ className={
"flex mt-4 items-center gap-1 mt-2 underline text-blue-700 focus:outline-none " "flex flex-col md:flex-row md:items-center md:gap-6 mt-2 "
} }
onClick={() => {
isSubscribe ||
(!isEnableSubscription &&
setIsCustomize(!isCustomize));
}}
> >
<span <div className="flex flex-row gap-2">
className={ <button
isSubscribe || !isEnableSubscription onClick={() => sendEmailToSigners()}
? "cursor-pointer" className=" shadow rounded-[2px] py-[3px] px-[25px] font-[500] text-sm "
: "opacity-30 select-none" style={{
} background: themeColor,
> color: "white"
Cutomize Email }}
</span> >
Send
</button>
{isCustomize && (
<button
onClick={() => {
setIsCustomize(false);
}}
className=" shadow rounded-[2px] py-[3px] px-[25px] font-[500] text-sm border-[0.1px] border-gray-300"
>
Close
</button>
)}
</div>
{!isCustomize && (
<span
className={
isSubscribe || !isEnableSubscription
? "cursor-pointer underline text-blue-700 focus:outline-none"
: "opacity-30 select-none underline text-blue-700 focus:outline-none"
}
onClick={() => {
isSubscribe ||
(!isEnableSubscription &&
setIsCustomize(!isCustomize));
}}
>
Cutomize Email
</span>
)}
{!isSubscribe && isEnableSubscription && ( {!isSubscribe && isEnableSubscription && (
<Upgrade message="Upgrade to customize Email" /> <Upgrade message="Upgrade to customize Email" />
)} )}
</div> </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",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
{isSendAlert.mssg === "confirm" && ( {isSendAlert.mssg === "confirm" && (
<button <>
onClick={() => sendEmailToSigners()} <div className="flex justify-center items-center mt-4">
style={{ background: themeColor }} <span
type="button" style={{
className="finishBtn" height: 1,
> width: "20%",
Send backgroundColor: "#ccc"
</button> }}
></span>
<span className="ml-[5px] mr-[5px]">or</span>
<span
style={{
height: 1,
width: "20%",
backgroundColor: "#ccc"
}}
></span>
</div>
<div className="mt-4 mb-3">{handleShareList()}</div>
</>
)} )}
<button
onClick={() => setIsSendAlert({})}
type="button"
className="finishBtn cancelBtn"
>
Close
</button>
</div> </div>
</ModalUi> </ModalUi>
@@ -1746,6 +1799,7 @@ function PlaceHolderSign() {
Yes Yes
</button> </button>
)} )}
<button <button
onClick={() => { onClick={() => {
setIsSend(false); setIsSend(false);
+3 -1
View File
@@ -23,7 +23,7 @@ const Signup = () => {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [company, setCompany] = useState(""); const [company, setCompany] = useState("");
const [jobTitle, setJobTitle] = useState(""); const [jobTitle, setJobTitle] = useState("");
const [image, setImage] = useState(appInfo?.applogo); const [image, setImage] = useState();
const [state, setState] = useState({ const [state, setState] = useState({
loading: false, loading: false,
alertType: "success", alertType: "success",
@@ -482,6 +482,8 @@ const Signup = () => {
} else { } else {
setImage(appInfo?.applogo || undefined); setImage(appInfo?.applogo || undefined);
} }
} else {
setImage(appInfo?.applogo || undefined);
} }
}; };