import { useEffect, useLayoutEffect, useState } from "react"; import axios from "axios" import { RootState } from "../../store/store"; import { useNavigate, useParams } from "react-router-dom" import { DroppedItem, DroppedItemsObj, TagsTypes } 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, IoMdClose } 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"; import SignCompletionScreen from "./SignCompletionScreen"; import api from "../../config/api"; import RightSideBar from "../process/processStepTwo/rightSideBar/RightSideBar"; import { RiDragMoveFill } from "react-icons/ri"; import { handleGroupBorderPosition } from "../../utlity/tagGroupConUtils"; 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; } export 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 { signatureId: string | null 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(null); const [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null); const [recipient, setRecipient] = useState(null); const [tagJson, setTagJson] = useState([]); const [transaction, setTransaction] = useState(null); const [recSignStatus, setRecSignStatus] = useState(null); const [droppedItems, setDroppedItems] = useState([]); const [signTagId, setSignTagId] = useState(0) const [stamp, setStamp] = useState({ open: false, blob: "" }); // Handling Date Opacity const [dateValues, setDateValues] = useState<{ [key: string]: string }>({}); const [isDateOpen, setIsDateOpen] = useState(null); // Signer data - END // Show is fields - Showing all fields There const [isFields, setIsFields] = useState(true); const [isFitDd, setIsFitDd] = useState(false) const [selFitIndx, setSelFitIndx] = useState(0); const globalLoader = useSelector((state: RootState) => state.loader) const [openSign, setOpenSign] = useState(false); const [focusElement, setFocusElement] = useState(null); const [dropDownTogglePage, setDropDownTogglePage] = useState(""); const [nav2DocToggle, setNav2DocToggle] = useState(false); const [hideTags, setHideTags] = useState(false); const [transparentTags, setTransparentTags] = useState(false); const [provideTags, setProvideTags] = useState(false) const [showCompletionScreen, setShowCompletionScreen] = useState(false) const [saved, setSaved] = useState({ signatureId: null, saveType: null, save: false, signature: null, initial: null, }); const navigate = useNavigate(); const [signConfObj, setSignConfObj] = useState({}); const zoom = (percentage: number, contentClass: string) => { const content = document.querySelector(`.${contentClass}`); if (content) { (content as HTMLElement).style.zoom = `${percentage}%`; } } const handleTagClick = (key: string, index: number) => { setSelectedTag({ key, index }) } 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]) useEffect(() => { if (recSignStatus === "COMPLETED") { // setHideTags(true); setTransparentTags(true); } else { 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) => { 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) => { setStamp({ open: true, blob: "" }) } const handleSignAcknowledgeClick = (index: number) => { if (saved.save && signConfObj === null) { setSignConfObj([...signConfObj, index]) } } // Step 2: Prevent browser back when status === 'COMPLETED' // useEffect(() => { // if (recSignStatus !== "COMPLETED") return; // const currentPath = window.location.pathname; // const handlePopState = (event) => { // // Show warning // alert("Back navigation is blocked on this screen."); // // Push the current URL back to prevent navigating away // window.history.pushState(null, "", currentPath); // }; // // Push extra states to build history buffer // for (let i = 0; i < 50; i++) { // window.history.pushState(null, "", currentPath); // } // // Add popstate listener // window.addEventListener("popstate", handlePopState); // // Cleanup on unmount // return () => { // window.removeEventListener("popstate", handlePopState); // }; // }, [recSignStatus]); 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) => { if (res.status === 200) { setSelectedDocuments(res?.data?.documents) setRecipient(res?.data?.details) setDroppedItems(res?.data?.tagJsons) setTransaction(res?.data?.transaction) setRecSignStatus(res?.data?.status) if ("provideOwnTags" in res?.data?.details && res?.data?.details.provideOwnTags) { setProvideTags(true) } if (res?.data?.status === "COMPLETED") { setShowCompletionScreen(true) } setTimeout(() => { dispatch(hideLoader()); }, 600); } }) .catch((err) => { console.log(err) }); } const handleToastError = (msg: string) => { toast.dismiss(); toast.error(msg, { position: "top-center", autoClose: 5000, hideProgressBar: false, closeOnClick: false, pauseOnHover: true, draggable: true, progress: undefined, theme: "light", transition: Bounce, }); } const handleFormSubmit = () => { // 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; } } if (signInitialsCount !== Object.keys(signConfObj).length) { handleToastError("Please ensure all signature and initials are signed") return; } for (const tag of droppedItems) { if ("required" in tag && tag.required && ((tag.type !== "SIGNATURE" && tag.type !== "INITIALS") && (tag.value === undefined || tag.value === null || tag.value === "" || !("value" in tag)))) { handleToastError(`${tag.label} is required`) 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); } } // return; dispatch(showLoader("Signing ...")); axios.post(`${import.meta.env.VITE_BASE_URI}/api/sign?recipientId=${recipientId}`, submitData, { headers: { "ngrok-skip-browser-warning": "69420", } }) .then((res) => { 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, }); // Completion Sign Screen Modal and download file setShowCompletionScreen(true) }, 800) } }) .catch((err) => { } ) }; 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) { return response.data; } } catch (error) { } return null; } // useEffect(() => { // (async () => { // if (droppedItems) { // for (const tag of droppedItems) { // if (tag.type === "SIGNATURE" && "value" in tag) { // const signatureData = await fetchSignatureData(tag["value"]); // } // } // } // })() // }, [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; // }); } const handlePrefillValues = () => { const prefilledTags = ["NAME", "EMAIL", "TEXT", "COMPANY", "TEMPORAL"] const updatedItems = droppedItems.map((item, index) => { if (prefilledTags.includes(item.type)) { let value = null; let isUpdate = false switch (item.type) { case "NAME": value = recipient?.firstName + " " + recipient?.lastName; isUpdate = true; break; case "EMAIL": value = recipient?.email; isUpdate = true; break; case "TEXT": value = item.value; isUpdate = true; break; case "COMPANY": value = item.value; isUpdate = true; break; case "TEMPORAL": if (item.currentTimeStamp) isUpdate = true; if (isUpdate) { value = dayjs(Date.now()).format(item.format); } break; default: break; } return { ...item, value } } else { return item } }) setDroppedItems(updatedItems); } useEffect(() => { if (droppedItems.length > 0) { handlePrefillValues(); } }, [droppedItems.length]) // HANDLE DROP - START const handleDrop = (event: React.DragEvent, docId: string, documentId: number, pageIndex: number) => { event.preventDefault(); const data = window.dragElemData; window.dragElemData = null; 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] || ; // let prepData = { // properties: data.properties, // icon: typeToIconMap[field.label.toUpperCase()] || , // 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 // } let prepData = { ...data.properties, icon: typeToIconMap[field.label.toUpperCase()] || , type: dragElemData.label.toUpperCase(), label: dragElemData.label, // subType: (dragElemData.label in droppedTagsDefaultField) ? droppedTagsDefaultField[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, required: (dragElemData.label !== TagsTypes.SIGNATURE && dragElemData.label !== TagsTypes.INITIALS) ? false : true, update: true, isDeleted: false } if (prepData.type === "NAME") { prepData.firstName = true prepData.lastName = true } 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.toLowerCase() === "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); // } if (prepData.type.toLowerCase() === TagsTypes.SIGNATURE || prepData.type.toLowerCase() === TagsTypes.INITIALS) { const currDocTags = droppedItems.filter((val) => val.pageIndex === prepData.pageIndex) setSignConfObj({ ...signConfObj, [currDocTags.length]: { accepted: true, index: currDocTags.length } }) } 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) => { event.preventDefault(); // Allows the drop event to work }; // HANDLE TAG MOUSE DOWN - START const handleTagMouseDown = (event: React.MouseEvent, key: string, index: number) => { setSelectedTag({ key, index }) 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 | 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)); 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 // Updated handleRemoveTag function with your existing API logic and group handling const handleRemoveTag = (key: string, targetIndex: number) => { const tag: DroppedItem = droppedItems[targetIndex]; const recipientId = tag.recipientId; (async () => { if ("id" in tag) { try { const response = await api.delete(`/api/sign/tags?recipientId=${recipientId}`, { data: [{ tagId: tag.id }] }) if (response.status !== 200) throw "Tag Deletion error" } catch (err) { return; } } setSelectedTag(null); // Check if this item has a group for special group handling if ('group' in tag && tag.group) { const groupId = tag.group; // Find all items in the same group const groupItems = droppedItems[key] .map((item, idx) => ({ item, index: idx })) .filter(({ item }) => 'group' in item && item.group === groupId && !item.isDeleted); // Sort group items by their top position to identify the parent (top-most) groupItems.sort((a, b) => a.item.dimensions.top - b.item.dimensions.top); // Check if the item being removed is the parent (top-most item) const isParentItem = groupItems.length > 0 && groupItems[0].index === targetIndex; if (isParentItem && groupItems.length > 1) { // If removing the parent and there are child items, mark all items in the group as deleted // Get indices of all group items to mark as deleted const indicesToDelete = groupItems.map(({ index }) => index); setDroppedItems(prevState => ({ ...prevState, [key]: prevState[key].map((item, index) => indicesToDelete.includes(index) ? { ...item, isDeleted: true } : item ) })); // Remove group border elements after state update // setTimeout(() => { // removeGroupBorders(groupId); // }, 100); } else { // If removing a child item or the only item in group, just mark this item as deleted setDroppedItems(prevState => ({ ...prevState, [key]: prevState[key].map((item, index) => index === targetIndex ? { ...item, isDeleted: true } : item ) })); // Check if this was the last item in the group after state update // setTimeout(() => { // const remainingGroupItems = droppedItems[key].filter(item => // 'group' in item && item.group === groupId && !item.isDeleted // ); // if (remainingGroupItems.length === 0) { // // Remove group borders if no items left // removeGroupBorders(groupId); // } else { // // Update group borders for remaining items // handleIGroupBorderPosition(groupId); // } // }, 100); } } else { // Handle non-group item removal (your existing logic) setDroppedItems(prevState => prevState.filter((item, idx) => { return idx !== targetIndex; })); } })() }; // const [showTerms, setShowTerms] = useState(true); return (
<> {/* */} {showCompletionScreen && }
) => handleFormSubmit(e)} > {/* Nav Two - Options - START */}
{/* Left Side */}

Sign Made Easy

{/* FIT - START */}
setIsFitDd(false)} >
setIsFitDd(!isFitDd)} className='flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'>

Fit

{isFitDd &&
{fitObj.map((val, indx) => { return (
setSelFitIndx(indx)} className='flex z-50 items-center justify-between gap-2 w-[70px] p-2 text-xs cursor-default hover:bg-slate-100'>

{val.label}

{selFitIndx === indx && }
) }) }
}
{/* FIT - END */}

Hi! {recipient && recipient.firstName} {recipient && recipient.lastName}

{/* Right Side - STARTS */}
{/* Documnt Nav */} <> {selectedDocuments !== null &&
setNav2DocToggle(false)}>
setNav2DocToggle(!nav2DocToggle)} className='flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'>

{selectedDocuments && selectedDocuments.length} Documents

{selectedDocuments.length && selectedDocuments.map((document, indx) => { return (
{/*

*/}

{document.name}

) })}
} {/* Send */} {(!transparentTags && !hideTags) ? :

Signed Successfully

}
{/* Right Side - ENDS */}
{/* Nav Two - Options - End */} {/* Document Container Start */}
{/* Left Side pages start */} {/*
*/}
{recipientId && provideTags && }
{/* Pages - END */} {/* Main container */}
{ selectedDocuments && selectedDocuments.length > 0 && selectedDocuments.map((doc) => { return (

{doc.name}

{ doc.pages && doc.pages.length > 0 && doc.pages.map((page, indx) => { const key = `${doc.documentId}_${page.index}`; // const tagJsonData = [...droppedItems]; // debugger const currDocTags = droppedItems.filter((val) => val.pageIndex === page.index) return (
handleDrop(e, doc.documentId, doc.documentId, page.index)} onDragOver={(e) => handleDragOver(e)} className='drop-page-in shadow-md relative' > {!hideTags && <> {currDocTags.map((item, index) => { if (item.pageIndex !== page.index || item.documentId != doc.documentId) return <>; return (
{ setSelectedTag({ key, index }) setFocusElement({ ...item, focusElementIndx: index }) } } key={index} // 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'}` }} > {provideTags && !transparentTags && !hideTags &&
handleTagMouseDown(e, key, index)} // className="absolute top-[-15px] left-[-15px] w-4 h-4 bg-blue-600 rounded-tr cursor-move z-30" title="Drag" >
} <> {selectedTag?.key && selectedTag.key === key && selectedTag.index === index &&

{/*

{IconComponent}

*/}

{item.label}

{ e.stopPropagation(); handleRemoveTag(key, index) }} className='cursor-pointer' />
} {/* TEXT FIELD - START */} { item.type === "TEXT" &&
{/* */} { !item.multiLine && !item.maskType &&

{ 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" >{item.value ?? ""}

} { item.multiLine && !item.maskType &&

{ const val = e.currentTarget.textContent; handleTagUpdate(index, val as string); }} contentEditable={true} className="w-full outline-none max-h-max" >{item.value ?? ""}

} { "maskType" in item && "maskCharacter" in item && item.maskType &&

{ // const val = e.currentTarget.textContent; // handleTagUpdate(index, val as string); // }} onKeyDown={(e) => { if (e.key === "Enter") e.preventDefault(); }} // onFocus={() => { // }} onBlur={(e) => { let val = e.target.textContent; handleTagUpdate(index, val as string); if (typeof item.maskType === "string" && typeof val === "string" && item.maskCharacter) { val = val.replace(/ /g, ""); e.target.textContent = maskUtils(item.maskType, val, item.maskCharacter) } }} contentEditable={true} className="outline-none whitespace-nowrap w-full max-h-max" >

}
} {/* TEXT FIELD - END */} {/* EMAIL FIELD - START */} { item.type === "EMAIL" &&
{

{ const val = e.currentTarget.textContent; handleTagUpdate(index, val as string); }} onKeyDown={(e) => { if (e.key === "Enter") { const val = e.currentTarget.textContent?.trim() || ""; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (val && !emailRegex.test(val)) { alert("Please enter a valid email address"); handleTagUpdate(index, "" as string) e.currentTarget.textContent = ""; e.currentTarget.focus(); // Optional: return focus if invalid } e.preventDefault() }; }} onBlur={(e) => { const val = e.currentTarget.textContent?.trim() || ""; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (val && !emailRegex.test(val)) { alert("Please enter a valid email address"); handleTagUpdate(index, "" as string) e.currentTarget.textContent = ""; e.currentTarget.focus(); // Optional: return focus if invalid } }} // contentEditable={true} className="outline-none whitespace-nowrap w-full max-h-max" > {item.value ?? ""}

}
} {/* EMAIL FIELD - END */} {/* NAME FIELD - START */} { item.type === "NAME" &&
{

{ const val = e.currentTarget.textContent; handleTagUpdate(index, val as string); }} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault() }; }} onBlur={(e) => { }} // contentEditable={true} className="outline-none whitespace-nowrap w-full max-h-max" > {item.value ?? ""}

}
} {/* NAME FIELD - END */} {/* NUMBER FIELD - START */} { item.type === "NUMBER" &&
{ { const val = e.currentTarget.value; handleTagUpdate(index, val as string); }} type="number" className="outline-none h-full max-w-max" /> }
} {/* NUMBER FIELD - END */} {/* COMPANY FIELD - START */} { item.type === "COMPANY" &&
{

{ const val = e.currentTarget.textContent; handleTagUpdate(index, val as string); }} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault() }; }} onBlur={(e) => { }} contentEditable={true} className="outline-none whitespace-nowrap w-full max-h-max" >

}
} {/* COMPANY FIELD - END */} {/* CHECKBOX FIELD - START */} { item.type === "CHECKBOX" &&
} {/* CHECKBOX FIELD - END */} {/* SIGNATURE FIELD - START */} { item.type === "SIGNATURE" &&
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" }} > {/* */} { (!transparentTags && !hideTags && !(saved.save && (index in signConfObj && signConfObj[index].accepted))) ? <> : src={`data:image/png;base64,${saved.signature}`} /> }
} {/* SIGNATURE FIELD - END */} {/* INITIALS FIELD - START */} { item.type === "INITIALS" &&
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)) ? : }
} {/* INITIALS FIELD - END */} {/* STAMP FIELD - START */} { item.type === "STAMP" &&
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 ? : }
} {/* STAMP FIELD - END */} {/* IMAGE FIELD - START */} { item.type === "FILE" &&
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 ? : }
} {/* IMAGE FIELD - END */} {/* SELECT FIELD - START */} { item.type === "SELECT" &&
} {/* SELECT FIELD - END */} {/* TEMPORAL FIELD - START */} { item.type === "TEMPORAL" &&

{(item.id && item.id in dateValues) ? dateValues[item.id] : item.value ? item.value : item.dateFormat?.toLowerCase()}

{ 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.dateFormat?.toLowerCase() === "time") val = "09/02/2025 " + val if (!item.id) return; setIsDateOpen(null); setDateValues({ ...dateValues, [item.id as string]: dayjs(val).format(item.format) }) }} onInput={(e) => { const val = e.currentTarget.value; handleTagUpdate(index, val as string); }} style={{ opacity: `${item.id === isDateOpen ? "100" : "0"}` }} className="w-full px-2 outline-none absolute" onClick={() => { }} type={`${item.dateFormat?.toLowerCase() === "date" ? "date" : (item.dateFormat?.toLowerCase() === "time" ? "time" : "datetime-local")}`} />
} {/* TEMPORAL FIELD - END */} {/* Attachment Field - START */} { item.type.toLowerCase() === TagsTypes.ATTACHMENT &&
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 ? (

{typeToIconMap.ATTACHMENT}

) : (

{typeToIconMap.ATTACHMENT}

) }
} {/* Attachment Field - END */} {/* Focus Show Element */} { (!transparentTags && !hideTags) && (focusElement && focusElement.documentId === item.documentId && focusElement.focusElementIndx === index) && <> {/*

{focusElement.label}

*/}
{/* */}
}
) }) } } {/* 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}

{/* Side details of documents Pages and document Name - END */}
) }) }
) }) } {/* tag pagination Start */} {/* {!transparentTags && !hideTags &&
handleTagNavClick("P")} >Prev
{focusElement ? focusElement.label : "NotSelected"}
handleTagNavClick("N")} >Next
} */}
{selectedDocuments !== null && } {/* {provideTags && selectedTag && selectedTag !== null && "key" in selectedTag && "index" in selectedTag && } */}
{/* Right Side - START */} {/*
{ focusElement && <> }
*/} {/* Right Side - END */}
{/* Document Container End */}
{recipient && }
{/* */} {globalLoader.isLoading && }
) } export default SignDocument