mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-21 23:22:35 +02:00
Merge pull request #662 from OpenSignLabs/signPdf
fix: verify email otp varification for inProgress reports
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import React from "react";
|
||||
import { rejectBtn, submitBtn, themeColor } from "../../constant/const";
|
||||
|
||||
function VerifyEmail(props) {
|
||||
return (
|
||||
<div className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center w-full h-full rounded">
|
||||
<div className="bg-white rounded outline-none md:w-[40%] w-[80%]">
|
||||
<div
|
||||
style={{ backgroundColor: themeColor }}
|
||||
className=" text-white p-[10px] rounded-t"
|
||||
>
|
||||
OTP verification
|
||||
</div>
|
||||
{props.isVerifyModal ? (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
props.setIsVerifyModal(false);
|
||||
props.handleVerifyEmail(e);
|
||||
}}
|
||||
>
|
||||
<div className="px-6 py-3">
|
||||
<label className="mb-2">Enter OTP</label>
|
||||
<input
|
||||
required
|
||||
type="tel"
|
||||
pattern="[0-9]{4}"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
placeholder="Enter OTP received over email"
|
||||
value={props.otp}
|
||||
onChange={(e) => props.setOtp(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="px-6 my-3">
|
||||
<button type="submit" className={submitBtn}>
|
||||
Verify
|
||||
</button>
|
||||
<button
|
||||
className={`${rejectBtn} ml-2`}
|
||||
onClick={(e) => props.handleResend(e)}
|
||||
>
|
||||
Resend
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
) : props.otpLoader ? (
|
||||
<div
|
||||
style={{
|
||||
height: "150px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-6 py-3">
|
||||
<p className="mb-2">Please verify your email !</p>
|
||||
<hr />
|
||||
<div className="px-0 mt-3">
|
||||
<button
|
||||
className={submitBtn}
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
props.handleVerifyBtn();
|
||||
}}
|
||||
>
|
||||
Send OTP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default VerifyEmail;
|
||||
@@ -2008,3 +2008,18 @@ export const convertPdfArrayBuffer = async (url) => {
|
||||
return "Error";
|
||||
}
|
||||
};
|
||||
|
||||
//`handleSendOTP` function is used to send otp on user's email using `SendOTPMailV1` cloud function
|
||||
export const handleSendOTP = async (email) => {
|
||||
try {
|
||||
let url = `${localStorage.getItem("baseUrl")}functions/SendOTPMailV1`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
|
||||
};
|
||||
const body = { email: email };
|
||||
await axios.post(url, body, { headers: headers });
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
+328
-159
@@ -14,7 +14,8 @@ import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import { getFileName, toDataUrl } from "../constant/Utils";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import axios from "axios";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { isEnableSubscription, submitBtn } from "../constant/const";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
|
||||
// `Form` render all type of Form on this basis of their provided in path
|
||||
function Form() {
|
||||
@@ -43,7 +44,9 @@ const Forms = (props) => {
|
||||
Description: "",
|
||||
Note: "",
|
||||
TimeToCompleteDays: 15,
|
||||
SendinOrder: "false"
|
||||
SendinOrder: "false",
|
||||
password: "",
|
||||
file: ""
|
||||
});
|
||||
const [fileupload, setFileUpload] = useState("");
|
||||
const [fileload, setfileload] = useState(false);
|
||||
@@ -52,6 +55,8 @@ const Forms = (props) => {
|
||||
const [isAlert, setIsAlert] = useState(false);
|
||||
const [isSubmit, setIsSubmit] = useState(false);
|
||||
const [isErr, setIsErr] = useState("");
|
||||
const [isPassword, setIsPassword] = useState(false);
|
||||
const [isDecrypting, setIsDecrypting] = useState(false);
|
||||
const handleStrInput = (e) => {
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
@@ -80,6 +85,7 @@ const Forms = (props) => {
|
||||
setpercentage(0);
|
||||
try {
|
||||
let files = e.target.files;
|
||||
setFormData((prev) => ({ ...prev, file: e.target.files[0] }));
|
||||
if (typeof files[0] !== "undefined") {
|
||||
const mb = Math.round(files[0].size / Math.pow(1024, 2));
|
||||
if (mb > maxFileSize) {
|
||||
@@ -96,16 +102,83 @@ const Forms = (props) => {
|
||||
await PDFDocument.load(res);
|
||||
handleFileUpload(files[0]);
|
||||
} catch (err) {
|
||||
alert(`Currently encrypted pdf files are not supported.`);
|
||||
setFileUpload("");
|
||||
e.target.value = "";
|
||||
console.log("err ", err);
|
||||
try {
|
||||
await Parse.Cloud.run("encryptedpdf", {
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("err in sending posthog encryptedpdf", err);
|
||||
if (err?.message?.includes("is encrypted")) {
|
||||
try {
|
||||
await Parse.Cloud.run("encryptedpdf", {
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("err in sending posthog encryptedpdf", err);
|
||||
}
|
||||
// console.log("err ", err);
|
||||
try {
|
||||
setIsDecrypting(true);
|
||||
const fileName = files?.[0].name;
|
||||
const size = files?.[0].size;
|
||||
const name = sanitizeFileName(fileName);
|
||||
const url = "https://ai.nxglabs.in/decryptpdf"; //"https://ai.nxglabs.in/decryptpdf"; //
|
||||
let formData = new FormData();
|
||||
formData.append("file", files[0]);
|
||||
formData.append("password", "");
|
||||
const config = {
|
||||
headers: {
|
||||
"content-type": "multipart/form-data"
|
||||
// sessiontoken: Parse.User.current().getSessionToken()
|
||||
},
|
||||
responseType: "blob"
|
||||
};
|
||||
const response = await axios.post(url, formData, config);
|
||||
const pdfBlob = new Blob([response.data], {
|
||||
type: "application/pdf"
|
||||
});
|
||||
const pdfFile = new File([pdfBlob], name, {
|
||||
type: "application/pdf"
|
||||
});
|
||||
setIsDecrypting(false);
|
||||
setfileload(true);
|
||||
|
||||
// Upload the file to Parse Server
|
||||
const parseFile = new Parse.File(
|
||||
name,
|
||||
pdfFile,
|
||||
"application/pdf"
|
||||
);
|
||||
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round(
|
||||
(loaded * 100) / total
|
||||
);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
console.log("parseFile.url() ", parseFile.url());
|
||||
setFileUpload(parseFile.url());
|
||||
setfileload(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, parseFile.url(), tenantId);
|
||||
return parseFile.url();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Error uploading file: ", err?.response);
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
if (err?.response?.status === 401) {
|
||||
setIsPassword(true);
|
||||
} else {
|
||||
setIsDecrypting(false);
|
||||
e.target.value = "";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("err ", err);
|
||||
setFileUpload("");
|
||||
e.target.value = "";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -174,7 +247,7 @@ const Forms = (props) => {
|
||||
if (isEnableSubscription) {
|
||||
try {
|
||||
setfileload(true);
|
||||
const url = "http://tools.opensignlabs.com/docxtopdf";
|
||||
const url = "https://ai.nxglabs.in/docxtopdf";
|
||||
let formData = new FormData();
|
||||
formData.append("file", files[0]);
|
||||
const config = {
|
||||
@@ -387,7 +460,68 @@ const Forms = (props) => {
|
||||
setpercentage(0);
|
||||
setTimeout(() => setIsReset(false), 50);
|
||||
};
|
||||
const handlePasswordSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsPassword(false);
|
||||
setfileload(true);
|
||||
try {
|
||||
const fileName = formData?.file?.name;
|
||||
const size = formData?.file?.size;
|
||||
const name = sanitizeFileName(fileName);
|
||||
const url = "https://ai.nxglabs.in/decryptpdf"; //
|
||||
let Data = new FormData();
|
||||
Data.append("file", formData?.file);
|
||||
Data.append("password", formData.password);
|
||||
const config = {
|
||||
headers: {
|
||||
"content-type": "multipart/form-data"
|
||||
// sessiontoken: Parse.User.current().getSessionToken()
|
||||
},
|
||||
responseType: "blob"
|
||||
};
|
||||
const response = await axios.post(url, Data, config);
|
||||
const pdfBlob = new Blob([response.data], {
|
||||
type: "application/pdf"
|
||||
});
|
||||
const pdfFile = new File([pdfBlob], name, {
|
||||
type: "application/pdf"
|
||||
});
|
||||
setIsDecrypting(false);
|
||||
// Upload the file to Parse Server
|
||||
const parseFile = new Parse.File(name, pdfFile, "application/pdf");
|
||||
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
console.log("parseFile.url() ", parseFile.url());
|
||||
setFileUpload(parseFile.url());
|
||||
setfileload(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, parseFile.url(), tenantId);
|
||||
return parseFile.url();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Error uploading file: ", err?.response);
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
if (err?.response?.status === 401) {
|
||||
setIsPassword(true);
|
||||
} else {
|
||||
setIsDecrypting(false);
|
||||
e.target.value = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
||||
<Title title={props?.title} />
|
||||
@@ -411,173 +545,208 @@ const Forms = (props) => {
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1>
|
||||
{fileload && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
|
||||
<div
|
||||
className="h-2 rounded-full bg-blue-500"
|
||||
style={{ width: `${percentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-black text-sm">{percentage}%</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs">
|
||||
<label className="block">
|
||||
{`File (pdf, png, jpg, jpeg${
|
||||
isEnableSubscription ? ", docx)" : ")"
|
||||
}`}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
{fileupload.length > 0 ? (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected : {getFileName(fileupload)}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => setFileUpload("")}
|
||||
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<>
|
||||
<ModalUi isOpen={isPassword} title={"Enter Pdf Password"}>
|
||||
<form onSubmit={handlePasswordSubmit}>
|
||||
<div className="px-6 py-3">
|
||||
{/* <label className="mb-2">Enter OTP</label> */}
|
||||
<input
|
||||
type="file"
|
||||
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
onChange={(e) => handleFileInput(e)}
|
||||
accept={
|
||||
isEnableSubscription
|
||||
? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg"
|
||||
: "application/pdf,image/png,image/jpeg"
|
||||
}
|
||||
type="text"
|
||||
name="password"
|
||||
value={formData.password}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
placeholder="Enter pdf password"
|
||||
required
|
||||
/>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<hr />
|
||||
<div className="px-6 my-3">
|
||||
<button type="submit" className={submitBtn}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalUi>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1>
|
||||
{fileload && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
|
||||
<div
|
||||
className="h-2 rounded-full bg-blue-500"
|
||||
style={{ width: `${percentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-black text-sm">{percentage}%</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
{props.title === "New Template"
|
||||
? "Template Title"
|
||||
: "Document Title"}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="Name"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Description</label>
|
||||
<input
|
||||
name="Description"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
/>
|
||||
</div>
|
||||
{props.signers && (
|
||||
<SignersInput onChange={handleSigners} isReset={isReset} required />
|
||||
)}
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Note<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="Note"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Note}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<SelectFolder
|
||||
onSuccess={handleFolder}
|
||||
folderCls={props.Cls}
|
||||
isReset={isReset}
|
||||
/>
|
||||
|
||||
{props.title === "Request Signatures" && (
|
||||
{isDecrypting && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<span className="text-black text-sm">
|
||||
Decrypting pdf please wait...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs">
|
||||
<label className="block">
|
||||
{`File (pdf, png, jpg, jpeg${
|
||||
isEnableSubscription ? ", docx)" : ")"
|
||||
}`}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
{fileupload.length > 0 ? (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected : {getFileName(fileupload)}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => setFileUpload("")}
|
||||
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<input
|
||||
type="file"
|
||||
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
onChange={(e) => handleFileInput(e)}
|
||||
accept={
|
||||
isEnableSubscription
|
||||
? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg"
|
||||
: "application/pdf,image/png,image/jpeg"
|
||||
}
|
||||
required
|
||||
/>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Time To Complete (Days)
|
||||
{props.title === "New Template"
|
||||
? "Template Title"
|
||||
: "Document Title"}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="TimeToCompleteDays"
|
||||
name="Name"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.TimeToCompleteDays}
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Send In Order</label>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<label className="block">Description</label>
|
||||
<input
|
||||
name="Description"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
/>
|
||||
</div>
|
||||
{props.signers && (
|
||||
<SignersInput
|
||||
onChange={handleSigners}
|
||||
isReset={isReset}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Note<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="Note"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Note}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<SelectFolder
|
||||
onSuccess={handleFolder}
|
||||
folderCls={props.Cls}
|
||||
isReset={isReset}
|
||||
/>
|
||||
|
||||
{props.title === "Request Signatures" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Time To Complete (Days)
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
className=""
|
||||
onChange={handleStrInput}
|
||||
type="number"
|
||||
name="TimeToCompleteDays"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.TimeToCompleteDays}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
<div className="text-center">Yes</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">No</div>
|
||||
)}
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Send In Order</label>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
className=""
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">Yes</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">No</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
className={`${
|
||||
isSubmit && "cursor-progress"
|
||||
} bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`}
|
||||
type="submit"
|
||||
disabled={isSubmit}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
<div
|
||||
className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
|
||||
onClick={() => handleReset()}
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
className={`${
|
||||
isSubmit && "cursor-progress"
|
||||
} bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`}
|
||||
type="submit"
|
||||
disabled={isSubmit}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
<div
|
||||
className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
|
||||
onClick={() => handleReset()}
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect } from "react";
|
||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import loader from "../assets/images/loader2.gif";
|
||||
import { DndProvider } from "react-dnd";
|
||||
@@ -25,7 +26,8 @@ import {
|
||||
replaceMailVaribles,
|
||||
fetchSubscription,
|
||||
convertPdfArrayBuffer,
|
||||
contractUsers
|
||||
contractUsers,
|
||||
handleSendOTP
|
||||
} from "../constant/Utils";
|
||||
import Loader from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
@@ -35,6 +37,7 @@ import PdfDeclineModal from "../primitives/PdfDeclineModal";
|
||||
import Title from "../components/Title";
|
||||
import DefaultSignature from "../components/pdf/DefaultSignature";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
|
||||
function PdfRequestFiles() {
|
||||
const { docId } = useParams();
|
||||
@@ -57,10 +60,12 @@ function PdfRequestFiles() {
|
||||
const imageRef = useRef(null);
|
||||
const [handleError, setHandleError] = useState();
|
||||
const [selectWidgetId, setSelectWidgetId] = useState("");
|
||||
const [otpLoader, setOtpLoader] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoad: true,
|
||||
message: "This might take some time"
|
||||
});
|
||||
|
||||
const [defaultSignImg, setDefaultSignImg] = useState();
|
||||
const [isDocId, setIsDocId] = useState(false);
|
||||
const [pdfNewWidth, setPdfNewWidth] = useState();
|
||||
@@ -100,6 +105,9 @@ function PdfRequestFiles() {
|
||||
const [isSubscriptionExpired, setIsSubscriptionExpired] = useState(false);
|
||||
const [extUserId, setExtUserId] = useState("");
|
||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||||
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||
const [otp, setOtp] = useState("");
|
||||
const divRef = useRef(null);
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const rowLevel =
|
||||
@@ -139,6 +147,44 @@ function PdfRequestFiles() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current]);
|
||||
|
||||
//function to use resend otp for email verification
|
||||
const handleResend = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
setOtpLoader(false);
|
||||
alert("OTP sent on you email");
|
||||
};
|
||||
//`handleVerifyEmail` function is used to verify email with otp
|
||||
const handleVerifyEmail = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
try {
|
||||
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||
otp: otp,
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
if (resEmail?.message === "Email is verified.") {
|
||||
setIsEmailVerified(true);
|
||||
} else if (resEmail?.message === "Email is already verified.") {
|
||||
setIsEmailVerified(true);
|
||||
}
|
||||
setOtp("");
|
||||
alert(resEmail.message);
|
||||
setIsVerifyModal(false);
|
||||
// handleRecipientSign();
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setOtpLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
//`handleVerifyBtn` function is used to send otp on user mail
|
||||
const handleVerifyBtn = async () => {
|
||||
setIsVerifyModal(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
};
|
||||
async function checkIsSubscribed(extUserId, contactId) {
|
||||
const isGuestSign = location.pathname.includes("/load/") || false;
|
||||
const res = await fetchSubscription(extUserId, contactId, isGuestSign);
|
||||
@@ -238,6 +284,33 @@ function PdfRequestFiles() {
|
||||
setExpiredDate(expireDateFormat);
|
||||
}
|
||||
|
||||
const isGuestSign = location.pathname.includes("/load/");
|
||||
if (
|
||||
!isGuestSign &&
|
||||
!isCompleted &&
|
||||
!declined &&
|
||||
currDate < expireUpdateDate
|
||||
) {
|
||||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||
let isEmailVerified;
|
||||
isEmailVerified = currentUser?.emailVerified;
|
||||
if (isEmailVerified) {
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
} else {
|
||||
try {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(currentUser.objectId, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
isEmailVerified = user?.get("emailVerified");
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
}
|
||||
} catch (e) {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (documentData.length > 0) {
|
||||
const checkDocIdExist =
|
||||
documentData[0].AuditTrail &&
|
||||
@@ -1179,6 +1252,18 @@ function PdfRequestFiles() {
|
||||
headMsg="Document Expired!"
|
||||
bodyMssg={`This document expired on ${expiredDate} and is no longer available to sign.`}
|
||||
/>
|
||||
{!isEmailVerified && (
|
||||
<VerifyEmail
|
||||
isVerifyModal={isVerifyModal}
|
||||
setIsVerifyModal={setIsVerifyModal}
|
||||
handleVerifyEmail={handleVerifyEmail}
|
||||
setOtp={setOtp}
|
||||
otp={otp}
|
||||
otpLoader={otpLoader}
|
||||
handleVerifyBtn={handleVerifyBtn}
|
||||
handleResend={handleResend}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ModalUi
|
||||
headerColor={defaultSignImg ? themeColor : "#dc3545"}
|
||||
|
||||
@@ -251,6 +251,7 @@ function PlaceHolderSign() {
|
||||
navigate(`/subscription`);
|
||||
}
|
||||
}
|
||||
|
||||
//function for get document details
|
||||
const getDocumentDetails = async () => {
|
||||
fetchTenantDetails();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||
import axios from "axios";
|
||||
import Loader from "../primitives/LoaderWithMsg";
|
||||
@@ -31,7 +32,8 @@ import {
|
||||
checkIsSubscribed,
|
||||
convertPdfArrayBuffer,
|
||||
fetchImageBase64,
|
||||
changeImageWH
|
||||
changeImageWH,
|
||||
handleSendOTP
|
||||
} from "../constant/Utils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Tour from "reactour";
|
||||
@@ -44,6 +46,7 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import Title from "../components/Title";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
|
||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||
function SignYourSelf() {
|
||||
@@ -91,6 +94,7 @@ function SignYourSelf() {
|
||||
const [containerWH, setContainerWH] = useState({});
|
||||
const [isPageCopy, setIsPageCopy] = useState(false);
|
||||
const [selectWidgetId, setSelectWidgetId] = useState("");
|
||||
const [otpLoader, setOtpLoader] = useState(false);
|
||||
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
||||
status: false
|
||||
});
|
||||
@@ -107,6 +111,9 @@ function SignYourSelf() {
|
||||
const [isCompleted, setIsCompleted] = useState(false);
|
||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||
const [activeMailAdapter, setActiveMailAdapter] = useState("");
|
||||
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||||
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||
const [otp, setOtp] = useState("");
|
||||
const divRef = useRef(null);
|
||||
const nodeRef = useRef(null);
|
||||
const [, drop] = useDrop({
|
||||
@@ -226,6 +233,29 @@ function SignYourSelf() {
|
||||
setSignBtnPosition([]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCompleted) {
|
||||
//check current user email verified or not
|
||||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||
let isEmailVerified;
|
||||
isEmailVerified = currentUser?.emailVerified;
|
||||
if (isEmailVerified) {
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
} else {
|
||||
try {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(currentUser.objectId, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
isEmailVerified = user?.get("emailVerified");
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
}
|
||||
} catch (e) {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
documentData === "Error: Something went wrong!" ||
|
||||
(documentData.result && documentData.result.error)
|
||||
@@ -533,6 +563,44 @@ function SignYourSelf() {
|
||||
setSelectWidgetId(key);
|
||||
setSignKey(key);
|
||||
};
|
||||
|
||||
//`handleResend` function is used to resend otp for email verification
|
||||
const handleResend = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
setOtpLoader(false);
|
||||
alert("OTP sent on you email");
|
||||
};
|
||||
//`handleVerifyEmail` function is used to verify email with otp
|
||||
const handleVerifyEmail = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
try {
|
||||
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||
otp: otp,
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
if (resEmail?.message === "Email is verified.") {
|
||||
setIsEmailVerified(true);
|
||||
} else if (resEmail?.message === "Email is already verified.") {
|
||||
setIsEmailVerified(true);
|
||||
}
|
||||
setOtp("");
|
||||
alert(resEmail.message);
|
||||
setIsVerifyModal(false);
|
||||
// handleRecipientSign();
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setOtpLoader(false);
|
||||
}
|
||||
};
|
||||
//`handleVerifyBtn` function is used to send otp on user mail
|
||||
const handleVerifyBtn = async () => {
|
||||
setIsVerifyModal(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
};
|
||||
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
||||
async function embedWidgetsData() {
|
||||
let showAlert = false;
|
||||
@@ -1037,6 +1105,18 @@ function SignYourSelf() {
|
||||
)}
|
||||
|
||||
<div className="signatureContainer" ref={divRef}>
|
||||
{!isEmailVerified && (
|
||||
<VerifyEmail
|
||||
isVerifyModal={isVerifyModal}
|
||||
setIsVerifyModal={setIsVerifyModal}
|
||||
handleVerifyEmail={handleVerifyEmail}
|
||||
setOtp={setOtp}
|
||||
otp={otp}
|
||||
otpLoader={otpLoader}
|
||||
handleVerifyBtn={handleVerifyBtn}
|
||||
handleResend={handleResend}
|
||||
/>
|
||||
)}
|
||||
{/* this component used for UI interaction and show their functionality */}
|
||||
{pdfLoadFail && !checkTourStatus && (
|
||||
<Tour
|
||||
|
||||
@@ -9,7 +9,7 @@ import axios from "axios";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
|
||||
import { checkIsSubscribed } from "../constant/Utils";
|
||||
import { checkIsSubscribed, handleSendOTP } from "../constant/Utils";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
|
||||
@@ -53,9 +53,26 @@ function UserProfile() {
|
||||
if (HeaderDocId) {
|
||||
setIsDisableDocId(HeaderDocId);
|
||||
}
|
||||
const isEmailVerified = Parse.User.current()?.attributes?.emailVerified;
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
setIsLoader(false);
|
||||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||
let isEmailVerified = currentUser?.emailVerified || false;
|
||||
if (isEmailVerified) {
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
try {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(currentUser.objectId, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
isEmailVerified = user?.get("emailVerified");
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (e) {
|
||||
alert("something went wrong!");
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
@@ -185,27 +202,15 @@ function UserProfile() {
|
||||
const handleDisableDocId = () => {
|
||||
setIsDisableDocId((prevChecked) => !prevChecked);
|
||||
};
|
||||
//`handleVerifyBtn` function is used to send otp on user mail
|
||||
const handleVerifyBtn = async () => {
|
||||
setIsVerifyModal(true);
|
||||
await handleSendOTP();
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
};
|
||||
const handleCloseVerifyModal = async () => {
|
||||
setIsVerifyModal(false);
|
||||
};
|
||||
const handleSendOTP = async () => {
|
||||
try {
|
||||
let url = `${parseBaseUrl}functions/SendOTPMailV1`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
};
|
||||
const body = { email: Parse.User.current().getEmail() };
|
||||
await axios.post(url, body, { headers: headers });
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
//`handleVerifyEmail` function is used to verify email with otp
|
||||
const handleVerifyEmail = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
@@ -228,7 +233,8 @@ function UserProfile() {
|
||||
setOtpLoader(false);
|
||||
}
|
||||
};
|
||||
const handleReset = async (e) => {
|
||||
//function to use resend otp for email verification
|
||||
const handleResend = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
await handleSendOTP();
|
||||
@@ -497,10 +503,11 @@ function UserProfile() {
|
||||
<div className="px-6 py-3">
|
||||
<label className="mb-2">Enter OTP</label>
|
||||
<input
|
||||
required
|
||||
type="tel"
|
||||
pattern="[0-9]{4}"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
placeholder="Enter OTP sent on mail"
|
||||
placeholder="Enter OTP received over email"
|
||||
value={otp}
|
||||
onChange={(e) => setOtp(e.target.value)}
|
||||
/>
|
||||
@@ -512,7 +519,7 @@ function UserProfile() {
|
||||
</button>
|
||||
<button
|
||||
className={`${rejectBtn} ml-2`}
|
||||
onClick={(e) => handleReset(e)}
|
||||
onClick={(e) => handleResend(e)}
|
||||
>
|
||||
Resend
|
||||
</button>
|
||||
|
||||
@@ -166,7 +166,6 @@ app.use(cors());
|
||||
app.use(express.json({ limit: '50mb' }));
|
||||
app.use(express.urlencoded({ limit: '50mb', extended: true }));
|
||||
app.use(function (req, res, next) {
|
||||
console.log('ip', getUserIP(req));
|
||||
req.headers['x-real-ip'] = getUserIP(req);
|
||||
next();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user