diff --git a/src/components/process/processStepTwo/DocumentContainer.tsx b/src/components/process/processStepTwo/DocumentContainer.tsx index cfcec4e..29c111d 100644 --- a/src/components/process/processStepTwo/DocumentContainer.tsx +++ b/src/components/process/processStepTwo/DocumentContainer.tsx @@ -8,19 +8,13 @@ import RightSideBar, { MASKTYPE, TEXT } from './rightSideBar/RightSideBar'; import { DroppedItem, TagsTypes } from '../../../utlity/types'; import { useNavigate, useParams } from 'react-router-dom'; -import { IoMdClose } from 'react-icons/io'; - import { fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'; import { fetchDocuments } from '../../../store/slices/documentSlice'; import { FaQuestionCircle } from 'react-icons/fa'; import typeToIconMap from '../../../helper/iconMap'; -import { IoAdd } from 'react-icons/io5'; import Loader from '../../dashboard/Loader'; -// import { TRANSACTION_TEMPLATE_TOGGLE } from "../../../store/slices/selectedRecipientsSlice"; -import { handleGroupBorderPosition } from '../../../utlity/tagGroupConUtils'; -import { hideLoader, showLoader } from '../../../store/slices/loaderSlice'; -import { IsSendInterface } from './ProcessStepTwo'; +import { hideLoader } from '../../../store/slices/loaderSlice'; import DroppedItemsTagsCon from './DroppedItemsTagsCon'; export interface DroppedItemsObj { @@ -73,6 +67,44 @@ const DocumentContainer: React.FC = ({ transactionSaving const [droppedItems, setDroppedItems] = useState({}); + interface HotKeysObj { + isActive: boolean; + key: string | null; + tagId?: string | null; + hotKey?: string | null; + + } + + + + // const hotKeysObjInitial: HotKeysObj = { + // "CtrlA": { + // enabled: false, + // key: null + // }, + // "Delete": { + // enabled: false, + // key: null + // }, + // "CtrlC": { + // enabled: false, + // key: null, + // tagId: null + // } + // } + + const hotKeysObjInitial: HotKeysObj = { + isActive: false, + key: null, + tagId: null, + hotKey: null + } + + + + const [hotKeysObj, setHotKeysObj] = useState(hotKeysObjInitial); + + useEffect(() => { selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId })); selectedDocuments.length === 0 && dispatch(fetchDocuments({ accountId, transactionId })); @@ -97,105 +129,6 @@ const DocumentContainer: React.FC = ({ transactionSaving } } - // let updatedTags = { ...recTags } - // let tagsData = [] - - // for (const dropPageIds of Object.keys(updatedTags)) { - - // for (const dropTag of updatedTags[dropPageIds]) { - // let mask = null - - // // console.log(dropTag, "line 132") - - // // if (dropTag.subType?.toLowerCase() === "password") { - // // console.log(dropTag) - // mask = { - // "type": dropTag.maskType ? `${dropTag.maskType}`.toUpperCase() : null, - // "character": dropTag.maskCharacter ? `${dropTag.maskCharacter}`.toUpperCase() : null - // } - // // } - - // // console.log(dropTag, "line 204") - - // const isDeleted = "isDeleted" in dropTag ? dropTag.isDeleted : false - - // const updatedTagData = { - // "id": "id" in dropTag ? dropTag.id : null, - // "label": dropTag.label, - // "dimensions": dropTag.dimensions, - // "type": dropTag.type.toUpperCase(), - // // "subType": (dropTag.subType ? dropTag.subType.toUpperCase() : null), - // "minLength": "minLength" in dropTag ? dropTag.minLength : 0, - // "maxLength": "maxLength" in dropTag ? dropTag.maxLength : 0, - // "error": ("error" in dropTag && dropTag.error) ? `${dropTag.error}`.toUpperCase() : "error", - // "regex": "regex" in dropTag ? dropTag.regex : [] - // // } - // , - // "documentId": dropTag.documentId, - // "options": ("options" in dropTag) ? dropTag.options : [], - // "pageIndex": dropTag.pageIndex, - // "recipientId": dropTag.recipientId, - // "styles": "styles" in dropTag ? dropTag.styles : { - // "font": "", - // "italic": false, - // "bold": false, - // "underline": false, - // "color": "black", - // "textSize": 0 - // }, - // "required": (dropTag.type.toUpperCase() !== "SIGNATURE" && dropTag.type.toUpperCase() !== "INITIALS") ? dropTag.required : true, - // // "required": "required" in dropTag ? dropTag.required : , - // "update": "update" in dropTag ? dropTag.update : false, - // "readOnly": false, - // } - - // // if ("format" in dropTag) { - // // updatedTagData.format = `${dropTag.subType}`.toUpperCase(); - // // updatedTagData.dateFormat = dropTag.format; - // // updatedTagData.currentTimeStamp = dropTag.currentTimeStamp - // // } - - // if("format" in dropTag && dropTag.format) { - // updatedTagData.subType = dropTag.format.toLowerCase(); - // updatedTagData.format = dropTag.dateFormat; - // } - - // if (`${dropTag.type}`.toLowerCase() === "number") { - // updatedTagData.decimalPlace = dropTag.decimalPlace - // updatedTagData.isCurrency = dropTag.isCurrency - // } - - // if (dropTag.type === "TEXT") { - // // updatedTagData.multiLine = dropTag.subType === "paragraph" - - // if(dropTag.multiLine) { - // updatedTagData.subtype = TEXT.PARAGRAPH - // } else { - // updatedTagData.subtype = TEXT.SINGLE_LINE - // } - - // if ("maskType" in dropTag) { - // updatedTagData.maskCharacter = mask.character - // updatedTagData.maskType = mask.type - // } - // } - - // // if ("required" in updatedTagData && !updatedTagData.required) delete updatedTagData.required; - - // if (updatedTagData.subType === 'SINGLE SELECT') updatedTagData.subType = "SINGLE_SELECT" - // if (updatedTagData.subType === 'MULTI SELECT') updatedTagData.subType = "MULTI_SELECT" - - // if (updatedTagData.options && !updatedTagData.options.length) delete updatedTagData.options; - - // if (!updatedTagData.id) delete updatedTagData.id; - - // if (!isDeleted) { - // // sendTags.mod.push(updatedTagData); - // tagsData.push(updatedTagData) - // } - // } - // } - setDroppedItems(recTags) // Handle your response here } catch (err) { @@ -293,104 +226,6 @@ const DocumentContainer: React.FC = ({ transactionSaving if (Object.keys(droppedItems).length === 0) return; if (!save && isSend === null) return; - // !save && dispatch(showLoader("Loading ...")) - - // let updatedTags = { ...droppedItems } - - // const sendTags = { - // "mod": [], - // "remove": [] - // } - - // let tagsData = [] - - // const modified = [] - // const remove = [] - - // console.log(updatedTags, "updatedTags"); - // for (const dropPageIds of Object.keys(updatedTags)) { - // for (const dropTag of updatedTags[dropPageIds]) { - // let mask = null - - // // console.log(dropTag, "line 132") - - // // if (dropTag.subType?.toLowerCase() === "password") { - // // console.log(dropTag) - // mask = { - // "type": dropTag.maskType ? `${dropTag.maskType}`.toUpperCase() : null, - // "character": dropTag.maskCharacter ? `${dropTag.maskCharacter}`.toUpperCase() : null - // } - // // } - - // // console.log(dropTag, "line 204") - - // const isDeleted = "isDeleted" in dropTag ? dropTag.isDeleted : false - - // const updatedTagData = { - // "id": "id" in dropTag ? dropTag.id : null, - // "label": dropTag.label, - // "dimensions": dropTag.dimensions, - // "type": dropTag.type.toUpperCase(), - // // "subType": (dropTag.subType ? dropTag.subType.toUpperCase() : null), - // "minLength": "minLength" in dropTag ? dropTag.minLength : 0, - // "maxLength": "maxLength" in dropTag ? dropTag.maxLength : 0, - // "error": ("error" in dropTag && dropTag.error) ? `${dropTag.error}`.toUpperCase() : "error", - // "regex": "regex" in dropTag ? dropTag.regex : [] - // // } - // , - // "documentId": dropTag.documentId, - // "options": ("options" in dropTag) ? dropTag.options : [], - // "pageIndex": dropTag.pageIndex, - // "recipientId": dropTag.recipientId, - // "styles": "styles" in dropTag ? dropTag.styles : { - // "font": "", - // "italic": false, - // "bold": false, - // "underline": false, - // "color": "black", - // "textSize": 0 - // }, - // "required": (dropTag.type.toUpperCase() !== "SIGNATURE" && dropTag.type.toUpperCase() !== "INITIALS") ? dropTag.required : true, - // // "required": "required" in dropTag ? dropTag.required : , - // "update": "update" in dropTag ? dropTag.update : false, - // "readOnly": false, - // } - - // if ("format" in dropTag) { - // updatedTagData.format = `${dropTag.subType}`.toUpperCase(); - // updatedTagData.dateFormat = dropTag.format; - // updatedTagData.currentTimeStamp = dropTag.currentTimeStamp - // } - - // if (`${dropTag.type}`.toLowerCase() === "number") { - // updatedTagData.decimalPlace = dropTag.decimalPlace - // updatedTagData.isCurrency = dropTag.isCurrency - // } - - // if (updatedTagData.type === "TEXT") { - // updatedTagData.multiLine = dropTag.subType === "paragraph" - // if (dropTag.subType === "password") { - // updatedTagData.maskCharacter = mask.character - // updatedTagData.maskType = mask.type - // } - // } - - // // if ("required" in updatedTagData && !updatedTagData.required) delete updatedTagData.required; - - // if (updatedTagData.subType === 'SINGLE SELECT') updatedTagData.subType = "SINGLE_SELECT" - // if (updatedTagData.subType === 'MULTI SELECT') updatedTagData.subType = "MULTI_SELECT" - - // if (updatedTagData.options && !updatedTagData.options.length) delete updatedTagData.options; - - // if (!updatedTagData.id) delete updatedTagData.id; - - // if (!isDeleted) { - // // sendTags.mod.push(updatedTagData); - // tagsData.push(updatedTagData) - // } - // } - // } - let updatedTags: any = []; for (const dropPagesId of Object.keys(droppedItems)) { @@ -415,17 +250,6 @@ const DocumentContainer: React.FC = ({ transactionSaving }, [isSend]) // Save Tags - END - // handleTransactionSaving - START - - // useEffect(() => { - // const interval = setInterval(async () => { - // setTransactionSaving(true) - // }, 8000) - // return () => clearInterval(interval); - // }, [droppedItems]) - - // handleTransactionSaving - END - useEffect(() => { if (transactionSaving) { setTimeout(() => { @@ -494,12 +318,6 @@ const DocumentContainer: React.FC = ({ transactionSaving 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") { @@ -519,301 +337,104 @@ const DocumentContainer: React.FC = ({ transactionSaving prepData], } - console.log("data311", dataToSet) setDroppedItems(dataToSet); setDroppedItemsCounter((prev) => prev + 1); setTimeout(() => { const tagIndex = key in droppedItems ? droppedItems[key].length : 0; + console.log(tagIndex, "line 307", key); + setSelectedTag({ key, tagIndex }) }, 0) }; // HANDLE DROP - END - - - // HANDLE EXPAND TAG - START - const handleExpandTag = (event: React.MouseEvent, key: string, index: number) => { - console.log("HANDLE EXPAND TAG - START") - event.preventDefault(); // Prevent text selection issues - - const expandingElemIcon = event.currentTarget; - const targetElement = expandingElemIcon.nextElementSibling?.parentElement; - const targetContainer = targetElement?.parentElement; - - if (!targetContainer || !targetElement) return; - - const elementRect = targetElement.getBoundingClientRect(); - - const startX = event.clientX; - const startY = event.clientY; - - const startWidth = elementRect.width; - const startHeight = elementRect.height; - - const handleMouseMove = (event: MouseEvent) => { - - let newWidth = startWidth + (event.clientX - startX); - let newHeight = startHeight + (event.clientY - startY); - - if (newHeight > 200 || newWidth > 400) { - if (newHeight > 200) { - newHeight = 200 - } - - if (newWidth > 400) { - newWidth = 400 - } - } - - // Apply the new width & height - setDroppedItems({ - ...droppedItems, - [key]: Array.isArray(droppedItems[key]) // Ensure it's an array - ? droppedItems[key].map((item, i) => - i === index ? { ...item, dimensions: { ...item.dimensions, width: newWidth, height: newHeight }, update: true } : item - ) - : [], // Provide a fallback in case it's undefined or not an array - }); - - }; - - const handleMouseUp = () => { - document.removeEventListener("pointermove", handleMouseMove); - document.removeEventListener("pointerup", handleMouseUp); - }; - - document.addEventListener("pointermove", handleMouseMove); - document.addEventListener("pointerup", handleMouseUp); - }; - // HANDLE EXPAND TAG - END - - - // Helper function to remove group border elements from DOM - const removeGroupBorders = (groupId: string) => { - console.log(`Removing borders for group: ${groupId}`); - - const borderPositions = ['top', 'right', 'bottom', 'left']; - - borderPositions.forEach(position => { - const borderElement = document.querySelector(`[border-group='${groupId}'][border-pos='${position}']`); - if (borderElement && borderElement.parentNode) { - borderElement.parentNode.removeChild(borderElement); - } - }); - }; - - // const handleAddRadioClick = (key:string) => { - // console.log("Handle Radio Clicled", key) - // console.log(droppedItems, "line 525") - // } - - const handleIGroupBorderPosition = (groupId: string) => { - console.log("handleIGroupBorderPosition for group:", groupId); - - const groupElements: NodeListOf = document.querySelectorAll(`div[group="${groupId}"]`); - - if (groupElements.length === 0) { - console.log("No group elements found for:", groupId); - return; - } - - let targetContainer = groupElements[0].parentElement?.parentElement; - - if (!targetContainer) { - console.log("No target container found"); - return; - } - - const containerRect = targetContainer.getBoundingClientRect(); - - // Use consistent offset values - const offsetX = 10; - const offsetY = 10; - - // Ensure DOM is fully updated before positioning - requestAnimationFrame(() => { - requestAnimationFrame(() => { - handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY); - }); - }); - }; - - // Enhanced handleGroupBorderPosition with better positioning logic - const handleGroupBorderPosition = (groupElements: NodeListOf, containerRect: DOMRect, offsetX: number, offsetY: number) => { - if (!groupElements || groupElements.length === 0) return; - - const groupId = groupElements[0].getAttribute('group'); - if (!groupId) return; - - // Calculate the bounding box of all group elements - let minLeft = Infinity, minTop = Infinity, maxRight = -Infinity, maxBottom = -Infinity; - - groupElements.forEach(element => { - const parentElement = element.parentElement; - if (!parentElement) return; - - const rect = parentElement.getBoundingClientRect(); - const relativeLeft = rect.left - containerRect.left; - const relativeTop = rect.top - containerRect.top; - const relativeRight = relativeLeft + rect.width; - const relativeBottom = relativeTop + rect.height; - - minLeft = Math.min(minLeft, relativeLeft); - minTop = Math.min(minTop, relativeTop); - maxRight = Math.max(maxRight, relativeRight); - maxBottom = Math.max(maxBottom, relativeBottom); - }); - - // Add some padding around the group - const padding = 10; - minLeft -= padding; - minTop -= padding; - maxRight += padding; - maxBottom += padding; - - const width = maxRight - minLeft; - const height = maxBottom - minTop; - - // Update or create border elements - const borderPositions = ['top', 'right', 'bottom', 'left']; - - borderPositions.forEach(position => { - let borderElement = document.querySelector(`[border-group='${groupId}'][border-pos='${position}']`) as HTMLElement; - - if (!borderElement) { - // Create border element if it doesn't exist - borderElement = document.createElement('div'); - borderElement.className = `group-border ${position === 'bottom' ? 'bottom-border' : ''}`; - borderElement.setAttribute('border-group', groupId); - borderElement.setAttribute('border-pos', position); - - // Add the + icon for bottom border - if (position === 'bottom') { - const addIcon = document.createElement('div'); - addIcon.innerHTML = ''; // You might need to adjust this based on your icon system - addIcon.onclick = () => { - // Find the original item to pass to handleAddGroupItemClicked - const originalItem = droppedItems[key]?.find(item => 'group' in item && item.group === groupId); - if (originalItem) { - handleAddGroupItemClicked(key, originalItem); - } - }; - borderElement.appendChild(addIcon); - } - - // Append to container - groupElements[0].parentElement?.parentElement?.appendChild(borderElement); - } - - // Position the border element - borderElement.style.position = 'absolute'; - borderElement.style.pointerEvents = position === 'bottom' ? 'auto' : 'none'; - - switch (position) { - case 'top': - borderElement.style.left = `${minLeft}px`; - borderElement.style.top = `${minTop}px`; - borderElement.style.width = `${width}px`; - borderElement.style.height = '2px'; - break; - case 'right': - borderElement.style.left = `${maxRight - 2}px`; - borderElement.style.top = `${minTop}px`; - borderElement.style.width = '2px'; - borderElement.style.height = `${height}px`; - break; - case 'bottom': - borderElement.style.left = `${minLeft}px`; - borderElement.style.top = `${maxBottom - 2}px`; - borderElement.style.width = `${width}px`; - borderElement.style.height = '2px'; - break; - case 'left': - borderElement.style.left = `${minLeft}px`; - borderElement.style.top = `${minTop}px`; - borderElement.style.width = '2px'; - borderElement.style.height = `${height}px`; - break; - } - }); - }; - - const handleAddGroupItemClicked = (key: string, item: any) => { - console.log("handleAddGroupItemClicked", item); - - const groupId = item.group; - - // Find all existing items in this group to determine the bottom-most position - const existingGroupItems = droppedItems[key]?.filter(existingItem => - 'group' in existingItem && existingItem.group === groupId - ) || []; - - const existingGroupItem = droppedItems[key]?.filter(existingItem => - 'group' in existingItem && existingItem.group === groupId - ) || []; - - existingGroupItem[0].options - - // Find the bottom-most item in the group - let bottomMostItem = item; - if (existingGroupItems.length > 0) { - bottomMostItem = existingGroupItems.reduce((lowest, current) => { - const currentBottom = current.dimensions.top + current.dimensions.height; - const lowestBottom = lowest.dimensions.top + lowest.dimensions.height; - return currentBottom > lowestBottom ? current : lowest; - }); - } - - // Calculate new position: place below the bottom-most item - const spacing = 10; // Adjust spacing as needed - const newTop = bottomMostItem.dimensions.top + bottomMostItem.dimensions.height + spacing; - - // Use the same left position as the parent (original) item - const newItem = { - ...item, - dimensions: { - ...item.dimensions, - left: item.dimensions.left, // Keep same horizontal position as parent - top: newTop // Position below the bottom-most item - }, - isGroupExist: true - }; - - // Update the dropped items - setDroppedItems({ - ...droppedItems, - [key]: [...droppedItems[key], newItem], - }); - - // Update group borders after adding the new item - // Use setTimeout to ensure state update is processed - setTimeout(() => { - handleIGroupBorderPosition(groupId); - }, 50); // Slightly longer timeout to ensure DOM updates - }; - - useEffect(() => { - // Initialize group borders for all existing groups after component updates - const timeoutId = setTimeout(() => { - const allGroupElements = document.querySelectorAll('[group]'); - const groupIds = new Set(); + if (!hotKeysObj.isActive) return - allGroupElements.forEach((element) => { - const groupId = element.getAttribute('group'); - if (groupId) { - groupIds.add(groupId); - } + const docCon = document.getElementById("doc-con"); + + switch (hotKeysObj.hotKey) { + case "Ctrl+c": + // Handle Ctrl+C + break; + case "Ctrl+v": + // Handle Ctrl+V + break; + case "Ctrl+x": + // Handle Ctrl+V + break; + case "Ctrl+z": + // Handle Ctrl+V + break; + case "Ctrl+y": + // Handle Ctrl+V + break; + case "Ctrl+a": + docCon?.querySelectorAll("[data-tag-id]").forEach(tag => { + tag.classList.add("border-2", "border-slate-800") + tag.setAttribute("all-select-group", "true") + + docCon.addEventListener("click", () => { + console.log("focus in", tag) + tag.classList.remove("border-2", "border-slate-800") + tag.removeAttribute("all-select-group") + }) + // docCon.addEventListener("focusout", () => { + // console.log("focus out", tag) + // tag.classList.remove("border-2", "border-slate-800") + // tag.removeAttribute("all-select-group") + // }) + }); + break; + case "delete": + docCon?.querySelectorAll("[all-select-group]").forEach((tag) => { + console.log(tag, "TAGID") + }) + break; + + default: + break; + } + }, [hotKeysObj]) + + const handleDocumentFocus = (key: string) => { + const id = key.replace("_", ""); + const targetCon = document.getElementById(id); + + targetCon?.addEventListener("keydown", (event) => { + event.preventDefault(); + const ctrl = event.ctrlKey; + const alt = event.altKey; + const metaKey = event.metaKey; + const shift = event.shiftKey; + const pressedKey = event.key.toLowerCase(); + + let hotKey = pressedKey + + + if (ctrl) { + hotKey = "Ctrl+" + pressedKey; + } else if (alt) { + hotKey = "Alt" + pressedKey; + } else if (metaKey) { + hotKey = "Meta+" + pressedKey; + } else if (shift) { + hotKey = "Shift+" + pressedKey; + } + + setHotKeysObj({ + isActive: true, + key: pressedKey, + tagId: null, + hotKey: hotKey }); - groupIds.forEach((groupId) => { - handleIGroupBorderPosition(groupId); - }); - }, 100); + }); - return () => clearTimeout(timeoutId); - }, [droppedItems]); // Re-run when droppedItems changes + + // return () => targetCon?.removeEventListener("keydown"); + } return ( <> @@ -824,7 +445,8 @@ const DocumentContainer: React.FC = ({ transactionSaving selectedDocuments.length > 0 ? selectedDocuments.map((doc, indxDoc) => { return (
-
+

{doc.name}

@@ -834,8 +456,9 @@ const DocumentContainer: React.FC = ({ transactionSaving const key = `${doc.id}_${page.index}`; return (
handleDocumentFocus(key)} key={indx} - id={`${doc.id}${indx}`} onDrop={(e) => handleDrop(e, doc.id, indxDoc, page.index)} onDragOver={handleDragOver} @@ -844,11 +467,14 @@ const DocumentContainer: React.FC = ({ transactionSaving >; + pageIdkey: string; + selectedTag: { key: string; index: number } | null; + selectedRecipients: Recipient[]; + setSelectedTag: React.Dispatch>; setDroppedItems: React.Dispatch>; + accountId: string; + transactionId: string; + transactionTemplateToggle: string; } -const DroppedItemsTagsCon: React.FC = ({ droppedItems, key, selectedTag, setSelectedTag }) => { +const DroppedItemsTagsCon: React.FC = ({ + droppedItems, + pageIdkey, + selectedTag, + selectedRecipients, + setSelectedTag, + setDroppedItems, + accountId, + transactionId, + transactionTemplateToggle +}) => { + + const positionRef = useRef<{ left: number; top: number }>({ left: 0, top: 0 }); const handleTagClick = (key: string, index: number) => { setSelectedTag({ key, index }) @@ -127,10 +149,13 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems, 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; + positionRef.current = { left: event.clientX - containerRect.left - offsetX, top: event.clientY - containerRect.top - offsetY }; + const conScrollWidth = targetContainer?.scrollWidth; const conScrollHeight = targetContainer?.scrollHeight; @@ -147,7 +172,7 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems, handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY); } - let timeOut = null + // let timeOut = null const handleMouseMove = (event: MouseEvent) => { // Get new X and Y positions relative to the container let left = event.clientX - containerRect.left - offsetX; @@ -157,15 +182,23 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems, left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width)); top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height)); + // Save in ref (fast, no re-render) + positionRef.current = { left, top }; + + // Update DOM directly + targetElement!.style.top = `${top}px`; + targetElement!.style.left = `${left}px`; + // Apply the movement to the element first - setDroppedItems(prevState => ({ - ...prevState, - [key]: Array.isArray(prevState[key]) - ? prevState[key].map((item, i) => - i === index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item - ) - : [], - })); + // setDroppedItems(prevState => ({ + // ...prevState, + // [key]: Array.isArray(prevState[key]) + // ? prevState[key].map((item, i) => + // i === index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item + // ) + // : [], + // })); + // Update group border position during drag if it's a group element if (groupElements != null && groupElements.length) { // Use a small timeout to ensure DOM is updated before recalculating borders @@ -198,6 +231,17 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems, }); } + const { left, top } = positionRef.current; + setDroppedItems(prevState => ({ + ...prevState, + [key]: Array.isArray(prevState[key]) + ? prevState[key].map((item, i) => + i === index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item + ) + : [], + })); + + document.removeEventListener("pointermove", handleMouseMove); document.removeEventListener("pointerup", handleMouseUp); }; @@ -208,10 +252,279 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems, }; // HANDLE TAG MOUSE DOWN - END + // HANDLE EXPAND TAG - START + const handleExpandTag = (event: React.MouseEvent, key: string, index: number) => { + event.preventDefault(); // Prevent text selection issues + + const expandingElemIcon = event.currentTarget; + const targetElement = expandingElemIcon.nextElementSibling?.parentElement; + const targetContainer = targetElement?.parentElement; + + if (!targetContainer || !targetElement) return; + + const elementRect = targetElement.getBoundingClientRect(); + + const startX = event.clientX; + const startY = event.clientY; + + const startWidth = elementRect.width; + const startHeight = elementRect.height; + + const handleMouseMove = (event: MouseEvent) => { + + let newWidth = startWidth + (event.clientX - startX); + let newHeight = startHeight + (event.clientY - startY); + + if (newHeight > 200 || newWidth > 400) { + if (newHeight > 200) { + newHeight = 200 + } + + if (newWidth > 400) { + newWidth = 400 + } + } + + // Apply the new width & height + setDroppedItems({ + ...droppedItems, + [key]: Array.isArray(droppedItems[key]) // Ensure it's an array + ? droppedItems[key].map((item, i) => + i === index ? { ...item, dimensions: { ...item.dimensions, width: newWidth, height: newHeight }, update: true } : item + ) + : [], // Provide a fallback in case it's undefined or not an array + }); + + }; + + const handleMouseUp = () => { + document.removeEventListener("pointermove", handleMouseMove); + document.removeEventListener("pointerup", handleMouseUp); + }; + + document.addEventListener("pointermove", handleMouseMove); + document.addEventListener("pointerup", handleMouseUp); + }; + // HANDLE EXPAND TAG - END + + const removeGroupBorders = (groupId: string) => { + console.log(`Removing borders for group: ${groupId}`); + + const borderPositions = ['top', 'right', 'bottom', 'left']; + + borderPositions.forEach(position => { + const borderElement = document.querySelector(`[border-group='${groupId}'][border-pos='${position}']`); + if (borderElement && borderElement.parentNode) { + borderElement.parentNode.removeChild(borderElement); + } + }); + }; + + const handleIGroupBorderPosition = (groupId: string) => { + const groupElements: NodeListOf = document.querySelectorAll(`div[group="${groupId}"]`); + + if (groupElements.length === 0) { + console.log("No group elements found for:", groupId); + return; + } + + let targetContainer = groupElements[0].parentElement?.parentElement; + + if (!targetContainer) { + console.log("No target container found"); + return; + } + + const containerRect = targetContainer.getBoundingClientRect(); + + // Use consistent offset values + const offsetX = 10; + const offsetY = 10; + + // Ensure DOM is fully updated before positioning + requestAnimationFrame(() => { + requestAnimationFrame(() => { + handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY); + }); + }); + }; + + const handleGroupBorderPosition = (groupElements: NodeListOf, containerRect: DOMRect, offsetX: number, offsetY: number) => { + if (!groupElements || groupElements.length === 0) return; + + const groupId = groupElements[0].getAttribute('group'); + if (!groupId) return; + + // Calculate the bounding box of all group elements + let minLeft = Infinity, minTop = Infinity, maxRight = -Infinity, maxBottom = -Infinity; + + groupElements.forEach(element => { + const parentElement = element.parentElement; + if (!parentElement) return; + + const rect = parentElement.getBoundingClientRect(); + const relativeLeft = rect.left - containerRect.left; + const relativeTop = rect.top - containerRect.top; + const relativeRight = relativeLeft + rect.width; + const relativeBottom = relativeTop + rect.height; + + minLeft = Math.min(minLeft, relativeLeft); + minTop = Math.min(minTop, relativeTop); + maxRight = Math.max(maxRight, relativeRight); + maxBottom = Math.max(maxBottom, relativeBottom); + }); + + // Add some padding around the group + const padding = 10; + minLeft -= padding; + minTop -= padding; + maxRight += padding; + maxBottom += padding; + + const width = maxRight - minLeft; + const height = maxBottom - minTop; + + // Update or create border elements + const borderPositions = ['top', 'right', 'bottom', 'left']; + + borderPositions.forEach(position => { + let borderElement = document.querySelector(`[border-group='${groupId}'][border-pos='${position}']`) as HTMLElement; + + if (!borderElement) { + // Create border element if it doesn't exist + borderElement = document.createElement('div'); + borderElement.className = `group-border ${position === 'bottom' ? 'bottom-border' : ''}`; + borderElement.setAttribute('border-group', groupId); + borderElement.setAttribute('border-pos', position); + + // Add the + icon for bottom border + if (position === 'bottom') { + const addIcon = document.createElement('div'); + addIcon.innerHTML = ''; // You might need to adjust this based on your icon system + addIcon.onclick = () => { + // Find the original item to pass to handleAddGroupItemClicked + const originalItem = droppedItems[key]?.find(item => 'group' in item && item.group === groupId); + if (originalItem) { + handleAddGroupItemClicked(key, originalItem); + } + }; + borderElement.appendChild(addIcon); + } + + // Append to container + groupElements[0].parentElement?.parentElement?.appendChild(borderElement); + } + + // Position the border element + borderElement.style.position = 'absolute'; + borderElement.style.pointerEvents = position === 'bottom' ? 'auto' : 'none'; + + switch (position) { + case 'top': + borderElement.style.left = `${minLeft}px`; + borderElement.style.top = `${minTop}px`; + borderElement.style.width = `${width}px`; + borderElement.style.height = '2px'; + break; + case 'right': + borderElement.style.left = `${maxRight - 2}px`; + borderElement.style.top = `${minTop}px`; + borderElement.style.width = '2px'; + borderElement.style.height = `${height}px`; + break; + case 'bottom': + borderElement.style.left = `${minLeft}px`; + borderElement.style.top = `${maxBottom - 2}px`; + borderElement.style.width = `${width}px`; + borderElement.style.height = '2px'; + break; + case 'left': + borderElement.style.left = `${minLeft}px`; + borderElement.style.top = `${minTop}px`; + borderElement.style.width = '2px'; + borderElement.style.height = `${height}px`; + break; + } + }); + }; + + const handleAddGroupItemClicked = (key: string, item: any) => { + const groupId = item.group; + + // Find all existing items in this group to determine the bottom-most position + const existingGroupItems = droppedItems[key]?.filter(existingItem => + 'group' in existingItem && existingItem.group === groupId + ) || []; + + const existingGroupItem = droppedItems[key]?.filter(existingItem => + 'group' in existingItem && existingItem.group === groupId + ) || []; + + existingGroupItem[0].options + + // Find the bottom-most item in the group + let bottomMostItem = item; + if (existingGroupItems.length > 0) { + bottomMostItem = existingGroupItems.reduce((lowest, current) => { + const currentBottom = current.dimensions.top + current.dimensions.height; + const lowestBottom = lowest.dimensions.top + lowest.dimensions.height; + return currentBottom > lowestBottom ? current : lowest; + }); + } + + // Calculate new position: place below the bottom-most item + const spacing = 10; // Adjust spacing as needed + const newTop = bottomMostItem.dimensions.top + bottomMostItem.dimensions.height + spacing; + + // Use the same left position as the parent (original) item + const newItem = { + ...item, + dimensions: { + ...item.dimensions, + left: item.dimensions.left, // Keep same horizontal position as parent + top: newTop // Position below the bottom-most item + }, + isGroupExist: true + }; + + // Update the dropped items + setDroppedItems({ + ...droppedItems, + [key]: [...droppedItems[key], newItem], + }); + + // Update group borders after adding the new item + // Use setTimeout to ensure state update is processed + setTimeout(() => { + handleIGroupBorderPosition(groupId); + }, 50); // Slightly longer timeout to ensure DOM updates + }; + + useEffect(() => { + // Initialize group borders for all existing groups after component updates + const timeoutId = setTimeout(() => { + const allGroupElements = document.querySelectorAll('[group]'); + const groupIds = new Set(); + + allGroupElements.forEach((element) => { + const groupId = element.getAttribute('group'); + if (groupId) { + groupIds.add(groupId); + } + }); + + groupIds.forEach((groupId) => { + handleIGroupBorderPosition(groupId); + }); + }, 0); + + return () => clearTimeout(timeoutId); + }, [droppedItems]); + return ( <> {/* DROPPED ITEMS - Render only the dropped items for this specific docId and page - STARTS */} - {droppedItems[key]?.map((item, index) => { + {droppedItems[pageIdkey]?.map((item, index) => { if (item.isDeleted) return; const type = item.type.toUpperCase(); @@ -222,8 +535,10 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems,
handleTagClick(key, index)} + onClick={() => handleTagClick(pageIdkey, index)} style={{ left: `${item.dimensions.left}px`, top: `${item.dimensions.top}px`, @@ -234,7 +549,7 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems, {/* If Selected Tag Recipient Info- START */} - {selectedTag?.key && selectedTag.key === key && selectedTag.index === index && + {selectedTag?.key && selectedTag.key === pageIdkey && selectedTag.index === index &&

= ({ droppedItems,

{item.label}

{ e.stopPropagation(); - handleRemoveTag(key, index) + handleRemoveTag(pageIdkey, index) }} className='cursor-pointer' />
} {/* If Selected Tag Recipient Info- END */} - - {/* Expand Circle - STARTS */} {item.type !== "radio" && item.type !== "checkbox" &&
handleExpandTag(e, key, index)} + onPointerDown={(e) => handleExpandTag(e, pageIdkey, index)} className='w-2.5 h-2.5 absolute bg-red-400 rounded-full bottom-[-8px] right-[-8px] cursor-nw-resize hover:bg-blue-400'>
} {/* Expand Circle - ENDS */} @@ -266,7 +579,7 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems, {
handleTagMouseDown(e, key, index)} + onPointerDown={(e) => handleTagMouseDown(e, pageIdkey, index)} className='p-2 rounded-sm h-full shadow opacity-40 cursor-grab w-full' style={{ background: selectedRecipient[0]?.color }} > @@ -290,7 +603,7 @@ const DroppedItemsTagsCon: React.FC = ({ droppedItems,
handleAddGroupItemClicked(key, item)}> + onClick={() => handleAddGroupItemClicked(pageIdkey, item)}>
diff --git a/src/components/process/processStepTwo/FieldList.tsx b/src/components/process/processStepTwo/FieldList.tsx index f944994..0802340 100644 --- a/src/components/process/processStepTwo/FieldList.tsx +++ b/src/components/process/processStepTwo/FieldList.tsx @@ -84,7 +84,7 @@ const FieldList: React.FC = ({ color, recipientId, recipientIndx const priorityOrder = ['SIGNATURE', 'INITIALS', 'STAMP']; const data = res.data.filter((obj, indx) => { - if (obj.type === "STAMP" || obj.type === "RADIO" || obj.type === "CHECKBOX" || obj.type === "ATTACHMENT") return false + // if (obj.type === "STAMP" || obj.type === "RADIO" || obj.type === "CHECKBOX" || obj.type === "ATTACHMENT") return false return true }); diff --git a/src/components/process/processStepTwo/NavTwo.tsx b/src/components/process/processStepTwo/NavTwo.tsx index 89c1a48..5be3af9 100644 --- a/src/components/process/processStepTwo/NavTwo.tsx +++ b/src/components/process/processStepTwo/NavTwo.tsx @@ -128,10 +128,10 @@ const NavTwo: React.FC = ({ transactionSaving, isFields, setIsField setTimeout(() => { if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) { if (isSignSend) { - // window.open(`./../../../sign/${selfUserSign.recipientId}`, "_self"); + window.open(`./../../../sign/${selfUserSign.recipientId}`, "_self"); } else { - // navigate(`/document-completion-screen/${accountId}/${transactionId}`, { replace: true }) + navigate(`/document-completion-screen/${accountId}/${transactionId}`, { replace: true }) } } else { navigate(`/templates`); diff --git a/src/components/process/processStepTwo/rightSideBar/RightSideBar.tsx b/src/components/process/processStepTwo/rightSideBar/RightSideBar.tsx index 23b2c42..6267aed 100644 --- a/src/components/process/processStepTwo/rightSideBar/RightSideBar.tsx +++ b/src/components/process/processStepTwo/rightSideBar/RightSideBar.tsx @@ -119,11 +119,6 @@ const RightSideBar: React.FC = ({ droppedItems, selectedTag, s }; }); } else { - // setDroppedItems((prev) => { - // const updated = [...prev]; - // updated[selTagIndx] = { ...updated[selTagIndx], [changeTo]: changeWith, update: true } - // return updated; - // }) setDroppedItems((prev) => { return (prev.map((item, idx) => { if (idx === selTagIndx) { @@ -297,18 +292,20 @@ const RightSideBar: React.FC = ({ droppedItems, selectedTag, s - {selectedTag.label && - } - - - { (selectedTag.type !== TagsTypes.SIGNATURE && selectedTag.type !== TagsTypes.INITIALS) && } - {selectedTag.type.toLowerCase() === TagsTypes.TEMPORAL && - } + { + selectedTag.type.toLowerCase() === TagsTypes.TEMPORAL && + + } + + {selectedTag.label && + } + + { selectedType && Object.keys(selectedType).length > 0 && @@ -361,7 +358,7 @@ const RightSideBar: React.FC = ({ droppedItems, selectedTag, s { selectedTag?.type.toLowerCase() === TagsTypes.NUMBER &&
- + handleDecimalPlace(e)} @@ -392,7 +389,7 @@ const RightSideBar: React.FC = ({ droppedItems, selectedTag, s onClick={() => setStyleOpen(!styleOpen)} > + className="block mb-2 font-medium">Styles
{ diff --git a/src/components/process/processStepTwo/rightSideBar/attributes/CommonAttrDropDown.tsx b/src/components/process/processStepTwo/rightSideBar/attributes/CommonAttrDropDown.tsx index 9f57ef4..0b757e3 100644 --- a/src/components/process/processStepTwo/rightSideBar/attributes/CommonAttrDropDown.tsx +++ b/src/components/process/processStepTwo/rightSideBar/attributes/CommonAttrDropDown.tsx @@ -74,7 +74,7 @@ const CommonAttrDropDown: React.FC = ({ selectedType, sel return (
- +
setDropDownToggle(dropDownToggle === DROPDOWNTOGGLE.TYPE ? null : DROPDOWNTOGGLE.TYPE)} className="flex justify-between items-center border-1 rounded px-2 py-1 border-slate-200"> diff --git a/src/components/process/processStepTwo/rightSideBar/attributes/CurrTimeStampAttr.tsx b/src/components/process/processStepTwo/rightSideBar/attributes/CurrTimeStampAttr.tsx index 15b3314..0a3a966 100644 --- a/src/components/process/processStepTwo/rightSideBar/attributes/CurrTimeStampAttr.tsx +++ b/src/components/process/processStepTwo/rightSideBar/attributes/CurrTimeStampAttr.tsx @@ -17,9 +17,9 @@ const CurrTimeStampAttr: React.FC = ({ type, handlePropert } return ( -
+