From 79a1e314f30fdb1e89d35b12f76541f31ccdce4f Mon Sep 17 00:00:00 2001 From: Bhagwan Singh Date: Fri, 29 May 2026 16:46:36 +0530 Subject: [PATCH] Minor fixes --- src/components/CommonNav.tsx | 9 +- src/components/client/SignDocument.tsx | 2 - src/components/dashboard/Loader.tsx | 2 +- src/components/home/StartNow.tsx | 1 - .../TransactionSentSuccess.tsx | 34 +++++- .../process/processStepOne/ProcessStepOne.tsx | 46 +++++--- .../processStepTwo/DocumentContainer.tsx | 105 +++++++++++------- .../process/processStepTwo/FieldList.tsx | 40 +++---- .../process/processStepTwo/LeftSideBar.tsx | 83 +++++++------- .../process/processStepTwo/NavTwo.tsx | 11 +- .../process/processStepTwo/ProcessStepTwo.tsx | 4 +- .../documentPages/DocumentPages.tsx | 4 +- src/helper/iconMap.tsx | 34 +++--- src/store/slices/selectedRecipientsSlice.tsx | 1 + 14 files changed, 226 insertions(+), 150 deletions(-) diff --git a/src/components/CommonNav.tsx b/src/components/CommonNav.tsx index 4a2d5a4..5fe9c53 100644 --- a/src/components/CommonNav.tsx +++ b/src/components/CommonNav.tsx @@ -372,11 +372,18 @@ const CommonNav: React.FC = ({
- + {/* + */} + + + + + +
{/* SETINGS - END */} diff --git a/src/components/client/SignDocument.tsx b/src/components/client/SignDocument.tsx index ba07d66..cfb8294 100644 --- a/src/components/client/SignDocument.tsx +++ b/src/components/client/SignDocument.tsx @@ -966,8 +966,6 @@ const SignDocument = () => { // onSubmit={(e: React.FormEvent) => handleFormSubmit(e)} > - - {/* Nav Two - Options - START */}
diff --git a/src/components/dashboard/Loader.tsx b/src/components/dashboard/Loader.tsx index 66a6e46..4c7d33c 100644 --- a/src/components/dashboard/Loader.tsx +++ b/src/components/dashboard/Loader.tsx @@ -10,7 +10,7 @@ const Loader = () => { const loaderData = useSelector((state: RootState) => state.loader); return ( -
+
{/* Background */} {/*
*/}
diff --git a/src/components/home/StartNow.tsx b/src/components/home/StartNow.tsx index 159522d..5f794e5 100644 --- a/src/components/home/StartNow.tsx +++ b/src/components/home/StartNow.tsx @@ -194,7 +194,6 @@ const StartNow = () => { */} - {/* ❌ Remove the old "relative inline-block group" div entirely, replace with: */}
)} + {selfUserAlreadySigned && ( + + )} +
diff --git a/src/components/process/processStepOne/ProcessStepOne.tsx b/src/components/process/processStepOne/ProcessStepOne.tsx index 9ca4437..1fcd370 100644 --- a/src/components/process/processStepOne/ProcessStepOne.tsx +++ b/src/components/process/processStepOne/ProcessStepOne.tsx @@ -2,7 +2,7 @@ import SelectedDocuments from "./SelectedDocuments"; import api from "../../../config/api"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../../store/store"; -import { useEffect, useRef, useState } from "react"; +import { ChangeEvent, useEffect, useRef, useState } from "react"; import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { addRecipientAsync, fetchRecipients, recipientType, removeAllRecipients, removeRecipientAsync, resetRecipients, setSigningOrder, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientOrder } from "../../../store/slices/selectedRecipientsSlice"; @@ -91,16 +91,16 @@ const ProcessStepOne = () => { const [isTyping, setIsTyping] = useState(false); // Optional: use a timer to detect pause in typing - let typingTimeout = null; + const typingTimeout = useRef | null>(null); - const handleChange = (e) => { + const handleChange = (e: React.ChangeEvent) => { setTemplateName(e.target.value); setIsTyping(true); - clearTimeout(typingTimeout); - typingTimeout = setTimeout(() => { + if (typingTimeout.current) clearTimeout(typingTimeout.current); + typingTimeout.current = setTimeout(() => { setIsTyping(false); - }, 5000); // hide icon while typing, show it again after 500ms of no input + }, 5000); }; const [transactionDetails, setTransactionDetails] = useState([]); @@ -118,11 +118,14 @@ const ProcessStepOne = () => { setTransactionDetails(res.data) if (transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES) { - setTemplateName(res.data.name) + setTemplateName(res.data.name ?? 'Untitled') } setEmailDtls( - { subject: res.data.subject ? res.data.subject : "Documents for Signature", message: res.data.message ? res.data.message : "" } + { + subject: res.data.subject ?? "Documents for Signature", + message: res.data.message ?? "" + } ) } catch (e) { @@ -143,14 +146,29 @@ const ProcessStepOne = () => { if (submitError.duplicateEmailSameRoutingOrder) setSubmitError(prev => ({ ...prev, duplicateEmailSameRoutingOrder: false })) if (submitError.documents) setSubmitError(prev => ({ ...prev, documents: false })) if (submitError.recipients) setSubmitError(prev => ({ ...prev, recipients: false })) - - // Handle Signing Order for template and for transction - fill on refresh based on recipients routing order or prefill - START - let isSigningOrder = signingOrder; - selectedRecipients && selectedRecipients.length > 1 && selectedRecipients.some(recipient => recipient.routingOrder && recipient.routingOrder > 1) ? isSigningOrder = true : ""; - isSigningOrder && handleSigningOrderChange(isSigningOrder); - // Handle Signing Order for template and for transction - fill on refresh based on recipients routing order or prefill - END }, [selectedRecipients]) + + + + useEffect(() => { + // Handle Signing Order for template and for transction - fill on refresh based on recipients routing order or prefill - START + // let isSigningOrder = signingOrder; + // selectedRecipients && selectedRecipients.length > 1 && selectedRecipients.some(recipient => recipient.routingOrder && recipient.routingOrder > 1) ? isSigningOrder = true : ""; + // isSigningOrder && handleSigningOrderChange(isSigningOrder); + // Handle Signing Order for template and for transction - fill on refresh based on recipients routing order or prefill - END + + if (selectedRecipients.length > 1) { + const hasMultipleOrders = selectedRecipients.some( + r => r.routingOrder && r.routingOrder > 1 + ); + if (hasMultipleOrders && !signingOrder) { + handleSigningOrderChange(true); + } + } + + }, [selectedRecipients.length]) + const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const validationCheck = () => { diff --git a/src/components/process/processStepTwo/DocumentContainer.tsx b/src/components/process/processStepTwo/DocumentContainer.tsx index 4c494ed..0ac3bc3 100644 --- a/src/components/process/processStepTwo/DocumentContainer.tsx +++ b/src/components/process/processStepTwo/DocumentContainer.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from 'react' +import React, { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { RootState } from '../../../store/store' @@ -73,6 +73,7 @@ export interface DocumentContainerRef { const DocumentContainer = forwardRef( ({ isPages, zoomScale, transactionSaving, setTransactionSaving, isSend, isPreview = false, aId = null, tId = null, selectedRecipientIndx = 0, setIsPages, onHistoryChange }, ref) => { + const EMPTY_DROPPED_ITEMS: DroppedItemsObj = {}; const scaleFactor: number = (zoomScale ?? 100) / 100; let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; @@ -138,9 +139,25 @@ const DocumentContainer = forwardRef { + if (selectedDocuments.length > 0 && selectedDocuments[0].pages?.length > 0) { + const firstDoc = selectedDocuments[0]; + const firstPage = firstDoc.pages[0]; + if (!selectedDoc.key) { + selectedDoc.key = `${firstDoc.id}_${firstPage.index}`; + } + } + }, [selectedDocuments]); + // Redux state for dropped items - const transactionKey = `${accountId}_${transactionId}`; - const droppedItems = useSelector((state: RootState) => state.documentTags.items[transactionKey] || {}); + const transactionKey = useMemo( + () => `${accountId}_${transactionId}`, + [accountId, transactionId] + ); + + + const droppedItems = useSelector((state: RootState) => state.documentTags.items[transactionKey] ?? EMPTY_DROPPED_ITEMS); const droppedItemsCounter = useSelector((state: RootState) => state.documentTags.counter[transactionKey] || 0); interface HotKeysObj { @@ -219,8 +236,9 @@ const DocumentContainer = forwardRef(hotKeysObjInitial); // use ref, not state - const docKeyListenerRef = useRef<((e: KeyboardEvent) => void) | null>(null); + // const hotKeysObj = useRef(hotKeysObjInitial); // use ref, not state + // const docKeyListenerRef = useRef<((e: KeyboardEvent) => void) | null>(null); + const selectedTagRef = useRef<{ key: string; index: number } | null>(null); const droppedItemsRef = useRef(droppedItems); @@ -235,6 +253,40 @@ const DocumentContainer = forwardRef { + const handler = (event: KeyboardEvent) => { + // Don't intercept when user is typing in an input/textarea/contenteditable + const target = event.target as HTMLElement; + if ( + target.tagName === 'INPUT' || + target.tagName === 'TEXTAREA' || + target.isContentEditable + ) return; + + const ctrl = event.ctrlKey || event.metaKey; + const shift = event.shiftKey; + const pressedKey = event.key.toLowerCase(); + + // Only intercept known hotkeys to avoid blocking browser shortcuts + const isKnownHotkey = + (ctrl && ['c', 'v', 'a', 'z', 'y'].includes(pressedKey)) || + ['delete', 'backspace'].includes(pressedKey); + + if (!isKnownHotkey) return; + + event.preventDefault(); + + let hotKey = pressedKey; + if (ctrl && shift) hotKey = 'Ctrl+Shift+' + pressedKey; + else if (ctrl) hotKey = 'Ctrl+' + pressedKey; + + handleHotKey(hotKey, selectedDoc.key ?? ''); + }; + + document.addEventListener('keydown', handler); + return () => document.removeEventListener('keydown', handler); + }, []); // empty deps — handleHotKey uses refs so it's always fresh useEffect(() => { droppedItemsRef.current = droppedItems; @@ -263,7 +315,6 @@ const DocumentContainer = forwardRef { - console.log("Handle Undo Called") if (cursorRef.current <= 0) return; // nothing to undo cursorRef.current -= 1; @@ -386,7 +437,6 @@ const DocumentContainer = forwardRef { - return () => { - const targetCon = document.getElementById('doc-con'); - if (docKeyListenerRef.current && targetCon) { - targetCon.removeEventListener('keydown', docKeyListenerRef.current); - } - }; - }, []); + // useEffect(() => { + // return () => { + // const targetCon = document.getElementById('doc-con'); + // if (docKeyListenerRef.current && targetCon) { + // targetCon.removeEventListener('keydown', docKeyListenerRef.current); + // } + // }; + // }, []); const handleCopy = () => { const currentSelectedTag = selectedTagRef.current; @@ -677,33 +727,8 @@ const DocumentContainer = forwardRef { selectedDoc.key = key; - - const targetCon = document.getElementById('doc-con'); - if (!targetCon) return; - - // Remove previous listener before adding new one - if (docKeyListenerRef.current) { - targetCon.removeEventListener('keydown', docKeyListenerRef.current); - } - - const handler = (event: KeyboardEvent) => { - event.preventDefault(); - const ctrl = event.ctrlKey || event.metaKey; - const shift = event.shiftKey; - const pressedKey = event.key.toLowerCase(); - - let hotKey = pressedKey; - if (ctrl && shift) hotKey = 'Ctrl+Shift+' + pressedKey; - else if (ctrl) hotKey = 'Ctrl+' + pressedKey; - - handleHotKey(hotKey, key); - }; - - docKeyListenerRef.current = handler; - targetCon.addEventListener('keydown', handler); }; - return ( <> {/* Center Doc Container - START */} diff --git a/src/components/process/processStepTwo/FieldList.tsx b/src/components/process/processStepTwo/FieldList.tsx index a766846..43e1622 100644 --- a/src/components/process/processStepTwo/FieldList.tsx +++ b/src/components/process/processStepTwo/FieldList.tsx @@ -316,29 +316,25 @@ const FieldList: React.FC = ({ color, recipientId, recipientIndx if (!IconComponent) return null; return ( - <> - {/*
handleDragStart(e, field.label, recipientId)} className="p-1 flex justify-start items-center gap-2 rounded-sm cursor-grab hover:bg-slate-200 transition-all duration-300"> */} -
handlePointerDown(e, field.type.toLowerCase(), recipientId, recipientIndx, field)} - className={`border-[1px] flex w-full justify-left bg-white items-center gap-1 rounded-sm cursor-grab hover:bg-slate-200 transition-all duration-300`} - style={{ borderColor: color }} - > +
handlePointerDown(e, field.type.toLowerCase(), recipientId, recipientIndx, field)} + className={`border-[1px] flex w-full justify-left bg-white items-center gap-1 rounded-sm cursor-grab hover:bg-slate-200 transition-all duration-300`} + style={{ borderColor: color }} + > -
- {IconComponent} -
-

- {field.type.toLowerCase()} -

-
- {/*
*/} - +
+ {IconComponent} +
+

+ {field.type.toLowerCase()} +

+
) } diff --git a/src/components/process/processStepTwo/LeftSideBar.tsx b/src/components/process/processStepTwo/LeftSideBar.tsx index cd0f443..ecd4b0e 100644 --- a/src/components/process/processStepTwo/LeftSideBar.tsx +++ b/src/components/process/processStepTwo/LeftSideBar.tsx @@ -11,6 +11,7 @@ import { useParams } from 'react-router-dom'; import { fetchDocuments } from '../../../store/slices/documentSlice'; import { FaAngleUp } from 'react-icons/fa'; import { SlDocs } from "react-icons/sl"; +import { IoMdClose } from 'react-icons/io'; interface LeftSideBarProps { isFields: boolean; @@ -27,12 +28,15 @@ const LeftSideBar: React.FC = ({ isFields, selectedRecipientIn const dispatch = useDispatch(); const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>(); const accountId = params.accountId; - let transactionId = params.transactionId + let transactionId = params.transactionId; + + if (!transactionId) { transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES; transactionId = params.templateId; dispatch(toggleToTemplates()); } + if (!accountId || !transactionId) { window.location.href = "/"; return null; @@ -82,40 +86,23 @@ const LeftSideBar: React.FC = ({ isFields, selectedRecipientIn return
; } + return ( <> {/* Left Side Bar - START */}
{ - (isFields || window.innerWidth > 638) &&
638) ? "h-[100px] bottom-0 overflow-x-hidden w-full" : "h-[calc(100vh-40px)]"} `}> - {/*
{ - // handleSetIsPages(false) - handleSetIsFields(false) - }} - className='absolute sm:hidden top-[5px] right-[10px] bg-white rounded-full text-2xl z-50'> - -
*/} +
handleSetIsFields(false)} + className={` + ${(isFields || window.innerWidth > 638) ? "" : "hidden"} + sm:block p-4 fixed sm:top-[0] rounded-tr-md z-50 sm:relative sm:col-span-2 bg-white border-t-1 border-r-1 border-slate-300 sm:h-[calc(100vh-40px)] overflow-y-auto sm:w-auto ${(isFields || window.innerWidth > 638) ? "h-[100px] bottom-0 overflow-x-hidden w-full" : "h-[calc(100vh-40px)]"} + `} + > - {/*
-
handleSetIsFields(!isFields)} - className={`flex items-center gap-1 cursor-pointer text-xs w-full justify-center py-1.5 rounded-2xl ${isFields ? 'bg-[var(--primary)] text-white' : ''} transition-all duration-300`}> - -

Tags

-
- -
handleSetIsPages(!isPages)} - className={`flex items-center gap-1 cursor-pointer text-xs w-full justify-center py-1.5 rounded-2xl ${isPages ? 'bg-[var(--primary)] text-white' : ''} transition-all duration-300`}> - -

Pages

-
-
*/} - - - {selectedRecipients.length > 0 && (isFields || window.innerWidth > 638) && <> + {selectedRecipients.length > 0 && <> {/* Fields List and Recipeint - Start */} {/* Side Bar showing Recipients */}
@@ -124,22 +111,31 @@ const LeftSideBar: React.FC = ({ isFields, selectedRecipientIn tabIndex={0} onBlur={() => setDropDownToggle(false)}> {/* Selected Recipient */} -
setDropDownToggle(!dropDownToggle)} - className='flex items-center justify-between gap-2 w-[75%] sm:w-full p-2 text-xs rounded-sm border-1 border-slate-200 cursor-pointer'> -
-

- {/* */} -

- { - selectedRecipient.firstName.length == 0 && selectedRecipient.lastName.length == 0 ? - (selectedRecipient.email.length == 0 ? selectedRecipient.roleName : selectedRecipient.email) : - `${selectedRecipient.firstName + " " + selectedRecipient.lastName}` - } -

+ +
+ +
setDropDownToggle(!dropDownToggle)} + className='flex items-center justify-between gap-2 w-[75%] sm:w-full p-2 text-xs rounded-sm border-1 border-slate-200 cursor-pointer'> +
+

+ {/* */} +

+ { + selectedRecipient.firstName.length == 0 && selectedRecipient.lastName.length == 0 ? + (selectedRecipient.email.length == 0 ? selectedRecipient.roleName : selectedRecipient.email) : + `${selectedRecipient.firstName + " " + selectedRecipient.lastName}` + } +

+
+
- + + handleSetIsFields(false)} + className='absolute top-[-10px] left-[88vw] text-xl sm:hidden text-slate-400' + />
= ({ isFields, selectedRecipientIn {/* Small Devices - END */}
- ) } diff --git a/src/components/process/processStepTwo/NavTwo.tsx b/src/components/process/processStepTwo/NavTwo.tsx index a7c1193..4e84259 100644 --- a/src/components/process/processStepTwo/NavTwo.tsx +++ b/src/components/process/processStepTwo/NavTwo.tsx @@ -112,11 +112,10 @@ const NavTwo: React.FC = ({ "loading": false }); - console.log(editRecipientsModal) - 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; @@ -553,7 +552,13 @@ const NavTwo: React.FC = ({ {!isPreview && <>
setConfirmSendModalOpen({ open: true, confirm: false, loading: false })} + onClick={() => { + if (TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES) { + handleSend(); + } else { + setConfirmSendModalOpen({ open: true, confirm: false, loading: false }); + } + }} className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)]'>

{transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES ? "Save" : "Send"} diff --git a/src/components/process/processStepTwo/ProcessStepTwo.tsx b/src/components/process/processStepTwo/ProcessStepTwo.tsx index 202c780..0386208 100644 --- a/src/components/process/processStepTwo/ProcessStepTwo.tsx +++ b/src/components/process/processStepTwo/ProcessStepTwo.tsx @@ -29,6 +29,7 @@ const ProcessStepTwo = () => { 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; @@ -36,7 +37,6 @@ const ProcessStepTwo = () => { } if (!accountId || !transactionId) { window.location.href = "/"; - return null; } const windowWidth = window.innerWidth; @@ -74,7 +74,7 @@ const ProcessStepTwo = () => { const handleSetIsFields = (setTo: boolean) => { - setIsFields(true) + setIsFields(setTo) setIsPages(false) } diff --git a/src/components/process/processStepTwo/documentPages/DocumentPages.tsx b/src/components/process/processStepTwo/documentPages/DocumentPages.tsx index b6e125d..e8f0ca8 100644 --- a/src/components/process/processStepTwo/documentPages/DocumentPages.tsx +++ b/src/components/process/processStepTwo/documentPages/DocumentPages.tsx @@ -99,9 +99,9 @@ const DocumentPages: React.FC<{ const activeRecipientTagsCount = (droppedItems[key] && droppedItems[key].length) > 0 - ? droppedItems[key].reduce((acc, item) => { + ? droppedItems[key].reduce((acc, item, indx) => { return !item.isDeleted && - item.recipientIndx == selectedRecipientIndx + (("id" in selectedRecipient && "recipientId" in item && selectedRecipient.id === item.recipientId) || ("recipientIndx" in item && item.recipientIndx == selectedRecipientIndx)) ? acc + 1 : acc; }, 0) diff --git a/src/helper/iconMap.tsx b/src/helper/iconMap.tsx index 538a81e..b382a27 100644 --- a/src/helper/iconMap.tsx +++ b/src/helper/iconMap.tsx @@ -51,28 +51,28 @@ const typeToIconMap = { , TEMPORAL: - - + + , NUMBER: - - - - + + + + , IMAGE: , STAMP: - + - - - + + + @@ -98,26 +98,26 @@ const typeToIconMap = { FILE: , ATTACHMENT: - - + + , RADIO: - + , EMAIL: - - + + , NAME: - - + + , COMPANY: diff --git a/src/store/slices/selectedRecipientsSlice.tsx b/src/store/slices/selectedRecipientsSlice.tsx index ab886dd..521ca3d 100644 --- a/src/store/slices/selectedRecipientsSlice.tsx +++ b/src/store/slices/selectedRecipientsSlice.tsx @@ -25,6 +25,7 @@ export interface Recipient { provideOwnTags?: boolean; uuid?: string; newOne?: boolean; + completed?: boolean; } export interface SelectedRecipientInt {