recipientSign

This commit is contained in:
=bhagwanSingh 2025-07-22 13:34:28 +05:30
parent b65614ae69
commit b6c08123f0
9 changed files with 792 additions and 326 deletions

View File

@ -0,0 +1,63 @@
import React from 'react'
import { MdKeyboardArrowDown } from 'react-icons/md';
interface DocumentPagesInterface {
selectedDocuments: DocumentType[];
setDropDownTogglePage: React.Dispatch<React.SetStateAction<string>>;
dropDownTogglePage: string;
}
const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownTogglePage }: DocumentPagesInterface) => {
return (
<div className='col-span-2 bg-white h-full p-4 overflow-y-auto'>
<div className="flex flex-col gap-2">
{selectedDocuments && selectedDocuments.length && selectedDocuments.map((document, indx) => {
return (
<div
key={indx}
// tabIndex={1}
// onBlur={() => setDropDownTogglePage('')}
>
<div
onClick={() => (`${(dropDownTogglePage === 'pageDropDown' + indx) ? '' : 'pageDropDown' + indx}`)}
className={`p-2 ${(dropDownTogglePage === 'pageDropDown' + indx) ? "bg-[var(--primary)]" : "bg-slate-200"} flex flex-col justify-center items-center gap-2 rounded`}>
{
document.images != undefined && <img
key={indx}
className='rounded-sm shadow'
src={document.images[0].src}
alt="" />
}
<div
className={`flex items-center justify-between w-full text-xs rounded-sm cursor-pointer ${(dropDownTogglePage === 'pageDropDown' + indx) ? "text-slate-100" : "text-slate-600"}`}>
<p className={`whitespace-nowrap overflow-hidden text-ellipsis max-w-[18ch]`}>{document.name}</p>
<MdKeyboardArrowDown className={`${(dropDownTogglePage === 'pageDropDown' + indx) ? 'rotate-180' : ''}`} />
</div>
</div>
{
dropDownTogglePage === 'pageDropDown' + indx &&
<div className='my-1 flex justify-center items-center gap-1 flex-wrap'>
{document.images != undefined && document.images.map((img, indx) => (
<a
href={"#" + document.documentId + indx}
className='max-w-max px-4 py-2 shadow cursor-pointer border-1 rounded-sm border-slate-200 hover:border-[var(--primary)] transition-all duration-300 bg-slate-300'
>
<p className='text-center text-xs text-slate-600'>{indx + 1}</p>
</a>
))}
</div>
}
</div>
)
})}
</div>
</div>
)
}
export default DocumentPages

View File

@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
import axios from "axios"
import { RootState } from "../../store/store";
import { useParams } from "react-router-dom"
import { DroppedItem } from "../../utlity/types";
import { DroppedItem, DroppedItemsObj } from "../../utlity/types";
import Loader from "../dashboard/Loader";
import SignCon from "../signature/SignCon";
@ -11,7 +11,7 @@ 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 } from 'react-icons/fa'
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";
@ -20,6 +20,10 @@ 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";
@ -139,9 +143,12 @@ const SignDocument = () => {
const [tagJson, setTagJson] = useState<DroppedItem[] | []>([]);
const [transaction, setTransaction] = useState<TransactionType | null>(null);
const [recSignStatus, setRecSignStatus] = useState<string | null>(null);
const [droppedItems, setDroppedItems] = useState([]);
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);
@ -195,11 +202,11 @@ const SignDocument = () => {
useEffect(() => {
if (!focusElement) {
if (tagJson && tagJson.length) {
setFocusElement({ ...tagJson[0], focusElementIndx: 0 });
if (droppedItems && droppedItems.length) {
setFocusElement({ ...droppedItems[0], focusElementIndx: 0 });
}
}
}, [tagJson])
}, [droppedItems])
console.log(saved, "Line 162")
@ -282,9 +289,12 @@ const SignDocument = () => {
.then((res) => {
console.dir(res)
if (res.status === 200) {
console.log(res, "Line 288")
setSelectedDocuments(res.data.documents)
setRecipient(res.data.recipient)
setTagJson(res.data.tagJsons)
setDroppedItems(res.data.tagJsons)
setTransaction(res.data.transaction)
setRecSignStatus(res.data.status)
@ -301,7 +311,7 @@ const SignDocument = () => {
}
console.log(tagJson, "Line 261")
console.log(droppedItems, "Line 261")
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
@ -309,7 +319,7 @@ const SignDocument = () => {
tags: []
}
if (!tagJson) return;
if (!droppedItems) return;
// const form = e.currentTarget;
// const formData = new FormData(form);
@ -321,8 +331,8 @@ const SignDocument = () => {
// });
let signInitialsCount = 0;
for (let i = 0; i < tagJson.length; i++) {
if (tagJson[i].type === "SIGNATURE" || tagJson[i].type === "INITIALS") {
for (let i = 0; i < droppedItems.length; i++) {
if (droppedItems[i].type === "SIGNATURE" || droppedItems[i].type === "INITIALS") {
signInitialsCount += 1;
}
}
@ -344,7 +354,7 @@ const SignDocument = () => {
return;
}
for (const tag of tagJson) {
for (const tag of droppedItems) {
console.log(tag, "Tag Data")
// if ((tag.type === "SIGNATURE" || tag.subType === "INITIALS") && !tag.value) {
@ -506,8 +516,8 @@ const SignDocument = () => {
useEffect(() => {
(async () => {
if (tagJson) {
for (const tag of tagJson) {
if (droppedItems) {
for (const tag of droppedItems) {
if (tag.type === "SIGNATURE" && "value" in tag) {
const signatureData = await fetchSignatureData(tag.value);
console.log(signatureData);
@ -515,12 +525,12 @@ const SignDocument = () => {
}
}
})()
}, [tagJson])
}, [droppedItems])
const handleTagUpdate = (updateIndx: number, updateValue: string) => {
if (tagJson && tagJson.length) {
const updatedTag = tagJson.map((val, indx) => {
if (droppedItems && droppedItems.length) {
const updatedTag = droppedItems.map((val, indx) => {
if (indx === updateIndx) {
return {
...val,
@ -529,7 +539,7 @@ const SignDocument = () => {
}
return val;
})
setTagJson(updatedTag);
setDroppedItems(updatedTag);
}
// setTagJson((prev) => {
@ -541,6 +551,198 @@ const SignDocument = () => {
// });
}
// 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' : ''}`}>
@ -642,9 +844,6 @@ const SignDocument = () => {
<p>{recipient && recipient.firstName} {recipient && recipient.lastName}</p>
</div>
{/* Right Side - ENDS */}
</div>
@ -655,65 +854,23 @@ const SignDocument = () => {
{/* 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'>
<div className="flex flex-col gap-2">
{selectedDocuments && selectedDocuments.length && selectedDocuments.map((document, indx) => {
return (
<div
key={indx}
// tabIndex={1}
// onBlur={() => setDropDownTogglePage('')}
>
<div
onClick={() => setDropDownTogglePage(`${(dropDownTogglePage === 'pageDropDown' + indx) ? '' : 'pageDropDown' + indx}`)}
className={`p-2 ${(dropDownTogglePage === 'pageDropDown' + indx) ? "bg-[var(--primary)]" : "bg-slate-200"} flex flex-col justify-center items-center gap-2 rounded`}>
{
document.images != undefined && <img
key={indx}
className='rounded-sm shadow'
src={document.images[0].src}
alt="" />
}
<div
className={`flex items-center justify-between w-full text-xs rounded-sm cursor-pointer ${(dropDownTogglePage === 'pageDropDown' + indx) ? "text-slate-100" : "text-slate-600"}`}>
<p className={`whitespace-nowrap overflow-hidden text-ellipsis max-w-[18ch]`}>{document.name}</p>
<MdKeyboardArrowDown className={`${(dropDownTogglePage === 'pageDropDown' + indx) ? 'rotate-180' : ''}`} />
</div>
{recipientId && <FieldList color={"#8f87f1"} recipientId={Number(recipientId)} recipientIndx={0} acntId={"1"} />}
</div>
{
dropDownTogglePage === 'pageDropDown' + indx &&
<div className='my-1 flex justify-center items-center gap-1 flex-wrap'>
{document.images != undefined && document.images.map((img, indx) => (
<a
href={"#" + document.documentId + indx}
className='max-w-max px-4 py-2 shadow cursor-pointer border-1 rounded-sm border-slate-200 hover:border-[var(--primary)] transition-all duration-300 bg-slate-300'
>
<p className='text-center text-xs text-slate-600'>{indx + 1}</p>
</a>
))}
</div>
}
</div>
)
})}
</div>
</div>
{/* Pages - END */}
{/* Main container */}
<div
id="doc-con"
className="zoom-con relative col-span-10 px-4 bg-slate-100 h-full w-full overflow-auto scroll-smooth"
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(tagJson, "tagJsontagJsontagJson")
console.log(droppedItems, "tagJsontagJsontagJson")
return (
<div key={doc.documentId} className='flex flex-col'>
<div className='py-2 text-sm'>
@ -722,16 +879,20 @@ const SignDocument = () => {
<div className='flex flex-col gap-3 max-w-max relative left-[50%] translate-x-[-50%]'>
{
tagJson && tagJson.length > 0 && doc.images && doc.images.length > 0 && doc.images.map((page, indx) => {
doc.images && doc.images.length > 0 && doc.images.map((page, indx) => {
const key = `${doc.documentId}_${page.index}`;
const currDocTags = tagJson.filter((val) => val.pageIndex === page.index)
console.log(currDocTags, tagJson, page, doc, "innnnnnnnnnnnn")
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={handleDragOver}
onDrop={(e) => handleDrop(e, doc.documentId, doc.documentId, page.index)}
onDragOver={(e) => handleDragOver(e)}
className='drop-page-in shadow-md relative'
>
@ -739,13 +900,15 @@ const SignDocument = () => {
{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"
// draggable="false"
onPointerDown={(e) => handleTagMouseDown(e, key, index)}
className="absolute rounded dropped-item text-sm"
// onClick={() => handleTagClick(key, index)}
style={{
@ -1074,6 +1237,10 @@ const SignDocument = () => {
</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">
{

View File

@ -52,7 +52,7 @@ const StartSection = () => {
const handleStartSectionClick = (card: StartCard) => {
if ("buttonText" in card && card.buttonText === "Start Now") {
api.post(`/api/accounts/${defaultAccountId}/transactions`)
api.post(`/api/accounts/${defaultAccountId}/transactions`, {templateId: 2})
.then(res => {
if (res.status === 200 || res.status === 201) {
dispatch(updateTransactionStatus(TRANSACTION_STATUS_ENUMS.TRANSACTION_INITIATED));

View File

@ -17,6 +17,7 @@ import typeToIconMap from '../../../helper/iconMap';
import { IoAdd } from 'react-icons/io5';
import Loader from '../../dashboard/Loader';
import { handleGroupBorderPosition } from '../../../utlity/tagGroupConUtils';
export interface DroppedItemsObj {
[key: string]: DroppedItem[]
@ -26,7 +27,7 @@ interface DroppedTagsDefaultFieldType {
[key: string]: string
}
const droppedTagsDefaultField: DroppedTagsDefaultFieldType = {
export const droppedTagsDefaultField: DroppedTagsDefaultFieldType = {
text: "paragraph",
temporal: "date",
number: "integer",
@ -55,6 +56,8 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
// const [uniqGroupIds, setUniqGroupIds] = useState<string[]>([]);
useEffect(() => {
selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId }));
@ -134,7 +137,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
// debugger
for (const [recipientId, tags] of Object.entries(tagsData)) {
console.log(recipientId, tags, "line 133")
// console.log(recipientId, tags, "line 133")
const updateTags = []
for (const tag of tags) {
if ("update" in tag && tag.update) {
@ -193,7 +196,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
let mask = null
console.log(dropTag, "line 132")
// console.log(dropTag, "line 132")
// if (dropTag.subType?.toLowerCase() === "password") {
// console.log(dropTag)
@ -203,7 +206,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
}
// }
console.log(dropTag, "line 204")
// console.log(dropTag, "line 204")
const isDeleted = "isDeleted" in dropTag ? dropTag.isDeleted : false
@ -340,6 +343,18 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
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;
}
@ -352,36 +367,44 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
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
}
}
]
:
[]
}],
prepData],
}
if (dragElemData.label === "radio") {
setDroppedItems(dataToSet);
} else {
setDroppedItems(dataToSet);
}
// 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(dataToSet);
setDroppedItemsCounter((prev) => prev + 1);
setTimeout(() => {
const tagIndex = key in droppedItems ? droppedItems[key].length : 0;
handleTagClick(key, tagIndex)
@ -394,6 +417,9 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
event.preventDefault();
let targetContainer = event?.currentTarget?.parentElement?.parentElement;
console.log(targetContainer, "targetContainer line 421")
let targetElement = event?.currentTarget?.parentElement;
if (!targetContainer || !targetElement) return;
@ -408,16 +434,29 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
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));
if (groupElements != null && groupElements.length) handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY);
// Apply the movement to the element
setDroppedItems({
...droppedItems,
@ -524,9 +563,28 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
})()
}
const handleAddRadioClick = (key:string) => {
console.log("Handle Radio Clicled", key)
console.log(droppedItems, "line 525")
// const handleAddRadioClick = (key: string) => {
// console.log("Handle Radio Clicled", key)
// console.log(droppedItems, "line 525")
// }
const handleAddGroupItemClicked = (key: string, item) => {
const groupId = item.group;
item = { ...item, dimensions: { ...item.dimensions, top: item.dimensions.top + item.dimensions.height + 20 } }
// dimensions: { ...item.dimensions, left: newLeft, top: newTop }
setTimeout(() => {
handleIGroupBorderPosition(item.group)
}, 0)
setDroppedItems({
...droppedItems,
[key]: [...droppedItems[key], { ...item, isGroupExist: true }],
});
}
// const handleAddRadio = (e: React.MouseEvent<HTMLElement>, key: string, index: number) => {
@ -675,6 +733,23 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
};
function handleIGroupBorderPosition(groupId: string) {
const groupElements: NodeListOf<HTMLElement> = document.querySelectorAll(`div[group="${groupId}"]`);
let targetContainer = groupElements[0].parentElement?.parentElement;
console.log(targetContainer, "targetContainer line 421")
let targetElement = groupElements[0].parentElement;
if (!targetContainer || !targetElement) return;
const containerRect = targetContainer.getBoundingClientRect();
handleGroupBorderPosition(groupElements, containerRect, 40, 40);
}
return (
<>
{/* Center Doc Container - START */}
@ -705,10 +780,12 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
{/* DROPPED ITEMS - Render only the dropped items for this specific docId and page - STARTS */}
{droppedItems[key]?.map((item, index) => {
if (item.isDeleted) return;
const type = item.type.toUpperCase();
const IconComponent = typeToIconMap[type] || <FaQuestionCircle />;
const selectedRecipient = selectedRecipients.filter((obj, indx) => obj.id === item.recipientId);
return (
<>
<div
key={index}
draggable="false"
@ -752,55 +829,42 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
</div>}
{/* Expand Circle - ENDS */}
{/* Tag - START */}
{
<div
{...("group" in item && item.group ? { "group": item.group } : {})}
onPointerDown={(e) => handleTagMouseDown(e, key, index)}
className='p-2 rounded-sm h-full shadow opacity-40 cursor-grab w-full'
style={{ background: selectedRecipient[0]?.color }}
>
<p
className={`flex justify-center items-center w-full text-xl text-white h-full`}
// style={{ color: selectedRecipients[item.recipientIndx]?.color }}
>
{IconComponent}
</p>
</div>
}
{/* Tag - END */}
</div>
{
item.type !== "radio" ?
<div
onPointerDown={(e) => handleTagMouseDown(e, key, index)}
// style={{ width: `${item.width}px !important` }}
className='p-2 rounded-sm h-full shadow opacity-40 cursor-grab w-full'
style={{ background: selectedRecipient[0]?.color }}
>
<p
className={`flex justify-center items-center w-full text-xl text-white h-full`}
// style={{ color: selectedRecipients[item.recipientIndx]?.color }}
>
{IconComponent}
</p>
</div>
:
"group" in item && item.group && "isGroupExist" in item && !item.isGroupExist &&
<>
<div>
{/* Radio - Container - START */}
<div>
{/* Radio- Item - START */}
<div
onPointerDown={(e) => handleTagMouseDown(e, key, index)}
// style={{ width: `${item.width}px !important` }}
className='p-2 rounded-sm h-full shadow opacity-40 cursor-grab w-full'
style={{ background: selectedRecipient[0]?.color }}
>
<p
className={`flex justify-center items-center w-full text-xl text-white h-full`}
// style={{ color: selectedRecipients[item.recipientIndx]?.color }}
>
{IconComponent}
</p>
<div className="group-border" border-group={item.group} border-pos="top"></div>
<div className="group-border" border-group={item.group} border-pos="right"></div>
<div className="group-border bottom-border" border-group={item.group} border-pos="bottom">
<div onClick={() => handleAddGroupItemClicked(key, item)}><IoAdd /></div>
</div>
{/* Radio- Item - END */}
</div>
{/* Radio - Container - END */}
<IoAdd onClick={() => handleAddRadioClick(key)} className='ml-[50%] translate-x-[-50%] cursor-pointer' />
</div>
<div className="group-border" border-group={item.group} border-pos="left"></div>
</>
}
</div>
</>
)
}
@ -848,7 +912,8 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
disabled: false,
stepSize: 5,
className: ''
}} />}
}} />
}
{/* Center Doc Container - END */}
</>
)

View File

@ -1,10 +1,12 @@
import React, { useEffect, useState } from 'react';
import fields from '../../../helper/fieldsListData';
import api from '../../../config/api';
// import api from '../../../config/api';
import { useParams } from 'react-router-dom';
import typeToIconMap from '../../../helper/iconMap';
import { FaQuestionCircle } from 'react-icons/fa';
import { TagsProperties } from '../../../utlity/types';
import { v4 as uuidv4 } from "uuid";
import axios from 'axios';
const handlePointerDown = (event: React.DragEvent<HTMLDivElement>, label: string, recipientId: number, recipientIndx: number, tagsProperties: TagsProperties) => {
const dragElemRect = event.currentTarget.getBoundingClientRect();
@ -31,6 +33,8 @@ const handlePointerDown = (event: React.DragEvent<HTMLDivElement>, label: string
properties: tagsProperties
}
if (obj.label.toLowerCase() === "radio" || obj.label.toLowerCase() === "checkbox") obj.group = uuidv4();
window.dragElemData = JSON.stringify(obj);
}
@ -38,21 +42,31 @@ interface FieldListProps {
color: string;
recipientId: number;
recipientIndx: number;
acntId?: string | null;
}
const FieldList: React.FC<FieldListProps> = ({ color, recipientId, recipientIndx }) => {
const FieldList: React.FC<FieldListProps> = ({ color, recipientId, recipientIndx, acntId = null }) => {
const [tagsData, setTagsData] = useState<TagsProperties[]>([]);
const { accountId } = useParams<{ accountId: string }>();
let { accountId } = useParams<{ accountId: string }>();
if(acntId) {
accountId = acntId;
}
if (!accountId) {
window.location.href = "/";
return null;
}
useEffect(() => {
api
.get(`/api/accounts/${accountId}/tags`)
axios
.get(`${import.meta.env.VITE_BASE_URI}/api/accounts/${accountId}/tags`, {
headers: {
"ngrok-skip-browser-warning": "69420",
}
})
.then((res) => {
const priorityOrder = ['SIGNATURE', 'INITIALS', 'STAMP'];

View File

@ -107,7 +107,7 @@ const RightSideBar: React.FC<DroppedItemProps> = ({ selectedTag, setDroppedItems
const selectedType = selectedTag.type in enumMap && enumMap[selectedTag.type as keyof typeof enumMap];
console.log("selectedTag", selectedTag, selectedType)
// console.log("selectedTag", selectedTag, selectedType)

View File

@ -26,6 +26,8 @@ import { FaSpinner } from "react-icons/fa";
import { GrCheckboxSelected } from "react-icons/gr";
import { Bounce, toast } from "react-toastify";
import axios from 'axios';
import { SaveType } from '../client/SignDocument';
import { CiSaveDown1 } from 'react-icons/ci';
interface Signature {
id: string;
@ -44,9 +46,14 @@ interface Signature {
}
interface AddSignatureModalProps {
setShowAddSignature?: (value: boolean) => void;
getAllSignatures: () => Promise<void>;
editingSignature?: Signature | null;
// setShowAddSignature?: (value: boolean) => void;
// getAllSignatures: () => Promise<void>;
// editingSignature?: Signature | null;
recipientId?: string;
setOpenSign: React.Dispatch<React.SetStateAction<boolean>>;
saved: SaveType;
setSaved: React.Dispatch<React.SetStateAction<SaveType>>;
}
interface Account {
@ -70,11 +77,12 @@ interface SelectStyle {
}
const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
setShowAddSignature,
getAllSignatures,
editingSignature
setOpenSign,
recipientId,
saved,
setSaved
}) => {
const [activeTab, setActiveTab] = useState<'SELECT' | 'DRAW' | 'UPLOAD'>('SELECT');
const [activeTab, setActiveTab] = useState<'TYPED' | 'DRAW' | 'UPLOAD'>('TYPED');
const [fullName, setFullName] = useState('');
const [initials, setInitials] = useState('');
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
@ -105,61 +113,76 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
const [preview2, setPreview2] = useState<string | null>(null);
// User data
const [user, setUser] = useState<UserData>({
id: "",
firstName: "",
lastName: "",
email: "",
language: "",
account: {
id: 0,
isDefault: false,
name: "",
},
});
// const [user, setUser] = useState<UserData>({
// id: "",
// firstName: "",
// lastName: "",
// email: "",
// language: "",
// account: {
// id: 0,
// isDefault: false,
// name: "",
// },
// });
const isEditMode = !!editingSignature;
// const isEditMode = !!editingSignature;
// Navigation items
const navigationItems = [
{ id: 'SELECT', label: 'SELECT', icon: <GrCheckboxSelected className="w-4 h-4" /> },
{ id: 'TYPED', label: 'TYPED', icon: <GrCheckboxSelected className="w-4 h-4" /> },
{ id: 'DRAW', label: 'DRAW', icon: <AiOutlineEdit className="w-5 h-5" /> },
{ id: 'UPLOAD', label: 'UPLOAD', icon: <AiOutlineUpload className="w-5 h-5" /> },
{ id: 'SAVED', label: 'SAVED', icon: <CiSaveDown1 className="w-5 h-5" /> },
];
// Initialize form with editing signature data
useEffect(() => {
if (editingSignature) {
setFullName(`${editingSignature.firstName} ${editingSignature.lastName}`);
setInitials(`${editingSignature.firstName.slice(0, 1)}${editingSignature.lastName.slice(0, 1)}`);
setActiveTab(editingSignature.signatureType as 'SELECT' | 'DRAW' | 'UPLOAD');
// useEffect(() => {
// if (editingSignature) {
// setFullName(`${editingSignature.firstName} ${editingSignature.lastName}`);
// setInitials(`${editingSignature.firstName.slice(0, 1)}${editingSignature.lastName.slice(0, 1)}`);
// setActiveTab(editingSignature.signatureType as 'TYPED' | 'DRAW' | 'UPLOAD');
// Set font for SELECT tab
const fontOption = signatureOptions.find(option => option.style === editingSignature.font);
if (fontOption) {
setSelectedSignature({ id: fontOption.id, style: fontOption.style });
}
// // Set font for SELECT tab
// const fontOption = signatureOptions.find(option => option.style === editingSignature.font);
// if (fontOption) {
// setSelectedSignature({ id: fontOption.id, style: fontOption.style });
// }
// Set signature blobs for all tabs
if (editingSignature.signatureBlob && editingSignature.initialsBlob) {
setSignatures({
fullSignature: `data:image/png;base64,${editingSignature.signatureBlob}`,
initials: `data:image/png;base64,${editingSignature.initialsBlob}`
});
// // Set signature blobs for all tabs
// if (editingSignature.signatureBlob && editingSignature.initialsBlob) {
// setSignatures({
// fullSignature: `data:image/png;base64,${editingSignature.signatureBlob}`,
// initials: `data:image/png;base64,${editingSignature.initialsBlob}`
// });
// Set preview URLs for UPLOAD tab
if (editingSignature.signatureType === 'UPLOAD') {
setPreview1(`data:image/png;base64,${editingSignature.signatureBlob}`);
setPreview2(`data:image/png;base64,${editingSignature.initialsBlob}`);
}
}
}
}, [editingSignature]);
// // Set preview URLs for UPLOAD tab
// if (editingSignature.signatureType === 'UPLOAD') {
// setPreview1(`data:image/png;base64,${editingSignature.signatureBlob}`);
// setPreview2(`data:image/png;base64,${editingSignature.initialsBlob}`);
// }
// }
// }
// }, [editingSignature]);
const handleSignatureChange = (data: { fullSignature: string | null; initials: string | null }) => {
setSignatures(data);
};
// Getting Signatures - START
useEffect(() => {
(async () => {
const res = await axios.get(`${import.meta.env.VITE_BASE_URI}/api/sign/signatures?recipientId=${recipientId}`, {
headers: {
"ngrok-skip-browser-warning": "69420"
}
})
console.log(res, "Geting Signatures")
})()
}, [])
// Getting Signatures - END
// Cleanup preview URLs when component unmounts or image changes
useEffect(() => {
return () => {
@ -202,7 +225,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
const handleSubmit = async () => {
setIsSubmitting(true);
console.log("handleSubmit Called")
if (!signatures.fullSignature || !signatures.initials) {
alert('Please provide both signature and initials');
setIsSubmitting(false);
@ -220,29 +243,35 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
};
let res;
if (isEditMode && editingSignature) {
// Update existing signature
const updatePayload = {
...payload,
signatureId: editingSignature.id
};
res = await axios.put(`${import.meta.env.VITE_BASE_URI}/api/user/signatures`, updatePayload);
console.log("handleUpdate", res);
// if (isEditMode && editingSignature) {
// // Update existing signature
// const updatePayload = {
// ...payload,
// signatureId: editingSignature.id
// };
// res = await axios.put(`${import.meta.env.VITE_BASE_URI}/api/user/signatures`, updatePayload);
// console.log("handleUpdate", res);
toast.success("Signature Updated Successfully", {
position: "top-center",
autoClose: 500,
hideProgressBar: false,
closeOnClick: false,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
transition: Bounce,
});
} else {
// toast.success("Signature Updated Successfully", {
// position: "top-center",
// autoClose: 500,
// hideProgressBar: false,
// closeOnClick: false,
// pauseOnHover: true,
// draggable: true,
// progress: undefined,
// theme: "light",
// transition: Bounce,
// });
// } else {
// Create new signature
res = await api.post(`/api/user/signatures`, payload);
res = await axios.post(`${import.meta.env.VITE_BASE_URI}/api/user/signatures`,
payload,
{
headers: {
"ngrok-skip-browser-warning": "69420"
}
});
console.log("handleSubmit", res);
toast.success("Signature Created Successfully", {
@ -256,7 +285,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
theme: "light",
transition: Bounce,
});
}
// }
if (res?.data) {
await getAllSignatures();
@ -264,17 +293,17 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
}
} catch (error) {
console.error("Error submitting signature:", error);
toast.error(isEditMode ? "Failed to update signature" : "Failed to create signature", {
position: "top-center",
autoClose: 2000,
hideProgressBar: false,
closeOnClick: false,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
transition: Bounce,
});
// toast.error(isEditMode ? "Failed to update signature" : "Failed to create signature", {
// position: "top-center",
// autoClose: 2000,
// hideProgressBar: false,
// closeOnClick: false,
// pauseOnHover: true,
// draggable: true,
// progress: undefined,
// theme: "light",
// transition: Bounce,
// });
} finally {
setIsSubmitting(false);
}
@ -282,7 +311,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
const renderContent = () => {
switch (activeTab) {
case 'SELECT':
case 'TYPED':
return (
<div className="h-full flex flex-col ">
<div className="mb-6">
@ -340,7 +369,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
return (
<DrawSignatureCanvas
onSignatureChange={handleSignatureChange}
initialSignatures={isEditMode ? signatures : undefined}
// initialSignatures={isEditMode ? signatures : undefined}
/>
);
@ -408,26 +437,26 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
}
};
const getUserDetails = async () => {
try {
let res = await api.get(`/api/user/details`);
console.log("getuser", res);
let currUser = res?.data;
setUser(currUser);
// const getUserDetails = async () => {
// try {
// let res = await api.get(`/api/user/details`);
// console.log("getuser", res);
// let currUser = res?.data;
// setUser(currUser);
// Only set default values if not in edit mode
if (!isEditMode) {
setFullName(currUser.firstName + " " + currUser.lastName);
setInitials(currUser.firstName.slice(0, 1) + currUser.lastName.slice(0, 1));
}
} catch (error) {
console.error("Error fetching user details:", error);
}
};
// // Only set default values if not in edit mode
// if (!isEditMode) {
// setFullName(currUser.firstName + " " + currUser.lastName);
// setInitials(currUser.firstName.slice(0, 1) + currUser.lastName.slice(0, 1));
// }
// } catch (error) {
// console.error("Error fetching user details:", error);
// }
// };
useEffect(() => {
getUserDetails();
}, []);
// useEffect(() => {
// getUserDetails();
// }, []);
return (
<div className="fixed inset-0 backdrop-blur-sm bg-opacity-50 flex items-center justify-center z-50 p-4">
@ -482,7 +511,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
{navigationItems.map((item) => (
<button
key={item.id}
onClick={() => setActiveTab(item.id as 'SELECT' | 'DRAW' | 'UPLOAD')}
onClick={() => setActiveTab(item.id as 'TYPED' | 'DRAW' | 'UPLOAD')}
className={`w-full flex items-center space-x-3 px-2 py-2 text-sm font-medium rounded-lg cursor-pointer transition-colors ${activeTab === item.id
? 'bg-blue-50 text-blue-700 border border-blue-200'
: 'text-gray-600 hover:bg-gray-100 hover:text-gray-900'
@ -519,7 +548,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
<div className="p-4 border-t border-gray-200 flex justify-end gap-3">
<button
className="cursor-pointer px-4 py-2 text-sm font-medium rounded-md border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors disabled:opacity-60 disabled:cursor-not-allowed"
onClick={() => setShowAddSignature?.(false)}
onClick={() => setOpenSign?.(false)}
disabled={isSubmitting}
>
CANCEL
@ -532,10 +561,13 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
{isSubmitting ? (
<>
<FaSpinner className="animate-spin" />
{isEditMode ? 'UPDATING...' : 'CREATING...'}
{/* {isEditMode ? 'UPDATING...' : 'CREATING...'} */}
</>
) : (
isEditMode ? 'UPDATE SIGNATURE' : 'CREATE SIGNATURE'
<>
CREATE SIGNATURE
{/* isEditMode ? 'UPDATE SIGNATURE' : 'CREATE SIGNATURE' */}
</>
)}
</button>
</div>

View File

@ -62,36 +62,70 @@
canvas {
touch-action: none;
-ms-touch-action: none; /* For older IE/Edge */
-ms-touch-action: none;
/* For older IE/Edge */
pointer-events: auto;
}
@keyframes textColorChange {
0%, 100% {
0%,
100% {
color: white;
}
50% {
color: #41644A; /* Tailwind blue-500 */
color: #41644A;
/* Tailwind blue-500 */
}
}
.animate-text {
animation: textColorChange 2s infinite ease-in-out;
}
@layer utilities {
/* Hide scrollbar for Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none;
/* IE and Edge */
scrollbar-width: none;
/* Firefox */
}
}
/* Group Border - START */
.group-border {
position: absolute;
border: 1px dashed var(--primary);
/* pointer-events: none; */
}
.bottom-border>div {
position: absolute;
top: -10px;
left: 50%;
transform: translateX(-50%);
z-index: 50;
background-color: var(--primary);
color: var(--primaryLight);
padding: 2px 8px;
border-radius: 5px;
}
.bottom-border>div:hover {
background-color: var(--primaryLight2) ;
opacity: 0.9;
transition: all 0.3s ease-in-out;
cursor: pointer;
}
/* Group Border - END */
/* Loader CSS */

View File

@ -0,0 +1,91 @@
interface Dimension {
maxTop: number | null;
minTop: number | null;
maxLeft: number | null;
minLeft: number | null;
}
const getGroupDimension = (
group: NodeListOf<HTMLElement>,
containerRect: DOMRect,
offsetX: number,
offsetY: number
) => {
let dimension: Dimension = { maxTop: null, minTop: null, maxLeft: null, minLeft: null };
group.forEach((element: HTMLElement) => {
const rect = element.getBoundingClientRect();
dimension.minTop = Math.min(dimension.minTop ?? Infinity, rect.top);
dimension.maxTop = Math.max(dimension.maxTop ?? 0, rect.bottom);
dimension.minLeft = Math.min(dimension.minLeft ?? Infinity, rect.left);
dimension.maxLeft = Math.max(dimension.maxLeft ?? 0, rect.right);
});
dimension.maxTop = dimension.maxTop !== null ? dimension.maxTop - containerRect.top + (offsetY - 10) : null;
dimension.minTop = dimension.minTop !== null ? dimension.minTop - containerRect.top - (offsetY - 10) : null;
dimension.maxLeft = dimension.maxLeft !== null ? dimension.maxLeft - containerRect.left + (offsetX - 10) : null;
dimension.minLeft = dimension.minLeft !== null ? dimension.minLeft - containerRect.left - (offsetX - 10) : null;
return dimension;
}
const handleGroupBorderPosition = (groupElements: NodeListOf<HTMLElement>, containerRect: DOMRect, offsetX: number, offsetY: number) => {
const dimension = getGroupDimension(groupElements, containerRect, offsetX, offsetY);
const borderTop: HTMLElement | null = document.querySelector("[border-group='" + groupElements[0].getAttribute("group") + "'][border-pos='top']");
const borderRight: HTMLElement | null = document.querySelector("[border-group='" + groupElements[0].getAttribute("group") + "'][border-pos='right']");
const borderBottom: HTMLElement | null = document.querySelector("[border-group='" + groupElements[0].getAttribute("group") + "'][border-pos='bottom']");
const borderLeft: HTMLElement | null = document.querySelector("[border-group='" + groupElements[0].getAttribute("group") + "'][border-pos='left']");
if (borderTop !== null && dimension.maxLeft !== null && dimension.minLeft !== null) {
borderTop.style.width = (dimension.maxLeft - dimension.minLeft) + "px";
borderTop.style.height = "1px";
borderTop.style.top = dimension.minTop + "px";
borderTop.style.left = dimension.minLeft + "px";
}
if (borderRight !== null && dimension.maxTop !== null && dimension.minTop !== null) {
borderRight.style.width = "1px";
borderRight.style.height = (dimension.maxTop - dimension.minTop) + "px";
borderRight.style.top = dimension.minTop + "px";
borderRight.style.left = dimension.maxLeft + "px";
}
if (borderBottom !== null && dimension.maxLeft !== null && dimension.minLeft !== null) {
borderBottom.style.width = (dimension.maxLeft - dimension.minLeft) + "px";
borderBottom.style.height = "1px";
borderBottom.style.top = dimension.maxTop + "px";
borderBottom.style.left = dimension.minLeft + "px";
}
if (borderLeft !== null && dimension.maxTop !== null && dimension.minTop !== null) {
borderLeft.style.width = "1px";
borderLeft.style.height = (dimension.maxTop - dimension.minTop) + "px";
borderLeft.style.top = dimension.minTop + "px";
borderLeft.style.left = dimension.minLeft + "px";
}
}
// const handleTargetBorderEventListener = (groupId: string, containerRect: DOMRect) => {
// console.log("handleTargetBorderEventListener called for groupId:", groupId);
// if (groupId) {
// const groupElements: NodeListOf<HTMLElement> = document.querySelectorAll(`div[group="${groupId}"]`);
// handleGroupBorderPosition(groupElements, containerRect);
// }
// }
const handleInitialGroupBorderPosition = (groupElements: NodeListOf<HTMLElement>, containerRect: DOMRect, offsetX: number, offsetY: number) => {
setTimeout(() => {
handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY)
}, 0)
}
export {
getGroupDimension,
handleGroupBorderPosition,
handleInitialGroupBorderPosition
// handleTargetBorderEventListener
}