mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-22 15:42:31 +02:00
feat: add download, certificate & print buttons on the document completion popup
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import React, { useState } from "react";
|
||||
import PrevNext from "./PrevNext";
|
||||
import printModule from "print-js";
|
||||
import { getBase64FromUrl } from "../../constant/Utils";
|
||||
import { saveAs } from "file-saver";
|
||||
import {
|
||||
handleDownloadCertificate,
|
||||
handleDownloadPdf,
|
||||
handleToPrint
|
||||
} from "../../constant/Utils";
|
||||
import "../../styles/signature.css";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import axios from "axios";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { appInfo } from "../../constant/appinfo";
|
||||
|
||||
function Header({
|
||||
isPdfRequestFiles,
|
||||
@@ -59,123 +59,6 @@ function Header({
|
||||
setIsDecline(currentDecline);
|
||||
};
|
||||
|
||||
//function for print digital sign pdf
|
||||
const handleToPrint = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
const axiosRes = await axios.post(
|
||||
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||
{ url: pdfUrl },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": appInfo.appId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
const url = axiosRes.data.result;
|
||||
const pdf = await getBase64FromUrl(url);
|
||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||
const isAppleDevice =
|
||||
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||
(navigator.platform === "MacIntel" &&
|
||||
navigator.maxTouchPoints > 1)) &&
|
||||
!window.MSStream;
|
||||
if (isAndroidDevice || isAppleDevice) {
|
||||
const byteArray = Uint8Array.from(
|
||||
atob(pdf)
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0))
|
||||
);
|
||||
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
window.open(blobUrl, "_blank");
|
||||
} else {
|
||||
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
};
|
||||
|
||||
//handle download signed pdf
|
||||
const handleDownloadPdf = async () => {
|
||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
const axiosRes = await axios.post(
|
||||
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||
{ url: pdfUrl },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": appInfo.appId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
const url = axiosRes.data.result;
|
||||
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
|
||||
//handle download signed pdf
|
||||
const handleDownloadCertificate = async () => {
|
||||
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
||||
try {
|
||||
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
||||
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
} catch (err) {
|
||||
console.log("err in download in certificate", err);
|
||||
}
|
||||
} else {
|
||||
setIsCertificate(true);
|
||||
try {
|
||||
const data = {
|
||||
docId: pdfDetails[0]?.objectId
|
||||
};
|
||||
const docDetails = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/getDocument`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
if (docDetails.data && docDetails.data.result) {
|
||||
const doc = docDetails.data.result;
|
||||
if (doc?.CertificateUrl) {
|
||||
await fetch(doc?.CertificateUrl);
|
||||
const certificateUrl = doc?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
setIsCertificate(false);
|
||||
} else {
|
||||
setIsCertificate(true);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in download in certificate", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
||||
{isMobile && isShowHeader ? (
|
||||
@@ -248,7 +131,7 @@ function Header({
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
onClick={() => handleDownloadPdf(pdfDetails, pdfUrl)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
@@ -267,7 +150,12 @@ function Header({
|
||||
{isCompleted && (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
pdfDetails,
|
||||
setIsCertificate
|
||||
)
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
@@ -306,7 +194,7 @@ function Header({
|
||||
)}
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={handleToPrint}
|
||||
onClick={(e) => handleToPrint(e, pdfUrl)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
@@ -388,7 +276,9 @@ function Header({
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl)
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa fa-arrow-down mr-[5px]"
|
||||
@@ -475,7 +365,9 @@ function Header({
|
||||
{isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(pdfDetails, setIsCertificate)
|
||||
}
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
||||
>
|
||||
<i
|
||||
@@ -487,7 +379,7 @@ function Header({
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
onClick={(e) => handleToPrint(e, pdfUrl)}
|
||||
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]"
|
||||
>
|
||||
@@ -498,7 +390,7 @@ 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()}
|
||||
onClick={() => handleDownloadPdf(pdfDetails, pdfUrl)}
|
||||
>
|
||||
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Download</span>
|
||||
@@ -547,7 +439,9 @@ function Header({
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl)
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa fa-arrow-down mr-[5px]"
|
||||
@@ -567,7 +461,9 @@ function Header({
|
||||
{isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(pdfDetails, setIsCertificate)
|
||||
}
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
||||
>
|
||||
<i
|
||||
@@ -578,7 +474,7 @@ function Header({
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
onClick={(e) => handleToPrint(e, pdfUrl)}
|
||||
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]"
|
||||
>
|
||||
@@ -588,7 +484,7 @@ 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()}
|
||||
onClick={() => handleDownloadPdf(pdfDetails, pdfUrl)}
|
||||
>
|
||||
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Download</span>
|
||||
|
||||
@@ -4,6 +4,9 @@ import { isEnableSubscription, themeColor } from "./const";
|
||||
import React from "react";
|
||||
import { rgb } from "pdf-lib";
|
||||
import Parse from "parse";
|
||||
import { appInfo } from "./appinfo";
|
||||
import { saveAs } from "file-saver";
|
||||
import printModule from "print-js";
|
||||
|
||||
export const isMobile = window.innerWidth < 767;
|
||||
export const textInputWidget = "text input";
|
||||
@@ -1350,8 +1353,8 @@ export const multiSignEmbed = async (
|
||||
position.type === radioButtonWidget
|
||||
? 10
|
||||
: position.type === "checkbox"
|
||||
? 10
|
||||
: newUpdateHeight;
|
||||
? 10
|
||||
: newUpdateHeight;
|
||||
const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight;
|
||||
|
||||
if (signyourself) {
|
||||
@@ -2029,3 +2032,121 @@ export const handleSendOTP = async (email) => {
|
||||
alert(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
//handle download signed pdf
|
||||
export const handleDownloadPdf = async (pdfDetails, pdfUrl) => {
|
||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
const axiosRes = await axios.post(
|
||||
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||
{ url: pdfUrl },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": appInfo.appId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
const url = axiosRes.data.result;
|
||||
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
};
|
||||
|
||||
export const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
//function for print digital sign pdf
|
||||
export const handleToPrint = async (event, pdfUrl) => {
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
const axiosRes = await axios.post(
|
||||
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||
{ url: pdfUrl },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": appInfo.appId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
const url = axiosRes.data.result;
|
||||
const pdf = await getBase64FromUrl(url);
|
||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||
const isAppleDevice =
|
||||
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
|
||||
!window.MSStream;
|
||||
if (isAndroidDevice || isAppleDevice) {
|
||||
const byteArray = Uint8Array.from(
|
||||
atob(pdf)
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0))
|
||||
);
|
||||
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
window.open(blobUrl, "_blank");
|
||||
} else {
|
||||
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
};
|
||||
|
||||
//handle download signed pdf
|
||||
export const handleDownloadCertificate = async (
|
||||
pdfDetails,
|
||||
setIsCertificate
|
||||
) => {
|
||||
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
||||
try {
|
||||
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
||||
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
} catch (err) {
|
||||
console.log("err in download in certificate", err);
|
||||
}
|
||||
} else {
|
||||
setIsCertificate(true);
|
||||
try {
|
||||
const data = {
|
||||
docId: pdfDetails[0]?.objectId
|
||||
};
|
||||
const docDetails = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/getDocument`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
if (docDetails.data && docDetails.data.result) {
|
||||
const doc = docDetails.data.result;
|
||||
if (doc?.CertificateUrl) {
|
||||
await fetch(doc?.CertificateUrl);
|
||||
const certificateUrl = doc?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
setIsCertificate(false);
|
||||
} else {
|
||||
setIsCertificate(true);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in download in certificate", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,7 +29,10 @@ import {
|
||||
convertPdfArrayBuffer,
|
||||
contractUsers,
|
||||
handleSendOTP,
|
||||
contactBook
|
||||
contactBook,
|
||||
handleDownloadPdf,
|
||||
handleToPrint,
|
||||
handleDownloadCertificate
|
||||
} from "../constant/Utils";
|
||||
import Loader from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
@@ -91,6 +94,7 @@ function PdfRequestFiles() {
|
||||
const [expiredDate, setExpiredDate] = useState("");
|
||||
const [signerUserId, setSignerUserId] = useState();
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const [isCertificate, setIsCertificate] = useState(false);
|
||||
const [defaultSignAlert, setDefaultSignAlert] = useState({
|
||||
isShow: false,
|
||||
alertMessage: ""
|
||||
@@ -1577,38 +1581,87 @@ function PdfRequestFiles() {
|
||||
handleClose={() => {
|
||||
setIsCompleted((prev) => ({ ...prev, isModal: false }));
|
||||
}}
|
||||
reduceWidth={!isCompleted?.message}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
<p>
|
||||
{" "}
|
||||
{isCompleted?.message ||
|
||||
"This document has been signed by all Signers."}
|
||||
</p>
|
||||
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={() => {
|
||||
setIsCompleted((prev) => ({
|
||||
...prev,
|
||||
isModal: false
|
||||
}));
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
{isCompleted?.message ? (
|
||||
<p>{isCompleted?.message}</p>
|
||||
) : (
|
||||
<div className="px-[15px]">
|
||||
<span>
|
||||
Congratulations! 🎉 This document has been
|
||||
successfully
|
||||
</span>
|
||||
<br />
|
||||
<span>signed by all participants!</span>
|
||||
</div>
|
||||
)}
|
||||
{!isCompleted?.message && (
|
||||
<div className="flex mt-4 gap-1 px-[15px]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(
|
||||
pdfDetails,
|
||||
setIsCertificate
|
||||
)
|
||||
}
|
||||
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={(e) => handleToPrint(e, pdfUrl)}
|
||||
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]"
|
||||
>
|
||||
<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="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)
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa fa-download py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block ml-1">
|
||||
Download
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
isOpen={isCertificate}
|
||||
title={"Generating certificate"}
|
||||
handleClose={() => setIsCertificate(false)}
|
||||
>
|
||||
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
|
||||
<p>
|
||||
Your completion certificate is being generated. Please
|
||||
wait momentarily.
|
||||
</p>
|
||||
<p>
|
||||
If the download doesn't start shortly, click the
|
||||
button again.
|
||||
</p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
{/* this component is used for signature pad modal */}
|
||||
<SignPad
|
||||
isSignPad={isSignPad}
|
||||
|
||||
@@ -10,7 +10,7 @@ const ModalUi = ({
|
||||
handleClose,
|
||||
showHeader = true,
|
||||
showClose = true,
|
||||
styleClass,
|
||||
reduceWidth,
|
||||
showHeaderMessage
|
||||
}) => {
|
||||
return (
|
||||
@@ -18,11 +18,9 @@ const ModalUi = ({
|
||||
{isOpen && (
|
||||
<div className="fixed z-[999] top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]">
|
||||
<div
|
||||
className={
|
||||
styleClass
|
||||
? styleClass
|
||||
: "fixed z-[1000] 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-[500px] overflow-y-auto hide-scrollbar"
|
||||
}
|
||||
className={`${
|
||||
reduceWidth ? "md:min-w-[430px]" : "md:min-w-[500px]"
|
||||
} fixed z-[1000] 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%] overflow-y-auto hide-scrollbar `}
|
||||
>
|
||||
{showHeader && (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user