From cf7a516d386813ec75c0448aa4dc6d3da0b6b9c4 Mon Sep 17 00:00:00 2001 From: =bhagwanSingh <=bhagwan.r@hicglobalsolutions.com> Date: Wed, 8 Oct 2025 14:05:12 +0530 Subject: [PATCH] Sign Button Remove Asyn | Add Loader | Added Decline Reason in Transaction View | ... 08-10 --- .../processStepTwo/DocumentContainer.tsx | 1061 +++++++++-------- .../process/processStepTwo/NavTwo.tsx | 48 +- .../process/processStepTwo/ProcessStepTwo.tsx | 28 +- .../transactions/TransactionView.tsx | 3 + 4 files changed, 569 insertions(+), 571 deletions(-) diff --git a/src/components/process/processStepTwo/DocumentContainer.tsx b/src/components/process/processStepTwo/DocumentContainer.tsx index 124c0ca..2fb7745 100644 --- a/src/components/process/processStepTwo/DocumentContainer.tsx +++ b/src/components/process/processStepTwo/DocumentContainer.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useLayoutEffect, useRef, useState } from 'react' +import React, { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { RootState } from '../../../store/store' @@ -14,7 +14,7 @@ import { FaQuestionCircle } from 'react-icons/fa'; import typeToIconMap from '../../../helper/iconMap'; import Loader from '../../dashboard/Loader'; -import { hideLoader } from '../../../store/slices/loaderSlice'; +import { hideLoader, showLoader } from '../../../store/slices/loaderSlice'; import DroppedItemsTagsCon from './DroppedItemsTagsCon'; export interface DroppedItemsObj { @@ -49,526 +49,543 @@ let selectedDoc: { [key: string]: string | null } = { key: null } const fetchedTags: string[] = [] -const DocumentContainer: React.FC = ({ transactionSaving, setTransactionSaving, isSend, isPreview = false, aId = null, tId = null }) => { - let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; - const dispatch = useDispatch(); - const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>(); - let accountId = params.accountId; - let transactionId = params.transactionId - if (!transactionId) { - transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES; - transactionId = params.templateId; - dispatch(toggleToTemplates()); - } - if (!accountId || !transactionId) { - window.location.href = "/"; - return null; - } - - // If is preview is enable - if (isPreview && aId !== null && tId !== null) { - accountId = aId; - transactionId = tId; - } - - // const transactionId = useSelector((state: RootState) => state.transaction.transactionId) - const navigate = useNavigate(); - const childRef = useRef(null); - const tagUndoRef = useRef([]); - - const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients); - // const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments) - const selectedDocuments = useSelector((state: RootState) => state.documents.documents) - - const [droppedItems, setDroppedItems] = useState({}); - - interface HotKeysObj { - isActive: boolean; - key: string | null; - tagId?: string | null; - hotKey?: string | 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 })); - }, []) - - - const fetchTags = async () => { - try { - const requests = selectedRecipients.filter((recipient) => { - // Adding new rec doesn't have id till we click on IoMdClose or save so removing them and adding rec after click then fetching tags - if (!recipient.id) return false; - // also checking if have id but tags already fetched so to avoid same req - if (fetchedTags.includes(recipient.id)) return false; - fetchedTags.push(recipient.id) - return true - }).map((recipient) => api.get(`/api/v1/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipient.id}/tags`)); - - if (!requests.length) return - - const response = await Promise.all(requests); - let recTags = {} - for (const res of response) { - if (!res && !res.data) return - for (const tag of res.data) { - const key = `${tag.documentId}_${tag.pageIndex}`; - if (recTags[key]) { - recTags[key].push(tag); - } else { - recTags[key] = [tag]; // Initialize with an array - } - } - } - - setDroppedItems(recTags) - // Handle your response here - } catch (err) { - console.error(err); - } - }; - - // FETCHING TAG - START - useEffect(() => { - if (selectedRecipients.length === 0) return; - fetchTags(); - }, [selectedRecipients]); - // FETCHING TAG - END - - const [droppedItemsCounter, setDroppedItemsCounter] = useState(1); - const [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null); - - // NAVIGATING to Dashboard if Transaction is not created - START - useLayoutEffect(() => { - if (transactionId) return; - navigate("/"); - }, [transactionId]) - // NAVIGATING to Dashboard if Transaction is not created - END - - - const groupByRecipientId = (tagsArray: any[]) => { - const tagsData: Record = {}; - for (const tag of tagsArray) { - const recipientId = tag.recipientId; - - if (!tagsData[recipientId]) { - tagsData[recipientId] = []; - } - tagsData[recipientId].push(tag); - } - return tagsData; - }; - - const sendTagsToRecipients = async ( - tagsData: Record, - accountId: string, - transactionId: string - ) => { - // tagsData = tagsData.filter((tag, indx) => (("update" in tag && tag.update) || !("id" in tag)) ) - // Filter Tags that needs to update - const updatedData = {} - - for (const [recipientId, tags] of Object.entries(tagsData)) { - // console.log(recipientId, tags, "line 133") - const updateTags = [] - for (const tag of tags) { - if ("update" in tag && tag.update) { - updateTags.push(tag) - } - if (updateTags.length && "update" in updateTags[updateTags.length - 1]) { - delete updateTags[updateTags.length - 1].update - delete updateTags[updateTags.length - 1].icon - } - } - - updatedData[recipientId] = updateTags.filter((tag, indx) => !tag.isDeleted); - } - - const requests = Object.entries(updatedData) - .filter(([_, tags]) => tags.length > 0) // filter out empty ones - .map(([recipientId, tags]) => { - const updatedTagsWithNavOrder = tags.map((tag, indx) => ({ - ...tag, - navigationOrder: indx + 1, - })); - - return api.put( - `/api/v1/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipientId}/tags`, - updatedTagsWithNavOrder - ); - }); - - - try { - const responses = await Promise.all(requests); - - const droppedItemsUpdateFalse = Object.fromEntries( - Object.entries(droppedItems).map(([recipientId, tags]) => { - return [recipientId, tags.map(tag => ({ ...tag, update: false }))]; - }) - ) - - setDroppedItems(droppedItemsUpdateFalse); - } catch (error) { - console.error("Error submitting recipient tags:", error); - } - }; - - - const handleTransactionProcess = async (save: boolean = false) => { - if (Object.keys(droppedItems).length === 0) return; - if (!save && isSend === null) return; - - let updatedTags: any = []; - - for (const dropPagesId of Object.keys(droppedItems)) { - console.log("Dropped Item:", droppedItems[dropPagesId]); - updatedTags.push(...droppedItems[dropPagesId]); - } - - let tagsData = groupByRecipientId(updatedTags); - await sendTagsToRecipients(tagsData, accountId, transactionId); - // return - // SENDING Transaction - START - if (!save) { - const res = await api.put(`/api/v1/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, { status: "SENT" }) - dispatch(hideLoader()) - } - // SENDING Transaction - END - } - - // Save Tags - START - useEffect(() => { - handleTransactionProcess() - }, [isSend]) - // Save Tags - END - - useEffect(() => { - if (transactionSaving) { - setTimeout(() => { - setTransactionSaving(false) - }, 1000) - } - }, [transactionSaving]) - - const handleDragOver = (event: React.DragEvent) => { - event.preventDefault(); // Allows the drop event to work - }; - - // HANDLE DROP - START - const handleDrop = (event: React.DragEvent, docId: string, documentId: number, pageIndex: number) => { - 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(); - let x = event.clientX - dropZone.left - dragElemData.offsetX; - let y = event.clientY - dropZone.top - dragElemData.offsetY; - - // Handling Drop Over Right - if(dropZone.width < x + dragElemData.width) { - x -= (x + dragElemData.width) - dropZone.width; - } - // Handling Drop Over Bottom - if(dropZone.height < y + dragElemData.height) { - y -= (y + dragElemData.height) - dropZone.height; - } - // Handling Drop Over Left - if(x < 0) { - x = 0; - } - // Handling Drop Over Top - if(y < 0) { - y = 0; - } - - const key = `${docId}_${pageIndex}`; - // const IconComponent = typeToIconMap[field.type] || ; - - let prepData = { - ...data.properties, - icon: typeToIconMap[field.label.toUpperCase()] || , - type: dragElemData.label, - label: dragElemData.label, - // subType: (dragElemData.label in droppedTagsDefaultField) ? droppedTagsDefaultField[dragElemData.label] : '', - dimensions: { - left: x, - top: y, - width: dragElemData.width, - height: dragElemData.height, - }, - styles: dragElemData.label === "text" ? { - "font": "Great Vibes", - "italic": false, - "bold": false, - "underline": false, - "color": "black", - "textSize": 14.0 - } : null, - documentId: docId, - pageIndex: pageIndex, - recipientId: dragElemData.recipientId, - recipientIndx: dragElemData.recipientIndx, - required: (dragElemData.label !== TagsTypes.SIGNATURE && dragElemData.label !== TagsTypes.INITIALS) ? false : true, - update: true, - isDeleted: false - } - - if ("group" in dragElemData) { - prepData.group = dragElemData.group; - prepData.isGroupExist = false; - - } - - 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], - } - - setDroppedItems(dataToSet); - setDroppedItemsCounter((prev) => prev + 1); - - setTimeout(() => { - const tagIndex = key in droppedItems ? droppedItems[key].length : 0; - setSelectedTag({ key, index: tagIndex }) - }, 0) - }; - // HANDLE DROP - END - - useEffect(() => { - if (!hotKeysObj.isActive) return - - const docCon = document.getElementById("doc-con"); - - switch (hotKeysObj.hotKey) { - case "Ctrl+c": - const tempCopy = { ...droppedItems[selectedTag?.key][selectedTag?.index] } - delete tempCopy.id; - copiedTags = [{ ...tempCopy, dimensions: { ...tempCopy.dimensions, top: tempCopy.dimensions.top + 10, left: tempCopy.dimensions.left + 10 } }] - console.log("from Copy copiedTags", copiedTags) - break; - case "Ctrl+v": - console.log(selectedDoc.key, "keyss") - let updatedKey = droppedItems[selectedDoc.key] - updatedKey.push(...copiedTags) - - console.log("copiedTags", copiedTags) - console.log("updatedKey", updatedKey) - - setDroppedItems(prevState => ({ - ...prevState, - [selectedDoc.key]: updatedKey - })); - - // 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-sky-700") - tag.setAttribute("all-select-group", "true") - - docCon.addEventListener("click", () => { - tag.classList.remove("border-2", "border-sky-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": - const allselectedTags = docCon?.querySelectorAll("[all-select-group]") ?? [] - allselectedTags.forEach((tag) => { - const tagIndex = tag.getAttribute("data-tag-id") ?? ""; - const key = tag.getAttribute("data-drop-key") ?? ""; - - childRef.current.handleRemoveTag(key, Number(tagIndex)) - }) - - if (allselectedTags.length === 0) childRef.current.handleRemoveTag(selectedTag?.key, selectedTag?.index) - break; - - default: - break; - } - }, [hotKeysObj]) - - const handleDocumentFocus = (key: string) => { - // const id = key.replace("_", ""); - const targetCon = document.getElementById('doc-con'); - selectedDoc.key = key; - - 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: key, - tagId: null, - hotKey: hotKey - }); - - }); - - - // return () => targetCon?.removeEventListener("keydown"); - } - - - return ( - <> - {/* Center Doc Container - START */} -
- - { - selectedDocuments.length > 0 ? selectedDocuments.map((doc, indxDoc) => { - return ( -
-
-

{doc.name}

-
- -
- { - doc.pages && doc.pages.length > 0 && doc.pages.map((page, indx) => { - 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} - className='drop-page-in shadow-md relative' - > - - - - - - - {/* Side details of documents Pages and document Name - START */} -

- page {indx + 1} of {doc.pages && doc.pages.length} -

- {/*

- Document {indxDoc + 1} {doc.name} -

*/} - {/* Side details of documents Pages and document Name - END */} - - -
- ) - }) - } -
- - -
- ) - }) : - } - - -
- - {selectedTag && selectedTag !== null && "key" in selectedTag && "index" in selectedTag && setSelectedTag(null)} - /> - - } - {/* Center Doc Container - END */} - - ) +// 👇 Create an interface for the exposed methods +export interface DocumentContainerRef { + handleTransactionProcess: () => void; } + + +const DocumentContainer = forwardRef( + ({ transactionSaving, setTransactionSaving, isSend, isPreview = false, aId = null, tId = null }, ref) => { + let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; + const dispatch = useDispatch(); + const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>(); + let accountId = params.accountId; + let transactionId = params.transactionId + if (!transactionId) { + transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES; + transactionId = params.templateId; + dispatch(toggleToTemplates()); + } + if (!accountId || !transactionId) { + window.location.href = "/"; + return null; + } + + // If is preview is enable + if (isPreview && aId !== null && tId !== null) { + accountId = aId; + transactionId = tId; + } + + // const transactionId = useSelector((state: RootState) => state.transaction.transactionId) + const navigate = useNavigate(); + const childRef = useRef(null); + const tagUndoRef = useRef([]); + + const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients); + // const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments) + const selectedDocuments = useSelector((state: RootState) => state.documents.documents) + + const [droppedItems, setDroppedItems] = useState({}); + + interface HotKeysObj { + isActive: boolean; + key: string | null; + tagId?: string | null; + hotKey?: string | 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 })); + }, []) + + + const fetchTags = async () => { + try { + const requests = selectedRecipients.filter((recipient) => { + // Adding new rec doesn't have id till we click on IoMdClose or save so removing them and adding rec after click then fetching tags + if (!recipient.id) return false; + // also checking if have id but tags already fetched so to avoid same req + if (fetchedTags.includes(recipient.id)) return false; + fetchedTags.push(recipient.id) + return true + }).map((recipient) => api.get(`/api/v1/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipient.id}/tags`)); + + if (!requests.length) return + + const response = await Promise.all(requests); + let recTags = {} + for (const res of response) { + if (!res && !res.data) return + for (const tag of res.data) { + const key = `${tag.documentId}_${tag.pageIndex}`; + if (recTags[key]) { + recTags[key].push(tag); + } else { + recTags[key] = [tag]; // Initialize with an array + } + } + } + + setDroppedItems(recTags) + // Handle your response here + } catch (err) { + console.error(err); + } + }; + + // FETCHING TAG - START + useEffect(() => { + if (selectedRecipients.length === 0) return; + fetchTags(); + }, [selectedRecipients, isPreview]); + // FETCHING TAG - END + + const [droppedItemsCounter, setDroppedItemsCounter] = useState(1); + const [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null); + + // NAVIGATING to Dashboard if Transaction is not created - START + useLayoutEffect(() => { + if (transactionId) return; + navigate("/"); + }, [transactionId]) + // NAVIGATING to Dashboard if Transaction is not created - END + + + const groupByRecipientId = (tagsArray: any[]) => { + const tagsData: Record = {}; + for (const tag of tagsArray) { + const recipientId = tag.recipientId; + + if (!tagsData[recipientId]) { + tagsData[recipientId] = []; + } + tagsData[recipientId].push(tag); + } + return tagsData; + }; + + const sendTagsToRecipients = async ( + tagsData: Record, + accountId: string, + transactionId: string + ) => { + // tagsData = tagsData.filter((tag, indx) => (("update" in tag && tag.update) || !("id" in tag)) ) + // Filter Tags that needs to update + const updatedData = {} + + for (const [recipientId, tags] of Object.entries(tagsData)) { + // console.log(recipientId, tags, "line 133") + const updateTags = [] + for (const tag of tags) { + if ("update" in tag && tag.update) { + updateTags.push(tag) + } + if (updateTags.length && "update" in updateTags[updateTags.length - 1]) { + delete updateTags[updateTags.length - 1].update + delete updateTags[updateTags.length - 1].icon + } + } + + updatedData[recipientId] = updateTags.filter((tag, indx) => !tag.isDeleted); + } + + const requests = Object.entries(updatedData) + .filter(([_, tags]) => tags.length > 0) // filter out empty ones + .map(([recipientId, tags]) => { + const updatedTagsWithNavOrder = tags.map((tag, indx) => ({ + ...tag, + navigationOrder: indx + 1, + })); + + return api.put( + `/api/v1/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}/recipients/${recipientId}/tags`, + updatedTagsWithNavOrder + ); + }); + + + try { + const responses = await Promise.all(requests); + + const droppedItemsUpdateFalse = Object.fromEntries( + Object.entries(droppedItems).map(([recipientId, tags]) => { + return [recipientId, tags.map(tag => ({ ...tag, update: false }))]; + }) + ) + + setDroppedItems(droppedItemsUpdateFalse); + } catch (error) { + console.error("Error submitting recipient tags:", error); + } + }; + + + const handleTransactionProcess = async (save: boolean = false) => { + dispatch(showLoader("Saving Tags ...")) + if (Object.keys(droppedItems).length === 0) return; + // if (!save) return; + + let updatedTags: any = []; + + for (const dropPagesId of Object.keys(droppedItems)) { + console.log("Dropped Item:", droppedItems[dropPagesId]); + updatedTags.push(...droppedItems[dropPagesId]); + } + + let tagsData = groupByRecipientId(updatedTags); + await sendTagsToRecipients(tagsData, accountId, transactionId); + // return + // SENDING Transaction - START + if (!save) { + dispatch(showLoader("Sending ...")) + const res = await api.put(`/api/v1/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`, { status: "SENT" }) + if (res.status >= 200 && res.status < 300) { + // navigate(`/transaction-sent/${accountId}/${transactionId}`, { replace: true }) + window.location.href = `${import.meta.env.VITE_BASE_URI}/transaction-sent/${accountId}/${transactionId}` + } + dispatch(hideLoader()) + } + // SENDING Transaction - END + } + + // Save Tags - START + // useEffect(() => { + // handleTransactionProcess() + // }, [isSend]) + useImperativeHandle(ref, () => ({ + handleTransactionProcess, + })); + // Save Tags - END + + useEffect(() => { + if (transactionSaving) { + setTimeout(() => { + setTransactionSaving(false) + }, 1000) + } + }, [transactionSaving]) + + const handleDragOver = (event: React.DragEvent) => { + event.preventDefault(); // Allows the drop event to work + }; + + // HANDLE DROP - START + const handleDrop = (event: React.DragEvent, docId: string, documentId: number, pageIndex: number) => { + 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(); + let x = event.clientX - dropZone.left - dragElemData.offsetX; + let y = event.clientY - dropZone.top - dragElemData.offsetY; + + // Handling Drop Over Right + if (dropZone.width < x + dragElemData.width) { + x -= (x + dragElemData.width) - dropZone.width; + } + // Handling Drop Over Bottom + if (dropZone.height < y + dragElemData.height) { + y -= (y + dragElemData.height) - dropZone.height; + } + // Handling Drop Over Left + if (x < 0) { + x = 0; + } + // Handling Drop Over Top + if (y < 0) { + y = 0; + } + + const key = `${docId}_${pageIndex}`; + // const IconComponent = typeToIconMap[field.type] || ; + + let prepData = { + ...data.properties, + icon: typeToIconMap[field.label.toUpperCase()] || , + type: dragElemData.label, + label: dragElemData.label, + // subType: (dragElemData.label in droppedTagsDefaultField) ? droppedTagsDefaultField[dragElemData.label] : '', + dimensions: { + left: x, + top: y, + width: dragElemData.width, + height: dragElemData.height, + }, + styles: dragElemData.label === "text" ? { + "font": "Great Vibes", + "italic": false, + "bold": false, + "underline": false, + "color": "black", + "textSize": 14.0 + } : null, + documentId: docId, + pageIndex: pageIndex, + recipientId: dragElemData.recipientId, + recipientIndx: dragElemData.recipientIndx, + required: (dragElemData.label !== TagsTypes.SIGNATURE && dragElemData.label !== TagsTypes.INITIALS) ? false : true, + update: true, + isDeleted: false + } + + if ("group" in dragElemData) { + prepData.group = dragElemData.group; + prepData.isGroupExist = false; + + } + + 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], + } + + setDroppedItems(dataToSet); + setDroppedItemsCounter((prev) => prev + 1); + + setTimeout(() => { + const tagIndex = key in droppedItems ? droppedItems[key].length : 0; + setSelectedTag({ key, index: tagIndex }) + }, 0) + }; + // HANDLE DROP - END + + useEffect(() => { + if (!hotKeysObj.isActive) return + + const docCon = document.getElementById("doc-con"); + + switch (hotKeysObj.hotKey) { + case "Ctrl+c": + const tempCopy = { ...droppedItems[selectedTag?.key][selectedTag?.index] } + delete tempCopy.id; + copiedTags = [{ ...tempCopy, dimensions: { ...tempCopy.dimensions, top: tempCopy.dimensions.top + 10, left: tempCopy.dimensions.left + 10 } }] + console.log("from Copy copiedTags", copiedTags) + break; + case "Ctrl+v": + console.log(selectedDoc.key, "keyss") + let updatedKey = droppedItems[selectedDoc.key] + updatedKey.push(...copiedTags) + + console.log("copiedTags", copiedTags) + console.log("updatedKey", updatedKey) + + setDroppedItems(prevState => ({ + ...prevState, + [selectedDoc.key]: updatedKey + })); + + // 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-sky-700") + tag.setAttribute("all-select-group", "true") + + docCon.addEventListener("click", () => { + tag.classList.remove("border-2", "border-sky-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": + const allselectedTags = docCon?.querySelectorAll("[all-select-group]") ?? [] + allselectedTags.forEach((tag) => { + const tagIndex = tag.getAttribute("data-tag-id") ?? ""; + const key = tag.getAttribute("data-drop-key") ?? ""; + + childRef.current.handleRemoveTag(key, Number(tagIndex)) + }) + + if (allselectedTags.length === 0) childRef.current.handleRemoveTag(selectedTag?.key, selectedTag?.index) + break; + + default: + break; + } + }, [hotKeysObj]) + + const handleDocumentFocus = (key: string) => { + // const id = key.replace("_", ""); + const targetCon = document.getElementById('doc-con'); + selectedDoc.key = key; + + 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: key, + tagId: null, + hotKey: hotKey + }); + + }); + + + // return () => targetCon?.removeEventListener("keydown"); + } + + + return ( + <> + {/* Center Doc Container - START */} +
+ + { + selectedDocuments.length > 0 ? selectedDocuments.map((doc, indxDoc) => { + return ( +
+
+

{doc.name}

+
+ +
+ { + doc.pages && doc.pages.length > 0 && doc.pages.map((page, indx) => { + 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} + className='drop-page-in shadow-md relative' + > + + + + + + + {/* Side details of documents Pages and document Name - START */} +

+ page {indx + 1} of {doc.pages && doc.pages.length} +

+ {/*

+ Document {indxDoc + 1} {doc.name} +

*/} + {/* Side details of documents Pages and document Name - END */} + + +
+ ) + }) + } +
+ + +
+ ) + }) : + } + + +
+ + {selectedTag && selectedTag !== null && "key" in selectedTag && "index" in selectedTag && setSelectedTag(null)} + /> + + } + {/* Center Doc Container - END */} + + ) + }) + export default DocumentContainer; diff --git a/src/components/process/processStepTwo/NavTwo.tsx b/src/components/process/processStepTwo/NavTwo.tsx index a5f1881..0bb2bd7 100644 --- a/src/components/process/processStepTwo/NavTwo.tsx +++ b/src/components/process/processStepTwo/NavTwo.tsx @@ -28,6 +28,7 @@ interface NavTwoProps { isPreview?: boolean aId?: string | null tId?: string | null + onSendClick?: () => void; } @@ -65,7 +66,8 @@ const NavTwo: React.FC = ({ setIsSend, isPreview = false, aId = null, - tId = null + tId = null, + onSendClick, }) => { let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; const dispatch = useDispatch(); @@ -175,55 +177,25 @@ const NavTwo: React.FC = ({ return } - if (isSend === null) { - setIsSend(false); - } - setIsSend(!isSend); + // if (isSend === null) { + // setIsSend(false); + // } + // setIsSend(!isSend); - setTimeout(() => { + // setTimeout(() => { if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) { if (isSignSend) { window.open(`./../../../sign/${selfUserSign.recipientId}`, "_self"); } else { - navigate(`/transaction-sent/${accountId}/${transactionId}`, { replace: true }) + onSendClick(); } } else { navigate(`/templates`); - // toast.success("Template Saved...!", { - // position: "top-center", - // autoClose: 1500, - // hideProgressBar: false, - // closeOnClick: false, - // pauseOnHover: true, - // draggable: true, - // progress: undefined, - // theme: "light", - // transition: Bounce, - // }); } - }, 1000) + // }, 1000) } - - // const handleSetIsFields = (setTo: boolean) => { - // if (setTo !== undefined && setTo !== isFields) { - // setIsFields(setTo) - // if(setTo === true) { - // setIsPages(false) - // } - // }; - // } - - // const handleSetIsPages = (setTo: boolean) => { - // if (setTo !== undefined && setTo !== isPages) { - // setIsPages(setTo) - // if(setTo === true) { - // setIsFields(false) - // } - // }; - // } - const zoom = (percentage: number, contentClass: string) => { const content = document.querySelector(`.${contentClass}`); if (content) { diff --git a/src/components/process/processStepTwo/ProcessStepTwo.tsx b/src/components/process/processStepTwo/ProcessStepTwo.tsx index 84a1eda..61679d8 100644 --- a/src/components/process/processStepTwo/ProcessStepTwo.tsx +++ b/src/components/process/processStepTwo/ProcessStepTwo.tsx @@ -1,8 +1,8 @@ -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import NavTwo from './NavTwo'; import LeftSideBar from './LeftSideBar'; -import DocumentContainer from './DocumentContainer'; +import DocumentContainer, { DocumentContainerRef } from './DocumentContainer'; import { useDispatch, useSelector } from 'react-redux'; import { RootState } from '../../../store/store'; @@ -48,21 +48,27 @@ const ProcessStepTwo = () => { } const handleSetIsPages = (setTo: boolean) => { - if (setTo !== undefined && setTo !== isPages) { - setIsPages(setTo) - if(setTo === true) { - setIsFields(false) - } - }; - } + if (setTo !== undefined && setTo !== isPages) { + setIsPages(setTo) + if (setTo === true) { + setIsFields(false) + } + }; + } + + const docRef = useRef(null); + + const handleSendClick = () => { + docRef.current?.handleTransactionProcess(); + }; return (
- +
- +
{globalLoader.isLoading && } diff --git a/src/components/transactions/TransactionView.tsx b/src/components/transactions/TransactionView.tsx index 3391886..404af8e 100644 --- a/src/components/transactions/TransactionView.tsx +++ b/src/components/transactions/TransactionView.tsx @@ -205,6 +205,9 @@ const TransactionView = () => { {"message" in recipient &&

{recipient.message}

} + {"declineReason" in recipient &&

+ {recipient.declineReason} +

}