From f6e0b3b8afbdb4a99fb64c7d5b77332811536243 Mon Sep 17 00:00:00 2001 From: Bhagwan Singh Date: Fri, 22 May 2026 18:22:41 +0530 Subject: [PATCH] In App Navigation --- src/components/home/StartNow.tsx | 5 +- .../processStepTwo/DocumentContainer.tsx | 110 +++++++-- .../processStepTwo/DroppedItemsTagsCon.tsx | 32 ++- .../process/processStepTwo/NavTwo.tsx | 21 +- src/store/slices/documentTagsSlice.ts | 218 ++++++++++++++++++ src/store/slices/localTagSlice.ts | 9 - src/store/store.ts | 2 + 7 files changed, 346 insertions(+), 51 deletions(-) create mode 100644 src/store/slices/documentTagsSlice.ts diff --git a/src/components/home/StartNow.tsx b/src/components/home/StartNow.tsx index 04ee77f..b1eb641 100644 --- a/src/components/home/StartNow.tsx +++ b/src/components/home/StartNow.tsx @@ -225,12 +225,11 @@ const StartNow = () => { diff --git a/src/components/process/processStepTwo/DocumentContainer.tsx b/src/components/process/processStepTwo/DocumentContainer.tsx index e0d9d09..9ea150c 100644 --- a/src/components/process/processStepTwo/DocumentContainer.tsx +++ b/src/components/process/processStepTwo/DocumentContainer.tsx @@ -17,6 +17,12 @@ import Loader from '../../dashboard/Loader'; import { hideLoader, showLoader } from '../../../store/slices/loaderSlice'; import DroppedItemsTagsCon from './DroppedItemsTagsCon'; import DocumentPages from './documentPages/DocumentPages'; +import { + initializeDroppedItems, + setDroppedItems, + clearTransactionTags, + incrementCounter, +} from '../../../store/slices/documentTagsSlice'; export interface DroppedItemsObj { [key: string]: DroppedItem[] @@ -87,12 +93,16 @@ const DocumentContainer = forwardRef([]); const imageRefs = useRef<{ [key: string]: HTMLImageElement | null }>({}); + const [imageSizes, setImageSizes] = useState>({}); 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({}); + // Redux state for dropped items + const transactionKey = `${accountId}_${transactionId}`; + const droppedItems = useSelector((state: RootState) => state.documentTags.items[transactionKey] || {}); + const droppedItemsCounter = useSelector((state: RootState) => state.documentTags.counter[transactionKey] || 0); interface HotKeysObj { isActive: boolean; @@ -143,7 +153,11 @@ const DocumentContainer = forwardRef(1); const [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null); // NAVIGATING to Dashboard if Transaction is not created - START @@ -197,13 +210,17 @@ const DocumentContainer = forwardRef !tag.isDeleted); + updatedData[recipientId] = updateTags.filter((tag) => !tag.isDeleted); } const requests = Object.entries(updatedData) @@ -224,13 +241,18 @@ const DocumentContainer = forwardRef { return [recipientId, tags.map(tag => ({ ...tag, update: false }))]; }) ) - setDroppedItems(droppedItemsUpdateFalse); + dispatch(setDroppedItems({ + transactionKey, + items: droppedItemsUpdateFalse + })); + return responses; } catch (error) { console.error("Error submitting recipient tags:", error); @@ -302,10 +324,11 @@ const DocumentContainer = forwardRef, docId: string, documentId: number, pageIndex: number, originalWidth: number, originalHeight: number) => { event.preventDefault(); - const imageElement = document.getElementById(`${docId}-${pageIndex}`) as HTMLImageElement; + const pageRefKey = `${docId}_${pageIndex}`; + const imageElement = imageRefs.current[pageRefKey] || document.getElementById(`${docId}-${pageIndex}`) as HTMLImageElement; - const currentWidth = imageElement.offsetWidth; - const currentHeight = imageElement.offsetHeight; + const currentWidth = imageElement?.offsetWidth || originalWidth; + const currentHeight = imageElement?.offsetHeight || originalHeight; const scaleX = currentWidth / originalWidth; // 350 / 700 = 0.5 const scaleY = currentHeight / originalHeight; // 225 / 450 = 0.5 @@ -371,10 +394,10 @@ const DocumentContainer = forwardRef prev + 1); + dispatch(setDroppedItems({ + transactionKey, + items: dataToSet + })); + dispatch(incrementCounter(transactionKey)); // setTimeout(() => { // const tagIndex = key in droppedItems ? droppedItems[key].length : 0; @@ -440,9 +466,12 @@ const DocumentContainer = forwardRef ({ - ...prevState, - [selectedDoc.key]: updatedKey + dispatch(setDroppedItems({ + transactionKey, + items: { + ...droppedItems, + [selectedDoc.key]: updatedKey + } })); // Handle Ctrl+V @@ -566,7 +595,21 @@ const DocumentContainer = forwardRef (imageRefs.current[page.index] = el)} + ref={(el) => { + const pageRefKey = `${doc.id}_${page.index}`; + imageRefs.current[pageRefKey] = el; + }} + onLoad={(event) => { + const pageRefKey = `${doc.id}_${page.index}`; + const el = event.currentTarget; + setImageSizes((prev) => ({ + ...prev, + [pageRefKey]: { + width: el.offsetWidth, + height: el.offsetHeight, + }, + })); + }} draggable="false" width={page.width} height={page.height} @@ -579,19 +622,28 @@ const DocumentContainer = forwardRef { + const newItems = typeof updateFn === 'function' + ? updateFn(droppedItems) + : updateFn; + dispatch(setDroppedItems({ + transactionKey, + items: newItems + })); + }} accountId={accountId} transactionId={transactionId} transactionTemplateToggle={transactionTemplateToggle} tagUndoRef={tagUndoRef} + transactionKey={transactionKey} /> {/* Side details of documents Pages and document Name - START */} @@ -625,7 +677,15 @@ const DocumentContainer = forwardRef { + const newItems = typeof updateFn === 'function' + ? updateFn(droppedItems) + : updateFn; + dispatch(setDroppedItems({ + transactionKey, + items: newItems + })); + }} selTagKey={selectedTag.key} selTagIndx={selectedTag.index} signClient={false} diff --git a/src/components/process/processStepTwo/DroppedItemsTagsCon.tsx b/src/components/process/processStepTwo/DroppedItemsTagsCon.tsx index 1a854c5..736aa9c 100644 --- a/src/components/process/processStepTwo/DroppedItemsTagsCon.tsx +++ b/src/components/process/processStepTwo/DroppedItemsTagsCon.tsx @@ -9,6 +9,8 @@ // -- handleAddGroupItemClicked import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react' +import { useDispatch } from 'react-redux' +import type { AppDispatch } from '../../../store/store' import { DroppedItemsObj } from './DocumentContainer'; import typeToIconMap from '../../../helper/iconMap'; import { IoMdClose } from 'react-icons/io'; @@ -17,6 +19,7 @@ import { FaQuestionCircle } from 'react-icons/fa'; import api from '../../../config/api'; import { Recipient } from '../../../store/slices/selectedRecipientsSlice'; import { DroppedItem } from '../../../utlity/types'; +import { setDroppedItems as setDroppedItemsAction } from '../../../store/slices/documentTagsSlice'; interface DroppedItemsTagsConProps { pageWidth: number, @@ -28,11 +31,12 @@ interface DroppedItemsTagsConProps { selectedTag: { key: string; index: number } | null; selectedRecipients: Recipient[]; setSelectedTag: React.Dispatch>; - setDroppedItems: React.Dispatch>; + setDroppedItems: (updateFn: ((prevState: DroppedItemsObj) => DroppedItemsObj) | DroppedItemsObj) => void; accountId: string; transactionId: string; transactionTemplateToggle: string; tagUndoRef: React.MutableRefObject; + transactionKey: string; } // 👶 Child component with forwardRef @@ -50,9 +54,12 @@ const DroppedItemsTagsCon = forwardRef(({ accountId, transactionId, transactionTemplateToggle, - tagUndoRef + tagUndoRef, + transactionKey }: DroppedItemsTagsConProps, ref) => { + const dispatch = useDispatch(); + const positionRef = useRef<{ left: number; top: number }>({ left: 0, top: 0 }); const handleTagClick = (key: string, index: number) => { @@ -388,11 +395,17 @@ const DroppedItemsTagsCon = forwardRef(({ // check pageIndex match const isSamePage = currentPageIndex == tagDocumentUpdate.pageIndex; + const scaleX = renderedWidth && pageWidth ? renderedWidth / pageWidth : 1; + const scaleY = renderedHeight && pageHeight ? renderedHeight / pageHeight : 1; // same document + same page if (isSameDocument && isSamePage) { left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width)); top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height)); + + const normalizedLeft = left / scaleX; + const normalizedTop = top / scaleY; + return { ...prevState, [key]: Array.isArray(prevState[key]) @@ -406,8 +419,8 @@ const DroppedItemsTagsCon = forwardRef(({ tagDocumentUpdate.pageIndex, dimensions: { ...item.dimensions, - left, - top, + left: normalizedLeft, + top: normalizedTop, }, update: true, } @@ -561,8 +574,11 @@ const DroppedItemsTagsCon = forwardRef(({ borderPositions.forEach(position => { const borderElement = document.querySelector(`[border-group='${groupId}'][border-pos='${position}']`); - if (borderElement && borderElement.parentNode) { - borderElement.parentNode.removeChild(borderElement); + if (borderElement) { + borderElement.remove?.(); + if (!borderElement.remove && borderElement.parentNode && borderElement.parentNode.contains(borderElement)) { + borderElement.parentNode.removeChild(borderElement); + } } }); }; @@ -790,8 +806,8 @@ const DroppedItemsTagsCon = forwardRef(({ const IconComponent = typeToIconMap[type] || ; const selectedRecipient = selectedRecipients.filter((obj, indx) => obj.id == item.recipientId); - const scaleX = renderedWidth / Math.round(pageWidth); - const scaleY = renderedHeight / Math.round(pageHeight); + const scaleX = renderedWidth && pageWidth ? renderedWidth / pageWidth : 1; + const scaleY = renderedHeight && pageHeight ? renderedHeight / pageHeight : 1; return ( diff --git a/src/components/process/processStepTwo/NavTwo.tsx b/src/components/process/processStepTwo/NavTwo.tsx index 8c42d5a..f820529 100644 --- a/src/components/process/processStepTwo/NavTwo.tsx +++ b/src/components/process/processStepTwo/NavTwo.tsx @@ -254,11 +254,18 @@ const NavTwo: React.FC = ({ {/* Left Side */}
-
+ {/*
Sign Made Easy -
+
*/} + + + {/*
+ + Sign Made Easy + +
*/}
= ({ {editRecipientsModal && accountId && transactionId && <> -
-
+
+ {/*
*/} +
= ({ } {messageModal && accountId && transactionId && <> -
-
+
+ {/*
*/} +
+ ) => { + const { transactionKey, items } = action.payload; + state.items[transactionKey] = items; + state.isLoading[transactionKey] = false; + }, + + /** + * Set all dropped items (replaces existing) + * Used when user adds/modifies tags in current session + */ + setDroppedItems: ( + state, + action: PayloadAction<{ + transactionKey: string; + items: DroppedItemsObj; + }> + ) => { + const { transactionKey, items } = action.payload; + state.items[transactionKey] = items; + }, + + /** + * Add a dropped item to specific page + */ + addDroppedItem: ( + state, + action: PayloadAction<{ + transactionKey: string; + pageKey: string; + item: DroppedItem; + }> + ) => { + const { transactionKey, pageKey, item } = action.payload; + if (!state.items[transactionKey]) { + state.items[transactionKey] = {}; + } + if (!state.items[transactionKey][pageKey]) { + state.items[transactionKey][pageKey] = []; + } + state.items[transactionKey][pageKey].push(item); + state.counter[transactionKey] = (state.counter[transactionKey] || 0) + 1; + }, + + /** + * Update dropped items for a specific page + */ + updatePageItems: ( + state, + action: PayloadAction<{ + transactionKey: string; + pageKey: string; + items: DroppedItem[]; + }> + ) => { + const { transactionKey, pageKey, items } = action.payload; + if (!state.items[transactionKey]) { + state.items[transactionKey] = {}; + } + state.items[transactionKey][pageKey] = items; + }, + + /** + * Remove an item by index from a page + */ + removeDroppedItem: ( + state, + action: PayloadAction<{ + transactionKey: string; + pageKey: string; + index: number; + }> + ) => { + const { transactionKey, pageKey, index } = action.payload; + if (state.items[transactionKey]?.[pageKey]) { + state.items[transactionKey][pageKey].splice(index, 1); + } + }, + + /** + * Update a specific item by index + */ + updateDroppedItem: ( + state, + action: PayloadAction<{ + transactionKey: string; + pageKey: string; + index: number; + updates: Partial; + }> + ) => { + const { transactionKey, pageKey, index, updates } = action.payload; + if (state.items[transactionKey]?.[pageKey]?.[index]) { + state.items[transactionKey][pageKey][index] = { + ...state.items[transactionKey][pageKey][index], + ...updates, + }; + } + }, + + /** + * Clear all tags for a specific transaction + */ + clearTransactionTags: (state, action: PayloadAction) => { + const transactionKey = action.payload; + delete state.items[transactionKey]; + delete state.counter[transactionKey]; + delete state.isLoading[transactionKey]; + }, + + /** + * Increment counter for a transaction + */ + incrementCounter: (state, action: PayloadAction) => { + const transactionKey = action.payload; + state.counter[transactionKey] = (state.counter[transactionKey] || 0) + 1; + }, + + /** + * Set loading state + */ + setLoading: ( + state, + action: PayloadAction<{ + transactionKey: string; + isLoading: boolean; + }> + ) => { + const { transactionKey, isLoading } = action.payload; + state.isLoading[transactionKey] = isLoading; + }, + }, +}); + +export const { + initializeDroppedItems, + setDroppedItems, + addDroppedItem, + updatePageItems, + removeDroppedItem, + updateDroppedItem, + clearTransactionTags, + incrementCounter, + setLoading, +} = documentTagsSlice.actions; + +export default documentTagsSlice.reducer; diff --git a/src/store/slices/localTagSlice.ts b/src/store/slices/localTagSlice.ts index c3eba60..55c9ff3 100644 --- a/src/store/slices/localTagSlice.ts +++ b/src/store/slices/localTagSlice.ts @@ -2,7 +2,6 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; /* TYPES */ - export interface Position { top: number; left: number; @@ -43,8 +42,6 @@ export interface Tag { /* PAYLOAD TYPES */ - - interface AddTagPayload { recipientId: string; tag: Tag; @@ -99,7 +96,6 @@ const localTagsSlice = createSlice({ reducers: { /* ------------------------------ ADD TAG ----------------------------- */ - addTag: (state, action: PayloadAction) => { const { recipientId, tag } = action.payload; @@ -111,7 +107,6 @@ const localTagsSlice = createSlice({ }, /* ---------------------------- UPDATE TAG ---------------------------- */ - updateTag: (state, action: PayloadAction) => { const { recipientId, tagId, updatedData } = action.payload; @@ -125,7 +120,6 @@ const localTagsSlice = createSlice({ }, /* ---------------------------- REMOVE TAG ---------------------------- */ - removeTag: (state, action: PayloadAction) => { const { recipientId, tagId } = action.payload; @@ -140,7 +134,6 @@ const localTagsSlice = createSlice({ }, /* ------------------------- REPLACE ALL TAGS ------------------------- */ - replaceRecipientTags: ( state, action: PayloadAction @@ -155,7 +148,6 @@ const localTagsSlice = createSlice({ }, /* -------------------------- CLEAR RECIPIENT ------------------------- */ - clearRecipientTags: ( state, action: PayloadAction @@ -164,7 +156,6 @@ const localTagsSlice = createSlice({ }, /* ----------------------------- CLEAR ALL ---------------------------- */ - clearTags: () => { return initialState; }, diff --git a/src/store/store.ts b/src/store/store.ts index 2672f4c..3ab69c5 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -72,6 +72,7 @@ import documentReducer from "./slices/documentSlice"; import tagsReducer from "./slices/tagsSlice"; import transactionReducer from "./slices/transactionSlice"; import signatureReducer from "./slices/signatureSlice"; +import documentTagsReducer from "./slices/documentTagsSlice"; const rootReducer = combineReducers({ user: userReducer, @@ -83,6 +84,7 @@ const rootReducer = combineReducers({ tags: tagsReducer, transaction: transactionReducer, signature: signatureReducer, + documentTags: documentTagsReducer, }); export const store = configureStore({