Merge pull request #558 from OpenSignLabs/patch-1

feat: attach digital sign to completion certificate
This commit is contained in:
prafull-opensignlabs
2024-04-04 15:29:27 +05:30
committed by GitHub
10 changed files with 535 additions and 524 deletions
+122 -352
View File
@@ -1,7 +1,5 @@
import React from "react";
import PrevNext from "./PrevNext";
import { PDFDownloadLink } from "@react-pdf/renderer";
import Certificate from "./Certificate";
import printModule from "print-js";
import { getBase64FromUrl } from "../../constant/Utils";
import { saveAs } from "file-saver";
@@ -13,14 +11,11 @@ import { themeColor } from "../../constant/const";
function Header({
isPdfRequestFiles,
isPlaceholder,
recipient,
setIsDecline,
pageNumber,
isAlreadySign,
allPages,
changePage,
pdfUrl,
documentStatus,
embedWidgetsData,
pdfDetails,
signerPos,
@@ -94,109 +89,15 @@ function Header({
// Replace spaces with underscore
return pdfName.replace(/ /g, "_");
};
//certificate generate and download component in mobile view
const CertificateDropDown = () => {
//after generate download certifcate pdf
const handleDownload = (pdfBlob, fileName) => {
if (pdfBlob) {
const url = window.URL.createObjectURL(pdfBlob);
// Create a temporary anchor element
const link = document.createElement("a");
link.href = url;
link.download = fileName;
// Append the anchor to the body
document.body.appendChild(link);
// Programmatically click the anchor to trigger the download
link.click();
// Remove the anchor from the body
document.body.removeChild(link);
// Release the object URL to free up resources
window.URL.revokeObjectURL(url);
}
};
return (
<PDFDownloadLink
onClick={(e) => e.preventDefault()}
style={{ textDecoration: "none", zIndex: "35" }}
document={<Certificate pdfData={pdfDetails} />}
>
{({ blob, loading }) => (
<>
{loading ? (
<div
style={{
border: "none",
backgroundColor: "#fff"
}}
>
<i
className="fa-solid fa-award"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Certificate
</div>
) : (
<div
style={{ border: "none", backgroundColor: "#fff" }}
onClick={() =>
handleDownload(
blob,
`completion certificate-${
pdfDetails[0] && pdfDetails[0].Name
}.pdf`
)
}
>
<i
className="fa-solid fa-award"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Certificate
</div>
)}
</>
)}
</PDFDownloadLink>
);
};
const CertificateComponent = () => {
return (
<PDFDownloadLink
style={{ textDecoration: "none" }}
document={<Certificate pdfData={pdfDetails} />}
fileName={`completion certificate-${
pdfDetails[0] && pdfDetails[0].Name
}.pdf`}
>
{({ loading }) => (
<button
type="button"
className="defaultBtn certificateBtn"
disabled={loading}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
>
<i
className="fa-solid fa-award"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Certificate
</button>
)}
</PDFDownloadLink>
);
//handle download signed pdf
const handleDownloadCertificate = () => {
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
}
};
return (
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
{isMobile && isShowHeader ? (
@@ -210,11 +111,7 @@ function Header({
}}
>
<div className="preBtn2">
<div
onClick={() => {
navigate(-1);
}}
>
<div onClick={() => navigate(-1)}>
<i
className="fa fa-arrow-left"
aria-hidden="true"
@@ -266,7 +163,6 @@ function Header({
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="DropdownMenuContent"
@@ -290,42 +186,42 @@ function Header({
Download
</div>
</DropdownMenu.Item>
{recipient && pdfDetails[0] && pdfDetails.length > 0 ? (
<DropdownMenu.Item className="DropdownMenuItem">
<CertificateDropDown />
{isCompleted && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => handleDownloadCertificate()}
>
<div
style={{ border: "none", backgroundColor: "#fff" }}
>
<i
className="fa-solid fa-award"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Certificate
</div>
</DropdownMenu.Item>
) : isPdfRequestFiles &&
alreadySign &&
isCompleted.isCertificate ? (
<DropdownMenu.Item className="DropdownMenuItem">
<CertificateDropDown />
)}
{isSignYourself && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => setIsEmail(true)}
>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa fa-envelope"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Mail
</div>
</DropdownMenu.Item>
) : (
isSignYourself && (
<>
<DropdownMenu.Item className="DropdownMenuItem">
<CertificateDropDown />
</DropdownMenu.Item>
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => setIsEmail(true)}
>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa fa-envelope"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Mail
</div>
</DropdownMenu.Item>
</>
)
)}
<DropdownMenu.Item
className="DropdownMenuItem"
@@ -337,7 +233,6 @@ function Header({
flexDirection: "row"
}}
>
{" "}
<i
className="fa fa-print"
aria-hidden="true"
@@ -416,113 +311,34 @@ function Header({
</div>
</div>
) : (
<div className="signatureHeader headerBtn">
<div className="flex flex-wrap justify-between items-center mt-[5px]">
<PrevNext
pageNumber={pageNumber}
allPages={allPages}
changePage={changePage}
/>
{recipient ? (
pdfUrl || isAlreadySign.mssg ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{pdfDetails && pdfDetails.length > 0 && (
<CertificateComponent />
)}
<button
onClick={handleToPrint}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="defaultBtn printBtn"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
Print
</button>
<button
type="button"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="defaultBtn downloadBtn"
onClick={() => handleDownloadPdf()}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Download
</button>
</div>
) : (
<div style={{ display: "flex", flexDirection: "row" }}>
<button
style={{
background: "#de4337"
}}
className="finishBtn hoverCss"
onClick={() => {
handleDeclinePdfAlert();
}}
>
Decline
</button>
<button
data-tut="reactourThird"
style={{
background: !pdfUrl ? "#188ae2" : "#d3edeb"
}}
className="finishBtn sendHover"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
}
}}
>
Finish
</button>
</div>
)
) : isPlaceholder ? (
{isPlaceholder ? (
<>
{!isMailSend &&
signersdata.length > 0 &&
signersdata.length !== filterPrefill.length && (
<div>
{filterPrefill.length === 0 ? (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length}{" "}
recipients signature
</span>
) : (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length} more
recipients signature
</span>
)}
</div>
)}
<div>
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none">
{!isMailSend &&
signersdata.length > 0 &&
signersdata.length !== filterPrefill.length && (
<div>
{filterPrefill.length === 0 ? (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length}{" "}
recipients signature
</span>
) : (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length} more
recipients signature
</span>
)}
</div>
)}
</div>
<div className="flex">
{setIsEditTemplate && (
<button
onClick={() => setIsEditTemplate(true)}
@@ -540,93 +356,79 @@ function Header({
navigate(-1);
}}
type="button"
className="defaultBtn backBtn"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
<button
disabled={isMailSend && true}
data-tut="reactourFour"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] font-[500] text-sm mr-[5px]"
style={{
background: isMailSend ? "rgb(203, 203, 203)" : "#188ae2",
color: isMailSend && "rgb(77, 75, 75)"
color: isMailSend ? "rgb(77, 75, 75)" : "white"
}}
onClick={() => {
alertSendEmail();
}}
className={isMailSend ? "sendMail" : "sendMail sendHover"}
>
{completeBtnTitle ? completeBtnTitle : "Send"}
{completeBtnTitle
? completeBtnTitle
: isMailSend
? "Sent"
: "Send"}
</button>
</div>
</>
) : isPdfRequestFiles ? (
alreadySign ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{isCompleted.isCertificate && <CertificateComponent />}
{isCompleted && (
<button
type="button"
onClick={() => handleDownloadCertificate()}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
>
<i
className="fa-solid fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block ml-1">Certificate</span>
</button>
)}
<button
onClick={handleToPrint}
type="button"
className="defaultBtn printBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
Print
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Print</span>
</button>
<button
type="button"
className="defaultBtn downloadBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
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()}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Download
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
</button>
</div>
) : (
<div>
<div className="flex">
<button
onClick={() => {
navigate(-1);
}}
onClick={() => navigate(-1)}
type="button"
className="defaultBtn backBtn"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
{currentSigner && (
<>
<button
style={{
background: "#de4337"
}}
className="finishBtn hoverCss"
style={{ background: "#de4337" }}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
onClick={() => {
handleDeclinePdfAlert();
}}
@@ -638,7 +440,7 @@ function Header({
background: "#188ae2"
}}
type="button"
className="finishBtn sendHover"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
onClick={() => {
embedWidgetsData();
}}
@@ -649,93 +451,61 @@ function Header({
)}
</div>
)
) : pdfUrl || (documentStatus && documentStatus.isCompleted) ? (
) : isCompleted ? (
<div style={{ display: "flex", flexDirection: "row" }}>
<CertificateComponent />
<button
type="button"
onClick={() => handleDownloadCertificate()}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
>
<i
className="fa-solid fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block ml-1">Certificate</span>
</button>
<button
onClick={handleToPrint}
type="button"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="defaultBtn printBtn"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
Print
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Print</span>
</button>
<button
type="button"
className="defaultBtn downloadBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px"
}}
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()}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Download
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
</button>
<button
type="button"
className="defaultBtn mailBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px"
}}
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#3ba7e5]"
onClick={() => setIsEmail(true)}
>
<i
className="fa fa-envelope"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Mail
<i className="fa fa-envelope py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Mail</span>
</button>
</div>
) : (
<div>
<div className="flex">
<button
onClick={() => {
navigate(-1);
}}
type="button"
className="defaultBtn backBtn"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
>
Back
</button>
<button
style={{
background: "#188ae2"
}}
type="button"
className="finishBtn sendHover"
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
+20 -25
View File
@@ -1,4 +1,5 @@
import React from "react";
import { themeColor } from "../../constant/const";
function PrevNext({ pageNumber, allPages, changePage }) {
//for go to previous page
@@ -14,41 +15,35 @@ function PrevNext({ pageNumber, allPages, changePage }) {
<div>
<div className="preBtn1">
<button
style={{
padding: "3px 20px ",
fontSize: "10px",
background: "#d3edeb",
fontWeight: "600",
border: "0px",
marginRight: "5px"
}}
className="py-[3px] px-[10px] text-xs bg-[#d3edeb] font-[600] mr-[5px]"
disabled={pageNumber <= 1}
onClick={previousPage}
>
Prev
<span className="block lg:hidden">
<i
className="fa fa-backward"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</span>
<span className="lg:block hidden">Prev</span>
</button>
<span
style={{
fontSize: "13px",
fontWeight: "600"
}}
>
<span className="text-xs font-[500]">
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<button
style={{
padding: "3px 20px ",
fontSize: "10px",
background: "#d3edeb",
fontWeight: "600",
marginLeft: "5px",
border: "0px"
}}
className="py-[3px] px-[10px] text-xs bg-[#d3edeb] font-[600] ml-[5px]"
disabled={pageNumber >= allPages}
onClick={nextPage}
>
Next
<span className="block lg:hidden">
<i
className="fa fa-forward"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</span>
<span className="lg:block hidden">Next</span>
</button>
</div>
</div>
+8 -8
View File
@@ -1152,7 +1152,7 @@ function PdfRequestFiles() {
isOpen={isCompleted.isModal}
title={"Sign Documents"}
handleClose={() => {
setIsCompleted({ isModal: false, isCertificate: true });
setIsCompleted((prev) => ({ ...prev, isModal: false }));
}}
>
<div style={{ height: "100%", padding: 20 }}>
@@ -1170,12 +1170,12 @@ function PdfRequestFiles() {
<button
type="button"
className="finishBtn cancelBtn"
onClick={() =>
setIsCompleted({
isModal: false,
isCertificate: true
})
}
onClick={() => {
setIsCompleted((prev) => ({
...prev,
isModal: false
}));
}}
>
Close
</button>
@@ -1213,7 +1213,7 @@ function PdfRequestFiles() {
pdfDetails={pdfDetails}
signerPos={signerPos}
isSigned={isSigned}
isCompleted={isCompleted}
isCompleted={isCompleted.isCertificate}
embedWidgetsData={embedWidgetsData}
isShowHeader={true}
setIsDecline={setIsDecline}
+4 -7
View File
@@ -67,7 +67,6 @@ function SignYourSelf() {
const [pdfOriginalWidth, setPdfOriginalWidth] = useState();
const [successEmail, setSuccessEmail] = useState(false);
const imageRef = useRef(null);
const [documentStatus, setDocumentStatus] = useState({ isCompleted: false });
const [myInitial, setMyInitial] = useState("");
const [isInitial, setIsInitial] = useState(false);
const [isUiLoading, setIsUiLoading] = useState(false);
@@ -100,6 +99,7 @@ function SignYourSelf() {
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
const [isDontShow, setIsDontShow] = useState(false);
const [extUserId, setExtUserId] = useState("");
const [isCompleted, setIsCompleted] = useState(false);
const divRef = useRef(null);
const nodeRef = useRef(null);
const [, drop] = useDrop({
@@ -190,10 +190,7 @@ function SignYourSelf() {
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
if (isCompleted) {
const docStatus = {
isCompleted: isCompleted
};
setDocumentStatus(docStatus);
setIsCompleted(true);
setPdfUrl(documentData[0].SignedUrl);
const alreadySign = {
status: true,
@@ -1129,7 +1126,6 @@ function SignYourSelf() {
allPages={allPages}
changePage={changePage}
pdfUrl={pdfUrl}
documentStatus={documentStatus}
embedWidgetsData={embedWidgetsData}
pdfDetails={pdfDetails}
isShowHeader={true}
@@ -1137,6 +1133,7 @@ function SignYourSelf() {
alreadySign={pdfUrl ? true : false}
isSignYourself={true}
setIsEmail={setIsEmail}
isCompleted={isCompleted}
/>
<div data-tut="reactourSecond">
@@ -1189,7 +1186,7 @@ function SignYourSelf() {
}}
className="autoSignScroll"
>
{!documentStatus.isCompleted ? (
{!isCompleted ? (
<div>
<WidgetComponent
dataTut="reactourFirst"
-51
View File
@@ -395,14 +395,6 @@
margin-left: 10px;
}
.backBtn {
background-color: white;
}
.backBtn:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.defaultBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 26px;
@@ -420,42 +412,6 @@
outline: none;
}
.downloadBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 15px !important;
background-color: rgb(241 79 79);
border: none !important;
color: white !important;
}
.downloadBtn:hover {
box-shadow: 0 2px 4px rgba(154, 36, 36, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.printBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 15px !important;
background: #188ae2;
border: none !important;
color: white !important;
}
.printBtn:hover {
box-shadow: 0 2px 4px rgba(23, 48, 137, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.certificateBtn {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
padding: 3px 15px !important;
background: #08bc66;
border: none !important;
color: white !important;
text-decoration: none !important;
}
.certificateBtn:hover {
box-shadow: 0 2px 4px rgba(15, 150, 87, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
}
.signerList {
overflow-y: scroll;
@@ -561,13 +517,6 @@
position: "relative";
}
.signatureHeader {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-top: 5px;
}
.signerComponent {
/* box-shadow: rgba(17, 12, 46, 0.15) 0px 48px 100px 0px; */