diff --git a/src/components/client/DocumentPages.tsx b/src/components/client/DocumentPages.tsx new file mode 100644 index 0000000..7b97f2f --- /dev/null +++ b/src/components/client/DocumentPages.tsx @@ -0,0 +1,63 @@ +import React from 'react' +import { MdKeyboardArrowDown } from 'react-icons/md'; + +interface DocumentPagesInterface { + selectedDocuments: DocumentType[]; + setDropDownTogglePage: React.Dispatch>; + dropDownTogglePage: string; +} + +const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownTogglePage }: DocumentPagesInterface) => { + return ( +
+ +
+ {selectedDocuments && selectedDocuments.length && selectedDocuments.map((document, indx) => { + return ( +
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 && + } + +
+

{document.name}

+ +
+
+ + + { + dropDownTogglePage === 'pageDropDown' + indx && +
+ {document.images != undefined && document.images.map((img, indx) => ( + +

{indx + 1}

+
+ ))} +
+ } + +
+ ) + })} +
+
+ ) +} + +export default DocumentPages \ No newline at end of file diff --git a/src/components/client/SignDocument.tsx b/src/components/client/SignDocument.tsx index 8edc3c1..5710db6 100644 --- a/src/components/client/SignDocument.tsx +++ b/src/components/client/SignDocument.tsx @@ -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([]); const [transaction, setTransaction] = useState(null); const [recSignStatus, setRecSignStatus] = useState(null); + const [droppedItems, setDroppedItems] = useState([]); const [stamp, setStamp] = useState({ open: false, blob: "" }); + console.log(recipient, "recipient Line 145") + // Handling Date Opacity const [dateValues, setDateValues] = useState<{ [key: string]: string }>({}); const [isDateOpen, setIsDateOpen] = useState(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) => { 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, 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] || ; + + let prepData = { + properties: data.properties, + icon: typeToIconMap[field.label.toUpperCase()] || , + type: dragElemData.label.toUpperCase(), + subType: (dragElemData.label in droppedTagsDefaultField) ? droppedTagsDefaultField[dragElemData.label] : '', + label: dragElemData.label, + dimensions: { + left: x, + top: y, + width: dragElemData.width, + height: dragElemData.height, + }, + styles: dragElemData.label === "text" ? { + "font": "Great Vibes", + "italic": false, + "bold": false, + "underline": false, + "color": "black", + "textSize": 14.0 + } : null, + documentId: docId, + pageIndex: pageIndex, + recipientId: dragElemData.recipientId, + recipientIndx: dragElemData.recipientIndx, + update: true, + isDeleted: false + } + + 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) => { + event.preventDefault(); // Allows the drop event to work + }; + + // HANDLE TAG MOUSE DOWN - START + const handleTagMouseDown = (event: React.MouseEvent, 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 | 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(true); return (
@@ -642,9 +844,6 @@ const SignDocument = () => {

{recipient && recipient.firstName} {recipient && recipient.lastName}

- - -
{/* Right Side - ENDS */} @@ -655,65 +854,23 @@ const SignDocument = () => { {/* Left Side pages start */} {/*
*/} +
- -
- {selectedDocuments && selectedDocuments.length && selectedDocuments.map((document, indx) => { - return ( -
setDropDownTogglePage('')} - > -
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 && - } - -
-

{document.name}

- -
-
- - - { - dropDownTogglePage === 'pageDropDown' + indx && -
- {document.images != undefined && document.images.map((img, indx) => ( - -

{indx + 1}

-
- ))} -
- } - -
- ) - })} -
+ {recipientId && }
+ + {/* Pages - END */} {/* Main container */}
{ selectedDocuments && selectedDocuments.length > 0 && selectedDocuments.map((doc) => { - console.log(tagJson, "tagJsontagJsontagJson") + console.log(droppedItems, "tagJsontagJsontagJson") return (
@@ -722,16 +879,20 @@ const SignDocument = () => {
{ - 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 (
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 (
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 = () => {
+ + + + {/* Right Side - START */} {/*
{ diff --git a/src/components/home/StartSection.tsx b/src/components/home/StartSection.tsx index 70b989b..ecd9269 100644 --- a/src/components/home/StartSection.tsx +++ b/src/components/home/StartSection.tsx @@ -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)); diff --git a/src/components/process/processStepTwo/DocumentContainer.tsx b/src/components/process/processStepTwo/DocumentContainer.tsx index 2264c29..57ccd1c 100644 --- a/src/components/process/processStepTwo/DocumentContainer.tsx +++ b/src/components/process/processStepTwo/DocumentContainer.tsx @@ -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 = ({ isSend }) => { // const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments) const selectedDocuments = useSelector((state: RootState) => state.documents.documents) + // const [uniqGroupIds, setUniqGroupIds] = useState([]); + useEffect(() => { selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId })); @@ -134,7 +137,7 @@ const DocumentContainer: React.FC = ({ 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 = ({ 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 = ({ isSend }) => { } // } - console.log(dropTag, "line 204") + // console.log(dropTag, "line 204") const isDeleted = "isDeleted" in dropTag ? dropTag.isDeleted : false @@ -279,7 +282,7 @@ const DocumentContainer: React.FC = ({ isSend }) => { sendTagsToRecipients(tagsData, accountId, transactionId); (async () => { - const res = await api.put(`/api/accounts/${accountId}/transactions/${transactionId}`, {status: "SENT"}) + const res = await api.put(`/api/accounts/${accountId}/transactions/${transactionId}`, { status: "SENT" }) console.log(res, "Check123") })() }, [isSend]) @@ -340,6 +343,18 @@ const DocumentContainer: React.FC = ({ 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 = ({ 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 = ({ 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 = ({ 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 | 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 = ({ 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, key: string, index: number) => { @@ -675,6 +733,23 @@ const DocumentContainer: React.FC = ({ isSend }) => { }; + + + function handleIGroupBorderPosition(groupId: string) { + + const groupElements: NodeListOf = 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,59 +780,60 @@ const DocumentContainer: React.FC = ({ 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] || ; const selectedRecipient = selectedRecipients.filter((obj, indx) => obj.id === item.recipientId); return ( -
handleTagClick(key, index)} - style={{ - left: `${item.dimensions.left}px`, - top: `${item.dimensions.top}px`, - width: item.type === "radio" ? `${item.dimensions.width + 20}px` : `${item.dimensions.width}px`, - height: item.type === "radio" ? `max-content` : `${item.dimensions.height}px` - }} - > + <> +
handleTagClick(key, index)} + style={{ + left: `${item.dimensions.left}px`, + top: `${item.dimensions.top}px`, + width: item.type === "radio" ? `${item.dimensions.width + 20}px` : `${item.dimensions.width}px`, + height: item.type === "radio" ? `max-content` : `${item.dimensions.height}px` + }} + > - {/* If Selected Tag Recipient Info- START */} - {selectedTag?.key && selectedTag.key === key && selectedTag.index === index && -
-
-

-

{IconComponent} -

-

{item.label}

- { - e.stopPropagation(); - handleRemoveTag(key, index) - }} className='cursor-pointer' /> -
+ {/* If Selected Tag Recipient Info- START */} + {selectedTag?.key && selectedTag.key === key && selectedTag.index === index && +
+
+

+

{IconComponent} +

+

{item.label}

+ { + e.stopPropagation(); + handleRemoveTag(key, index) + }} className='cursor-pointer' /> +
+
} + {/* If Selected Tag Recipient Info- END */} + + + + + {/* Expand Circle - STARTS */} + {item.type !== "radio" &&
handleExpandTag(e, key, 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'>
} - {/* If Selected Tag Recipient Info- END */} + {/* Expand Circle - ENDS */} - - - - {/* Expand Circle - STARTS */} - {item.type !== "radio" &&
handleExpandTag(e, key, 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 */} - - - { - item.type !== "radio" ? + {/* Tag - START */} + {
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 }} > @@ -768,39 +844,27 @@ const DocumentContainer: React.FC = ({ isSend }) => { {IconComponent}

- : - <> -
- {/* Radio - Container - START */} -
+ } + {/* Tag - END */} - {/* Radio- Item - START */} -
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 }} - > -

- {IconComponent} -

-
- {/* Radio- Item - END */} +
-
- {/* Radio - Container - END */} + { + "group" in item && item.group && "isGroupExist" in item && !item.isGroupExist && + <> - handleAddRadioClick(key)} className='ml-[50%] translate-x-[-50%] cursor-pointer' /> -
- - +
+
+
+
handleAddGroupItemClicked(key, item)}>
+
+
+ } -
+ + ) } @@ -841,14 +905,15 @@ const DocumentContainer: React.FC = ({ isSend }) => { } -
+
{selectedTag && } + onMove: directionalControlsHandler, + disabled: false, + stepSize: 5, + className: '' + }} /> + } {/* Center Doc Container - END */} ) diff --git a/src/components/process/processStepTwo/FieldList.tsx b/src/components/process/processStepTwo/FieldList.tsx index 35862cd..b5a01bf 100644 --- a/src/components/process/processStepTwo/FieldList.tsx +++ b/src/components/process/processStepTwo/FieldList.tsx @@ -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, label: string, recipientId: number, recipientIndx: number, tagsProperties: TagsProperties) => { const dragElemRect = event.currentTarget.getBoundingClientRect(); @@ -31,6 +33,8 @@ const handlePointerDown = (event: React.DragEvent, 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 = ({ color, recipientId, recipientIndx }) => { - +const FieldList: React.FC = ({ color, recipientId, recipientIndx, acntId = null }) => { + const [tagsData, setTagsData] = useState([]); - 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']; diff --git a/src/components/process/processStepTwo/rightSideBar/RightSideBar.tsx b/src/components/process/processStepTwo/rightSideBar/RightSideBar.tsx index 5747f6e..211f67f 100644 --- a/src/components/process/processStepTwo/rightSideBar/RightSideBar.tsx +++ b/src/components/process/processStepTwo/rightSideBar/RightSideBar.tsx @@ -107,7 +107,7 @@ const RightSideBar: React.FC = ({ 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) diff --git a/src/components/signature/SignCon.tsx b/src/components/signature/SignCon.tsx index dde2de7..1529b19 100644 --- a/src/components/signature/SignCon.tsx +++ b/src/components/signature/SignCon.tsx @@ -21,11 +21,13 @@ import React, { useEffect, useState } from 'react'; // import api from '../../config/api'; import DrawSignatureCanvas from '../setting/Signature/DrawSignatureCanvas'; import { AiOutlineUpload, AiOutlineEdit } from 'react-icons/ai'; -import { MdCloudUpload } from 'react-icons/md'; +import { MdCloudUpload } from 'react-icons/md'; 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; - editingSignature?: Signature | null; + // setShowAddSignature?: (value: boolean) => void; + // getAllSignatures: () => Promise; + // editingSignature?: Signature | null; + + recipientId?: string; + setOpenSign: React.Dispatch>; + saved: SaveType; + setSaved: React.Dispatch>; } interface Account { @@ -70,11 +77,12 @@ interface SelectStyle { } const AddSignatureModal: React.FC = ({ - 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(false); @@ -105,61 +113,76 @@ const AddSignatureModal: React.FC = ({ const [preview2, setPreview2] = useState(null); // User data - const [user, setUser] = useState({ - id: "", - firstName: "", - lastName: "", - email: "", - language: "", - account: { - id: 0, - isDefault: false, - name: "", - }, - }); + // const [user, setUser] = useState({ + // 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: }, + { id: 'TYPED', label: 'TYPED', icon: }, { id: 'DRAW', label: 'DRAW', icon: }, { id: 'UPLOAD', label: 'UPLOAD', icon: }, + { id: 'SAVED', label: 'SAVED', icon: }, ]; // 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 = ({ const handleSubmit = async () => { setIsSubmitting(true); - +console.log("handleSubmit Called") if (!signatures.fullSignature || !signatures.initials) { alert('Please provide both signature and initials'); setIsSubmitting(false); @@ -220,53 +243,40 @@ const AddSignatureModal: React.FC = ({ }; 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, + // 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 axios.post(`${import.meta.env.VITE_BASE_URI}/api/user/signatures`, + payload, + { + headers: { + "ngrok-skip-browser-warning": "69420" + } }); - } else { - // Create new signature - res = await api.post(`/api/user/signatures`, payload); - console.log("handleSubmit", res); + console.log("handleSubmit", res); - toast.success("Signature Created Successfully", { - position: "top-center", - autoClose: 500, - hideProgressBar: false, - closeOnClick: false, - pauseOnHover: true, - draggable: true, - progress: undefined, - theme: "light", - transition: Bounce, - }); - } - - if (res?.data) { - await getAllSignatures(); - setShowAddSignature?.(false); - } - } catch (error) { - console.error("Error submitting signature:", error); - toast.error(isEditMode ? "Failed to update signature" : "Failed to create signature", { + toast.success("Signature Created Successfully", { position: "top-center", - autoClose: 2000, + autoClose: 500, hideProgressBar: false, closeOnClick: false, pauseOnHover: true, @@ -275,6 +285,25 @@ const AddSignatureModal: React.FC = ({ theme: "light", transition: Bounce, }); + // } + + if (res?.data) { + await getAllSignatures(); + setShowAddSignature?.(false); + } + } 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, + // }); } finally { setIsSubmitting(false); } @@ -282,7 +311,7 @@ const AddSignatureModal: React.FC = ({ const renderContent = () => { switch (activeTab) { - case 'SELECT': + case 'TYPED': return (
@@ -340,7 +369,7 @@ const AddSignatureModal: React.FC = ({ return ( ); @@ -408,26 +437,26 @@ const AddSignatureModal: React.FC = ({ } }; - 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 (
@@ -482,7 +511,7 @@ const AddSignatureModal: React.FC = ({ {navigationItems.map((item) => (
diff --git a/src/index.css b/src/index.css index a75bff8..71fe6fe 100644 --- a/src/index.css +++ b/src/index.css @@ -11,11 +11,11 @@ --menuTextCol: #98a0a8; --menuTextColActive: #41644A; --menuBgColActive: #edeaeb; - --menuTextHoverCol: #e7e7e7; - + --menuTextHoverCol: #e7e7e7; + --greyTextCol: #98a0a8; --lightGreyCol: #e1e1e1; - --compBgCol: #ffffff; + --compBgCol: #ffffff; --avatarBgCol: #41644A; --avatarTextCol: #ffffff; @@ -30,7 +30,7 @@ --sendForSignTextCol: #ffffff; --sendForSignBgCol: #d02f56; - + --sendForSignDropDownText: #000000; --sendForSignDropDownBg: #ffffff; --sendForSignDropDownHover: #e7e7e7; @@ -44,8 +44,8 @@ --processStepOneTextHead: #000000; - --modalSubmitBG:#1919d7; - --modalSubmitBGHover:#395ee1; + --modalSubmitBG: #1919d7; + --modalSubmitBGHover: #395ee1; } @@ -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 */ - } + + /* 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 */ + } +} +/* 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; +} - /* Loader CSS */ - \ No newline at end of file +.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 */ \ No newline at end of file diff --git a/src/utlity/tagGroupConUtils.ts b/src/utlity/tagGroupConUtils.ts new file mode 100644 index 0000000..c47ef85 --- /dev/null +++ b/src/utlity/tagGroupConUtils.ts @@ -0,0 +1,91 @@ +interface Dimension { + maxTop: number | null; + minTop: number | null; + maxLeft: number | null; + minLeft: number | null; +} + +const getGroupDimension = ( + group: NodeListOf, + 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, 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 = document.querySelectorAll(`div[group="${groupId}"]`); +// handleGroupBorderPosition(groupElements, containerRect); +// } +// } + +const handleInitialGroupBorderPosition = (groupElements: NodeListOf, containerRect: DOMRect, offsetX: number, offsetY: number) => { + setTimeout(() => { + handleGroupBorderPosition(groupElements, containerRect, offsetX, offsetY) + }, 0) +} + +export { + getGroupDimension, + handleGroupBorderPosition, + handleInitialGroupBorderPosition + // handleTargetBorderEventListener +} \ No newline at end of file