1294 lines
69 KiB
TypeScript
1294 lines
69 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
|
|
import axios from "axios"
|
|
import { RootState } from "../../store/store";
|
|
import { useParams } from "react-router-dom"
|
|
import { DroppedItem, DroppedItemsObj } from "../../utlity/types";
|
|
|
|
import Loader from "../dashboard/Loader";
|
|
import SignCon from "../signature/SignCon";
|
|
import { Bounce, toast } from "react-toastify";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import { hideLoader, showLoader } from "../../store/slices/loaderSlice";
|
|
|
|
import { FaRegImage, FaFileAlt, FaSignature, FaStamp, FaCheck, FaQuestionCircle } from 'react-icons/fa'
|
|
import { IoIosDocument } from 'react-icons/io'
|
|
import { MdKeyboardArrowDown } from 'react-icons/md'
|
|
import { TbChevronsDownLeft, TbSignature } from "react-icons/tb";
|
|
import { BsFileArrowDownFill } from "react-icons/bs";
|
|
import dayjs from "dayjs";
|
|
import maskUtils from "../../utlity/maskUtils";
|
|
import StampUpload from "../stamp/StampUpload";
|
|
import TermsModal from "./TermsModal";
|
|
import DocumentPages from "./DocumentPages";
|
|
import FieldList from "../process/processStepTwo/FieldList";
|
|
import typeToIconMap from "../../helper/iconMap";
|
|
import { droppedTagsDefaultField } from "../process/processStepTwo/DocumentContainer";
|
|
|
|
|
|
|
|
interface RecipientType {
|
|
id: string;
|
|
firstName: string | null;
|
|
lastName: string;
|
|
fullName: string | null;
|
|
email: string;
|
|
title: string | null;
|
|
initials: string | null;
|
|
company: string | null;
|
|
website: string | null;
|
|
phone: string | null;
|
|
index: number;
|
|
type: string;
|
|
recipientStatus: string;
|
|
authType: string | null;
|
|
language: string | null;
|
|
transaction: string | null;
|
|
acceptedTerms: boolean;
|
|
name: string | null;
|
|
}
|
|
interface pagesType {
|
|
index: number;
|
|
type: string;
|
|
src: string;
|
|
height: number;
|
|
width: number;
|
|
}
|
|
interface DocumentType {
|
|
documentId: string;
|
|
pages: pagesType[];
|
|
name: string;
|
|
}
|
|
|
|
interface TransactionType {
|
|
status: string;
|
|
transationId: string;
|
|
}
|
|
interface TagSubmitData {
|
|
id: string;
|
|
type?: string;
|
|
subType: string;
|
|
signed: boolean;
|
|
value: string;
|
|
}
|
|
interface TagSubmitRecData {
|
|
recipientId: string;
|
|
}
|
|
export enum SaveTypeEnum {
|
|
DRAW = "DRAW",
|
|
TYPE = "TEXT",
|
|
UPLOAD = "IMAGE",
|
|
}
|
|
export interface SaveType {
|
|
saveType: SaveTypeEnum | null;
|
|
save: boolean;
|
|
signature: string | null;
|
|
initial: string | null;
|
|
}
|
|
interface TagsSubmitDataObj {
|
|
tags: TagSubmitData[];
|
|
recipient?: TagSubmitRecData
|
|
}
|
|
|
|
interface SignConfType {
|
|
accepted: boolean;
|
|
index: number;
|
|
}
|
|
|
|
interface SignConfTypeObj {
|
|
[key: number]: SignConfType;
|
|
}
|
|
|
|
export interface StampTypeObj {
|
|
open: boolean;
|
|
blob: string;
|
|
}
|
|
|
|
interface FIT {
|
|
label: string,
|
|
value: number | null
|
|
}
|
|
|
|
const fitObj = [
|
|
{
|
|
label: "Fit",
|
|
value: "FIT"
|
|
},
|
|
{
|
|
label: "50%",
|
|
value: "50"
|
|
},
|
|
{
|
|
label: "150%",
|
|
value: "150"
|
|
},
|
|
{
|
|
label: "200%",
|
|
value: "200"
|
|
},
|
|
]
|
|
|
|
// Functions - LIST
|
|
// handleSignClick - Handle when sign || Initial tag got clicked
|
|
// fetchData - Fetch document pages and tags data
|
|
// handleFormSubmit - handle form submit and send data to server
|
|
|
|
const SignDocument = () => {
|
|
const { recipientId } = useParams();
|
|
const dispatch = useDispatch();
|
|
|
|
// Signer data - START
|
|
const [selectedDocuments, setSelectedDocuments] = useState<DocumentType[] | null>(null);
|
|
const [recipient, setRecipient] = useState<RecipientType | null>(null);
|
|
const [tagJson, setTagJson] = useState<DroppedItem[] | []>([]);
|
|
const [transaction, setTransaction] = useState<TransactionType | null>(null);
|
|
const [recSignStatus, setRecSignStatus] = useState<string | null>(null);
|
|
const [droppedItems, setDroppedItems] = useState([]);
|
|
const[signTagId , setSignTagId] = useState<number>(0)
|
|
|
|
const [stamp, setStamp] = useState<StampTypeObj>({ open: false, blob: "" });
|
|
|
|
console.log(recipient, "recipient Line 145")
|
|
|
|
// Handling Date Opacity
|
|
const [dateValues, setDateValues] = useState<{ [key: string]: string }>({});
|
|
const [isDateOpen, setIsDateOpen] = useState<string | null>(null);
|
|
// Signer data - END
|
|
|
|
// Show is fields - Showing all fields There
|
|
const [isFields, setIsFields] = useState<boolean>(true);
|
|
|
|
const [isFitDd, setIsFitDd] = useState<boolean>(false)
|
|
const [selFitIndx, setSelFitIndx] = useState<number>(0);
|
|
|
|
const globalLoader = useSelector((state: RootState) => state.loader)
|
|
|
|
const [openSign, setOpenSign] = useState<boolean>(false);
|
|
const [focusElement, setFocusElement] = useState<DroppedItem | null>(null);
|
|
const [dropDownTogglePage, setDropDownTogglePage] = useState<string>("");
|
|
|
|
const [nav2DocToggle, setNav2DocToggle] = useState<boolean>(false);
|
|
|
|
const [hideTags, setHideTags] = useState<boolean>(false);
|
|
const [transparentTags, setTransparentTags] = useState<boolean>(false);
|
|
|
|
const [saved, setSaved] = useState<SaveType>({
|
|
saveType: null,
|
|
save: false,
|
|
signature: null,
|
|
initial: null,
|
|
});
|
|
|
|
|
|
|
|
const [signConfObj, setSignConfObj] = useState<SignConfTypeObj | {}>({});
|
|
|
|
console.log("signConfObj",signConfObj)
|
|
|
|
const zoom = (percentage: number, contentClass: string) => {
|
|
const content = document.querySelector(`.${contentClass}`);
|
|
if (content) {
|
|
(content as HTMLElement).style.zoom = `${percentage}%`;
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
let indx = fitObj[selFitIndx].value
|
|
if (indx === "FIT") {
|
|
indx = "100";
|
|
}
|
|
zoom(Number(indx), 'zoom-con')
|
|
}, [selFitIndx])
|
|
|
|
useEffect(() => {
|
|
dispatch(showLoader("Loading ..."));
|
|
fetchData();
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (!focusElement) {
|
|
if (droppedItems && droppedItems.length) {
|
|
setFocusElement({ ...droppedItems[0], focusElementIndx: 0 });
|
|
}
|
|
}
|
|
}, [droppedItems])
|
|
|
|
console.log(saved, "Line 162")
|
|
|
|
useEffect(() => {
|
|
if (recSignStatus === "COMPLETED") {
|
|
console.log("SETTING HIDE TAGS to TRUE LIne 161")
|
|
// setHideTags(true);
|
|
setTransparentTags(true);
|
|
} else {
|
|
console.log("SETTING HIDE TAGS to TRUE LIne 161")
|
|
setHideTags(false);
|
|
}
|
|
}, [recSignStatus])
|
|
|
|
useEffect(() => {
|
|
if (saved.save && Object.keys(signConfObj).length) {
|
|
const keys = Object.keys(signConfObj)
|
|
const index = Number(keys[0])
|
|
|
|
const newObj = {
|
|
accepted: true,
|
|
index: index
|
|
}
|
|
|
|
setSignConfObj((prev) => {
|
|
return (
|
|
{ ...prev, [index]: { ...newObj } }
|
|
)
|
|
});
|
|
}
|
|
}, [saved])
|
|
|
|
const handleSignClick = (index: number , id:number) => {
|
|
console.log("IDNumber" , id)
|
|
setSignTagId(id)
|
|
if (saved.save) {
|
|
if (index in signConfObj) {
|
|
setOpenSign(true);
|
|
return
|
|
};
|
|
setSignConfObj({
|
|
...signConfObj,
|
|
[index]: {
|
|
accepted: true,
|
|
index: index
|
|
}
|
|
})
|
|
return
|
|
}
|
|
setOpenSign(true);
|
|
|
|
const newObj = {
|
|
[index]: {
|
|
"accepted": false,
|
|
index: index
|
|
}
|
|
}
|
|
setSignConfObj({ ...newObj })
|
|
|
|
}
|
|
|
|
|
|
|
|
const handleStampClick = (index: number) => {
|
|
console.log("Stamp Clicked", index)
|
|
setStamp({
|
|
open: true,
|
|
blob: ""
|
|
})
|
|
|
|
}
|
|
|
|
const handleSignAcknowledgeClick = (index: number) => {
|
|
if (saved.save && signConfObj === null) {
|
|
setSignConfObj([...signConfObj, index])
|
|
}
|
|
}
|
|
|
|
const fetchData = (isSign: boolean = false) => {
|
|
axios.get(`${import.meta.env.VITE_BASE_URI}/api/sign?recipientId=${recipientId}`,
|
|
{
|
|
headers:
|
|
{ "ngrok-skip-browser-warning": "69420", }
|
|
})
|
|
.then((res) => {
|
|
console.dir(res)
|
|
if (res.status === 200) {
|
|
|
|
console.log(res, "Line 288")
|
|
|
|
setSelectedDocuments(res.data.documents)
|
|
setRecipient(res.data.recipient)
|
|
setDroppedItems(res.data.tagJsons)
|
|
setTransaction(res.data.transaction)
|
|
setRecSignStatus(res.data.status)
|
|
|
|
setTimeout(() => {
|
|
dispatch(hideLoader());
|
|
}, 600);
|
|
|
|
}
|
|
|
|
})
|
|
.catch((err) => {
|
|
console.error(err.message)
|
|
});
|
|
}
|
|
|
|
|
|
console.log(droppedItems, "Line 261")
|
|
|
|
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
const submitData: TagsSubmitDataObj = {
|
|
tags: []
|
|
}
|
|
|
|
if (!droppedItems) return;
|
|
|
|
// const form = e.currentTarget;
|
|
// const formData = new FormData(form);
|
|
|
|
// / Convert FormData to an object
|
|
// const data: { [key: string]: FormDataEntryValue } = {};
|
|
// formData.forEach((value, key) => {
|
|
// data[key] = value;
|
|
// });
|
|
|
|
let signInitialsCount = 0;
|
|
for (let i = 0; i < droppedItems.length; i++) {
|
|
if (droppedItems[i].type === "SIGNATURE" || droppedItems[i].type === "INITIALS") {
|
|
signInitialsCount += 1;
|
|
}
|
|
}
|
|
|
|
console.log(signInitialsCount, Object.keys(signConfObj).length, "boom")
|
|
|
|
if (signInitialsCount !== Object.keys(signConfObj).length) {
|
|
toast.error("Please ensure all signature and initials are signed", {
|
|
position: "bottom-right",
|
|
autoClose: 5000,
|
|
hideProgressBar: false,
|
|
closeOnClick: false,
|
|
pauseOnHover: true,
|
|
draggable: true,
|
|
progress: undefined,
|
|
theme: "light",
|
|
transition: Bounce,
|
|
});
|
|
return;
|
|
}
|
|
|
|
for (const tag of droppedItems) {
|
|
console.log(tag, "Tag Data")
|
|
|
|
// if ((tag.type === "SIGNATURE" || tag.subType === "INITIALS") && !tag.value) {
|
|
if (("required" in tag && tag.required) && ((tag.type !== "SIGNATURE" && tag.type !== "INITIALS") && (tag.value === "" || !("value" in tag)))) {
|
|
toast.error(`${tag.label} is required`, {
|
|
position: "bottom-right",
|
|
autoClose: 5000,
|
|
hideProgressBar: false,
|
|
closeOnClick: false,
|
|
pauseOnHover: true,
|
|
draggable: true,
|
|
progress: undefined,
|
|
theme: "light",
|
|
transition: Bounce,
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (tag.type === "SIGNATURE" || tag.type === "INITIALS") {
|
|
const tagData: TagSubmitData = {
|
|
id: tag.id as string,
|
|
type: tag.type,
|
|
subType: saved.saveType as string,
|
|
signed: true,
|
|
value: ""
|
|
}
|
|
submitData.tags.push(tagData)
|
|
} else {
|
|
const tagData: TagSubmitData = {
|
|
id: tag.id as string,
|
|
type: tag.type,
|
|
subType: tag.subType ? tag.subType : "",
|
|
signed: true,
|
|
value: tag.value ? tag.value as string : ""
|
|
}
|
|
submitData.tags.push(tagData);
|
|
}
|
|
}
|
|
|
|
// for (const tag of tagJson) {
|
|
// if (data[tag.id as string]) {
|
|
// const tagData: TagSubmitData = {
|
|
// id: tag.id as string,
|
|
// type: tag.type,
|
|
// subType: tag.subType ? tag.subType : "",
|
|
// signed: true,
|
|
// value: data[tag.id as string] as string
|
|
// }
|
|
// submitData.tags.push(tagData)
|
|
// }
|
|
|
|
// if (tag.type === "SIGNATURE") {
|
|
// submitData.tags.push({
|
|
// id: tag.id as string,
|
|
// type: "SIGNATURE",
|
|
// subType: "DRAW",
|
|
// signed: true,
|
|
// value: ""
|
|
// })
|
|
// }
|
|
|
|
// if (tag.type === "INITIALS") {
|
|
// submitData.tags.push({
|
|
// id: tag.id as string,
|
|
// type: "INITIALS",
|
|
// subType: "DRAW",
|
|
// signed: true,
|
|
// value: ""
|
|
// })
|
|
// }
|
|
|
|
// }
|
|
|
|
dispatch(showLoader("Signing ..."));
|
|
axios.post(`${import.meta.env.VITE_BASE_URI}/api/sign?recipientId=${recipientId}`, submitData, { headers: { "ngrok-skip-browser-warning": "69420", } })
|
|
.then((res) => {
|
|
console.group("Submitting Sign Details");
|
|
console.dir(res);
|
|
console.groupEnd();
|
|
if (res.status === 200) {
|
|
setTransparentTags(true)
|
|
setTimeout(() => {
|
|
dispatch(hideLoader())
|
|
toast.dismiss();
|
|
toast.info("Signed Successfully!", {
|
|
position: "bottom-right",
|
|
autoClose: 5000,
|
|
hideProgressBar: false,
|
|
closeOnClick: false,
|
|
pauseOnHover: true,
|
|
draggable: true,
|
|
progress: undefined,
|
|
theme: "light",
|
|
transition: Bounce,
|
|
});
|
|
}, 800)
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.error(err.message)
|
|
}
|
|
)
|
|
};
|
|
|
|
|
|
const handleTagNavClick = (action: "N" | "P") => {
|
|
if (!tagJson || !tagJson.length) return
|
|
|
|
// If FocusElement is Null Setting up 0 th Tag
|
|
if (!focusElement) {
|
|
setFocusElement({ ...tagJson[0], focusElementIndx: 0 });
|
|
return;
|
|
}
|
|
|
|
// If FocusElement is present updating in Based on Next and Prev
|
|
const currFocusElemIndx = focusElement.focusElementIndx;
|
|
if (currFocusElemIndx === undefined) return;
|
|
const updIndx = (action === "N") ? (currFocusElemIndx + 1) : (currFocusElemIndx - 1)
|
|
|
|
if (updIndx >= tagJson.length || updIndx < 0 || !tagJson[updIndx].id) return
|
|
|
|
scrollToElement(tagJson[updIndx].id)
|
|
setFocusElement({ ...tagJson[updIndx], focusElementIndx: updIndx })
|
|
}
|
|
|
|
const scrollToElement = (elementId: string) => {
|
|
const element = document.getElementById(elementId);
|
|
if (element) {
|
|
element.scrollIntoView({
|
|
behavior: "smooth", // You can adjust this to "auto" if you don't want smooth scrolling
|
|
block: "center" // Align the element in the center of the viewport
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
// const handleSelectOptClick = (tagId: string | undefined, optionIndx: number) => {
|
|
// if (!tagId) return;
|
|
// const selectedTag = tagJson?.filter((val, indx) => val.id === tagId)
|
|
// if (selectedTag && selectedTag?.length && "options" in selectedTag[0] && selectedTag[0]["options"]) {
|
|
// selectedTag[0].label = selectedTag[0]["options"][optionIndx]
|
|
// setTagJson(tagJson)
|
|
// }
|
|
// }
|
|
|
|
const fetchSignatureData = async (signatureId: string) => {
|
|
try {
|
|
const response = await axios.get(`${import.meta.env.VITE_BASE_URI}/api/sign/signatures/${signatureId}?recipientId=${recipientId}`, {
|
|
headers: { "ngrok-skip-browser-warning": "69420" }
|
|
});
|
|
if (response.status === 200) {
|
|
console.log("line 516" , response.data)
|
|
return response.data;
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching signature data:", error);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// useEffect(() => {
|
|
// (async () => {
|
|
// if (droppedItems) {
|
|
// for (const tag of droppedItems) {
|
|
// if (tag.type === "SIGNATURE" && "value" in tag) {
|
|
// console.log(tag, "line 533")
|
|
// const signatureData = await fetchSignatureData(tag["value"]);
|
|
// console.log("Currnet Signature",signatureData);
|
|
// }
|
|
// }
|
|
// }
|
|
// })()
|
|
// }, [droppedItems])
|
|
|
|
|
|
const handleTagUpdate = (updateIndx: number, updateValue: string) => {
|
|
if (droppedItems && droppedItems.length) {
|
|
const updatedTag = droppedItems.map((val, indx) => {
|
|
if (indx === updateIndx) {
|
|
return {
|
|
...val,
|
|
value: updateValue
|
|
}
|
|
}
|
|
return val;
|
|
})
|
|
setDroppedItems(updatedTag);
|
|
}
|
|
|
|
// setTagJson((prev) => {
|
|
// const newTagJson = [...prev];
|
|
// if (newTagJson[updateIndx]) {
|
|
// newTagJson[updateIndx].label = updateValue;
|
|
// }
|
|
// return newTagJson;
|
|
// });
|
|
}
|
|
|
|
|
|
// HANDLE DROP - START
|
|
const handleDrop = (event: React.DragEvent<HTMLDivElement>, docId: string, documentId: number, pageIndex: number) => {
|
|
console.log("// HANDLE DROP - START")
|
|
event.preventDefault();
|
|
|
|
const data = window.dragElemData;
|
|
if (!data) return;
|
|
|
|
const dragElemData = JSON.parse(data); // Get the dropped label
|
|
|
|
const field = { ...dragElemData }
|
|
if (!field.label) return;
|
|
|
|
// const field = fields.find((item) => item.label === dragElemData.label); // Find corresponding field
|
|
// if (!field) return; // If not found, exit
|
|
|
|
const dropZone = event.currentTarget.getBoundingClientRect();
|
|
const x = event.clientX - dropZone.left - dragElemData.offsetX;
|
|
const y = event.clientY - dropZone.top - dragElemData.offsetY;
|
|
|
|
const key = `${docId}_${pageIndex}`;
|
|
// const IconComponent = typeToIconMap[field.type] || <FaQuestionCircle />;
|
|
|
|
let prepData = {
|
|
properties: data.properties,
|
|
icon: typeToIconMap[field.label.toUpperCase()] || <FaQuestionCircle />,
|
|
type: dragElemData.label.toUpperCase(),
|
|
subType: (dragElemData.label in droppedTagsDefaultField) ? droppedTagsDefaultField[dragElemData.label] : '',
|
|
label: dragElemData.label,
|
|
dimensions: {
|
|
left: x,
|
|
top: y,
|
|
width: dragElemData.width,
|
|
height: dragElemData.height,
|
|
},
|
|
styles: dragElemData.label === "text" ? {
|
|
"font": "Great Vibes",
|
|
"italic": false,
|
|
"bold": false,
|
|
"underline": false,
|
|
"color": "black",
|
|
"textSize": 14.0
|
|
} : null,
|
|
documentId: docId,
|
|
pageIndex: pageIndex,
|
|
recipientId: dragElemData.recipientId,
|
|
recipientIndx: dragElemData.recipientIndx,
|
|
update: true,
|
|
isDeleted: false
|
|
}
|
|
|
|
if ("group" in dragElemData) {
|
|
prepData.group = dragElemData.group;
|
|
prepData.isGroupExist = false;
|
|
|
|
// if(uniqGroupIds.includes(dragElemData.group)) {
|
|
// prepData.isGroupExist = true;
|
|
// } else {
|
|
// prepData.isGroupExist = false;
|
|
// setUniqGroupIds([...uniqGroupIds, dragElemData.group]);
|
|
// }
|
|
}
|
|
|
|
if (prepData.type === "temporal") {
|
|
prepData.currentTimeStamp = true;
|
|
}
|
|
|
|
if (prepData.type.toLowerCase() === "number") {
|
|
prepData.decimalPlace = 0;
|
|
prepData.isCurrency = false;
|
|
}
|
|
|
|
// const dataToSet: DroppedItemsObj = {
|
|
// ...droppedItems,
|
|
// [key]: [...(droppedItems[key] || []),
|
|
// prepData],
|
|
// }
|
|
|
|
// const dataToSet: DroppedItemsObj = {
|
|
// ...droppedItems,
|
|
// [key]: [...(droppedItems[key] || []),
|
|
// {
|
|
// ...prepData,
|
|
// radioGroups: dragElemData.label === "radio" ?
|
|
// [
|
|
// {
|
|
// ...prepData,
|
|
// dimensions: {
|
|
// ...prepData.dimensions, top: 8, left: 10
|
|
// }
|
|
// },
|
|
// {
|
|
// ...prepData, dimensions: {
|
|
// ...prepData.dimensions,
|
|
// top: 8,
|
|
// left: 10
|
|
// }
|
|
// }
|
|
// ]
|
|
// :
|
|
// []
|
|
// }],
|
|
// }
|
|
|
|
// if (dragElemData.label === "radio") {
|
|
// setDroppedItems(dataToSet);
|
|
// } else {
|
|
// setDroppedItems(dataToSet);
|
|
// }
|
|
|
|
setDroppedItems((prev) => [...prev, prepData]);
|
|
// setDroppedItemsCounter((prev) => prev + 1);
|
|
|
|
// setTimeout(() => {
|
|
// const tagIndex = key in droppedItems ? droppedItems[key].length : 0;
|
|
// handleTagClick(key, tagIndex)
|
|
// }, 0)
|
|
};
|
|
// HANDLE DROP - END
|
|
|
|
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
|
|
event.preventDefault(); // Allows the drop event to work
|
|
};
|
|
|
|
// HANDLE TAG MOUSE DOWN - START
|
|
const handleTagMouseDown = (event: React.MouseEvent<HTMLDivElement>, key: string, index: number) => {
|
|
event.preventDefault();
|
|
|
|
let targetContainer = event?.currentTarget?.parentElement?.parentElement;
|
|
let targetElement = event?.currentTarget;
|
|
|
|
if (!targetContainer || !targetElement) return;
|
|
|
|
const containerRect = targetContainer.getBoundingClientRect();
|
|
const elementRect = targetElement.getBoundingClientRect();
|
|
|
|
// Calculate the initial offset (where the mouse was clicked inside the element)
|
|
const offsetX = event.clientX - elementRect.left;
|
|
const offsetY = event.clientY - elementRect.top;
|
|
|
|
const conScrollWidth = targetContainer?.scrollWidth;
|
|
const conScrollHeight = targetContainer?.scrollHeight;
|
|
|
|
let isGroupAttr = event.currentTarget.hasAttribute("group");
|
|
let groupId = null;
|
|
if (isGroupAttr) {
|
|
groupId = event.currentTarget.getAttribute("group");
|
|
}
|
|
|
|
let groupElements: NodeListOf<HTMLElement> | null = null
|
|
if (isGroupAttr && groupId) {
|
|
groupElements = document.querySelectorAll(`div[group="${groupId}"]`);
|
|
}
|
|
|
|
const handleMouseMove = (event: MouseEvent) => {
|
|
// Get new X and Y positions relative to the container
|
|
let left = event.clientX - containerRect.left - offsetX;
|
|
let top = event.clientY - containerRect.top - offsetY;
|
|
|
|
|
|
// **Restrict movement within container bounds**
|
|
left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
|
|
top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height));
|
|
|
|
console.log(left, top, "LEFTTOP")
|
|
|
|
if (groupElements != null && groupElements.length) handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY);
|
|
|
|
// Apply the movement to the element
|
|
setDroppedItems(
|
|
droppedItems.map((item, i) =>
|
|
i === index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item
|
|
)
|
|
);
|
|
|
|
};
|
|
|
|
const handleMouseUp = () => {
|
|
document.removeEventListener("pointermove", handleMouseMove);
|
|
document.removeEventListener("pointerup", handleMouseUp);
|
|
};
|
|
|
|
// Add event listeners
|
|
document.addEventListener("pointermove", handleMouseMove);
|
|
document.addEventListener("pointerup", handleMouseUp);
|
|
};
|
|
// HANDLE TAG MOUSE DOWN - END
|
|
|
|
|
|
console.log(droppedItems, "DROPPED ITEMS LINE 680")
|
|
|
|
// const [showTerms, setShowTerms] = useState<boolean>(true);
|
|
return (
|
|
<div className={`w-full h-screen overflow-hidden relative ${(globalLoader.isLoading) ? 'pointer-events-none overflow-hidden' : ''}`}>
|
|
<>
|
|
{/* <TermsModal showTerms={showTerms} setShowTerms={setShowTerms} recipientId={recipientId || ""} /> */}
|
|
|
|
<form className="bg-slate-100"
|
|
onSubmit={(e: React.FormEvent<HTMLFormElement>) => handleFormSubmit(e)}
|
|
>
|
|
{/* Nav Two - Options - START */}
|
|
<div className='flex justify-between items-center px-4 py-1 bg-white'>
|
|
|
|
{/* Left Side */}
|
|
<div className='flex justify-start items-center gap-1'>
|
|
|
|
<div className='flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'>
|
|
<p className='tracking-wide font-semibold text-[1rem] text-[var(--primary)]'>Sign Made Easy</p>
|
|
</div>
|
|
|
|
|
|
{/* FIT - START */}
|
|
<div
|
|
tabIndex={1}
|
|
onBlur={() => setIsFitDd(false)}
|
|
>
|
|
<div
|
|
onClick={() => setIsFitDd(!isFitDd)}
|
|
className='flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'>
|
|
<p className='tracking-wide'>Fit</p>
|
|
<MdKeyboardArrowDown style={{ transform: `${isFitDd ? "rotate(180deg)" : ""}` }} />
|
|
</div>
|
|
|
|
{isFitDd && <div className='absolute bg-white rounded-sm z-50'>
|
|
{fitObj.map((val, indx) => {
|
|
return (<div
|
|
key={indx}
|
|
onClick={() => setSelFitIndx(indx)}
|
|
className='flex z-50 items-center justify-between gap-2 w-[70px] p-2 text-xs cursor-default hover:bg-slate-100'>
|
|
<div
|
|
|
|
className='flex items-center justify-between w-full'>
|
|
<p className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[12ch]'>{val.label}</p>
|
|
{selFitIndx === indx && <FaCheck className='text-sm text-slate-400' />}
|
|
</div>
|
|
</div>)
|
|
})
|
|
}
|
|
</div>}
|
|
</div>
|
|
{/* FIT - END */}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{/* Right Side - STARTS */}
|
|
<div className='flex justify-center items-center'>
|
|
|
|
{/* Documnt Nav */}
|
|
<>
|
|
{selectedDocuments !== null &&
|
|
<div tabIndex={1}
|
|
onBlur={() => setNav2DocToggle(false)}>
|
|
<div onClick={() => setNav2DocToggle(!nav2DocToggle)} className='flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'>
|
|
<p className='tracking-wide'>{selectedDocuments && selectedDocuments.length} Documents</p>
|
|
<MdKeyboardArrowDown className={`${nav2DocToggle ? 'rotate-180' : ''} transition-all`} />
|
|
</div>
|
|
|
|
|
|
<div
|
|
className={`absolute max-w-max shadow rounded-md mt-1 px-2 py-1 z-20 bg-white ${nav2DocToggle ? '' : 'hidden'}`}>
|
|
{selectedDocuments.length && selectedDocuments.map((document, indx) => {
|
|
return (<div key={indx} className='flex items-center justify-between gap-2 w-full p-2 text-xs rounded-sm cursor-default'>
|
|
<div className='flex items-center gap-1'>
|
|
{/* <p className='w-3 h-3 rounded-sm' style={{ background: recipient.color }}></p> */}
|
|
<IoIosDocument className='text-slate-600 text-lg' />
|
|
<p className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[15ch]'>{document.name}</p>
|
|
</div>
|
|
</div>)
|
|
})}
|
|
</div>
|
|
|
|
|
|
</div>
|
|
}
|
|
</>
|
|
|
|
{/* Send */}
|
|
{(!transparentTags && !hideTags) ? <button
|
|
// onClick={() => handleFormSubmitBtnClick()}
|
|
className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)] outline-none'>
|
|
<p
|
|
className='tracking-wide text-white'>Sign</p>
|
|
<FaSignature className='text-white text-md' />
|
|
</button>
|
|
:
|
|
<p className="text-xs">Signed Successfully</p>
|
|
}
|
|
|
|
<p>{recipient && recipient.firstName} {recipient && recipient.lastName}</p>
|
|
|
|
</div>
|
|
{/* Right Side - ENDS */}
|
|
</div>
|
|
{/* Nav Two - Options - End */}
|
|
|
|
{/* Document Container Start */}
|
|
<div className="grid grid-cols-12 gap-1 mt-1 h-[calc(100vh-48px)] overflow-hidden">
|
|
|
|
{/* Left Side pages start */}
|
|
{/* <div className="lg:col-span-2 bg-slate-100 h-full p-4 overflow-y-auto"></div> */}
|
|
|
|
<div className='col-span-2 bg-white h-full p-4 overflow-y-auto'>
|
|
{recipientId && <FieldList color={"#8f87f1"} recipientId={Number(recipientId)} recipientIndx={0} acntId={"1"} />}
|
|
</div>
|
|
|
|
|
|
{/* Pages - END */}
|
|
|
|
{/* Main container */}
|
|
<div
|
|
id="doc-con"
|
|
className="zoom-con relative col-span-8 px-4 bg-slate-100 h-full w-full overflow-auto scroll-smooth"
|
|
>
|
|
|
|
{
|
|
selectedDocuments && selectedDocuments.length > 0 && selectedDocuments.map((doc) => {
|
|
console.log(droppedItems, "tagJsontagJsontagJson")
|
|
return (
|
|
<div key={doc.documentId} className='flex flex-col'>
|
|
<div className='py-2 text-sm'>
|
|
<p className='text-center'>{doc.name}</p>
|
|
</div>
|
|
|
|
<div className='flex flex-col gap-3 max-w-max relative left-[50%] translate-x-[-50%]'>
|
|
{
|
|
doc.pages && doc.pages.length > 0 && doc.pages.map((page, indx) => {
|
|
const key = `${doc.documentId}_${page.index}`;
|
|
console.log(droppedItems, "line 820")
|
|
// const tagJsonData = [...droppedItems];
|
|
// console.log(tagJsonData, "Line 821")
|
|
|
|
const currDocTags = droppedItems.filter((val) => val.pageIndex === page.index)
|
|
console.log(currDocTags, droppedItems, page, doc, "innnnnnnnnnnnn")
|
|
return (
|
|
<div
|
|
key={indx}
|
|
id={doc.documentId + indx}
|
|
onDrop={(e) => handleDrop(e, doc.documentId, doc.documentId, page.index)}
|
|
onDragOver={(e) => handleDragOver(e)}
|
|
className='drop-page-in shadow-md relative'
|
|
>
|
|
|
|
{!hideTags && <>
|
|
|
|
|
|
{currDocTags.map((item, index) => {
|
|
console.log(item, "micCheck 842")
|
|
if (item.pageIndex !== page.index) return;
|
|
return (
|
|
<div
|
|
tabIndex={1}
|
|
onFocus={() => setFocusElement({ ...item, focusElementIndx: index })}
|
|
key={item.id}
|
|
// draggable="false"
|
|
onPointerDown={(e) => handleTagMouseDown(e, key, index)}
|
|
className="absolute rounded dropped-item text-sm"
|
|
// onClick={() => handleTagClick(key, index)}
|
|
style={{
|
|
left: `${item.dimensions.left}px`,
|
|
top: `${item.dimensions.top}px`,
|
|
width: `${item.dimensions.width}px`,
|
|
height: `${item.dimensions.height}px`,
|
|
border: `${((!hideTags && focusElement) && !transparentTags && focusElement.documentId === item.documentId && focusElement.focusElementIndx === index) ? '2px dashed #1c398e' : 'none'}`,
|
|
// display: `${hideTags ? 'none' : 'block'}`
|
|
}}
|
|
>
|
|
|
|
{/* TEXT FIELD - START */}
|
|
{item.type === "TEXT" &&
|
|
<div
|
|
id={item.id}
|
|
tabIndex={1}
|
|
className={`h-full text-[14px] font-medium w-full border-slate-100 rounded ${(transparentTags || hideTags) ? "pointer-events-nones" : "bg-slate-200 shadow-sm opacity-80"} flex justify-start items-end`}
|
|
// style={{ height: item.dimensions.height + "px" }}
|
|
>
|
|
|
|
{/* <input
|
|
name={item.id}
|
|
className="w-full px-2 outline-none"
|
|
>
|
|
</input> */}
|
|
|
|
{
|
|
item.subType === "SINGLE_LINE" && <p
|
|
// name={item.id}
|
|
// style={{ height: item.dimensions.height + "px" }}
|
|
onInput={(e) => {
|
|
const val = e.currentTarget.textContent;
|
|
handleTagUpdate(index, val as string);
|
|
}}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") e.preventDefault();
|
|
}}
|
|
contentEditable={true}
|
|
className="outline-none whitespace-nowrap w-full max-h-max"
|
|
></p>
|
|
}
|
|
{
|
|
item.subType === "PARAGRAPH" && <p
|
|
onInput={(e) => {
|
|
const val = e.currentTarget.textContent;
|
|
handleTagUpdate(index, val as string);
|
|
}}
|
|
contentEditable={true}
|
|
className="w-full outline-none max-h-max"
|
|
>
|
|
</p>
|
|
}
|
|
{
|
|
item.subType === "PASSWORD" && item.mask && <p
|
|
onInput={(e) => {
|
|
const val = e.currentTarget.textContent;
|
|
handleTagUpdate(index, val as string);
|
|
}}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") e.preventDefault();
|
|
}}
|
|
onFocus={() => {
|
|
console.log("focus")
|
|
}}
|
|
onBlur={(e) => {
|
|
let val = e.target.textContent;
|
|
console.log(item.mask, val, "Line 586")
|
|
if (typeof item.mask?.maskType === "string" && typeof val === "string") {
|
|
val = val.replace(/ /g, "");
|
|
console.log("onBlur", item.mask.maskCharacter, maskUtils((item.mask as { maskType: string }).maskType, val, (item.mask as { maskCharacter: string }).maskCharacter))
|
|
e.target.textContent = maskUtils((item.mask as { maskType: string }).maskType, val, (item.mask as { maskCharacter: string }).maskCharacter)
|
|
}
|
|
}}
|
|
contentEditable={true}
|
|
className="outline-none whitespace-nowrap w-full max-h-max"
|
|
></p>
|
|
}
|
|
</div>}
|
|
{/* TEXT FIELD - END */}
|
|
|
|
|
|
{/* CHECKBOX FIELD - START */}
|
|
{item.type === "CHECKBOX" &&
|
|
<div
|
|
id={item.id}
|
|
className={`h-full w-full rounded flex justify-center items-center ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
|
// style={{ height: item.dimensions.height + "px" }}
|
|
>
|
|
<input
|
|
name={item.id}
|
|
// style={{ height: item.dimensions.height + "px" }}
|
|
className="w-full px-2 outline-none"
|
|
type="checkbox" />
|
|
</div>}
|
|
{/* CHECKBOX FIELD - END */}
|
|
|
|
|
|
{/* SIGNATURE FIELD - START */}
|
|
{item.type === "SIGNATURE" &&
|
|
<div
|
|
tabIndex={1}
|
|
id={item.id}
|
|
onClick={() => handleSignClick(index , item.id)}
|
|
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none bg-transparent" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
|
// style={{ height: item.dimensions.height + "px" }}
|
|
>
|
|
{/* <input type="checkbox" className="" /> */}
|
|
{
|
|
(!transparentTags && !hideTags && !(saved.save && (index in signConfObj && signConfObj[index].accepted))) ?
|
|
<>
|
|
<FaSignature className="text-lg text-slate-600" />
|
|
</>
|
|
:
|
|
<img
|
|
style={{
|
|
width: `${item.dimensions.width}px`,
|
|
height: `${item.dimensions.height}px`,
|
|
}}
|
|
// src={saved.signature as string} />
|
|
src={`data:image/png;base64,${saved.signature}`}
|
|
/>
|
|
}
|
|
</div>}
|
|
{/* SIGNATURE FIELD - END */}
|
|
|
|
|
|
{/* INITIALS FIELD - START */}
|
|
{item.type === "INITIALS" &&
|
|
<div
|
|
tabIndex={1}
|
|
id={item.id}
|
|
onClick={() => handleSignClick(index , item.id)}
|
|
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
|
// style={{ height: item.dimensions.height + "px" }}
|
|
>
|
|
{((!transparentTags && !hideTags) && !(saved.save && (index in signConfObj) && signConfObj[index].accepted)) ?
|
|
<TbSignature className="text-lg text-slate-600" />
|
|
:
|
|
<img
|
|
style={{
|
|
width: `${item.dimensions.width}px`,
|
|
height: `${item.dimensions.height}px`,
|
|
}}
|
|
className="w-[100%] h-full"
|
|
// src={saved.initial as string}
|
|
src={`data:image/png;base64,${saved.initial}`}
|
|
/>
|
|
}
|
|
</div>}
|
|
{/* INITIALS FIELD - END */}
|
|
|
|
|
|
{/* STAMP FIELD - START */}
|
|
{item.type === "STAMP" &&
|
|
<div
|
|
tabIndex={1}
|
|
id={item.id}
|
|
onClick={() => handleStampClick(index)}
|
|
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
|
// style={{ height: item.dimensions.height + "px" }}
|
|
>
|
|
{!transparentTags && !hideTags ?
|
|
<FaStamp className="text-lg text-slate-600" />
|
|
:
|
|
<img className="w-[100%] h-full" src={saved.initial as string} />
|
|
}
|
|
</div>}
|
|
{/* STAMP FIELD - END */}
|
|
|
|
{/* IMAGE FIELD - START */}
|
|
{item.type === "FILE" &&
|
|
<div
|
|
tabIndex={1}
|
|
id={item.id}
|
|
// onClick={() => handleSignClick(index)}
|
|
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100 opacity-80"}`}
|
|
// style={{ height: item.dimensions.height + "px" }}
|
|
>
|
|
{!transparentTags && !hideTags ?
|
|
<FaFileAlt className="text-lg text-slate-600" />
|
|
:
|
|
<img className="w-[100%] h-full" src={saved.initial as string} />
|
|
}
|
|
</div>}
|
|
{/* IMAGE FIELD - END */}
|
|
|
|
|
|
{/* SELECT FIELD - START */}
|
|
{item.type === "SELECT" &&
|
|
<div
|
|
id={item.id}
|
|
tabIndex={1}
|
|
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100"}`}
|
|
>
|
|
<select
|
|
name={item.id}
|
|
className="outline-none"
|
|
>
|
|
<option value="">Select</option>
|
|
{item.options && item.options.map((val, indx) => {
|
|
return (
|
|
<option
|
|
key={indx}
|
|
value={val}
|
|
>
|
|
{val}
|
|
</option>
|
|
)
|
|
})
|
|
}
|
|
</select>
|
|
</div>}
|
|
{/* SELECT FIELD - END */}
|
|
|
|
|
|
|
|
|
|
{/* TEMPORAL FIELD - START */}
|
|
|
|
{item.type === "TEMPORAL" &&
|
|
<div
|
|
id={item.id}
|
|
tabIndex={1}
|
|
className={`flex justify-center items-center h-full w-full rounded cursor-pointer ${transparentTags || hideTags ? "pointer-events-none" : "bg-slate-200 shadow-sm border-slate-100"}`}
|
|
>
|
|
<p
|
|
style={{ opacity: `${item.id === isDateOpen ? "0" : "100"}` }}
|
|
className="w-full h-full rounded pl-1 pt-1 whitespace-nowrap">
|
|
{(item.id && item.id in dateValues) ? dateValues[item.id] : item.subType?.toLowerCase()}
|
|
</p>
|
|
<input
|
|
name={item.id}
|
|
onFocus={(e) => {
|
|
e.target.showPicker?.()
|
|
if (item.id) setIsDateOpen(item.id)
|
|
}}
|
|
onBlur={(e) => {
|
|
let val = e.target.value;
|
|
|
|
// Adding dummy time in case of time as it giving Invalid date without that
|
|
if (item.subType?.toLowerCase() === "time") val = "09/02/2025 " + val
|
|
|
|
if (!item.id) return;
|
|
setIsDateOpen(null);
|
|
setDateValues({
|
|
...dateValues,
|
|
[item.id as string]: dayjs(val).format(item.format)
|
|
})
|
|
}}
|
|
style={{ opacity: `${item.id === isDateOpen ? "100" : "0"}` }}
|
|
className="w-full px-2 outline-none absolute"
|
|
onClick={() => {
|
|
|
|
}}
|
|
type={`${item.subType?.toLowerCase() === "date" ? "date" : (item.subType?.toLowerCase() === "time" ? "time" : "datetime-local")}`}
|
|
/>
|
|
|
|
</div>}
|
|
|
|
{/* TEMPORAL FIELD - END */}
|
|
|
|
|
|
|
|
|
|
{/* Focus Show Element */}
|
|
{(!transparentTags && !hideTags) && (focusElement && focusElement.documentId === item.documentId && focusElement.focusElementIndx === index) &&
|
|
<>
|
|
<div
|
|
className="absolute left-0 z-50 bg-blue-900 text-xs text-slate-100 px-2 py-[2px] rounded-sm whitespace-nowrap"
|
|
style={{ top: -23 + "px" }}
|
|
>
|
|
<p>{focusElement.label}</p>
|
|
</div>
|
|
<div
|
|
className="absolute left-[-10px] z-50 flex max-w-max"
|
|
style={{ left: -(item.dimensions.left + 20) + "px", top: ((item.dimensions.height / 2) - 18) + "px" }}
|
|
>
|
|
{/* <FaArrowRight className="text-[var(--primary)] text-xl" />
|
|
*/}
|
|
<BsFileArrowDownFill className="text-blue-900 text-4xl rotate-270" />
|
|
</div>
|
|
</>
|
|
}
|
|
</div>
|
|
)
|
|
})
|
|
}
|
|
|
|
</>}
|
|
|
|
<img
|
|
style={{ "minWidth": page.width, "minHeight": page.height, "maxWidth": page.width, "maxHeight": page.height }}
|
|
draggable="false"
|
|
width={page.width}
|
|
height={page.height}
|
|
src={page.src}
|
|
/>
|
|
|
|
|
|
{/* Side details of documents Pages and document Name - START */}
|
|
<p className='absolute top-[50%] translate-y-[-50%] rotate-[-90deg] text-xs left-[-60px] text-slate-600'>
|
|
page {indx + 1} of {doc.pages && doc.pages.length}
|
|
</p>
|
|
{/* Side details of documents Pages and document Name - END */}
|
|
|
|
|
|
</div>
|
|
)
|
|
})
|
|
}
|
|
</div>
|
|
|
|
|
|
</div>
|
|
)
|
|
})
|
|
}
|
|
|
|
{/* tag pagination Start */}
|
|
{/* {!transparentTags && !hideTags && <div className="sticky rounded-md bottom-2 left-[50%] translate-[-50%] max-w-max flex bg-slate-900 text-white text-sm opacity-70">
|
|
<div className="px-3 py-1 cursor-pointer hover:bg-black rounded-l-md transition-all duration-150 "
|
|
onClick={() => handleTagNavClick("P")}
|
|
>Prev</div>
|
|
<div className="px-3 py-1 border-l-1 border-r-1">{focusElement ? focusElement.label : "NotSelected"}</div>
|
|
<div className="px-3 py-1 cursor-pointer hover:bg-black rounded-r-md transition-all duration-150 "
|
|
onClick={() => handleTagNavClick("N")}
|
|
>Next</div>
|
|
</div>} */}
|
|
|
|
</div>
|
|
|
|
|
|
<DocumentPages selectedDocuments={selectedDocuments} setDropDownTogglePage={setDropDownTogglePage} dropDownTogglePage={dropDownTogglePage} />
|
|
|
|
|
|
{/* Right Side - START */}
|
|
{/* <div className="col-span-2 bg-slate-100 h-full p-4 overflow-y-auto">
|
|
{
|
|
focusElement && <>
|
|
</>
|
|
}
|
|
</div> */}
|
|
{/* Right Side - END */}
|
|
</div>
|
|
{/* Document Container End */}
|
|
|
|
|
|
</form>
|
|
</>
|
|
|
|
|
|
<div style={{ display: `${openSign ? 'block' : 'none'}` }}>
|
|
<SignCon recipientId={recipientId as string} setOpenSign={setOpenSign} saved={saved} setSaved={setSaved} signTagId={signTagId}/>
|
|
</div>
|
|
|
|
<div style={{ display: `${stamp.open ? 'block' : 'none'}` }}>
|
|
<StampUpload stamp={stamp.blob} setStamp={setStamp} />
|
|
</div>
|
|
|
|
|
|
{/* <SignatureCon recipientId={recipientId as string} openSign={openSign} setOpenSign={setOpenSign} savedSignature={savedSignature} setSavedSignature={setSavedSignature} />
|
|
<InitialCon openInitial={openInitial} setOpenInitial={setOpenInitial} savedInitial={savedInitial} setSavedInitial={setSavedInitial} /> */}
|
|
|
|
{globalLoader.isLoading && <Loader />}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default SignDocument
|