Merge pull request #813 from OpenSignLabs/raktima-opensignlabs-patch-9

fix: display the list of unsigned signers in the 'Yet to Sign' panel using the QuickSend flow.
This commit is contained in:
prafull-opensignlabs
2024-06-07 20:16:34 +05:30
committed by GitHub
10 changed files with 423 additions and 323 deletions
+81 -57
View File
@@ -9,7 +9,23 @@ const BulkSendUi = (props) => {
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
const [isSubmit, setIsSubmit] = useState(false);
const [allowedForm, setAllowedForm] = useState(0);
const [isSignatureExist, setIsSignatureExist] = useState();
const allowedSigners = 50;
useEffect(() => {
signatureExist();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//function to check atleast one signature field exist
const signatureExist = () => {
const getPlaceholder = props.item?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
)
);
setIsSignatureExist(checkIsSignatureExistt);
};
useEffect(() => {
if (scrollOnNextUpdate && formRef.current) {
formRef.current.scrollIntoView({
@@ -165,65 +181,73 @@ const BulkSendUi = (props) => {
</div>
)}
{props.Placeholders?.length > 0 ? (
<>
{props.Placeholders?.some((x) => !x.signerObjId) ? (
<form onSubmit={handleSubmit}>
<div className=" min-h-max max-h-[250px] overflow-y-auto">
{forms?.map((form, index) => (
<div
key={form.Id}
className="p-3 rounded-xl border-[1px] border-gray-400 m-4 bg-white text-black grid grid-cols-1 md:grid-cols-2 gap-2 relative"
isSignatureExist ? (
<>
{props.Placeholders?.some((x) => !x.signerObjId) ? (
<form onSubmit={handleSubmit}>
<div className=" min-h-max max-h-[250px] overflow-y-auto">
{forms?.map((form, index) => (
<div
key={form.Id}
className="p-3 rounded-xl border-[1px] border-gray-400 m-4 bg-white text-black grid grid-cols-1 md:grid-cols-2 gap-2 relative"
>
{form?.fields?.map((field, fieldIndex) => (
<div className="flex flex-col " key={field.fieldId}>
<label>{field.label}</label>
<SuggestionInput
required
type="email"
value={field.value}
index={fieldIndex}
onChange={(signer) =>
handleInputChange(index, signer, fieldIndex)
}
/>
</div>
))}
{forms?.length > 1 && (
<button
onClick={() => handleRemoveForm(index)}
className="absolute right-3 top-1 border border-gray-300 rounded-lg px-2 py-1"
>
<i className="fa-solid fa-trash"></i>
</button>
)}
<div ref={formRef}></div>
</div>
))}
</div>
<div className="flex flex-col mx-4 mb-4 gap-3">
<button
onClick={handleAddForm}
className="bg-[#32a3ac] p-2 text-white w-full rounded-full focus:outline-none"
>
{form?.fields?.map((field, fieldIndex) => (
<div className="flex flex-col " key={field.fieldId}>
<label>{field.label}</label>
<SuggestionInput
required
type="email"
value={field.value}
index={fieldIndex}
onChange={(signer) =>
handleInputChange(index, signer, fieldIndex)
}
/>
</div>
))}
{forms?.length > 1 && (
<button
onClick={() => handleRemoveForm(index)}
className="absolute right-3 top-1 border border-gray-300 rounded-lg px-2 py-1"
>
<i className="fa-solid fa-trash"></i>
</button>
)}
<div ref={formRef}></div>
</div>
))}
<i className="fa-solid fa-plus"></i> <span>Add new</span>
</button>
<button
type="submit"
className="bg-[#32a3ac] p-2 text-white w-full rounded-full focus:outline-none"
>
<i className="fa-solid fa-paper-plane"></i>{" "}
<span>Send</span>
</button>
</div>
</form>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
All roles in this document are currently linked to contacts. To
&apos;quick send&apos; copies of this template to multiple
signers, please ensure that at least one role is not linked to
any contact.
</div>
<div className="flex flex-col mx-4 mb-4 gap-3">
<button
onClick={handleAddForm}
className="bg-[#32a3ac] p-2 text-white w-full rounded-full focus:outline-none"
>
<i className="fa-solid fa-plus"></i> <span>Add new</span>
</button>
<button
type="submit"
className="bg-[#32a3ac] p-2 text-white w-full rounded-full focus:outline-none"
>
<i className="fa-solid fa-paper-plane"></i> <span>Send</span>
</button>
</div>
</form>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
All roles in this document are currently linked to contacts. To
&apos;quick send&apos; copies of this template to multiple
signers, please ensure that at least one role is not linked to any
contact.
</div>
)}
</>
)}
</>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please ensure there&#39;s at least one signature widget added for
all recipients.
</div>
)
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please add at least one role to this template in order to &apos;quick
@@ -204,8 +204,10 @@ function RenderPdf({
};
//function for render placeholder block over pdf document
const checkSignedSignes = (data) => {
const checkSign = signedSigners.filter(
(sign) => sign.objectId === data.signerObjId
let checkSign = [];
//condition to handle quick send flow and using normal request sign flow
checkSign = signedSigners.filter(
(sign) => sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
);
if (data.signerObjId === signerObjectId) {
setCurrentSigner(true);
+1
View File
@@ -669,6 +669,7 @@ export const createDocument = async (template, placeholders, signerData) => {
Name: Doc.Name,
URL: Doc.URL,
SignedUrl: Doc.SignedUrl,
SentToOthers: Doc.SentToOthers,
Description: Doc.Description,
Note: Doc.Note,
Placeholders: placeholdersArr,
+84 -57
View File
@@ -32,7 +32,8 @@ import {
contactBook,
handleDownloadPdf,
handleToPrint,
handleDownloadCertificate
handleDownloadCertificate,
darkenColor
} from "../constant/Utils";
import Loader from "../primitives/LoaderWithMsg";
import HandleError from "../primitives/HandleError";
@@ -74,7 +75,7 @@ function PdfRequestFiles() {
const [selectWidgetId, setSelectWidgetId] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isCelebration, setIsCelebration] = useState(false);
const [requestSignTour, setRequestSignTour] = useState(true);
const [requestSignTour, setRequestSignTour] = useState(false);
const [tourStatus, setTourStatus] = useState([]);
const [isLoading, setIsLoading] = useState({
isLoad: true,
@@ -296,6 +297,10 @@ function PdfRequestFiles() {
setExpiredDate(expireDateFormat);
} // Check if the current signer is not a last signer and handle the complete message.
else if (isNextUser) {
setIsCelebration(true);
setTimeout(() => {
setIsCelebration(false);
}, 5000);
setIsCompleted({
isModal: true,
message:
@@ -330,7 +335,7 @@ function PdfRequestFiles() {
}
}
}
const checkDocIdExist =
const audittrailData =
documentData[0].AuditTrail &&
documentData[0].AuditTrail.length > 0 &&
documentData[0].AuditTrail.filter((data) => data.Activity === "Signed");
@@ -399,41 +404,53 @@ function PdfRequestFiles() {
let signers = [];
let unSignedSigner = [];
//check document is signed or not
if (checkDocIdExist && checkDocIdExist.length > 0) {
const placeholdersOrSigners = [];
for (const placeholder of documentData[0].Placeholders) {
//`emailExist` variable to handle condition for quick send flow and show unsigned signers list
const emailExist = placeholder?.email;
if (emailExist) {
placeholdersOrSigners.push(placeholder);
} else {
const getSignerData = documentData[0].Signers.filter(
(data) => data.objectId === placeholder?.signerObjId
);
placeholdersOrSigners.push(getSignerData[0]);
}
}
//condition to check already signed document by someone
if (audittrailData && audittrailData.length > 0) {
setIsDocId(true);
const signerRes = documentData[0].Signers;
//comparison auditTrail user details with signers user details
for (let i = 0; i < signerRes.length; i++) {
const signerId = signerRes[i].objectId;
for (const item of placeholdersOrSigners) {
const checkEmail = item?.email;
//if email exist then compare user signed by using email else signers objectId
const emailOrId = checkEmail ? item.email : item.objectId;
//`isSignedSignature` variable to handle break loop whenever it get true
let isSignedSignature = false;
for (let j = 0; j < checkDocIdExist.length; j++) {
const signedExist =
checkDocIdExist[j] && checkDocIdExist[j].UserPtr.objectId;
//checking signerObjId and auditTrail User objId
// if match then add signed data in signer array and break loop
//checking the signer who signed the document by using audit trail details.
//and save signedSigners and unsignedSigners details
for (const doc of audittrailData) {
const signedExist = checkEmail
? doc?.UserPtr.Email
: doc?.UserPtr.objectId;
if (signerId === signedExist) {
signers.push({ ...signerRes[i], ...signerRes[i] });
if (emailOrId === signedExist) {
signers.push({ ...item });
isSignedSignature = true;
break;
}
// if does not match then add unsigned data in unSignedSigner array
}
if (!isSignedSignature) {
unSignedSigner.push({ ...signerRes[i], ...signerRes[i] });
unSignedSigner.push({ ...item });
}
}
setSignedSigners(signers);
setUnSignedSigners(unSignedSigner);
setSignerPos(documentData[0].Placeholders);
} else {
let unsigned = [];
for (let i = 0; i < documentData.length; i++) {
unsigned.push(documentData[i].Signers);
}
setUnSignedSigners(unsigned[0]);
//else condition is show there are no details in audit trail then direct push all signers details
//in unsignedsigners array
setUnSignedSigners(placeholdersOrSigners);
setSignerPos(documentData[0].Placeholders);
}
setPdfDetails(documentData);
@@ -447,7 +464,7 @@ function PdfRequestFiles() {
declined ||
currDate > expireUpdateDate
) {
setRequestSignTour(false);
setRequestSignTour(true);
} else {
//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
@@ -463,33 +480,25 @@ function PdfRequestFiles() {
setContractName("_Users");
currUserId = res[0].objectId;
setSignerUserId(currUserId);
const tourstatus = res[0].TourStatus && res[0].TourStatus;
if (tourstatus && tourstatus.length > 0) {
setTourStatus(tourstatus);
const checkTourRequestSign = tourstatus.filter(
(data) => data.requestSign
);
if (checkTourRequestSign && checkTourRequestSign.length > 0) {
setRequestSignTour(checkTourRequestSign[0].requestSign);
}
const tourData = res[0].TourStatus && res[0].TourStatus;
if (tourData && tourData.length > 0) {
setTourStatus(tourData);
setRequestSignTour(tourData[0]?.requestSign || false);
}
} else if (res?.length === 0) {
const res = await contactBook(currUserId);
if (res === "Error: Something went wrong!") {
setHandleError("Error: Something went wrong!");
} else if (res[0] && res.length) {
setContractName("_Contactbook");
const objectId = res[0].objectId;
setSignerUserId(objectId);
const tourstatus = res[0].TourStatus && res[0].TourStatus;
if (tourstatus && tourstatus.length > 0) {
setTourStatus(tourstatus);
const checkTourRequestSign = tourstatus.filter(
(data) => data.requestSign
);
if (checkTourRequestSign && checkTourRequestSign.length > 0) {
setRequestSignTour(checkTourRequestSign[0].requestSign);
}
const tourData = res[0].TourStatus && res[0].TourStatus;
if (tourData && tourData.length > 0) {
setTourStatus(tourData);
setRequestSignTour(tourData[0]?.requestSign || false);
}
} else if (res.length === 0) {
setHandleError("Error: User does not exist!");
@@ -552,7 +561,6 @@ function PdfRequestFiles() {
setIsLoading(loadObj);
});
};
//function for embed signature or image url in pdf
async function embedWidgetsData() {
try {
@@ -996,8 +1004,10 @@ function PdfRequestFiles() {
}
const getFirstLetter = (name) => {
const firstLetter = name.charAt(0);
return firstLetter;
if (name) {
const firstLetter = name.charAt(0);
return firstLetter;
}
};
//function for image upload or update
const onImageChange = (event) => {
@@ -1109,9 +1119,23 @@ function PdfRequestFiles() {
};
const checkSignerBackColor = (obj) => {
const data = signerPos.filter((data) => data.signerObjId === obj.objectId);
let data = "";
if (obj?.Id) {
data = signerPos.filter((data) => data.Id === obj.Id);
} else {
data = signerPos.filter((data) => data.signerObjId === obj.objectId);
}
return data && data.length > 0 && data[0].blockColor;
};
const checkUserNameColor = (obj) => {
const getBackColor = checkSignerBackColor(obj);
if (getBackColor) {
const color = darkenColor(getBackColor, 0.4);
return color;
} else {
return "#abd1d0";
}
};
//function for set decline true on press decline button
const declineDoc = async () => {
@@ -1210,7 +1234,7 @@ function PdfRequestFiles() {
};
//function to close tour and save tour status
const closeRequestSignTour = async () => {
setRequestSignTour(false);
setRequestSignTour(true);
if (isDontShow) {
let updatedTourStatus = [];
if (tourStatus.length > 0) {
@@ -1332,7 +1356,6 @@ function PdfRequestFiles() {
/>
);
};
return (
<DndProvider backend={HTML5Backend}>
<Title title={"Request Sign"} />
@@ -1401,7 +1424,7 @@ function PdfRequestFiles() {
}}
ref={divRef}
>
{requestSignTour && requestSignTourFunction()}
{!requestSignTour && requestSignTourFunction()}
<ModalUi
headerColor={"#dc3545"}
isOpen={isAlert.isShow}
@@ -1781,7 +1804,7 @@ function PdfRequestFiles() {
<div
className="signerStyle"
style={{
background: "#abd1d0",
background: checkUserNameColor(obj),
width: 30,
height: 30,
display: "flex",
@@ -1800,7 +1823,7 @@ function PdfRequestFiles() {
textTransform: "uppercase"
}}
>
{getFirstLetter(obj.Name)}
{getFirstLetter(obj?.Name || obj?.Role)}
</span>
</div>
<div
@@ -1809,9 +1832,11 @@ function PdfRequestFiles() {
flexDirection: "column"
}}
>
<span className="userName">{obj.Name}</span>
<span className="userName">
{obj?.Name || obj?.Role}
</span>
<span className="useEmail">
{obj.Email}
{obj?.Email || obj?.email}
</span>
</div>
</div>
@@ -1850,7 +1875,7 @@ function PdfRequestFiles() {
<div
className="signerStyle"
style={{
background: "#abd1d0",
background: checkUserNameColor(obj),
width: 30,
height: 30,
display: "flex",
@@ -1869,7 +1894,7 @@ function PdfRequestFiles() {
textTransform: "uppercase"
}}
>
{getFirstLetter(obj.Name)}
{getFirstLetter(obj?.Name || obj?.email)}
</span>
</div>
<div
@@ -1878,9 +1903,11 @@ function PdfRequestFiles() {
flexDirection: "column"
}}
>
<span className="userName">{obj.Name}</span>
<span className="userName">
{obj?.Name || obj?.Role}
</span>
<span className="useEmail">
{obj.Email}
{obj?.Email || obj?.email}
</span>
</div>
<hr />
+3 -1
View File
@@ -936,6 +936,7 @@ function PlaceHolderSign() {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers,
SentToOthers: true,
ExpiryDate: {
iso: updateExpiryDate,
__type: "Date"
@@ -945,7 +946,8 @@ function PlaceHolderSign() {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers
Signers: signers,
SentToOthers: true
};
}
await axios
+49 -45
View File
@@ -1,4 +1,8 @@
import React, { useState, useEffect, useRef } from "react";
import React, {
useState,
useEffect
// useRef
} from "react";
import { Navigate, useNavigate } from "react-router-dom";
import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize";
@@ -27,10 +31,10 @@ function UserProfile() {
const [percentage, setpercentage] = useState(0);
const [isDisableDocId, setIsDisableDocId] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [publicUserName, setPublicUserName] = useState(
extendUser && extendUser?.[0]?.UserName
);
const previousPublicUserName = useRef(publicUserName);
// const [publicUserName, setPublicUserName] = useState(
// extendUser && extendUser?.[0]?.UserName
// );
// const previousPublicUserName = useRef(publicUserName);
const [company, setCompany] = useState(
extendUser && extendUser?.[0]?.Company
);
@@ -41,7 +45,7 @@ function UserProfile() {
const [otp, setOtp] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isEmailVerified, setIsEmailVerified] = useState(false);
const [userNameError, setUserNameError] = useState("");
// const [userNameError, setUserNameError] = useState("");
useEffect(() => {
getUserDetail();
}, []);
@@ -80,31 +84,31 @@ function UserProfile() {
}
};
//function to check public username already exist
const handleCheckPublicUserName = async () => {
try {
const res = await Parse.Cloud.run("getpublicusername", {
username: publicUserName
});
if (res) {
setIsLoader(false);
setUserNameError("user name already exist");
setTimeout(() => {
setUserNameError("");
}, 3000);
return res;
}
} catch (e) {
console.log("error in getpublicusername cloud function");
}
};
// const handleCheckPublicUserName = async () => {
// try {
// const res = await Parse.Cloud.run("getpublicusername", {
// username: publicUserName
// });
// if (res) {
// setIsLoader(false);
// setUserNameError("user name already exist");
// setTimeout(() => {
// setUserNameError("");
// }, 3000);
// return res;
// }
// } catch (e) {
// console.log("error in getpublicusername cloud function");
// }
// };
const handleSubmit = async (e) => {
e.preventDefault();
let phn = Phone,
res;
res = "";
//condition to call cloud function when user change publicUserName
if (previousPublicUserName.current !== publicUserName) {
res = await handleCheckPublicUserName();
}
// if (previousPublicUserName.current !== publicUserName) {
// res = await handleCheckPublicUserName();
// }
if (!res) {
setIsLoader(true);
try {
@@ -158,8 +162,8 @@ function UserProfile() {
Name: obj.Name,
HeaderDocId: isDisableDocId,
JobTitle: jobTitle,
Company: company,
UserName: publicUserName || ""
Company: company
// UserName: publicUserName || ""
};
await axios.put(
@@ -273,23 +277,23 @@ function UserProfile() {
alert("OTP sent on you email");
};
//function to handle onchange username and restrict 6-characters username for free users
const handleOnchangeUserName = (e) => {
const value = e.target.value;
if (value.length > 6 && !isSubscribe) {
setUserNameError("Please upgrade to allow more than 6 characters.");
setTimeout(() => {
setUserNameError("");
}, 2000);
} else {
setPublicUserName(e.target.value);
}
};
// const handleOnchangeUserName = (e) => {
// const value = e.target.value;
// if (value.length > 6 && !isSubscribe) {
// setUserNameError("Please upgrade to allow more than 6 characters.");
// setTimeout(() => {
// setUserNameError("");
// }, 2000);
// } else {
// setPublicUserName(e.target.value);
// }
// };
const handleCancel = () => {
setEditMode(false);
SetName(localStorage.getItem("username"));
SetPhone(UserProfile && UserProfile.phone);
setImage(localStorage.getItem("profileImg"));
setPublicUserName(extendUser && extendUser?.[0]?.UserName);
// setPublicUserName(extendUser && extendUser?.[0]?.UserName);
setCompany(extendUser && extendUser?.[0]?.Company);
setJobTitle(extendUser?.[0]?.JobTitle);
};
@@ -315,13 +319,13 @@ function UserProfile() {
</div>
) : (
<div className="flex justify-center items-center w-full relative">
{userNameError && (
{/* {userNameError && (
<div
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
>
{userNameError}
</div>
)}
)} */}
<div className="bg-white flex flex-col justify-center shadow rounded w-[450px]">
<div className="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
@@ -449,7 +453,7 @@ function UserProfile() {
)}
</span>
</li>
{isEnableSubscription && (
{/* {isEnableSubscription && (
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">
Public profile :{" "}
@@ -479,7 +483,7 @@ function UserProfile() {
)}
</div>
</li>
)}
)} */}
<li className="border-y-[1px] border-gray-300 break-all">
<div className="flex justify-between items-center py-2">
<span
+193 -159
View File
@@ -6,8 +6,8 @@ import ModalUi from "./ModalUi";
import AddSigner from "../components/AddSigner";
import {
modalSubmitBtnColor,
modalCancelBtnColor,
isEnableSubscription
modalCancelBtnColor
// isEnableSubscription
} from "../constant/const";
import Alert from "./Alert";
import Tooltip from "./Tooltip";
@@ -15,8 +15,11 @@ import { RWebShare } from "react-web-share";
import Tour from "reactour";
import Parse from "parse";
import { saveAs } from "file-saver";
import { copytoData, replaceMailVaribles } from "../constant/Utils";
import Confetti from "react-confetti";
import {
// copytoData,
replaceMailVaribles
} from "../constant/Utils";
// import Confetti from "react-confetti";
import EditorToolbar, {
module1,
formats
@@ -41,7 +44,7 @@ const ReportTable = (props) => {
const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]);
const [isResendMail, setIsResendMail] = useState({});
const [isMakePublic, setIsMakePublic] = useState({});
// const [isMakePublic, setIsMakePublic] = useState({});
const [mail, setMail] = useState({ subject: "", body: "" });
const [userDetails, setUserDetails] = useState({});
const [isNextStep, setIsNextStep] = useState({});
@@ -49,12 +52,12 @@ const ReportTable = (props) => {
const [templateDeatils, setTemplateDetails] = useState({});
const [placeholders, setPlaceholders] = useState([]);
const [isLoader, setIsLoader] = useState({});
const [selectedPublicRole, setSelectedPublicRole] = useState("");
const [isCelebration, setIsCelebration] = useState(false);
// const [selectedPublicRole, setSelectedPublicRole] = useState("");
// const [isCelebration, setIsCelebration] = useState(false);
const [currentLists, setCurrentLists] = useState([]);
const [isPublic, setIsPublic] = useState({});
const [isPublicProfile, setIsPublicProfile] = useState({});
const [publicUserName, setIsPublicUserName] = useState("");
// const [isPublic, setIsPublic] = useState({});
// const [isPublicProfile, setIsPublicProfile] = useState({});
// const [publicUserName, setIsPublicUserName] = useState("");
const [isViewShare, setIsViewShare] = useState({});
const startIndex = (currentPage - 1) * props.docPerPage;
const { isMoreDocs, setIsNextRecord } = props;
@@ -143,6 +146,7 @@ const ReportTable = (props) => {
Name: Doc.Name,
URL: Doc.URL,
SignedUrl: Doc.SignedUrl,
SentToOthers: Doc?.SentToOthers || false,
Description: Doc.Description,
Note: Doc.Note,
Placeholders: placeholdersArr,
@@ -250,12 +254,12 @@ const ReportTable = (props) => {
// `currentLists` is total record render on current page
const currentList = props.List?.slice(indexOfFirstDoc, indexOfLastDoc);
//check public template and save in a object to show public and private template
setIsPublic(
currentList.reduce((acc, item) => {
acc[item.objectId] = item?.IsPublic || false;
return acc;
}, {})
);
// setIsPublic(
// currentList.reduce((acc, item) => {
// acc[item.objectId] = item?.IsPublic || false;
// return acc;
// }, {})
// );
setCurrentLists(currentList);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [indexOfLastDoc, indexOfFirstDoc]);
@@ -320,16 +324,16 @@ const ReportTable = (props) => {
setActLoader({});
}
};
const handleClose = (item) => {
const handleClose = () => {
setIsRevoke({});
setIsDeleteModal({});
setIsMakePublic({});
setSelectedPublicRole("");
setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: !prevStates[item.objectId]
}));
setIsPublicProfile({});
// setIsMakePublic({});
// setSelectedPublicRole("");
// setIsPublic((prevStates) => ({
// ...prevStates,
// [item.objectId]: !prevStates[item.objectId]
// }));
// setIsPublicProfile({});
};
const handleShare = (item) => {
@@ -338,7 +342,7 @@ const ReportTable = (props) => {
const sendMail = item?.SendMail || false;
const getUrl = (x) => {
//encode this url value `${item.objectId}/${x.Email}/${x.objectId}` to base64 using `btoa` function
if (x.objectId) {
if (x?.signerObjId) {
const encodeBase64 = btoa(
`${item.objectId}/${x.signerPtr.Email}/${x.signerPtr.objectId}/${sendMail}`
);
@@ -495,9 +499,9 @@ const ReportTable = (props) => {
// `handleSubjectChange` is used to add or change subject of resend mail
const handleSubjectChange = (subject, doc) => {
const encodeBase64 = btoa(
`${doc.objectId}/${userDetails.Email}/${userDetails.objectId}`
);
const encodeBase64 = userDetails?.objectId
? btoa(`${doc.objectId}/${userDetails.Email}/${userDetails.objectId}`)
: btoa(`${doc.objectId}/${userDetails.Email}`);
const expireDate = doc.ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString("en-US", {
@@ -511,8 +515,8 @@ const ReportTable = (props) => {
sender_name: doc.ExtUserPtr.Name,
sender_mail: doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: userDetails.Name,
receiver_email: userDetails.Email,
receiver_name: userDetails?.Name,
receiver_email: userDetails?.Email,
receiver_phone: userDetails?.Phone || "",
expiry_date: localExpireDate,
company_name: doc.ExtUserPtr.Company,
@@ -524,9 +528,9 @@ const ReportTable = (props) => {
};
// `handlebodyChange` is used to add or change body of resend mail
const handlebodyChange = (body, doc) => {
const encodeBase64 = btoa(
`${doc.objectId}/${userDetails.Email}/${userDetails.objectId}`
);
const encodeBase64 = userDetails?.objectId
? btoa(`${doc.objectId}/${userDetails.Email}/${userDetails.objectId}`)
: btoa(`${doc.objectId}/${userDetails.Email}`);
const expireDate = doc.ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString("en-US", {
@@ -540,8 +544,8 @@ const ReportTable = (props) => {
sender_name: doc.ExtUserPtr.Name,
sender_mail: doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: userDetails.Name,
receiver_email: userDetails.Email,
receiver_name: userDetails?.Name || "",
receiver_email: userDetails?.Email || "",
receiver_phone: userDetails?.Phone || "",
expiry_date: localExpireDate,
company_name: doc.ExtUserPtr.Company,
@@ -556,8 +560,18 @@ const ReportTable = (props) => {
// `handleNextBtn` is used to open edit mail template screen in resend mail modal
// as well as replace variable with original one
const handleNextBtn = (user, doc) => {
setUserDetails(user);
const encodeBase64 = btoa(`${doc.objectId}/${user.Email}/${user.objectId}`);
const userdata = {
Name: user?.signerPtr?.Name,
Email: user.email ? user?.email : user.signerPtr?.Email,
Phone: user?.signerPtr?.Phone,
objectId: user?.signerPtr?.objectId
};
setUserDetails(userdata);
const encodeBase64 = user.email
? btoa(`${doc.objectId}/${user.email}`)
: btoa(
`${doc.objectId}/${user.signerPtr.Email}/${user.signerPtr.objectId}`
);
const expireDate = doc.ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString("en-US", {
@@ -571,9 +585,9 @@ const ReportTable = (props) => {
sender_name: doc.ExtUserPtr.Name,
sender_mail: doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || "",
receiver_name: user.Name,
receiver_email: user.Email,
receiver_phone: user?.Phone || "",
receiver_name: user?.signerPtr?.Name || "",
receiver_email: user?.email ? user?.email : user?.signerPtr?.Email,
receiver_phone: user?.signerPtr?.Phone || "",
expiry_date: localExpireDate,
company_name: doc?.ExtUserPtr?.Company || "",
signing_url: `<a href=${signPdf}>Sign here</a>`
@@ -584,14 +598,14 @@ const ReportTable = (props) => {
`{{sender_name}} has requested you to sign "{{document_title}}"`;
const body =
doc?.RequestBody ||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}}&nbsp;has requested you to review and sign&nbsp;<b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p>{{signing_url}}</p><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team OpenSign™</p><br></body> </html>`;
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign <b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p>{{signing_url}}</p><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team OpenSign™</p><br></body> </html>`;
const res = replaceMailVaribles(subject, body, variables);
setMail((prev) => ({ ...prev, subject: res.subject, body: res.body }));
setIsNextStep({ [user.objectId]: true });
setIsNextStep({ [user.Id]: true });
};
const handleResendMail = async (e, doc, user) => {
e.preventDefault();
setActLoader({ [user.objectId]: true });
setActLoader({ [user?.Id]: true });
const url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
const headers = {
"Content-Type": "application/json",
@@ -601,7 +615,7 @@ const ReportTable = (props) => {
let params = {
mailProvider: doc?.ExtUserPtr?.active_mail_adapter,
extUserId: doc?.ExtUserPtr?.objectId,
recipient: userDetails.Email,
recipient: userDetails?.Email,
subject: mail.subject,
from: doc?.ExtUserPtr?.Email,
html: mail.body
@@ -633,9 +647,9 @@ const ReportTable = (props) => {
}
};
const fetchUserStatus = (user, doc) => {
const audit = doc?.AuditTrail?.find(
(x) => x.UserPtr.objectId === user.objectId
);
const email = user.email ? user.email : user.signerPtr.Email;
const audit = doc?.AuditTrail?.find((x) => x.UserPtr.Email === email);
return (
<div className="flex flex-row gap-2 justify-center items-center">
<div className="flex justify-center items-center bg-gray-200 text-xs text-black shadow rounded-full w-[65px] h-[23px] cursor-default">
@@ -719,115 +733,130 @@ const ReportTable = (props) => {
};
//function to make template public and set public role
const handlePublicTemplate = async (item) => {
if (selectedPublicRole || !isPublic[item.objectId]) {
setActLoader({ [item.objectId]: true });
setIsMakePublic(false);
try {
const res = await Parse.Cloud.run("createpublictemplate", {
templateid: item.objectId,
ispublic: isPublic[item.objectId],
publicrole: [selectedPublicRole]
});
// const handlePublicTemplate = async (item) => {
// if (selectedPublicRole || !isPublic[item.objectId]) {
// setActLoader({ [item.objectId]: true });
// setIsMakePublic(false);
// try {
// const res = await Parse.Cloud.run("createpublictemplate", {
// templateid: item.objectId,
// ispublic: isPublic[item.objectId],
// publicrole: [selectedPublicRole]
// });
if (res.status === "success") {
setIsAlert(true);
setTimeout(() => setIsAlert(false), 1500);
if (isPublic[item.objectId]) {
setAlertMsg({
type: "success",
message: "You have successfully made the template public."
});
setIsCelebration(true);
setTimeout(() => {
setIsCelebration(false);
}, 5000);
setIsPublicProfile({ [item.objectId]: isPublic[item.objectId] });
} else {
setAlertMsg({
type: "success",
message: "You have successfully made the template private."
});
setSelectedPublicRole("");
}
const updateList = props.List.map((x) =>
x.objectId === item.objectId
? { ...x, IsPublic: isPublic[item.objectId] }
: x
);
props.setList(updateList);
setActLoader({});
}
} catch (e) {
console.log("error in createpublictemplate", e);
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "Something went wrong, Please try again later!"
});
setTimeout(() => setIsAlert(false), 1500);
setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: !prevStates[item.objectId]
}));
}
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "You need to select a role for the public signers."
});
setTimeout(() => setIsAlert(false), 1500);
}
};
// if (res.status === "success") {
// setIsAlert(true);
// setTimeout(() => setIsAlert(false), 1500);
// if (isPublic[item.objectId]) {
// setAlertMsg({
// type: "success",
// message: "You have successfully made the template public."
// });
// setIsCelebration(true);
// setTimeout(() => {
// setIsCelebration(false);
// }, 5000);
// setIsPublicProfile({ [item.objectId]: isPublic[item.objectId] });
// } else {
// setAlertMsg({
// type: "success",
// message: "You have successfully made the template private."
// });
// setSelectedPublicRole("");
// }
// const updateList = props.List.map((x) =>
// x.objectId === item.objectId
// ? { ...x, IsPublic: isPublic[item.objectId] }
// : x
// );
// props.setList(updateList);
// setActLoader({});
// }
// } catch (e) {
// console.log("error in createpublictemplate", e);
// setIsAlert(true);
// setAlertMsg({
// type: "danger",
// message: "Something went wrong, Please try again later!"
// });
// setTimeout(() => setIsAlert(false), 1500);
// setIsPublic((prevStates) => ({
// ...prevStates,
// [item.objectId]: !prevStates[item.objectId]
// }));
// }
// } else {
// setIsAlert(true);
// setAlertMsg({
// type: "danger",
// message: "You need to select a role for the public signers."
// });
// setTimeout(() => setIsAlert(false), 1500);
// }
// };
const handleViewSigners = (item) => {
setIsViewShare({ [item.objectId]: true });
};
//function to handle change template status is public or private
const handlePublicChange = async (e, item) => {
const getPlaceholder = item?.Placeholders;
//condiiton to check role is exist or not
if (getPlaceholder && getPlaceholder.length > 0) {
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
const userName = extendUser[0]?.UserName;
setIsPublicUserName(extendUser[0]?.UserName);
//condition to check user have public url or not
if (userName) {
setIsPublic((prevStates) => ({
...prevStates,
[item.objectId]: e.target.checked
}));
const getPlaceholder = item?.Placeholders;
if (getPlaceholder.length === 1) {
setSelectedPublicRole(getPlaceholder[0]?.Role);
}
// const handlePublicChange = async (e, item) => {
// const getPlaceholder = item?.Placeholders;
// //condiiton to check role is exist or not
// if (getPlaceholder && getPlaceholder.length > 0) {
// const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
// placeholderObj?.placeHolder?.some((holder) =>
// holder?.pos?.some((posItem) => posItem?.type === "signature")
// )
// );
// if (checkIsSignatureExistt) {
// let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
// const userName = extendUser[0]?.UserName;
// setIsPublicUserName(extendUser[0]?.UserName);
// //condition to check user have public url or not
// if (userName) {
// setIsPublic((prevStates) => ({
// ...prevStates,
// [item.objectId]: e.target.checked
// }));
// const getPlaceholder = item?.Placeholders;
// if (getPlaceholder.length === 1) {
// setSelectedPublicRole(getPlaceholder[0]?.Role);
// }
setIsMakePublic({ [item.objectId]: true });
} else {
setIsPublicProfile({ [item.objectId]: true });
}
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "Please assign at least one role to make this template public."
});
setTimeout(() => setIsAlert(false), 3000);
}
};
// setIsMakePublic({ [item.objectId]: true });
// } else {
// setIsPublicProfile({ [item.objectId]: true });
// }
// } else {
// setIsAlert(true);
// setAlertMsg({
// type: "danger",
// message:
// " Please ensure there's at least one signature widget added for all recipients."
// });
// setTimeout(() => setIsAlert(false), 5000);
// }
// } else {
// setIsAlert(true);
// setAlertMsg({
// type: "danger",
// message: "Please assign at least one role to make this template public."
// });
// setTimeout(() => setIsAlert(false), 5000);
// }
// };
//function to copy public profile links
const copytoProfileLink = () => {
const url = `https://opensign-me.vercel.app/${publicUserName}`;
copytoData(url);
setIsAlert(true);
setAlertMsg({
type: "success",
message: "Copied."
});
setTimeout(() => setIsAlert(false), 1500);
};
// const copytoProfileLink = () => {
// const url = `https://opensign-me.vercel.app/${publicUserName}`;
// copytoData(url);
// setIsAlert(true);
// setAlertMsg({
// type: "success",
// message: "Copied."
// });
// setTimeout(() => setIsAlert(false), 1500);
// };
return (
<div className="relative">
@@ -840,11 +869,11 @@ const ReportTable = (props) => {
</div>
)}
<div className="p-2 overflow-x-scroll w-full bg-white rounded-md">
{isCelebration && (
{/* {isCelebration && (
<div style={{ position: "relative", zIndex: "1000" }}>
<Confetti width={window.innerWidth} height={window.innerHeight} />
</div>
)}
)} */}
{isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>}
{props.tourData && props.ReportName === "Templates" && (
<Tour
@@ -891,9 +920,9 @@ const ReportTable = (props) => {
{props.actions?.length > 0 && (
<th className="px-4 py-2 font-thin">Action</th>
)}
{props.ReportName === "Templates" && isEnableSubscription && (
{/* {props.ReportName === "Templates" && isEnableSubscription && (
<th className="px-4 py-2 font-thin">Public</th>
)}
)} */}
</tr>
</thead>
<tbody className="text-[12px]">
@@ -1240,11 +1269,11 @@ const ReportTable = (props) => {
}}
>
<div className=" overflow-y-auto max-h-[340px] md:max-h-[400px]">
{item?.Signers.map((user) => (
<React.Fragment key={user.objectId}>
{isNextStep[user.objectId] && (
{item?.Placeholders?.map((user) => (
<React.Fragment key={user.Id}>
{isNextStep[user.Id] && (
<div className="relative ">
{actLoader[user.objectId] && (
{actLoader[user.Id] && (
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30">
<div
style={{
@@ -1264,7 +1293,7 @@ const ReportTable = (props) => {
>
<div className="absolute right-5 text-xs z-40">
<Tooltip
id={`${user.objectId}_help`}
id={`${user.Id}_help`}
message={
"You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}."
}
@@ -1322,7 +1351,12 @@ const ReportTable = (props) => {
{Object?.keys(isNextStep) <= 0 && (
<div className="flex justify-between items-center gap-2 my-2 px-3">
<div className="text-black">
{user.Name} {`<${user.Email}>`}
{user?.signerPtr?.Name || "-"}{" "}
{`<${
user?.email
? user.email
: user.signerPtr.Email
}>`}
</div>
<>{fetchUserStatus(user, item)}</>
</div>
@@ -1333,7 +1367,7 @@ const ReportTable = (props) => {
</ModalUi>
)}
</td>
{isEnableSubscription && (
{/* {isEnableSubscription && (
<td className=" pl-[20px] py-2 ">
{props.ReportName === "Templates" && (
<div className=" flex flex-row-">
@@ -1496,7 +1530,7 @@ const ReportTable = (props) => {
</ModalUi>
)}
</td>
)}
)} */}
</tr>
)
)}
@@ -179,6 +179,7 @@ export default async function createDocumentWithTemplate(request, response) {
}
object.set('URL', template.URL);
object.set('SignedUrl', template.URL);
object.set('SentToOthers', true);
if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays);
}
@@ -141,6 +141,7 @@ export default async function createDocumentwithCoordinate(request, response) {
}
object.set('URL', fileUrl);
object.set('SignedUrl', fileUrl);
object.set('SentToOthers', true);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
if (TimeToCompleteDays) {
@@ -35,8 +35,10 @@ async function sendMail(document, sessionToken) {
const objectId = signerMail[i]?.signerObjId;
const hostUrl = baseUrl.origin;
let encodeBase64;
let existSigner = {};
if (objectId) {
encodeBase64 = btoa(`${document.objectId}/${signerMail[i].signerPtr.Email}/${objectId}`);
existSigner = document?.Signers?.find(user => user.objectId === objectId);
encodeBase64 = btoa(`${document.objectId}/${existSigner?.Email}/${objectId}`);
} else {
encodeBase64 = btoa(`${document.objectId}/${signerMail[i].email}`);
}
@@ -46,7 +48,7 @@ async function sendMail(document, sessionToken) {
const themeBGcolor = '#47a3ad';
let params = {
extUserId: document.ExtUserPtr.objectId,
recipient: objectId ? signerMail[i].signerPtr.Email : signerMail[i].email,
recipient: objectId ? existSigner?.Email : signerMail[i].email,
subject: `${document.ExtUserPtr.Name} has requested you to sign "${document.Name}"`,
mailProvider: document?.ExtUserPtr?.active_mail_adapter || '',
from: sender,
@@ -140,12 +142,14 @@ export default async function createBatchDocs(request) {
: { ...y, signerPtr: {}, signerObjId: '' }
),
SignedUrl: x.URL || x.SignedUrl,
SentToOthers: true,
Signers: allSigner?.map(y => ({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: y.objectId,
})),
ACL: Acl,
SentToOthers: true,
RemindOnceInEvery: x.RemindOnceInEvery || 5,
AutomaticReminders: x.AutomaticReminders || false,
TimeToCompleteDays: x.TimeToCompleteDays || 15,