Merge pull request #784 from OpenSignLabs/feat_bulksend

This commit is contained in:
Amol
2024-05-28 19:23:50 +05:30
committed by GitHub
8 changed files with 176 additions and 45 deletions
+44 -15
View File
@@ -40,7 +40,7 @@ function Header({
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
const isMobile = window.innerWidth < 767;
const navigate = useNavigate();
const [isCertificate, setIsCertificate] = useState(false);
const [isDownloading, setIsDownloading] = useState("");
const isGuestSigner = localStorage.getItem("isGuestSigner");
//for go to previous page
function previousPage() {
@@ -131,7 +131,9 @@ function Header({
>
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => handleDownloadPdf(pdfDetails, pdfUrl)}
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<div
style={{
@@ -153,7 +155,7 @@ function Header({
onClick={() =>
handleDownloadCertificate(
pdfDetails,
setIsCertificate
setIsDownloading
)
}
>
@@ -194,7 +196,9 @@ function Header({
)}
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={(e) => handleToPrint(e, pdfUrl)}
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
>
<div
style={{
@@ -277,7 +281,11 @@ function Header({
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl)
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<i
@@ -366,7 +374,7 @@ function Header({
<button
type="button"
onClick={() =>
handleDownloadCertificate(pdfDetails, setIsCertificate)
handleDownloadCertificate(pdfDetails, setIsDownloading)
}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
>
@@ -379,7 +387,7 @@ function Header({
)}
<button
onClick={(e) => handleToPrint(e, pdfUrl)}
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
@@ -390,7 +398,9 @@ function Header({
<button
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => handleDownloadPdf(pdfDetails, pdfUrl)}
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
@@ -440,7 +450,11 @@ function Header({
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl)
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<i
@@ -462,7 +476,7 @@ function Header({
<button
type="button"
onClick={() =>
handleDownloadCertificate(pdfDetails, setIsCertificate)
handleDownloadCertificate(pdfDetails, setIsDownloading)
}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
>
@@ -474,7 +488,7 @@ function Header({
</button>
)}
<button
onClick={(e) => handleToPrint(e, pdfUrl)}
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
@@ -484,7 +498,9 @@ function Header({
<button
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => handleDownloadPdf(pdfDetails, pdfUrl)}
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
@@ -524,12 +540,25 @@ function Header({
)}
</div>
)}
{isDownloading === "pdf" && (
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
<div
style={{ fontSize: "45px", color: "#3dd3e0" }}
className="loader-37"
></div>
</div>
)}
<ModalUi
isOpen={isCertificate}
title={"Generating certificate"}
handleClose={() => setIsCertificate(false)}
isOpen={isDownloading === "certificate"}
title={
isDownloading === "certificate"
? "Generating certificate"
: "PDF Download"
}
handleClose={() => setIsDownloading("")}
>
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
{isDownloading === "certificate"}{" "}
<p>
Your completion certificate is being generated. Please wait
momentarily.
+18 -8
View File
@@ -2034,8 +2034,13 @@ export const handleSendOTP = async (email) => {
}
};
//handle download signed pdf
export const handleDownloadPdf = async (pdfDetails, pdfUrl) => {
export const handleDownloadPdf = async (
pdfDetails,
pdfUrl,
setIsDownloading
) => {
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
setIsDownloading("pdf");
try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
const axiosRes = await axios.post(
@@ -2051,8 +2056,10 @@ export const handleDownloadPdf = async (pdfDetails, pdfUrl) => {
);
const url = axiosRes.data.result;
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
setIsDownloading("");
} catch (err) {
console.log("err in getsignedurl", err);
setIsDownloading("");
alert("something went wrong, please try again later.");
}
};
@@ -2062,9 +2069,9 @@ export const sanitizeFileName = (pdfName) => {
return pdfName.replace(/ /g, "_");
};
//function for print digital sign pdf
export const handleToPrint = async (event, pdfUrl) => {
export const handleToPrint = async (event, pdfUrl, setIsDownloading) => {
event.preventDefault();
setIsDownloading("pdf");
try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
const axiosRes = await axios.post(
@@ -2094,10 +2101,13 @@ export const handleToPrint = async (event, pdfUrl) => {
const blob = new Blob([byteArray], { type: "application/pdf" });
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, "_blank");
setIsDownloading("");
} else {
printModule({ printable: pdf, type: "pdf", base64: true });
setIsDownloading("");
}
} catch (err) {
setIsDownloading("");
console.log("err in getsignedurl", err);
alert("something went wrong, please try again later.");
}
@@ -2106,7 +2116,7 @@ export const handleToPrint = async (event, pdfUrl) => {
//handle download signed pdf
export const handleDownloadCertificate = async (
pdfDetails,
setIsCertificate
setIsDownloading
) => {
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
try {
@@ -2117,7 +2127,7 @@ export const handleDownloadCertificate = async (
console.log("err in download in certificate", err);
}
} else {
setIsCertificate(true);
setIsDownloading("certificate");
try {
const data = {
docId: pdfDetails[0]?.objectId
@@ -2139,9 +2149,9 @@ export const handleDownloadCertificate = async (
await fetch(doc?.CertificateUrl);
const certificateUrl = doc?.CertificateUrl;
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
setIsCertificate(false);
setIsDownloading("");
} else {
setIsCertificate(true);
setIsDownloading("certificate");
}
}
} catch (err) {
@@ -2150,7 +2160,7 @@ export const handleDownloadCertificate = async (
}
}
};
export async function findContact(value) {
export async function findContact(value) {
try {
const currentUser = Parse.User.current();
const contactbook = new Parse.Query("contracts_Contactbook");
+1 -1
View File
@@ -3,7 +3,7 @@ export default function reportJson(id) {
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
const templateReport = ["Sr.No", "Title", "File", "Owner", "Roles"];
switch (id) {
// draft documents report
case "ByHuevtCFY":
+30 -8
View File
@@ -95,7 +95,7 @@ function PdfRequestFiles() {
const [expiredDate, setExpiredDate] = useState("");
const [signerUserId, setSignerUserId] = useState();
const [isDontShow, setIsDontShow] = useState(false);
const [isCertificate, setIsCertificate] = useState(false);
const [isDownloading, setIsDownloading] = useState("");
const [defaultSignAlert, setDefaultSignAlert] = useState({
isShow: false,
alertMessage: ""
@@ -787,7 +787,10 @@ function PdfRequestFiles() {
(x) => x.Email === jsonSender.email
);
const newIndex = index + 1;
const user = pdfDetails?.[0].Signers[newIndex];
const usermail = {
Email: pdfDetails?.[0]?.Placeholders[newIndex]?.email || ""
};
const user = usermail || pdfDetails?.[0]?.Signers[newIndex];
if (sendmail !== "false" && sendInOrder) {
const requestBody = pdfDetails?.[0]?.RequestBody;
const requestSubject = pdfDetails?.[0]?.RequestSubject;
@@ -1588,7 +1591,7 @@ function PdfRequestFiles() {
onClick={() =>
handleDownloadCertificate(
pdfDetails,
setIsCertificate
setIsDownloading
)
}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
@@ -1602,7 +1605,9 @@ function PdfRequestFiles() {
</span>
</button>
<button
onClick={(e) => handleToPrint(e, pdfUrl)}
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
@@ -1616,7 +1621,11 @@ function PdfRequestFiles() {
type="button"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl)
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<i
@@ -1631,12 +1640,25 @@ function PdfRequestFiles() {
)}
</div>
</ModalUi>
{isDownloading === "pdf" && (
<div className="fixed z-[1000] inset-0 flex justify-center items-center bg-black bg-opacity-30">
<div
style={{ fontSize: "45px", color: "#3dd3e0" }}
className="loader-37"
></div>
</div>
)}
<ModalUi
isOpen={isCertificate}
title={"Generating certificate"}
handleClose={() => setIsCertificate(false)}
isOpen={isDownloading === "certificate"}
title={
isDownloading === "certificate"
? "Generating certificate"
: "PDF Download"
}
handleClose={() => setIsDownloading("")}
>
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
{isDownloading === "certificate"}{" "}
<p>
Your completion certificate is being generated. Please
wait momentarily.
@@ -43,7 +43,7 @@ const ReportTable = (props) => {
const [templateDeatils, setTemplateDetails] = useState({});
const [placeholders, setPlaceholders] = useState([]);
const [isLoader, setIsLoader] = useState({});
const [isViewShare, setIsViewShare] = useState({});
const startIndex = (currentPage - 1) * props.docPerPage;
const { isMoreDocs, setIsNextRecord } = props;
// For loop is used to calculate page numbers visible below table
@@ -340,9 +340,9 @@ const ReportTable = (props) => {
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Document/${item.objectId}`,
`${localStorage.getItem("baseUrl")}classes/contracts_Document/${
item.objectId
}`,
data,
{
headers: {
@@ -578,9 +578,15 @@ const ReportTable = (props) => {
};
try {
const res = await axios.post(url, params, { headers: headers });
if (res) {
if (res?.data?.result?.status === "success") {
setIsAlert(true);
setAlertMsg({ type: "success", message: "Mail sent successfully." });
} else {
setIsAlert(true);
setAlertMsg({
type: "danger",
message: "Something went wrong, please try again later!"
});
}
} catch (err) {
console.log("err in sendmail", err);
@@ -681,6 +687,9 @@ const ReportTable = (props) => {
setTimeout(() => setIsAlert(false), 1500);
}
};
const handleViewSigners = (item) => {
setIsViewShare({ [item.objectId]: true });
};
return (
<div className="relative">
{Object.keys(actLoader)?.length > 0 && (
@@ -835,7 +844,17 @@ const ReportTable = (props) => {
{formatRow(item?.ExtUserPtr)}
</td>
<td className="px-4 py-2">
{item?.Signers ? formatRow(item?.Signers) : "-"}
{/* {item?.Signers ? formatRow(item?.Signers) : "-"} */}
{item?.Placeholders ? (
<button
onClick={() => handleViewSigners(item)}
className="text-[blue] hover:underline focus:outline-none"
>
View
</button>
) : (
"-"
)}
</td>
<td className="px-2 py-2 text-white flex flex-row gap-x-2 gap-y-1 justify-center items-center">
{props.actions?.length > 0 &&
@@ -865,19 +884,21 @@ const ReportTable = (props) => {
)}
{isOption[item.objectId] &&
act.action === "option" && (
<div className="absolute -right-2 top-5 bg-white text-nowrap rounded shadow z-[20] overflow-hidden">
<div className="absolute -right-2 top-5 p-1.5 bg-white text-nowrap rounded shadow-md z-[20] overflow-hidden">
{act.subaction?.map((subact) => (
<div
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 rounded cursor-pointer px-2 py-1.5 flex justify-start items-center text-black"
onClick={() =>
handleActionBtn(subact, item)
}
title={subact.hoverLabel}
>
<i className={subact.btnIcon}></i>
<i
className={`${subact.btnIcon} mr-1.5`}
></i>
{subact.btnLabel && (
<span className="ml-[4px] text-xs capitalize">
<span className="ml-[4px] text-[13px] capitalize font-medium">
{subact.btnLabel}
</span>
)}
@@ -887,6 +908,48 @@ const ReportTable = (props) => {
)}
</button>
))}
{isViewShare[item.objectId] && (
<div className="fixed z-[999] inset-0 w-full h-full bg-black bg-opacity-[75%]">
<div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-sm bg-white rounded shadow-md max-h-90 min-w-[90%] md:min-w-[400px] overflow-y-auto max-h-[340px] md:max-h-[400px] hide-scrollbar">
<div
className="cursor-pointer absolute text-white text-[22px] font-medium rounded-full z-50 top-1 right-3"
onClick={() => setIsViewShare({})}
>
&times;
</div>
<table className="table-auto w-full">
<thead className="text-white h-[38px] sticky top-0 bg-[#32a3ac]">
<tr>
{props.ReportName === "Templates" && (
<th className="p-2">Roles</th>
)}
<th className="p-2">Signers</th>
</tr>
</thead>
<tbody>
{item.Placeholders.map((x, i) => (
<tr
key={i}
className="text-sm font-normal text-black odd:bg-white even:bg-gray-200"
>
{props.ReportName === "Templates" && (
<td className="text-[13px] md:text-sm font-semibold p-2 ">
{x.Role && x.Role}
</td>
)}
<td className="text-[13px] md:text-sm p-2 break-all">
{x.email
? x.email
: x?.signerPtr?.Email || "-"}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{isDeleteModal[item.objectId] && (
<ModalUi
isOpen
@@ -99,8 +99,8 @@ export default async function linkContactToDoc(req) {
};
updateDoc.set('Placeholders', Placeholders);
const Acl = docRes.getACL();
Acl.setReadAccess(existContact.get('CreatedBy').id, true);
Acl.setWriteAccess(existContact.get('CreatedBy').id, true);
Acl.setReadAccess(existContact.get('UserId').id, true);
Acl.setWriteAccess(existContact.get('UserId').id, true);
updateDoc.setACL(Acl);
// const parseData = JSON.parse(JSON.stringify(res));
const resDoc = await updateDoc.save(null, { useMasterKey: true });
@@ -27,6 +27,7 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
@@ -67,6 +68,7 @@ export default function reportJson(id, userId) {
'Signers.Phone',
'Signers.UserId',
'AuditTrail',
'Placeholders',
],
};
// In progess report
@@ -131,6 +133,7 @@ export default function reportJson(id, userId) {
'Signers.Email',
'Signers.Phone',
'TimeToCompleteDays',
'Placeholders',
],
};
@@ -158,6 +161,7 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
// Expired Documents report
@@ -188,6 +192,7 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
// Recently sent for signatures report show on dashboard
@@ -259,6 +264,7 @@ export default function reportJson(id, userId) {
'AuditTrail',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
// Drafts report show on dashboard
@@ -286,6 +292,7 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
// contact book report
+1 -1
View File
@@ -101,7 +101,7 @@ export const config = {
masterKey: process.env.MASTER_KEY, //Add your master key here. Keep it secret!
masterKeyIps: ['0.0.0.0/0', '::/0'], // '::1'
serverURL: 'http://localhost:8080/app', // Don't forget to change to https if needed
verifyUserEmails: isMailAdapter === true ? true : false,
verifyUserEmails: false,
publicServerURL: process.env.SERVER_URL || 'http://localhost:8080/app',
// Your apps name. This will appear in the subject and body of the emails that are sent.
appName: 'Opensign',