mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-06 17:57:39 +02:00
feat: quick send feature to create & send multiple documents at a time
This commit is contained in:
@@ -8,6 +8,8 @@ const BulkSendUi = (props) => {
|
|||||||
const formRef = useRef(null);
|
const formRef = useRef(null);
|
||||||
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
|
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
|
||||||
const [isSubmit, setIsSubmit] = useState(false);
|
const [isSubmit, setIsSubmit] = useState(false);
|
||||||
|
const [allowedForm, setAllowedForm] = useState(0);
|
||||||
|
const allowedSigners = 50;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (scrollOnNextUpdate && formRef.current) {
|
if (scrollOnNextUpdate && formRef.current) {
|
||||||
formRef.current.scrollIntoView({
|
formRef.current.scrollIntoView({
|
||||||
@@ -37,45 +39,48 @@ const BulkSendUi = (props) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
setForms((prevForms) => [...prevForms, { Id: 1, fields: users }]);
|
setForms((prevForms) => [...prevForms, { Id: 1, fields: users }]);
|
||||||
|
const totalForms = Math.floor(allowedSigners / users?.length);
|
||||||
|
setAllowedForm(totalForms);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
const handleInputChange = (index, signer, fieldIndex) => {
|
const handleInputChange = (index, signer, fieldIndex) => {
|
||||||
console.log("index", index);
|
|
||||||
console.log("signer", signer);
|
|
||||||
console.log("fieldIndex", fieldIndex);
|
|
||||||
|
|
||||||
const newForms = [...forms];
|
const newForms = [...forms];
|
||||||
newForms[index].fields[fieldIndex].email = signer?.Email
|
newForms[index].fields[fieldIndex].email = signer?.Email
|
||||||
? signer?.Email
|
? signer?.Email
|
||||||
: signer || "";
|
: signer || "";
|
||||||
newForms[index].fields[fieldIndex].signer = signer?.objectId ? signer : "";
|
newForms[index].fields[fieldIndex].signer = signer?.objectId ? signer : "";
|
||||||
console.log("newForms[index] ", newForms[index]);
|
|
||||||
setForms(newForms);
|
setForms(newForms);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddForm = (e) => {
|
const handleAddForm = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (props?.Placeholders.length > 0) {
|
// Check if the quick send limit has been reached
|
||||||
let newForm = [];
|
if (forms?.length < allowedForm) {
|
||||||
props?.Placeholders?.forEach((element) => {
|
if (props?.Placeholders.length > 0) {
|
||||||
if (!element.signerObjId) {
|
let newForm = [];
|
||||||
newForm = [
|
props?.Placeholders?.forEach((element) => {
|
||||||
...newForm,
|
if (!element.signerObjId) {
|
||||||
{
|
newForm = [
|
||||||
fieldId: element.Id,
|
...newForm,
|
||||||
email: "",
|
{
|
||||||
label: element.Role,
|
fieldId: element.Id,
|
||||||
signer: {}
|
email: "",
|
||||||
}
|
label: element.Role,
|
||||||
];
|
signer: {}
|
||||||
}
|
}
|
||||||
});
|
];
|
||||||
setForms([...forms, { Id: formId, fields: newForm }]);
|
}
|
||||||
|
});
|
||||||
|
setForms([...forms, { Id: formId, fields: newForm }]);
|
||||||
|
}
|
||||||
|
setFormId(formId + 1);
|
||||||
|
setScrollOnNextUpdate(true);
|
||||||
|
} else {
|
||||||
|
// If the limit has been reached, throw an error with the appropriate message
|
||||||
|
alert("Quick send reached limit.");
|
||||||
}
|
}
|
||||||
setFormId(formId + 1);
|
|
||||||
setScrollOnNextUpdate(true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveForm = (index) => {
|
const handleRemoveForm = (index) => {
|
||||||
@@ -102,7 +107,6 @@ const BulkSendUi = (props) => {
|
|||||||
);
|
);
|
||||||
// If a matching field is found, update the email value in the placeholder
|
// If a matching field is found, update the email value in the placeholder
|
||||||
const signer = field?.signer?.objectId ? field.signer : {};
|
const signer = field?.signer?.objectId ? field.signer : {};
|
||||||
console.log("signer ", signer);
|
|
||||||
if (field) {
|
if (field) {
|
||||||
return {
|
return {
|
||||||
...placeholder,
|
...placeholder,
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const SuggestionInput = (props) => {
|
|||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
placeholder="Enter text..."
|
placeholder="Enter text..."
|
||||||
className="w-full border border-gray-300 p-2 text-black rounded"
|
className="w-full border-[1px] border-gray-400 p-2 text-black rounded"
|
||||||
required={props.required}
|
required={props.required}
|
||||||
/>
|
/>
|
||||||
{showSuggestions && (
|
{showSuggestions && (
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import "../styles/loginPage.css";
|
|
||||||
import loader from "../assets/images/loader2.gif";
|
import loader from "../assets/images/loader2.gif";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
import {
|
||||||
|
isEnableSubscription,
|
||||||
|
modalSubmitBtnColor,
|
||||||
|
themeColor
|
||||||
|
} from "../constant/const";
|
||||||
import { contractUsers, getAppLogo } from "../constant/Utils";
|
import { contractUsers, getAppLogo } from "../constant/Utils";
|
||||||
import logo from "../assets/images/logo.png";
|
import logo from "../assets/images/logo.png";
|
||||||
import { appInfo } from "../constant/appinfo";
|
import { appInfo } from "../constant/appinfo";
|
||||||
|
import Parse from "parse";
|
||||||
|
|
||||||
function GuestLogin() {
|
function GuestLogin() {
|
||||||
const { id, userMail, contactBookId, base64url } = useParams();
|
const { id, userMail, contactBookId, base64url } = useParams();
|
||||||
let navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [email, setEmail] = useState(userMail);
|
const [email, setEmail] = useState(userMail);
|
||||||
const [OTP, setOTP] = useState("");
|
const [OTP, setOTP] = useState("");
|
||||||
const [EnterOTP, setEnterOtp] = useState(false);
|
const [EnterOTP, setEnterOtp] = useState(false);
|
||||||
@@ -20,9 +24,9 @@ function GuestLogin() {
|
|||||||
const [documentId, setDocumentId] = useState(id);
|
const [documentId, setDocumentId] = useState(id);
|
||||||
const [contactId, setContactId] = useState(contactBookId);
|
const [contactId, setContactId] = useState(contactBookId);
|
||||||
const [sendmail, setSendmail] = useState();
|
const [sendmail, setSendmail] = useState();
|
||||||
|
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleServerUrl();
|
handleServerUrl();
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -53,54 +57,51 @@ function GuestLogin() {
|
|||||||
//split url in array from '/'
|
//split url in array from '/'
|
||||||
const checkSplit = decodebase64.split("/");
|
const checkSplit = decodebase64.split("/");
|
||||||
setDocumentId(checkSplit[0]);
|
setDocumentId(checkSplit[0]);
|
||||||
|
setContact((prev) => ({ ...prev, email: checkSplit[1] }));
|
||||||
setEmail(checkSplit[1]);
|
setEmail(checkSplit[1]);
|
||||||
setContactId(checkSplit[2]);
|
const contactId = checkSplit?.[2];
|
||||||
setSendmail(checkSplit[3]);
|
setSendmail(checkSplit[3]);
|
||||||
|
if (!contactId) {
|
||||||
|
const params = { email: checkSplit[1], docId: checkSplit[0] };
|
||||||
|
try {
|
||||||
|
const linkContactRes = await Parse.Cloud.run(
|
||||||
|
"linkcontacttodoc",
|
||||||
|
params
|
||||||
|
);
|
||||||
|
// console.log("linkContactRes ", linkContactRes);
|
||||||
|
setContactId(linkContactRes?.contactId);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in link ext contact", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setContactId(checkSplit[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (event) => {
|
|
||||||
const { value } = event.target;
|
|
||||||
setOTP(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
//send email OTP function
|
//send email OTP function
|
||||||
const SendOtp = async (e) => {
|
const SendOtp = async () => {
|
||||||
const serverUrl =
|
setLoading(true);
|
||||||
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
|
setEmail(email);
|
||||||
const parseId =
|
try {
|
||||||
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
|
const params = { email: email.toString(), docId: documentId };
|
||||||
if (serverUrl && localStorage) {
|
const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
|
||||||
setLoading(true);
|
if (Otp) {
|
||||||
e.preventDefault();
|
setLoading(false);
|
||||||
setEmail(email);
|
setEnterOtp(true);
|
||||||
|
|
||||||
try {
|
|
||||||
let url = `${serverUrl}functions/SendOTPMailV1/`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseId
|
|
||||||
};
|
|
||||||
let body = {
|
|
||||||
email: email.toString(),
|
|
||||||
docId: documentId
|
|
||||||
};
|
|
||||||
let Otp = await axios.post(url, body, { headers: headers });
|
|
||||||
|
|
||||||
if (Otp) {
|
|
||||||
setLoading(false);
|
|
||||||
setEnterOtp(true);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
alert("something went wrong!");
|
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
alert("something went wrong!");
|
alert("something went wrong!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSendOTPBtn = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
await SendOtp();
|
||||||
|
};
|
||||||
|
|
||||||
//verify OTP send on via email
|
//verify OTP send on via email
|
||||||
const VerifyOTP = async (e) => {
|
const VerifyOTP = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -164,156 +165,171 @@ function GuestLogin() {
|
|||||||
alert("Please Enter OTP!");
|
alert("Please Enter OTP!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleUserData = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const params = { ...contact, docId: documentId };
|
||||||
|
try {
|
||||||
|
const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params);
|
||||||
|
// console.log("linkContactRes ", linkContactRes);
|
||||||
|
setContactId(linkContactRes.contactId);
|
||||||
|
setLoading(true);
|
||||||
|
setEnterOtp(true);
|
||||||
|
await SendOtp();
|
||||||
|
} catch (err) {
|
||||||
|
setLoading(false);
|
||||||
|
alert("something went wron, please try agian later.");
|
||||||
|
console.log("Err in link ext contact", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: "2rem", background: "white" }}>
|
<div style={{ padding: "2rem", background: "white" }}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div
|
<div className="flex flex-col justify-center items-center h-[100vh]">
|
||||||
style={{
|
<img className="w-[80px] h-[80px]" alt="loader" src={loader} />
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100vh",
|
|
||||||
flexDirection: "column"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="no img"
|
|
||||||
src={loader}
|
|
||||||
style={{ width: "80px", height: "80px" }}
|
|
||||||
/>
|
|
||||||
<span style={{ fontSize: "13px", color: "gray" }}>
|
<span style={{ fontSize: "13px", color: "gray" }}>
|
||||||
{isLoading.message}
|
{isLoading.message}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div className="m-1 md:m-2 border-[0.5px] border-[#c5c7c9] p-[30px] shadow-md">
|
||||||
style={{
|
<div className="md:w-[250px] md:h-[66px] inline-block overflow-hidden mt-2 mb-11">
|
||||||
margin: "10px",
|
{appLogo && (
|
||||||
border: "0.5px solid #c5c7c9",
|
<img src={appLogo} className="object-contain h-full" alt="logo" />
|
||||||
padding: "30px",
|
)}
|
||||||
boxShadow: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="main_head">
|
|
||||||
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
|
||||||
{appLogo && (
|
|
||||||
<img
|
|
||||||
src={appLogo}
|
|
||||||
className="object-contain h-full"
|
|
||||||
alt="logo"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{contactId ? (
|
||||||
{!EnterOTP ? (
|
<>
|
||||||
<div className="row">
|
{!EnterOTP ? (
|
||||||
<div className="col-sm-6 KLO">
|
<div className="w-full md:w-[50%]">
|
||||||
<span className="welcomeText">Welcome Back !</span>
|
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
|
||||||
<br />
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
<span className="KNLO">
|
Verification code is sent to your email
|
||||||
Verification code is sent to your email
|
</legend>
|
||||||
</span>
|
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 rounded shadow-md">
|
||||||
<div className="card card-box" style={{ borderRadius: "0px" }}>
|
|
||||||
<div className="card-body">
|
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="mobile"
|
name="mobile"
|
||||||
value={email}
|
value={email}
|
||||||
|
className="outline-none px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs disabled:bg-[#e0e5f5]"
|
||||||
disabled
|
disabled
|
||||||
className="loginInput"
|
|
||||||
/>
|
/>
|
||||||
<br />
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<button
|
||||||
|
className="w-[100px] text-white text-sm font-medium py-1 rounded-sm shadow-md hover:shadow-lg focus:outline-none"
|
||||||
|
style={{ background: themeColor }}
|
||||||
|
onClick={(e) => handleSendOTPBtn(e)}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "Loading..." : "Send OTP"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="btnContainer">
|
) : (
|
||||||
{loading ? (
|
<form className="w-full md:w-[50%]" onSubmit={VerifyOTP}>
|
||||||
<button
|
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
|
||||||
type="button"
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
style={{
|
You will get a OTP via Email
|
||||||
background: themeColor,
|
</legend>
|
||||||
color: "white"
|
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 rounded shadow-md">
|
||||||
}}
|
<p>Enter Verification Code</p>
|
||||||
className="verifyBtn"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className="spinner-border spinner-border-sm "
|
|
||||||
role="status"
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
Loading...
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
className="verifyBtn"
|
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white",
|
|
||||||
marginLeft: "0px !important"
|
|
||||||
}}
|
|
||||||
onClick={(e) => SendOtp(e)}
|
|
||||||
>
|
|
||||||
Send OTP
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-sm-6 KLO">
|
|
||||||
<span className="welcomeText">Welcome Back !</span>
|
|
||||||
<br />
|
|
||||||
<span className="KNLO">You will get a OTP via Email</span>
|
|
||||||
<div className="card card-box">
|
|
||||||
<div className="card-body">
|
|
||||||
<label>Enter Verification Code</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
className="loginInput"
|
className="mt-1 outline-none px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs bg-[#e0e5f5]"
|
||||||
name="OTP"
|
name="OTP"
|
||||||
value={OTP}
|
value={OTP}
|
||||||
onChange={handleChange}
|
onChange={(e) => setOTP(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<br />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-4">
|
||||||
<div>
|
|
||||||
{loading ? (
|
|
||||||
<button
|
<button
|
||||||
style={{
|
style={{ background: themeColor }}
|
||||||
background: themeColor,
|
className="w-[100px] text-white text-sm font-medium py-1 rounded-sm shadow-md hover:shadow-lg focus:outline-none"
|
||||||
color: "white"
|
type="submit"
|
||||||
}}
|
disabled={loading}
|
||||||
className="verifyBtn"
|
|
||||||
type="button"
|
|
||||||
disabled
|
|
||||||
>
|
>
|
||||||
<span
|
{loading ? "Loading..." : "Verify"}
|
||||||
className="spinner-border spinner-border-sm "
|
|
||||||
role="status"
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
Loading...
|
|
||||||
</button>
|
</button>
|
||||||
) : (
|
</div>
|
||||||
<button
|
</form>
|
||||||
type="button"
|
)}
|
||||||
onClick={(e) => VerifyOTP(e)}
|
</>
|
||||||
style={{
|
) : (
|
||||||
background: themeColor,
|
<div className="w-full md:w-[50%]">
|
||||||
color: "white"
|
<h1 className="text-2xl md:text-[30px]">Welcome</h1>
|
||||||
}}
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
className="verifyBtn"
|
Provide your details
|
||||||
>
|
</legend>
|
||||||
Verify
|
<form
|
||||||
</button>
|
className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 rounded shadow-md"
|
||||||
)}
|
onSubmit={handleUserData}
|
||||||
|
>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="name"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={contact.name}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded disabled:bg-[#e0e5f5] focus:outline-none text-xs"
|
||||||
|
disabled={loading}
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="email"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Email
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
value={contact.email}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 disabled:bg-[#e0e5f5] rounded focus:outline-none text-xs"
|
||||||
|
required
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="phone"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Phone
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="phone"
|
||||||
|
value={contact.phone}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex justify-start">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
style={{ backgroundColor: modalSubmitBtnColor }}
|
||||||
|
className="mr-2 px-[20px] py-1.5 text-white rounded shadow-md text-center focus:outline-none "
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "Loading..." : "Next"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import DefaultSignature from "../components/pdf/DefaultSignature";
|
|||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||||
|
import { appInfo } from "../constant/appinfo";
|
||||||
function useQuery() {
|
function useQuery() {
|
||||||
return new URLSearchParams(useLocation().search);
|
return new URLSearchParams(useLocation().search);
|
||||||
}
|
}
|
||||||
@@ -460,7 +461,10 @@ function PdfRequestFiles() {
|
|||||||
} else {
|
} else {
|
||||||
//else condition to check current user exist in contracts_Users class and check tour message status
|
//else condition to check current user exist in contracts_Users class and check tour message status
|
||||||
//if not then check user exist in contracts_Contactbook class and check tour message status
|
//if not then check user exist in contracts_Contactbook class and check tour message status
|
||||||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
const localuser = localStorage.getItem(
|
||||||
|
`Parse/${appInfo.appId}/currentUser`
|
||||||
|
);
|
||||||
|
const currentUser = JSON.parse(JSON.stringify(localuser));
|
||||||
const currentUserEmail = currentUser.email;
|
const currentUserEmail = currentUser.email;
|
||||||
const res = await contractUsers(currentUserEmail);
|
const res = await contractUsers(currentUserEmail);
|
||||||
if (res === "Error: Something went wrong!") {
|
if (res === "Error: Something went wrong!") {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const ReportTable = (props) => {
|
|||||||
const [isBulkSend, setIsBulkSend] = useState({});
|
const [isBulkSend, setIsBulkSend] = useState({});
|
||||||
const [templateDeatils, setTemplateDetails] = useState({});
|
const [templateDeatils, setTemplateDetails] = useState({});
|
||||||
const [placeholders, setPlaceholders] = useState([]);
|
const [placeholders, setPlaceholders] = useState([]);
|
||||||
const [isErr, setIsErr] = useState(false);
|
const [isLoader, setIsLoader] = useState({});
|
||||||
|
|
||||||
const startIndex = (currentPage - 1) * props.docPerPage;
|
const startIndex = (currentPage - 1) * props.docPerPage;
|
||||||
const { isMoreDocs, setIsNextRecord } = props;
|
const { isMoreDocs, setIsNextRecord } = props;
|
||||||
@@ -217,7 +217,6 @@ const ReportTable = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleActionBtn = (act, item) => {
|
const handleActionBtn = (act, item) => {
|
||||||
console.log("item", item);
|
|
||||||
if (act.action === "redirect") {
|
if (act.action === "redirect") {
|
||||||
handleURL(item, act);
|
handleURL(item, act);
|
||||||
} else if (act.action === "delete") {
|
} else if (act.action === "delete") {
|
||||||
@@ -560,7 +559,6 @@ const ReportTable = (props) => {
|
|||||||
};
|
};
|
||||||
const handleResendMail = async (e, doc, user) => {
|
const handleResendMail = async (e, doc, user) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log("first");
|
|
||||||
setActLoader({ [user.objectId]: true });
|
setActLoader({ [user.objectId]: true });
|
||||||
const url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
|
const url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
|
||||||
const headers = {
|
const headers = {
|
||||||
@@ -618,44 +616,73 @@ const ReportTable = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
// `handleQuickSendClose` is trigger when bulk send component trigger close event
|
||||||
const handleQuickSendClose = (status, count) => {
|
const handleQuickSendClose = (status, count) => {
|
||||||
setIsBulkSend({});
|
setIsBulkSend({});
|
||||||
setIsAlert(true);
|
setIsAlert(true);
|
||||||
if (status === "success") {
|
if (status === "success") {
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
setAlertMsg(count + " Documents sent successfully!");
|
setAlertMsg({
|
||||||
|
type: "success",
|
||||||
|
message: count + " Document sent successfully!"
|
||||||
|
});
|
||||||
|
setTimeout(() => setIsAlert(false), 1500);
|
||||||
} else {
|
} else {
|
||||||
setAlertMsg(count + " Document sent successfully!");
|
setAlertMsg({
|
||||||
|
type: "success",
|
||||||
|
message: count + " Document sent successfully!"
|
||||||
|
});
|
||||||
|
setTimeout(() => setIsAlert(false), 1500);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setIsAlert(true);
|
setAlertMsg({
|
||||||
setIsErr(true);
|
type: "danger",
|
||||||
|
message: "Something went wrong, Please try again later!"
|
||||||
|
});
|
||||||
|
setTimeout(() => setIsAlert(false), 1500);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// `handleBulkSend` is used to open modal as well as fetch template
|
||||||
|
// and show Ui on the basis template response
|
||||||
const handleBulkSend = async (template) => {
|
const handleBulkSend = async (template) => {
|
||||||
const params = {
|
setIsBulkSend({ [template.objectId]: true });
|
||||||
templateId: template.objectId,
|
setIsLoader({ [template.objectId]: true });
|
||||||
include: ["Placeholders.signerPtr"]
|
try {
|
||||||
};
|
const params = {
|
||||||
const axiosRes = await axios.post(
|
templateId: template.objectId,
|
||||||
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
|
include: ["Placeholders.signerPtr"]
|
||||||
params,
|
};
|
||||||
{
|
const axiosRes = await axios.post(
|
||||||
headers: {
|
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
|
||||||
"Content-Type": "application/json",
|
params,
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
{
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
const templateRes = axiosRes.data && axiosRes.data.result;
|
||||||
|
if (templateRes?.Placeholders?.length > 0) {
|
||||||
|
setPlaceholders(templateRes?.Placeholders);
|
||||||
|
setTemplateDetails(templateRes);
|
||||||
|
setIsLoader({});
|
||||||
|
} else {
|
||||||
|
setIsLoader(false);
|
||||||
|
setIsDocErr(true);
|
||||||
}
|
}
|
||||||
);
|
} catch (err) {
|
||||||
const templateRes = axiosRes.data && axiosRes.data.result;
|
console.log("err in fetch template in bulk modal", err);
|
||||||
if (templateRes?.Placeholders?.length > 0) {
|
setIsBulkSend({});
|
||||||
setPlaceholders(templateRes?.Placeholders);
|
setIsDocErr(false);
|
||||||
setTemplateDetails(templateRes);
|
setIsAlert(true);
|
||||||
setIsBulkSend({ [templateRes.objectId]: true });
|
setAlertMsg({
|
||||||
} else {
|
type: "danger",
|
||||||
setIsDocErr(true);
|
message: "Something went wrong, Please try again later!"
|
||||||
|
});
|
||||||
|
setTimeout(() => setIsAlert(false), 1500);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@@ -842,11 +869,11 @@ const ReportTable = (props) => {
|
|||||||
)}
|
)}
|
||||||
{isOption[item.objectId] &&
|
{isOption[item.objectId] &&
|
||||||
act.action === "option" && (
|
act.action === "option" && (
|
||||||
<div className="absolute -right-2 top-5 bg-white rounded shadow z-[20] overflow-hidden">
|
<div className="absolute -right-2 top-5 bg-white text-nowrap rounded shadow z-[20] overflow-hidden">
|
||||||
{act.subaction?.map((subact) => (
|
{act.subaction?.map((subact) => (
|
||||||
<div
|
<div
|
||||||
key={subact.btnId}
|
key={subact.btnId}
|
||||||
className="hover:bg-gray-300 cursor-pointer px-2 py-1.5 flex justify-start items-center text-black"
|
className="hover:bg-gray-300 cursor-pointer px-2 py-1.5 flex justify-start items-center text-black"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleActionBtn(subact, item)
|
handleActionBtn(subact, item)
|
||||||
}
|
}
|
||||||
@@ -898,17 +925,34 @@ const ReportTable = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
)}
|
)}
|
||||||
{isBulkSend[`${item.objectId}`] && (
|
{isBulkSend[item.objectId] && (
|
||||||
<ModalUi
|
<ModalUi
|
||||||
isOpen
|
isOpen
|
||||||
title={"Quick send"}
|
title={"Quick send"}
|
||||||
handleClose={() => setIsBulkSend({})}
|
handleClose={() => setIsBulkSend({})}
|
||||||
>
|
>
|
||||||
<BulkSendUi
|
{isLoader[item.objectId] ? (
|
||||||
Placeholders={placeholders}
|
<div className="w-full h-[100px] md:h-[100px] rounded-b-md flex justify-center items-center bg-black bg-opacity-30 z-30">
|
||||||
item={templateDeatils}
|
<div
|
||||||
handleClose={handleQuickSendClose}
|
style={{ fontSize: "45px", color: "#3dd3e0" }}
|
||||||
/>
|
className="loader-37"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{isDocErr ? (
|
||||||
|
<div className="text-black bg-white w-full h-[80px] md:h-[100px] text-sm md:text-xl flex justify-center items-center">
|
||||||
|
Please add Signers or Roles in template
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<BulkSendUi
|
||||||
|
Placeholders={placeholders}
|
||||||
|
item={templateDeatils}
|
||||||
|
handleClose={handleQuickSendClose}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
)}
|
)}
|
||||||
{isShare[item.objectId] && (
|
{isShare[item.objectId] && (
|
||||||
@@ -1158,16 +1202,6 @@ const ReportTable = (props) => {
|
|||||||
closePopup={handleContactFormModal}
|
closePopup={handleContactFormModal}
|
||||||
/>
|
/>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
<ModalUi
|
|
||||||
headColor={"#dc3545"}
|
|
||||||
isOpen={isDocErr}
|
|
||||||
title={"Receipent required"}
|
|
||||||
handleClose={() => setIsDocErr(false)}
|
|
||||||
>
|
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
|
||||||
<p>Please add receipent in template!</p>
|
|
||||||
</div>
|
|
||||||
</ModalUi>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,197 +0,0 @@
|
|||||||
.loginInput {
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
border: 1px solid #e2dddd;
|
|
||||||
background-color: #e0e5f5;
|
|
||||||
color: black;
|
|
||||||
padding-left: 1rem !important;
|
|
||||||
padding-right: 1rem !important;
|
|
||||||
padding-top: 0.5rem !important;
|
|
||||||
font-family: inherit;
|
|
||||||
font-feature-settings: inherit;
|
|
||||||
font-variation-settings: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
.loginInput:focus {
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.verifyBtn {
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
|
|
||||||
padding: 3px 30px;
|
|
||||||
color: white;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
font-size: 14px !important;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.verifyBtn:focus {
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verifyBtn:hover {
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.fakeimg {
|
|
||||||
height: 200px;
|
|
||||||
background: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GTRY {
|
|
||||||
border: 1px solid #dee2e6;
|
|
||||||
margin-top: 75px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main_head {
|
|
||||||
width: 100%;
|
|
||||||
height: 66px;
|
|
||||||
background-color: #fff;
|
|
||||||
margin-bottom: 52px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-logo {
|
|
||||||
width: 250px;
|
|
||||||
height: 66px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-box {
|
|
||||||
background: #fff;
|
|
||||||
min-height: 50px;
|
|
||||||
box-shadow: 0 20px 20px rgba(0, 0, 0, 0.1);
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
transition: 0.5s;
|
|
||||||
border: 1px solid #f2f2f2;
|
|
||||||
border-radius: 7px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.LKJH {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GTR {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.JUI {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #15b4e9;
|
|
||||||
border-color: #15b4e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #15b4e9;
|
|
||||||
border-color: #15b4e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-reg {
|
|
||||||
color: #15b4e9;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-color: #15b4e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-reg:hover {
|
|
||||||
color: #15b4e9;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-color: #15b4e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.SYTU {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MLKI {
|
|
||||||
float: right;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-btn {
|
|
||||||
width: 100% !important;
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 667px) {
|
|
||||||
.MLKI {
|
|
||||||
float: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-btn {
|
|
||||||
width: 210px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.MLKI a {
|
|
||||||
color: #212529;
|
|
||||||
}
|
|
||||||
|
|
||||||
.KNLO {
|
|
||||||
color: #878787;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-check-label {
|
|
||||||
margin-left: 17px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.welcomeText {
|
|
||||||
font-size: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
.KLO1 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MLKI a {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-check-label {
|
|
||||||
font-size: 12px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MKUY {
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main_head {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
padding-top: 10px;
|
|
||||||
border-bottom: 1px dashed #eeeeee;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 366px) {
|
|
||||||
.main-logo {
|
|
||||||
width: 150px;
|
|
||||||
height: 46px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.welcomeText {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
.KNLO {
|
|
||||||
color: #878787;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
.btnContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,6 +38,7 @@ import VerifyEmail from './parsefunction/VerifyEmail.js';
|
|||||||
import encryptedpdf from './parsefunction/encryptedPdf.js';
|
import encryptedpdf from './parsefunction/encryptedPdf.js';
|
||||||
import { getSignedUrl } from './parsefunction/getSignedUrl.js';
|
import { getSignedUrl } from './parsefunction/getSignedUrl.js';
|
||||||
import createBatchDocs from './parsefunction/createBatchDocs.js';
|
import createBatchDocs from './parsefunction/createBatchDocs.js';
|
||||||
|
import linkContactToDoc from './parsefunction/linkContactToDoc.js';
|
||||||
|
|
||||||
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
|
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
|
||||||
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
||||||
@@ -86,3 +87,4 @@ Parse.Cloud.define('verifyemail', VerifyEmail);
|
|||||||
Parse.Cloud.define('encryptedpdf', encryptedpdf);
|
Parse.Cloud.define('encryptedpdf', encryptedpdf);
|
||||||
Parse.Cloud.define('getsignedurl', getSignedUrl);
|
Parse.Cloud.define('getsignedurl', getSignedUrl);
|
||||||
Parse.Cloud.define('batchdocuments', createBatchDocs);
|
Parse.Cloud.define('batchdocuments', createBatchDocs);
|
||||||
|
Parse.Cloud.define('linkcontacttodoc', linkContactToDoc);
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ async function sendMail(document, sessionToken) {
|
|||||||
const hostUrl = baseUrl.origin;
|
const hostUrl = baseUrl.origin;
|
||||||
let encodeBase64;
|
let encodeBase64;
|
||||||
if (objectId) {
|
if (objectId) {
|
||||||
encodeBase64 = btoa(`${document.objectId}}/${signerMail[i].signerPtr.Email}/${objectId}`);
|
encodeBase64 = btoa(`${document.objectId}/${signerMail[i].signerPtr.Email}/${objectId}`);
|
||||||
} else {
|
} else {
|
||||||
encodeBase64 = btoa(`${document.objectId}}/${signerMail[i].email}`);
|
encodeBase64 = btoa(`${document.objectId}/${signerMail[i].email}`);
|
||||||
}
|
}
|
||||||
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||||
const openSignUrl = 'https://www.opensignlabs.com/';
|
const openSignUrl = 'https://www.opensignlabs.com/';
|
||||||
@@ -93,18 +93,12 @@ export default async function createBatchDocs(request) {
|
|||||||
try {
|
try {
|
||||||
const requests = Documents.map(x => {
|
const requests = Documents.map(x => {
|
||||||
const Signers = x.Signers;
|
const Signers = x.Signers;
|
||||||
const allSigner = x?.Placeholders?.map(item => {
|
const allSigner = x?.Placeholders?.map(
|
||||||
const signer = Signers?.find(e => item?.signerPtr?.objectId === e?.objectId);
|
item => Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr
|
||||||
if (signer) {
|
).filter(signer => Object.keys(signer).length > 0);
|
||||||
return signer;
|
|
||||||
} else {
|
|
||||||
return item?.signerPtr?.objectId && item?.signerPtr;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const isoDate = date.toISOString();
|
const isoDate = date.toISOString();
|
||||||
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||||
// console.log('allSigner ', allSigner);
|
|
||||||
if (allSigner && allSigner.length > 0) {
|
if (allSigner && allSigner.length > 0) {
|
||||||
allSigner.forEach(x => {
|
allSigner.forEach(x => {
|
||||||
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||||
@@ -146,7 +140,7 @@ export default async function createBatchDocs(request) {
|
|||||||
objectId: y.objectId,
|
objectId: y.objectId,
|
||||||
})),
|
})),
|
||||||
ACL: Acl,
|
ACL: Acl,
|
||||||
RemindOnceInEvery: x.RemindOnceInEvery,
|
RemindOnceInEvery: x.RemindOnceInEvery || 5,
|
||||||
AutomaticReminders: x.AutomaticReminders || false,
|
AutomaticReminders: x.AutomaticReminders || false,
|
||||||
TimeToCompleteDays: x.TimeToCompleteDays || 15,
|
TimeToCompleteDays: x.TimeToCompleteDays || 15,
|
||||||
OriginIp: Ip,
|
OriginIp: Ip,
|
||||||
|
|||||||
@@ -0,0 +1,276 @@
|
|||||||
|
// `saveRoleContact` is used to save user in contracts_Guest role and create contact
|
||||||
|
const saveRoleContact = async contact => {
|
||||||
|
try {
|
||||||
|
const Role = new Parse.Query(Parse.Role);
|
||||||
|
const guestRole = await Role.equalTo('name', 'contracts_Guest').first();
|
||||||
|
if (guestRole) {
|
||||||
|
// Check if the user is already in the role
|
||||||
|
const relation = guestRole.relation('users');
|
||||||
|
const usersInRoleQuery = relation.query();
|
||||||
|
usersInRoleQuery.equalTo('objectId', contact.UserId.objectId);
|
||||||
|
const usersInRole = await usersInRoleQuery.find();
|
||||||
|
if (usersInRole.length > 0) {
|
||||||
|
console.log('User already added to Guest role.');
|
||||||
|
} else {
|
||||||
|
relation.add({ __type: 'Pointer', className: '_User', id: contact.UserId.objectId });
|
||||||
|
await guestRole.save(null, { useMasterKey: true });
|
||||||
|
// console.log('User added to Guest role successfully.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err in role save', err);
|
||||||
|
}
|
||||||
|
const contactQuery = new Parse.Object('contracts_Contactbook');
|
||||||
|
contactQuery.set('Name', contact.Name);
|
||||||
|
contactQuery.set('Email', contact.Email);
|
||||||
|
if (contact?.Phone) {
|
||||||
|
contactQuery.set('Phone', contact.Phone);
|
||||||
|
}
|
||||||
|
contactQuery.set('CreatedBy', contact.CreatedBy);
|
||||||
|
contactQuery.set('UserId', contact.UserId);
|
||||||
|
contactQuery.set('UserRole', 'contracts_Guest');
|
||||||
|
contactQuery.set('TenantId', contact.TenantId);
|
||||||
|
contactQuery.set('IsDeleted', false);
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setReadAccess(contact.CreatedBy.objectId, true);
|
||||||
|
acl.setWriteAccess(contact.CreatedBy.objectId, true);
|
||||||
|
acl.setReadAccess(contact.UserId.objectId, true);
|
||||||
|
acl.setWriteAccess(contact.UserId.objectId, true);
|
||||||
|
contactQuery.setACL(acl);
|
||||||
|
const contactRes = await contactQuery.save();
|
||||||
|
if (contactRes) {
|
||||||
|
return contactRes;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `linkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and
|
||||||
|
// save contact pointer in placeholder, signers and ACL of Document
|
||||||
|
export default async function linkContactToDoc(req) {
|
||||||
|
const email = req.params.email;
|
||||||
|
const docId = req.params.docId;
|
||||||
|
const name = req.params.name;
|
||||||
|
const phone = req.params.phone;
|
||||||
|
try {
|
||||||
|
if (docId) {
|
||||||
|
// Execute the query to get the document with the specified 'docId'
|
||||||
|
const docQuery = new Parse.Query('contracts_Document');
|
||||||
|
docQuery.include('ExtUserPtr');
|
||||||
|
const docRes = await docQuery.get(docId, { useMasterKey: true });
|
||||||
|
// Check if the document was found; if not, throw an error indicating the document was not found
|
||||||
|
if (!docRes) {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
|
||||||
|
}
|
||||||
|
const _docRes = JSON.parse(JSON.stringify(docRes));
|
||||||
|
const Placeholders = _docRes?.Placeholders || [];
|
||||||
|
const index = Placeholders?.findIndex(x => x.email && x.email === email);
|
||||||
|
if (index !== -1) {
|
||||||
|
// `signerObjectId` holds the value of `signerObjId` from the `Placeholders` array at the current index.
|
||||||
|
// This value is used to check if `signerObjId` is present or not.
|
||||||
|
const signerObjectId = Placeholders[index]?.signerObjId;
|
||||||
|
if (signerObjectId) {
|
||||||
|
return { contactId: signerObjectId };
|
||||||
|
}
|
||||||
|
// Execute the query to check if a contact already exists in the 'contracts_Contactbook' class
|
||||||
|
const contactCls = new Parse.Query('contracts_Contactbook');
|
||||||
|
contactCls.equalTo('Email', email);
|
||||||
|
contactCls.equalTo('CreatedBy', _docRes.CreatedBy);
|
||||||
|
contactCls.notEqualTo('IsDeleted', true);
|
||||||
|
const existContact = await contactCls.first({ useMasterKey: true });
|
||||||
|
if (existContact) {
|
||||||
|
//update contact in placeholder, signers and update ACl in provide document
|
||||||
|
const updateDoc = new Parse.Object('contracts_Document');
|
||||||
|
updateDoc.id = docId;
|
||||||
|
const signers = _docRes?.Signers || [];
|
||||||
|
signers.push({
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Contactbook',
|
||||||
|
objectId: existContact.id,
|
||||||
|
});
|
||||||
|
updateDoc.set('Signers', signers);
|
||||||
|
|
||||||
|
Placeholders[index] = {
|
||||||
|
...Placeholders[index],
|
||||||
|
signerObjId: existContact.id,
|
||||||
|
signerPtr: {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Contactbook',
|
||||||
|
objectId: existContact.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateDoc.set('Placeholders', Placeholders);
|
||||||
|
const Acl = docRes.getACL();
|
||||||
|
Acl.setReadAccess(existContact.get('CreatedBy').id, true);
|
||||||
|
Acl.setWriteAccess(existContact.get('CreatedBy').id, true);
|
||||||
|
updateDoc.setACL(Acl);
|
||||||
|
// const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
const resDoc = await updateDoc.save(null, { useMasterKey: true });
|
||||||
|
if (resDoc) {
|
||||||
|
return { contactId: existContact.id };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Execute the query to check if a user already exists in the 'contracts_Users' class
|
||||||
|
const extUserQuery = new Parse.Query('contracts_Users');
|
||||||
|
extUserQuery.equalTo('Email', email);
|
||||||
|
const extUser = await extUserQuery.first({ useMasterKey: true });
|
||||||
|
if (extUser) {
|
||||||
|
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||||
|
const contact = {
|
||||||
|
UserId: _extUser.UserId,
|
||||||
|
Name: _extUser.Name,
|
||||||
|
Email: email,
|
||||||
|
Phone: _extUser?.Phone ? _extUser.Phone : '',
|
||||||
|
CreatedBy: _docRes.CreatedBy,
|
||||||
|
TenantId: _docRes.ExtUserPtr.TenantId,
|
||||||
|
};
|
||||||
|
// if user present on platform create contact on the basis of extended user details
|
||||||
|
const contactRes = await saveRoleContact(contact);
|
||||||
|
//update contact in placeholder, signers and update ACl in provide document
|
||||||
|
const updateDoc = new Parse.Object('contracts_Document');
|
||||||
|
updateDoc.id = docId;
|
||||||
|
const signers = _docRes?.Signers || [];
|
||||||
|
signers.push({
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Contactbook',
|
||||||
|
objectId: contactRes.id,
|
||||||
|
});
|
||||||
|
updateDoc.set('Signers', signers);
|
||||||
|
|
||||||
|
Placeholders[index] = {
|
||||||
|
...Placeholders[index],
|
||||||
|
signerObjId: contactRes.id,
|
||||||
|
signerPtr: {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Contactbook',
|
||||||
|
objectId: contactRes.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateDoc.set('Placeholders', Placeholders);
|
||||||
|
const Acl = docRes.getACL();
|
||||||
|
Acl.setReadAccess(_extUser.UserId.objectId, true);
|
||||||
|
Acl.setWriteAccess(_extUser.UserId.objectId, true);
|
||||||
|
updateDoc.setACL(Acl);
|
||||||
|
// const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
const resDoc = await updateDoc.save(null, { useMasterKey: true });
|
||||||
|
if (resDoc) {
|
||||||
|
return { contactId: contactRes.id };
|
||||||
|
}
|
||||||
|
} else if (name) {
|
||||||
|
try {
|
||||||
|
// Execute the query to check if a user already exists in the '_User' class
|
||||||
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
|
userQuery.equalTo('email', email);
|
||||||
|
const userRes = await userQuery.first({ useMasterKey: true });
|
||||||
|
if (userRes) {
|
||||||
|
const contact = {
|
||||||
|
UserId: { __type: 'Pointer', className: '_User', objectId: userRes.id },
|
||||||
|
Name: name,
|
||||||
|
Email: email,
|
||||||
|
Phone: phone,
|
||||||
|
CreatedBy: _docRes.CreatedBy,
|
||||||
|
TenantId: _docRes.ExtUserPtr.TenantId,
|
||||||
|
};
|
||||||
|
// Create new contract on the basis provided contact details by user and userId from _User class
|
||||||
|
const contactRes = await saveRoleContact(contact);
|
||||||
|
//update contact in placeholder, signers and update ACl in provide document
|
||||||
|
const updateDoc = new Parse.Object('contracts_Document');
|
||||||
|
updateDoc.id = docId;
|
||||||
|
const signers = _docRes?.Signers || [];
|
||||||
|
signers.push({
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Contactbook',
|
||||||
|
objectId: contactRes.id,
|
||||||
|
});
|
||||||
|
updateDoc.set('Signers', signers);
|
||||||
|
|
||||||
|
Placeholders[index] = {
|
||||||
|
...Placeholders[index],
|
||||||
|
signerObjId: contactRes.id,
|
||||||
|
signerPtr: {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Contactbook',
|
||||||
|
objectId: contactRes.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateDoc.set('Placeholders', Placeholders);
|
||||||
|
const Acl = docRes.getACL();
|
||||||
|
Acl.setReadAccess(userRes.id, true);
|
||||||
|
Acl.setWriteAccess(userRes.id, true);
|
||||||
|
updateDoc.setACL(Acl);
|
||||||
|
// const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
const resDoc = await updateDoc.save(null, { useMasterKey: true });
|
||||||
|
if (resDoc) {
|
||||||
|
return { contactId: contactRes.id };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// create new user in _User class on the basis of details provide by user
|
||||||
|
const _users = Parse.Object.extend('User');
|
||||||
|
const _user = new _users();
|
||||||
|
_user.set('name', name);
|
||||||
|
_user.set('username', email);
|
||||||
|
_user.set('email', email);
|
||||||
|
if (phone) {
|
||||||
|
_user.set('phone', phone);
|
||||||
|
_user.set('password', phone);
|
||||||
|
} else {
|
||||||
|
_user.set('password', email);
|
||||||
|
}
|
||||||
|
const newUserRes = await _user.save();
|
||||||
|
const contact = {
|
||||||
|
UserId: { __type: 'Pointer', className: '_User', objectId: newUserRes.id },
|
||||||
|
Name: name,
|
||||||
|
Email: email,
|
||||||
|
Phone: phone,
|
||||||
|
CreatedBy: _docRes.CreatedBy,
|
||||||
|
TenantId: _docRes.ExtUserPtr.TenantId,
|
||||||
|
};
|
||||||
|
// Create new contract on the basis provided contact details by user and userId from _User class
|
||||||
|
const contactRes = await saveRoleContact(contact);
|
||||||
|
//update contact in placeholder, signers and update ACl in provide document
|
||||||
|
const updateDoc = new Parse.Object('contracts_Document');
|
||||||
|
updateDoc.id = docId;
|
||||||
|
const signers = _docRes?.Signers || [];
|
||||||
|
signers.push({
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Contactbook',
|
||||||
|
objectId: contactRes.id,
|
||||||
|
});
|
||||||
|
updateDoc.set('Signers', signers);
|
||||||
|
|
||||||
|
Placeholders[index] = {
|
||||||
|
...Placeholders[index],
|
||||||
|
signerObjId: contactRes.id,
|
||||||
|
signerPtr: {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Contactbook',
|
||||||
|
objectId: contactRes.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
updateDoc.set('Placeholders', Placeholders);
|
||||||
|
const Acl = docRes.getACL();
|
||||||
|
Acl.setReadAccess(newUserRes.id, true);
|
||||||
|
Acl.setWriteAccess(newUserRes.id, true);
|
||||||
|
updateDoc.setACL(Acl);
|
||||||
|
// const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
const resDoc = await updateDoc.save(null, { useMasterKey: true });
|
||||||
|
if (resDoc) {
|
||||||
|
return { contactId: contactRes.id };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Err', err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'unauthorized');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err in linkcontacttodoc', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user