SME-Web/src/components/client/SignDocument.tsx

1579 lines
73 KiB
TypeScript

import { useEffect, useRef, useState } from "react";
import axios from "axios"
import { RootState } from "../../store/store";
import { useNavigate, useParams } from "react-router-dom"
import { DroppedItem, TagsTypes } from "../../utlity/types";
import Loader from "../dashboard/Loader";
import ClientSignCon from "../signature/ClientSignCon";
import { Bounce, toast } from "react-toastify";
import { useDispatch, useSelector } from "react-redux";
import { hideLoader, showLoader } from "../../store/slices/loaderSlice";
import { FaCheck, FaQuestionCircle, FaDownload, FaAngleUp, FaPrint } from 'react-icons/fa'
import { IoIosDocument, IoMdClose } from 'react-icons/io'
import { MdKeyboardArrowDown, MdNavigateNext } from 'react-icons/md'
import confetti from 'canvas-confetti'
import dayjs from "dayjs";
import StampUpload from "../stamp/StampUpload";
import DocumentPages from "./DocumentPages";
import FieldList from "../process/processStepTwo/FieldList";
import typeToIconMap from "../../helper/iconMap";
import TagProperties from "../process/processStepTwo/tagProperties/TagProperties";
import TagsCon from "./TagsCon";
import ConsentScreen from "./ConsentScreen";
import SignButton from "./SignButton";
import DeclineModal from "./DeclineModal";
import ReassignModal from "./ReassignModal";
import ClientAuditModal from "./ClientAuditModal";
import axiosInstance from "../../config/axiosInstance";
import "./style.css";
import ConfirmSignModal from "./ConfirmSignModal";
import { AiOutlineAudit, AiOutlineDownload } from "react-icons/ai";
import { SlPrinter } from "react-icons/sl";
import { CgPrinter } from "react-icons/cg";
import { ImPrinter } from "react-icons/im";
import { RiFileHistoryFill } from "react-icons/ri";
import { IoDocumentsOutline } from "react-icons/io5";
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;
provideOwnTags: boolean;
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",
SELECT = "SELECT",
}
export interface SaveType {
signatureId: string | null
saveType: SaveTypeEnum | null;
save: boolean;
signature: string | null;
initial: string | null;
isDefaultAndClicked?: boolean;
font?: string;
}
interface TagsSubmitDataObj {
tags: TagSubmitData[];
recipient?: TagSubmitRecData
}
interface SignConfType {
accepted: boolean;
index: number;
}
export 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: "130%",
value: "130"
},
{
label: "150%",
value: "150"
},
{
label: "200%",
value: "200"
},
{
label: "250%",
value: "250"
},
{
label: "300%",
value: "300"
},
{
label: "350%",
value: "350"
},
{
label: "400%",
value: "400"
},
]
enum RightSideNav {
ATTRIBUTES = "attributes",
PAGES = "pages"
}
const SignDocument = () => {
const windowWidth = window.innerWidth;
const { recipientId } = useParams();
const dispatch = useDispatch();
const imageRefs = useRef<{ [key: string]: HTMLImageElement | null }>({});
// Signer data - START
const [selectedDocuments, setSelectedDocuments] = useState<DocumentType[] | null>(null);
const [imageSizes, setImageSizes] = useState<Record<string, { width: number; height: number }>>({});
const [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null);
const [isPages, setIsPages] = useState<boolean>(true);
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<DroppedItem[]>([]);
const [signTagId, setSignTagId] = useState<number>(0)
const [showConfetti, setShowConfetti] = useState<boolean>(false)
const [pageScales, setPageScales] = useState<Record<string, { x: number; y: number }>>({});
const [requiredFilled, setRequiredFilled] = useState<boolean>(false);
const [horizontalFormLoader, setHorizontalFormLoader] = useState<{ requiredFilled: number, totalRequired: number | null }>({ requiredFilled: 0, totalRequired: null });
const [stamp, setStamp] = useState<StampTypeObj>({ open: false, blob: "" });
const [signerConsent, setSignerConsent] = useState<{ consent: boolean; decline: boolean }>({
consent: false,
decline: false
});
const [declineOpen, setDeclineOpen] = useState<boolean>(false);
const [reassignOpen, setReassignOpen] = useState<boolean>(false);
const [auditHistoryOpen, setAuditHistoryOpen] = useState<boolean>(false);
const [rightSideNavToggle, setRightSideNavToggle] = useState<RightSideNav>(RightSideNav.PAGES);
// Show is fields - Showing all fields There
const [isFields, setIsFields] = useState<boolean>(true);
const [isFitDd, setIsFitDd] = useState<boolean>(false)
const [selFitIndx, setSelFitIndx] = useState<number>(windowWidth > 638 ? 3 : 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 [provideTags, setProvideTags] = useState(false)
const [showCompletionScreen, setShowCompletionScreen] = useState(false)
const [zoomScale, setZoomScale] = useState<number>(windowWidth > 638 ? 150 : 100)
const scaleFactor: number = (zoomScale ?? 100) / 100;
// Handle Confirm Sign Modal - Start
// Add state
const [confirmSignOpen, setConfirmSignOpen] = useState(false);
const [confirmSignLoading, setConfirmSignLoading] = useState(false);
// Wrap handleFormSubmit
const handleConfirmSign = async () => {
setConfirmSignLoading(true);
await handleFormSubmit();
setConfirmSignLoading(false);
setConfirmSignOpen(false);
};
// Handle Confirm Sign Modal - END
// To center the container on every zoom scale change - START
useEffect(() => {
const container = document.getElementById("doc-con");
if (!container) return;
const prevScrollRatio = container.scrollTop / (container.scrollHeight || 1);
const newScrollTop = prevScrollRatio * container.scrollHeight;
const newScrollLeft = (container.scrollWidth - container.clientWidth) / 2;
container.scrollTo({
top: newScrollTop,
left: newScrollLeft,
behavior: "instant" as ScrollBehavior
});
}, [zoomScale]);
// To center the container on every zoom scale change - END
// Handle Tags and Image Size - START
useEffect(() => {
const observers: ResizeObserver[] = [];
Object.keys(imageRefs.current).forEach((key) => {
const el = imageRefs.current[key];
if (!el) return;
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
setImageSizes((prev) => ({
...prev,
[key]: { width, height },
}));
}
});
observer.observe(el);
observers.push(observer);
});
return () => observers.forEach((o) => o.disconnect());
}, [selectedDocuments]); // re-run when documents load
// Handle Tags anf Image Size = END
const [saved, setSaved] = useState<SaveType>({
signatureId: null,
saveType: null,
save: false,
signature: null,
initial: null,
});
enum NavigationTagPosEnum {
ABOVE = "above",
BELOW = "below",
VISIBLE = "VISIBLE"
}
const [navigationTagPos, setNavigationTagPos] = useState<NavigationTagPosEnum>(NavigationTagPosEnum.VISIBLE);
const navigate = useNavigate();
const [signConfObj, setSignConfObj] = useState<SignConfTypeObj | {}>({});
const handleFitSelect = (indx: number) => {
setSelFitIndx(indx);
let value = fitObj[indx].value;
if (value === "FIT") value = "100";
setZoomScale?.(Number(value));
}
// 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])
const handleTagClick = (key: string, index: number) => {
setSelectedTag({ key, index })
}
useEffect(() => {
dispatch(showLoader("Loading ..."));
fetchData();
}, [])
// Ping Request - START
useEffect(() => {
const intervalId = setInterval(() => {
axiosInstance.get(
`/api/v1/session/ping?sessionId=${recipientId}`,
{
maxRedirects: 5,
headers: { "ngrok-skip-browser-warning": "69420" },
}
).catch(err => console.error("Ping failed:", err));
}, 60000); // first call happens after 1 min
return () => clearInterval(intervalId); // cleanup on unmount
}, [recipientId]);
// Ping Request - END
useEffect(() => {
if (!focusElement) {
if (droppedItems && droppedItems.length) {
setFocusElement({ ...droppedItems[0], focusElementIndx: 0 });
}
if (focusElement == null || !("focusElementIndx" in focusElement)) return;
// const docCon = document.getElementById("doc-con");
// document.addEventListener("keydown", (e) => {
// if (e.key == "Tab") {
// e.preventDefault();
// console.log("Listening Tab Key Press")
// let focusIndx = focusElement?.focusElementIndx;
// if (focusIndx >= droppedItems.length - 1) focusIndx = -1
// setFocusElement({ ...droppedItems[++focusIndx], focusElementIndx: ++focusIndx })
// }
// })
}
}, [droppedItems])
useEffect(() => {
if (showConfetti) {
const duration = 3000;
const end = Date.now() + duration;
const interval = setInterval(() => {
if (Date.now() > end) {
clearInterval(interval);
return;
}
confetti({
particleCount: 5, // fewer particles per burst for smoothness
angle: 90, // falling straight down
spread: 30, // slight spread
startVelocity: 8, // gentle falling speed
origin: {
x: Math.random(), // random horizontal position across the top
y: 0,
},
scalar: 1,
zIndex: 9999,
});
}, 50); // fire every 50ms for continuous effect
return () => clearInterval(interval);
}
}, [showConfetti]);
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 = keys[0]
const newObj = {
accepted: true,
index: index
}
setSignConfObj((prev) => {
return (
{ ...prev, [index]: { ...newObj } }
)
});
}
}, [saved])
const fetchData = (isSign: boolean = false) => {
axiosInstance.get(`/api/v1/sign/${recipientId}`,
{
maxRedirects: 5,
headers:
{ "ngrok-skip-browser-warning": "69420", }
})
.then((res) => {
if (res.status == 200) {
setSelectedDocuments(res?.data?.documents)
setRecipient(res?.data?.details)
const sortedDroppedItems = res?.data?.tags.sort((a, b) => a.navigationOrder - b.navigationOrder);
setDroppedItems(sortedDroppedItems)
setFocusElement({ ...sortedDroppedItems[0], focusElementIndx: 0 });
setTransaction(res?.data?.transaction)
setRecSignStatus(res?.data?.status)
if (res?.data?.details.provideOwnTags) setRightSideNavToggle(RightSideNav.ATTRIBUTES)
// Removed this functionality - REMOVED
// if ("provideOwnTags" in res?.data?.details && res?.data?.details.provideOwnTags) {
// setProvideTags(true)
// }
// Removed this functionality - REMOVED
if (windowWidth > 638) {
setIsFields(false)
}
if (res?.data?.status == "COMPLETED" || res?.data?.status == "DECLINED" || res?.data?.details.type == "CC") {
// setShowCompletionScreen(true)
setTransparentTags(true)
setHideTags(true)
}
if (res?.data?.tags && res?.data?.tags.length > 0) {
for (const tag of res?.data?.tags) {
if (tag.type == "SIGNATURE" && "value" in tag && tag.value) {
axiosInstance.get(`/api/v1/sign/${recipientId}/signatures/${tag.value}`).then((res) => {
console.log(res, "line 386")
}).catch((err) => {
console.log(err)
})
// const [saved, setSaved] = useState<SaveType>({
// signatureId: null,
// saveType: null,
// save: false,
// signature: null,
// initial: null,
// });
} else if (tag.type == "INITIALS") {
}
}
}
dispatch(hideLoader());
}
})
.catch((err) => {
console.log(err)
});
}
const handleFormSubmit = async () => {
let newSignatureIdForCreateDefault = null;
if (saved.isDefaultAndClicked) {
try {
const res = await axiosInstance.post(`/api/v1/sign/${recipientId}/signatures`, {
signatureId: saved.signatureId
});
if (res.status == 201 || res.status == 202) {
newSignatureIdForCreateDefault = res.data.id;
}
} catch (err) {
console.log(err);
}
}
const submitData: TagsSubmitDataObj = {
tags: []
}
const createTagsAndSubmit: TagsSubmitDataObj = {
tags: []
}
if (!droppedItems) return;
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 (let 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 ("id" in tag && (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: saved.signatureId as string
}
submitData.tags.push(tagData)
} else if ("id" in tag) {
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);
} else if (tag.type == "SIGNATURE" || tag.type == "INITIALS") {
// Not getting recipient id in details it is undefined - not working self sign
tag.recipientId = recipient.id;
tag.documentId = tag.documentId
createTagsAndSubmit.tags.push(
{
...tag,
type: tag.type,
// subType: saved.saveType as string,
signed: true,
value: newSignatureIdForCreateDefault as string
})
} else {
tag.recipientId = recipient.id
tag.documentId = tag.documentId
createTagsAndSubmit.tags.push(
{
...tag,
type: tag.type,
// subType: tag.subType ? tag.subType : "",
signed: true,
value: tag.value ? tag.value as string : ""
})
}
}
// return;
try {
dispatch(showLoader("Signing ..."));
if (submitData.tags.length) {
await axiosInstance.put(`/api/v1/sign/${recipientId}/tags`, submitData, { headers: { "ngrok-skip-browser-warning": "69420", } })
} else if (createTagsAndSubmit.tags.length) {
await axiosInstance.post(`/api/v1/sign/${recipientId}/tags`, createTagsAndSubmit, { headers: { "ngrok-skip-browser-warning": "69420", } })
}
const signRes = await axiosInstance.post(`/api/v1/sign/${recipientId}`, {}, { headers: { "ngrok-skip-browser-warning": "69420", } })
if (signRes.status == 200) {
setTransparentTags(true)
// setShowConfetti(true)
// setShowCompletionScreen(true)
setSelectedTag(null)
// navigate("/transaction-complete")
dispatch(hideLoader());
window.location.href = `/transaction-complete/${recipientId}`
}
dispatch(hideLoader());
} catch (e) {
dispatch(hideLoader());
}
};
const handleTagNavClick = (action: "N" | "P") => {
// debugger
if (!droppedItems || !droppedItems.length) return
// If FocusElement is Null Setting up 0 th Tag
if (!focusElement) {
setFocusElement({ ...droppedItems[0], focusElementIndx: 0 });
return;
}
if (requiredFilled && focusElement.required == true) {
setConfirmSignOpen(true);
return;
}
// If FocusElement is present updating in Based on Next and Prev
const currFocusElemIndx = focusElement.focusElementIndx;
if (currFocusElemIndx == undefined) return;
let updIndx = (action == "N") ? (currFocusElemIndx + 1) : (currFocusElemIndx - 1)
if (updIndx >= droppedItems.length || updIndx < 0 || !droppedItems[updIndx].id) updIndx = 0;
scrollToElement(droppedItems[updIndx].navigationOrder)
setFocusElement({ ...droppedItems[updIndx], focusElementIndx: updIndx })
}
const scrollToElement = (elementId: string) => {
const element = document.getElementById(elementId);
if (element) {
element.scrollIntoView({
behavior: "smooth",
block: "center"
});
}
}
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])
useEffect(() => {
const allRequiredFields = droppedItems.filter((item, idx) => {
if (!item.required) return false
if (item.type == "SIGNATURE" || item.type == "INITIALS") {
if (
(item.id != null && item.id != undefined && item.id != "")
? (
(`${item.id}`) in signConfObj && signConfObj[`${item.id}`].accepted
) : (
(`${idx}`) in signConfObj && signConfObj[`${idx}`].accepted
)
) {
return false
} else {
return true
}
}
if (!("value" in item)) {
return true
} else if (item.value === null || item.value === undefined || item.value === "") {
return true
}
return false;
})
const totalRequiredFields = droppedItems.reduce((acc, obj) => {
if (obj.required == true) return acc + 1;
return acc;
}, 0)
setHorizontalFormLoader({ requiredFilled: (totalRequiredFields - allRequiredFields.length), totalRequired: totalRequiredFields })
setRequiredFilled(allRequiredFields.length || !droppedItems.length ? false : true)
}, [droppedItems, signConfObj])
// HANDLE DROP - START
const handleDrop = (event: React.DragEvent<HTMLDivElement>, 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 dropZone = event.currentTarget.getBoundingClientRect();
const x = event.clientX - dropZone.left - dragElemData.offsetX;
const y = event.clientY - dropZone.top - dragElemData.offsetY;
const key = `${docId}_${pageIndex}`;
let prepData = {
...data.properties,
icon: typeToIconMap[field.label.toUpperCase()] || <FaQuestionCircle />,
type: dragElemData.label.toUpperCase(),
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,
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 (prepData.type.toLowerCase() == "temporal") {
prepData.currentTimeStamp = true;
}
if (prepData.type.toLowerCase() == "number") {
prepData.decimalPlace = 0;
prepData.isCurrency = false;
}
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]);
};
// HANDLE DROP - END
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); // Allows the drop event to work
};
const handleSetIsFields = (setTo: boolean) => {
if (setTo != isFields) setIsFields(setTo);
}
const handleOnConsent = () => {
setSignerConsent((prev) => {
return { ...prev, consent: true }
});
}
const handleDeclineConfirm = async (reason: string, comments: string) => {
try {
await axiosInstance.post(`/api/v1/sign/${recipientId}/decline`, { reason: reason + ", " + comments });
setSignerConsent((prev) => ({ ...prev, decline: true }));
setRecSignStatus("DECLINED");
// setShowCompletionScreen(true);
// navigate("/transaction-declined")
window.location.href = `/transaction-declined`
} catch (e) { }
}
const handleReassignConfirm = (data: { firstName: string; lastName: string; email: string; message: string }) => {
axiosInstance.post(`/api/v1/sign/${recipientId}/reassign`, {
name: data.firstName ? data.firstName + " " + data.lastName : data.lastName,
email: data.email,
message: data.message,
language: "en"
})
.then((res) => {
if (res.status == 202) {
// setShowCompletionScreen(true);
// navigate("/transaction-reassigned")
window.location.href = `/transaction-reassigned`
}
})
.catch((err) => {
console.log(err)
});
}
// Simple throttle
function throttle(fn: Function, wait: number) {
let lastTime = 0;
return (...args: any[]) => {
const now = Date.now();
if (now - lastTime >= wait) {
lastTime = now;
fn(...args);
}
};
}
function printDocConDiv() {
var printContents = document.getElementById(divId).innerHTML;
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContents;
window.print();
document.body.innerHTML = originalContents;
}
useEffect(() => {
const docCon = document.getElementById("doc-con");
if (!docCon) return;
const checkPosition = throttle(() => {
let el = document.getElementById("tag-navigation-btn");
if (!el) return;
const elRect = el.getBoundingClientRect();
const containerRect = docCon.getBoundingClientRect();
if (elRect.bottom < containerRect.top) {
setNavigationTagPos(NavigationTagPosEnum.ABOVE)
} else if (elRect.top > containerRect.bottom) {
setNavigationTagPos(NavigationTagPosEnum.BELOW)
} else {
setNavigationTagPos(NavigationTagPosEnum.VISIBLE)
}
}, 10);
// Initial check
checkPosition();
docCon.addEventListener("scroll", checkPosition);
window.addEventListener("resize", checkPosition); // container may resize
return () => {
docCon.removeEventListener("scroll", checkPosition);
window.removeEventListener("resize", checkPosition);
};
}, []);
// if (showCompletionScreen) {
// if (recSignStatus == "COMPLETED") {
// return <CompletionScreen />
// } else if (recSignStatus == "DECLINED") {
// return <CompletionScreen hit="Action Canceled! " title="Maybe Next Time!" message={"Your request has been declined successfully. You can revisit anytime if you change your mind."} textColorSuccess={false} />
// } else {
// return <CompletionScreen />
// }
// }
return (
<>
<div className={`w-full overflow-hidden relative ${(globalLoader.isLoading) ? 'pointer-events-none overflow-hidden' : ''}`}>
{/* <TermsModal showTerms={showTerms} setShowTerms={setShowTerms} recipientId={recipientId || ""} /> */}
{/* {showCompletionScreen && <SignCompletionScreen />} */}
<div className="bg-slate-100"
// onSubmit={(e: React.FormEvent<HTMLFormElement>) => handleFormSubmit(e)}
>
{/* Nav Two - Options - START */}
<div className="flex flex-col w-full">
<div className='flex justify-between items-center px-4 py-1 bg-[var(--primary)] mt-0'>
<div className="flex items-center">
<img className="w-[150px] max-w-max py-1 pr-3.5" src="/sme-white.png" alt="" />
</div>
<div className="flex justify-center items-center text-shite gap-2">
{/* DOWNLOAD AND PRINT OPTION - START */}
{/* <div className='flex'>
<p className='p-2 cursor-pointer rounded-sm' title="Download Documents">
<FaDownload className='hover:text-slate-100 text-sm text-slate-200' />
</p>
<p className='p-2 cursor-pointer rounded-sm' title="Print">
<ImPrinter className='hover:text-slate-100 text-sm text-slate-200' />
</p>
</div>
<div className='w-full h-[20px] border-[1px] border-slate-400'></div>
<div className='flex' title="Audit">
<p className='p-2 cursor-pointer rounded-sm'>
<RiFileHistoryFill className='hover:text-slate-100 text-sm text-slate-200' />
</p>
</div> */}
{/* <div className='w-full h-[20px] border-[1px] border-slate-400 mr-2'></div> */}
{/* DOWNLOAD AND PRINT OPTION - START */}
{/* FIT - START */}
<div
className="hidden sm:block relative border border-slate-200 rounded-sm"
tabIndex={1}
onBlur={() => setIsFitDd(false)}
>
<div
onClick={() => setIsFitDd(!isFitDd)}
className='text-white flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm ml-2'>
<p className='tracking-wide'>{fitObj[selFitIndx].label}</p>
<MdKeyboardArrowDown style={{ transform: `${isFitDd ? "rotate(180deg)" : ""}` }} />
</div>
{isFitDd && <div className='absolute top-[30px] left-[20px] bg-white rounded-sm z-50'>
{fitObj.map((val, indx) => {
return (<div
key={indx}
// onClick={() => setSelFitIndx(indx)}
onClick={() => handleFitSelect(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
className=""
tabIndex={1}
onBlur={() => setNav2DocToggle(false)}>
{/* <div onClick={() => setNav2DocToggle(!nav2DocToggle)} className='bg-white flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'>
<p className='tracking-wide flex gap-1'><span>{selectedDocuments && selectedDocuments.length}</span> <span>Documents</span></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>
}
</>
{/* PRINT _ START */}
{/* <GrPrint
onClick={printDocConDiv}
className="text-white mr-2 cursor-pointer" /> */}
{/* PRINT _ END */}
{/* Send */}
{/* {(!transparentTags && !hideTags) ? <button
onClick={handleFormSubmit}
// 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
id="sign-client-side-btn"
className='tracking-wide text-white'>
Sign
</p>
<FaSignature className='text-white text-md' />
</button>
:
<p className="text-xs">Signed Successfully</p>
} */}
<SignButton
transparentTags={transparentTags}
hideTags={hideTags}
handleFormSubmit={() => setConfirmSignOpen(true)}
onDeclineClick={() => setDeclineOpen(true)}
onReassignClick={() => setReassignOpen(true)}
onAuditHistoryClick={() => setAuditHistoryOpen(true)}
showDecAndReassign={recipient?.type != "CC"}
transactionId={"1"}
/>
</div>
{/* Right Side - ENDS */}
</div>
{/* Horizontal Loader */}
{horizontalFormLoader.totalRequired != null && !transparentTags && (
<div className="w-full h-[4px] bg-slate-400 relative">
<div
className="h-full bg-yellow-300 transition-all duration-300"
style={{
width: `${Math.min(
(horizontalFormLoader.requiredFilled /
horizontalFormLoader.totalRequired) *
100
)}%`,
}}
/>
</div>
)}
</div>
{/* Nav Two - Options - End */}
{/* Document Container Start */}
<div className={`grid grid-cols-12 gap-1 mt-1 ${requiredFilled ? "h-[calc(100vh-100px)]" : "h-[calc(100vh-86px)]"} overflow-hidden`}>
{/* Left Side pages start */}
{/* <div className="lg:col-span-2 bg-slate-100 h-full p-4 overflow-y-auto"></div> */}
<div
id="tags-list-client-side"
className={`${isPages ? "block" : "hidden"} bg-slate-100 sm:block absolute w-[220px] left-0 top-[42px] border-r-2 sm:border-none border-slate-200 z-10 sm:static sm:w-auto sm:col-span-2 h-full overflow-y-auto ${recipient?.type != "CC" ? "bg-slate-100" : ""} ${!provideTags && isFields ? "hidden" : ""}`}>
<div
onClick={() => {
setIsPages(false)
setIsFields(true)
}}
className='absolute sm:hidden top-[5px] right-[10px] bg-white rounded-full text-2xl z-50'>
<IoMdClose />
</div>
<>
{provideTags && !transparentTags && <div className='flex flex-col md:flex-row justify-between items-center bg-slate-100 p-1.5 rounded-2xl border-1 border-gray-200 mb-3'>
{provideTags && recipient?.type != "CC" && <div
onClick={() => handleSetIsFields(true)}
className={`flex items-center gap-1 cursor-pointer text-xs w-full justify-center py-1.5 rounded-2xl ${isFields ? 'bg-[var(--primary)] text-white' : ''} transition-all duration-300`}
>
<IoIosDocument />
<p className='tracking-wide'>Tags</p>
</div>}
<div
className={`flex items-center gap-1 cursor-pointer text-xs w-full justify-center py-1.5 rounded-2xl ${!isFields ? 'bg-[var(--primary)] text-white' : ''} ${!provideTags ? "sm:hidden" : ""} transition-all duration-300`}
onClick={() => handleSetIsFields(provideTags ? false : !isFields)}
>
<IoIosDocument />
<p className='tracking-wide'>Pages</p>
</div>
</div>
}
</>
{!transparentTags && isFields && recipientId && provideTags && recipient?.type != "CC" && <FieldList color={"#8f87f1"} recipientId={recipientId} recipientIndx={0} acntId={"1"} />}
<DocumentPages
selectedDocuments={selectedDocuments}
setDropDownTogglePage={setDropDownTogglePage}
dropDownTogglePage={dropDownTogglePage}
droppedItems={droppedItems}
/>
</div>
{/* Pages - END */}
{/* Main container */}
<div
id="doc-con"
className={`col-span-12 zoom-con relative ${provideTags ? (isFields && selectedTag != null ? "sm:col-span-8" : "sm:col-span-10") : (isFields ? "sm:col-span-12" : "sm:col-span-10")} sm:px-4 bg-slate-100 h-full w-full ${zoomScale > 150 ? "overflow-x-auto" : "overflow-x-hidden"} scroll-smooth transition-all duration-200 pb-[4rem]`}
>
<div style={{
width: `${100 * scaleFactor}%`, // ← outer wrapper grows to make room
height: `${100 * scaleFactor}%`,
}}>
<div className="zoom-con"
style={{
transform: `scale(${scaleFactor})`,
transformOrigin: 'top left', // ← change to top left
width: `${100 / scaleFactor}%`,
height: scaleFactor < 1
? `${100 / scaleFactor}%` // zoomed out — expand to fill
: 'auto', // zoomed in — let content grow
}}>
{
selectedDocuments && selectedDocuments.length > 0 && selectedDocuments.map((doc) => {
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}`;
// const tagJsonData = [...droppedItems];
const currDocTags = droppedItems
.map((val, index) => ({ ...val, originalIndex: index })) // add original index
.filter(val => val.pageIndex === page.index);
let scale: { x: number | null, y: number | null } = { x: null, y: null }
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 overflow-hidden'
>
<img
// style={{ "minWidth": page.width, "minHeight": page.height, "maxWidth": page.width, "maxHeight": page.height }}
id={`${doc.documentId}-${page.index}`}
// style={{ "width": page.width, "height": page.height, "maxWidth": "100%", "maxHeight": "100%" }}
className={`w-[${page.width / 2}px] h-[${page.height / 2}px] sm:w-[${page.width}px] sm:h-[${page.height}px]`}
ref={(el) => (imageRefs.current[page.index] = el)}
onLoad={(e) => {
const el = e.currentTarget;
setImageSizes((prev) => ({
...prev,
[page.index]: {
width: el.offsetWidth,
height: el.offsetHeight,
},
}));
}}
draggable="false"
// onLoad={(imgElem) => {
// setPageScales((prev) => ({
// ...prev,
// [page.index]: {
// x: imgElem.clientWidth / page.width,
// y: imgElem.clientHeight / page.height,
// },
// }));
// }}
width={page.width}
height={page.height}
src={page.src}
loading='lazy'
/>
{!hideTags && <>
<TagsCon
zoomScale={zoomScale}
pageWidth={page.width}
pageHeight={page.height}
renderedWidth={imageSizes[page.index]?.width || imageRefs.current[page.index]?.offsetWidth || 0}
renderedHeight={imageSizes[page.index]?.height || imageRefs.current[page.index]?.offsetHeight || 0}
currDocTags={currDocTags}
pageIdkey={key}
provideTags={provideTags}
documentId={doc.documentId}
pageIndex={page.index}
hideTags={hideTags}
transparentTags={transparentTags}
focusElement={focusElement}
selectedTag={selectedTag}
droppedItems={droppedItems}
saved={saved}
signConfObj={signConfObj}
recipientId={recipientId as string}
setSaved={setSaved}
setStamp={setStamp}
setSignConfObj={setSignConfObj}
setOpenSign={setOpenSign}
setSignTagId={setSignTagId}
setDroppedItems={setDroppedItems}
setSelectedTag={setSelectedTag}
setFocusElement={setFocusElement}
handleTagNavClick={handleTagNavClick}
/>
</>}
{/* Side details of documents Pages and document Name - START */}
<p className='hidden sm:block absolute top-[50%] translate-y-[-50%] rotate-[-90deg] text-xs left-[-50px] text-slate-600' >
page {indx + 1} of {doc.pages && doc.pages.length}
</p>
{/* Side details of documents Pages and document Name - END */}
{!transparentTags && focusElement != null && focusElement && Object.keys(focusElement).length > 1 && focusElement.documentId == doc.documentId && focusElement.pageIndex == page.index &&
<div
id="tag-navigation-btn"
className={`hidden fixed right-0 translate-x-[100%] sm:flex justify-center gap-0 items-center z-50 cursor-pointer transition-all duration-300`}
style={{ top: (focusElement.dimensions.top) + "px" }}
onClick={() => handleTagNavClick("N")}
>
<svg width="100" height="27" viewBox="0 0 112 45" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.87927 20.8542L15.0554 2.53357C15.7668 1.54438 16.9108 0.958008 18.1292 0.958008H108.049C110.14 0.958008 111.835 2.65317 111.835 4.74426V41.0235C111.835 43.1146 110.14 44.8098 108.049 44.8098H18.1084C16.9016 44.8098 15.7671 44.2345 15.054 43.261L1.89871 25.3024C0.930133 23.9802 0.92229 22.1849 1.87927 20.8542Z" fill="#005AB5" />
{/* <path d="M53.2649 16.7474V28.3838H51.3899L45.907 20.4577H45.8104V28.3838H43.7024V16.7474H45.5888L51.0661 24.6792H51.1683V16.7474H53.2649ZM58.8545 28.5542C57.9795 28.5542 57.2238 28.3724 56.5875 28.0088C55.9549 27.6414 55.4682 27.1224 55.1272 26.452C54.7863 25.7777 54.6159 24.9842 54.6159 24.0713C54.6159 23.1736 54.7863 22.3857 55.1272 21.7077C55.4719 21.0258 55.953 20.4955 56.5704 20.1167C57.1879 19.7342 57.9132 19.5429 58.7466 19.5429C59.2844 19.5429 59.792 19.63 60.2693 19.8042C60.7504 19.9747 61.1746 20.2398 61.542 20.5997C61.9132 20.9595 62.2049 21.4179 62.417 21.9747C62.6291 22.5277 62.7352 23.1868 62.7352 23.952V24.5827H55.5818V23.1963H60.7636C60.7598 22.8023 60.6746 22.452 60.5079 22.1452C60.3413 21.8345 60.1083 21.5902 59.8091 21.4122C59.5136 21.2342 59.1689 21.1452 58.775 21.1452C58.3545 21.1452 57.9852 21.2474 57.667 21.452C57.3488 21.6527 57.1007 21.9179 56.9227 22.2474C56.7485 22.5732 56.6594 22.9311 56.6557 23.3213V24.5315C56.6557 25.0391 56.7485 25.4747 56.9341 25.8383C57.1197 26.1982 57.3791 26.4747 57.7125 26.6679C58.0458 26.8573 58.436 26.952 58.8829 26.952C59.1822 26.952 59.453 26.9103 59.6954 26.827C59.9379 26.7398 60.1481 26.613 60.3261 26.4463C60.5041 26.2796 60.6386 26.0732 60.7295 25.827L62.65 26.0429C62.5288 26.5505 62.2977 26.9936 61.9568 27.3724C61.6197 27.7474 61.1879 28.0391 60.6613 28.2474C60.1348 28.452 59.5325 28.5542 58.8545 28.5542ZM65.4896 19.6565L67.2509 22.8781L69.0407 19.6565H71.2168L68.5862 24.0202L71.2623 28.3838H69.0975L67.2509 25.2417L65.4214 28.3838H63.2396L65.8987 24.0202L63.3078 19.6565H65.4896ZM76.6857 19.6565V21.2474H71.6687V19.6565H76.6857ZM72.9073 17.5656H74.9641V25.7588C74.9641 26.0353 75.0058 26.2474 75.0891 26.3952C75.1762 26.5391 75.2899 26.6376 75.43 26.6906C75.5702 26.7436 75.7255 26.7702 75.8959 26.7702C76.0247 26.7702 76.1421 26.7607 76.2482 26.7417C76.3581 26.7228 76.4414 26.7058 76.4982 26.6906L76.8448 28.2986C76.735 28.3364 76.5778 28.3781 76.3732 28.4236C76.1725 28.469 75.9262 28.4955 75.6346 28.5031C75.1194 28.5183 74.6554 28.4406 74.2425 28.2702C73.8296 28.0959 73.502 27.827 73.2596 27.4633C73.0209 27.0997 72.9035 26.6452 72.9073 26.0997V17.5656Z" fill="white" /> */}
<text className="font-semibold capitalize" x="55%" y="54%" fill="white" font-size="14" font-family="Arial, sans-serif" dominant-baseline="middle" text-anchor="middle">
{requiredFilled && focusElement.required == true ? "Submit" : focusElement.type.toLowerCase()}
</text>
</svg>
{/* NEXT Button - END */}
</div>
}
</div>
)
})
}
</div>
</div>
)
})
}
<div className="flex justify-center mt-[60px] w-full">
{/* <div>
<button
onClick={() => handleConfirmSign()}
className="sm:hidden mb-[40px] bg-[var(--primary)] text-white px-4 py-2 cursor-pointer rounded-sm">Finish</button>
</div> */}
</div>
</div>
</div>
{(navigationTagPos == NavigationTagPosEnum.ABOVE || navigationTagPos == NavigationTagPosEnum.BELOW) &&
<div
onClick={() => scrollToElement(focusElement?.navigationOrder)}
className={`hidden sm:block cursor-pointer fixed ${navigationTagPos == NavigationTagPosEnum.ABOVE ? "top-[55px]" : requiredFilled ? "bottom-[82px]" : "bottom-[55px]"} right-[8%] flex flex-col items-center`}>
{/* Arrow pointing up */}
{/* {navigationTagPos == NavigationTagPosEnum.ABOVE && <div className="w-0 h-0 border-l-6 border-r-6 border-b-6 border-l-transparent border-r-transparent border-b-[var(--primary)]"></div>} */}
{/* Label box */}
<div
className="bg-[var(--primary)] text-white text-sm px-6 py-3 rounded-md shadow-md flex items-center gap-1 font-medium">
{/* <span className="capitalize">{focusElement?.type.toLowerCase()}</span> */}
<span className="capitalize">Next</span>
</div>
{/* {navigationTagPos == NavigationTagPosEnum.BELOW && <div className="w-0 h-0 border-l-6 border-r-6 border-b-6 border-l-transparent border-r-transparent border-b-[var(--primary)] rotate-[180deg]"></div>} */}
</div>}
</div>
{provideTags && selectedTag && selectedTag != null && "key" in selectedTag && "index" in selectedTag &&
<div className={`col-span-2 ${provideTags ? "" : "order-first"} h-full ${!isFields ? "bg-white" : isFields && provideTags ? "bg-white" : ""} overflow-y-auto`}>
{/* Right Side Attributes Section if providedTags - START */}
<TagProperties
droppedItems={droppedItems}
selectedTag={droppedItems[selectedTag.index]}
setDroppedItems={setDroppedItems}
selTagKey={selectedTag.key}
selTagIndx={selectedTag.index}
signClient={true}
/>
{/* Right Side Attributes Section if providedTags - END */}
</div>
}
</div>
{/* Document Container End */}
</div>
{/* {showCompletionScreen && recSignStatus == "COMPLETED" && <CompletionScreen />}
{showCompletionScreen && recSignStatus == "DECLINED" && <CompletionScreen title="No worries! you declined the signing request." message={"Your signing request has been declined. No changes were made to the document."} textColorSuccess={false} />} */}
<div style={{ display: `${openSign ? 'block' : 'none'}` }}>
{recipient &&
<ClientSignCon
zoomScale={zoomScale}
recipient={recipient}
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
onClick={() => {
setIsPages(true)
setIsFields(false)
}}
className='sm:hidden fixed top-[8%] left-0 bg-[var(--primary)] flex items-center text-white rounded-tr-md rounded-br-md cursor-pointer opacity-50 hover:opacity-100 transition-all duration-300'>
<div className='py-2 pl-2 pr-2'>
{/* <FaAngleUp className='rotate-90' /> */}
<IoDocumentsOutline />
{/* <p className='flex flex-col justify-center items-center text-xs tracking-widest mt-1 pt-1 border-t-1 border-white'>
<span>P</span>
<span>A</span>
<span>G</span>
<span>E</span>
<span>S</span>
</p> */}
</div>
</div>
{
!transparentTags && selectedDocuments != null && !showCompletionScreen && !signerConsent.consent && recipient?.type != "CC" &&
<ConsentScreen
onConsent={() => handleOnConsent()}
// onDecline={() => console.log("User declined")}
/>
}
<DeclineModal
open={declineOpen}
onClose={() => setDeclineOpen(false)}
onConfirm={({ reason, comments }) => handleDeclineConfirm(reason, comments)}
/>
<ReassignModal
open={reassignOpen}
onClose={() => setReassignOpen(false)}
onConfirm={({ firstName, lastName, email, message }) => handleReassignConfirm({ firstName, lastName, email, message })}
/>
<ClientAuditModal
open={auditHistoryOpen}
onClose={() => setAuditHistoryOpen(false)}
recipientId={recipientId}
// onConfirm={({ firstName, lastName, email, message }) => handleReassignConfirm({ firstName, lastName, email, message })}
/>
<ConfirmSignModal
open={confirmSignOpen}
onClose={() => setConfirmSignOpen(false)}
onConfirm={handleConfirmSign}
loading={confirmSignLoading}
recipientName={recipient ? `${recipient.firstName ?? ""} ${recipient.lastName ?? ""}`.trim() : ""}
documentCount={selectedDocuments?.length ?? 1}
/>
</div>
{!transparentTags && recipient?.type != "CC" && (
<div
className={`absolute z-80 left-0 bottom-0 bg-[var(--primary)] w-full flex justify-around items-center
px-2.5 transition-all duration-500 ease-in-out
${requiredFilled ? "translate-y-0" : "translate-y-full hidden"}`}
>
<div className="py-5 sm:py-5 text-white">
<p className="text-lg sm:text-xl font-semibold leading-snug">
All Set to Wrap This Up?
</p>
<p className="text-xs sm:text-sm leading-relaxed max-w-md">
You've completed the required fields. Review your work, then select Finish.
</p>
</div>
<button
onClick={() => setConfirmSignOpen(true)}
disabled={!requiredFilled}
className="font-semibold text-sm px-8 py-2 bg-slate-100 rounded-md text-[var(--primary)] cursor-pointer"
>
Finish
</button>
</div>
)}
{!transparentTags && recipient?.type !== "CC" && (
<footer className="fixed bottom-0 sm:static flex justify-between items-center sm:h-[30px] w-full bg-white border-t border-slate-200 z-50">
{/* Mobile Navigation */}
<div className="border-t-2 border-slate-200 py-2 sm:border-none sm:hidden justify-between flex items-center px-4 sm:py-1 w-full ">
{/* Left */}
<div className="flex items-center leading-tight gap-2">
{/* <span className="text-[11px] text-slate-500">
Powered by
</span> */}
<span className="font-normal text-sm text-slate-600">
Verify and Complete Tags
</span>
</div>
{/* Center Action */}
{!transparentTags &&
focusElement &&
Object.keys(focusElement).length > 1 && (
<>
<div className="flex bg-[var(--primary)] items-center gap-1 pl-2 pr-4 py-2 rounded-sm">
<MdNavigateNext className="text-white" />
<button
id="tag-navigation-btn-mb"
onClick={() => handleTagNavClick("N")}
className="rounded-sm text-white text-sm font-normal shadow-md active:scale-[0.98] transition-all duration-200"
>
{requiredFilled && focusElement.required
? "Submit"
: `${focusElement.type}`}
</button>
</div>
</>
)}
{/* Right */}
{/* <div className="text-right">
<p className="text-[11px] text-slate-400">
© 2026
</p>
</div> */}
</div>
{/* Desktop Footer */}
<div className="hidden sm:flex w-full items-center justify-between px-6 py-3 ">
{/* Left */}
<div className="flex items-center gap-2 text-sm text-slate-500">
<span>Powered by</span>
<span className="font-semibold text-[var(--mainPrimaryBtn)]">
Sign Made Easy
</span>
</div>
{/* Center */}
<div className="flex flex-col items-center gap-1">
{/* <select
className="px-3 py-1.5 rounded-lg border border-slate-200 text-sm outline-none focus:border-[var(--mainPrimaryBtn)] bg-white text-slate-600"
>
<option>English</option>
<option>Hindi</option>
<option>Spanish</option>
</select> */}
<p className="hidden sm:block text-xs text-slate-400 text-center">
By signing you agree to SME Terms & Conditions
</p>
</div>
{/* Right */}
<div className="hidden sm:block text-sm text-slate-500">
© 2026 SME. All rights reserved.
</div>
</div>
</footer>
)}
</>
)
}
export default SignDocument