This commit is contained in:
=bhagwanSingh 2025-07-31 12:25:44 +05:30
parent 1a4ea46c5b
commit 19abe70a64
11 changed files with 122 additions and 70 deletions

View File

@ -48,7 +48,7 @@ const SignersList: React.FC<{ signers: Signer[] }> = ({ signers }) => {
{signers.map((signer) => ( {signers.map((signer) => (
<div key={signer.id} className="flex items-center gap-4 p-3 bg-white/40 rounded-lg"> <div key={signer.id} className="flex items-center gap-4 p-3 bg-white/40 rounded-lg">
<div className="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center text-blue-600 font-medium"> <div className="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center text-blue-600 font-medium">
{signer.name.split(" ").map(n => n[0].toUpperCase()).join("")} {signer.name.length > 2 ? signer.name.split(" ").map(n => n[0].toUpperCase()).join("") : signer.email.split("").map(n => n[0].toUpperCase()).join("")}
</div> </div>
<div className="flex-1"> <div className="flex-1">
<div className="font-medium text-gray-800">{signer.name}</div> <div className="font-medium text-gray-800">{signer.name}</div>

View File

@ -104,7 +104,7 @@ const ProcessStepOne = () => {
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const handleNextClick = async (isSendNow: boolean) => { const handleNextClick = async (isSendNow: boolean, saveAndClose: boolean = false) => {
let formError = { let formError = {
documents: false, documents: false,
recipients: false, recipients: false,
@ -250,7 +250,11 @@ const ProcessStepOne = () => {
dispatch(hideLoader()); dispatch(hideLoader());
if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES) { if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES) {
console.log(`/process-step-two/${accountId}/template/${transactionId}`) console.log(`/process-step-two/${accountId}/template/${transactionId}`)
if(!saveAndClose) {
navigate(`/process-step-two/${accountId}/template/${transactionId}`) navigate(`/process-step-two/${accountId}/template/${transactionId}`)
} else {
navigate(`/templates`)
}
} else { } else {
if (signType === SignTypeEnum.selfSign) { if (signType === SignTypeEnum.selfSign) {
(async () => { (async () => {
@ -319,12 +323,22 @@ const ProcessStepOne = () => {
<div <div
id="add-tags-btn-step-one" id="add-tags-btn-step-one"
className='flex items-center gap-3'> className='flex items-center gap-3'>
{transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES && <button
onClick={() => handleNextClick(false, true)}
className="text-sm px-4 py-2 rounded bg-[var(--primary)] text-white shadow hover:bg-[var(--primaryLight2)] transition cursor-pointer"
>
Save & Close
</button>}
<button <button
onClick={() => handleNextClick(false)} onClick={() => handleNextClick(false)}
className="text-sm px-4 py-2 rounded bg-[var(--primary)] text-white shadow hover:bg-[var(--primaryLight2)] transition" className="text-sm px-4 py-2 rounded bg-[var(--primary)] text-white shadow hover:bg-[var(--primaryLight2)] transition cursor-pointer"
> >
{signType === SignTypeEnum.selfSign ? "Sign" : "Next"} {signType === SignTypeEnum.selfSign ? "Sign" : "Next"}
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { FaPlus } from "react-icons/fa"; import { FaPlus } from "react-icons/fa";
import { addRecipient, Recipient, recipientType, updateRecipient } from "../../../store/slices/selectedRecipientsSlice"; import { addRecipient, Recipient, recipientType, TRANSACTION_TEMPLATE_TOGGLE, updateRecipient } from "../../../store/slices/selectedRecipientsSlice";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { AppDispatch } from "../../../store/store"; import { AppDispatch } from "../../../store/store";
import api from "../../../config/api"; import api from "../../../config/api";
@ -36,7 +36,7 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase,
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const formRef = useRef<HTMLFormElement | null>(null); const formRef = useRef<HTMLFormElement | null>(null);
const [recipient, setRecipients] = useState<Recipient>({ uuid: null, newOne: false, firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: false, allowSenderToDelete: false }); const [recipient, setRecipients] = useState<Recipient>({ uuid: null, newOne: false, firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: true, allowSenderToDelete: true });
const [showAdvanceSettings, setShowAdvanceSettings] = useState(false) const [showAdvanceSettings, setShowAdvanceSettings] = useState(false)
@ -72,8 +72,8 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase,
hostEmail: editRecipient.hostEmail || "", hostEmail: editRecipient.hostEmail || "",
roleName: editRecipient.roleName || "", roleName: editRecipient.roleName || "",
type: editRecipient.type || recipientType.SIGNER, type: editRecipient.type || recipientType.SIGNER,
allowSenderToEdit: editRecipient.allowSenderToEdit ?? false, allowSenderToEdit: editRecipient.allowSenderToEdit ?? true,
allowSenderToDelete: editRecipient.allowSenderToDelete ?? false, allowSenderToDelete: editRecipient.allowSenderToDelete ?? true,
uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''), uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''),
newOne: editRecipient.newOne ?? false newOne: editRecipient.newOne ?? false
}); });
@ -86,6 +86,7 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase,
console.log("recipientrecipient", recipient) console.log("recipientrecipient", recipient)
if(transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) {
if (!recipient.lastName || !recipient.email || !recipient.type) return; if (!recipient.lastName || !recipient.email || !recipient.type) return;
// ✅ Email format validation // ✅ Email format validation
@ -95,6 +96,9 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase,
return; return;
} }
}
if (editCase === 'Edit') { if (editCase === 'Edit') {
dispatch(updateRecipient(recipient)); dispatch(updateRecipient(recipient));
} else { } else {
@ -113,8 +117,8 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase,
hostEmail: "", hostEmail: "",
roleName: "", roleName: "",
type: recipientType.SIGNER, type: recipientType.SIGNER,
allowSenderToEdit: false, allowSenderToEdit: true,
allowSenderToDelete: false, allowSenderToDelete: true,
uuid: "", uuid: "",
newOne: false newOne: false
}); });
@ -275,13 +279,13 @@ useEffect(() => {
id="add-recipient-step-one" id="add-recipient-step-one"
className="w-full bg-white border border-gray-200 rounded-lg shadow-md px-6 py-4 space-y-3 relative" className="w-full bg-white border border-gray-200 rounded-lg shadow-md px-6 py-4 space-y-3 relative"
> >
{transactionTemplateToggle === 'templates' && ( {transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES && (
<div className="space-y-1"> <div className="space-y-1">
{/* Top Row: Role Input + Settings */} {/* Top Row: Role Input + Settings */}
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
{/* Role Name Input */} {/* Role Name Input */}
<input <input
required required={recipient.firstName === "" && recipient.lastName === "" && recipient.email === ""}
type="text" type="text"
onFocus={() => setOpenContactModal(true)} onFocus={() => setOpenContactModal(true)}
onBlur={handleContactModalBlur} onBlur={handleContactModalBlur}
@ -413,7 +417,7 @@ useEffect(() => {
onChange={(e) => setRecipients({ ...recipient, firstName: e.target.value })} onChange={(e) => setRecipients({ ...recipient, firstName: e.target.value })}
/> />
<input <input
required required={transactionTemplateToggle !== TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES}
type="text" type="text"
onFocus={() => handleFocus("lastName")} onFocus={() => handleFocus("lastName")}
onBlur={handleContactModalBlur} onBlur={handleContactModalBlur}
@ -423,7 +427,7 @@ useEffect(() => {
onChange={(e) => setRecipients({ ...recipient, lastName: e.target.value })} onChange={(e) => setRecipients({ ...recipient, lastName: e.target.value })}
/> />
<input <input
required required={transactionTemplateToggle !== TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES}
type="email" type="email"
onFocus={() => handleFocus("email")} onFocus={() => handleFocus("email")}
onBlur={handleContactModalBlur} onBlur={handleContactModalBlur}
@ -437,7 +441,7 @@ useEffect(() => {
{recipient.type === recipientType.INPERSONSIGNER && <div className="grid grid-cols-1 sm:grid-cols-2 gap-4"> {recipient.type === recipientType.INPERSONSIGNER && <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<input <input
required required={transactionTemplateToggle !== TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES}
type="text" type="text"
onFocus={() => setOpenContactModal(true)} onFocus={() => setOpenContactModal(true)}
onBlur={handleContactModalBlur} onBlur={handleContactModalBlur}
@ -447,7 +451,7 @@ useEffect(() => {
onChange={(e) => setRecipients({ ...recipient, hostName: e.target.value })} onChange={(e) => setRecipients({ ...recipient, hostName: e.target.value })}
/> />
<input <input
required required={transactionTemplateToggle !== TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES}
type="email" type="email"
onFocus={() => setOpenContactModal(true)} onFocus={() => setOpenContactModal(true)}
onBlur={handleContactModalBlur} onBlur={handleContactModalBlur}

View File

@ -66,12 +66,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ isSend }) => {
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments) // const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
const selectedDocuments = useSelector((state: RootState) => state.documents.documents) const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
console.log("selectedDocuments", selectedDocuments)
const [droppedItems, setDroppedItems] = useState<DroppedItemsObj>({}); const [droppedItems, setDroppedItems] = useState<DroppedItemsObj>({});
console.log("droppedItems", droppedItems)
// const [uniqGroupIds, setUniqGroupIds] = useState<string[]>([]);
useEffect(() => { useEffect(() => {

View File

@ -11,6 +11,7 @@ import { RiResetLeftLine, RiResetRightLine } from 'react-icons/ri'
import { fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice' import { fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'
import { fetchDocuments } from '../../../store/slices/documentSlice' import { fetchDocuments } from '../../../store/slices/documentSlice'
import { useNavigate, useParams } from 'react-router-dom' import { useNavigate, useParams } from 'react-router-dom'
import { Bounce, toast } from 'react-toastify'
interface NavTwoProps { interface NavTwoProps {
isFields: boolean; isFields: boolean;
@ -85,7 +86,22 @@ const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSen
setIsSend(false); setIsSend(false);
} }
setIsSend(!isSend) setIsSend(!isSend)
if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) {
navigate(`/document-completion-screen/${accountId}/${transactionId}`) navigate(`/document-completion-screen/${accountId}/${transactionId}`)
} 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,
});
}
} }
const handleSetIsFields = (setTo: boolean) => { const handleSetIsFields = (setTo: boolean) => {
@ -265,7 +281,9 @@ const NavTwo: React.FC<NavTwoProps> = ({ isFields, setIsFields, isSend, setIsSen
id='send-sign-dash-btn' id='send-sign-dash-btn'
onClick={() => handleSend()} onClick={() => handleSend()}
className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)]'> className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)]'>
<p className='tracking-wide text-white'>Send</p> <p className='tracking-wide text-white'>
{transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES ? "Save" : "Send"}
</p>
<FaSignature className='text-white text-md' /> <FaSignature className='text-white text-md' />
</div> </div>

View File

@ -152,7 +152,7 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
{/* Middle Row: Left - Center (Inputs) - Right */} {/* Middle Row: Left - Center (Inputs) - Right */}
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1">
{/* Left Button */} {/* Left Button */}
<div className="flex flex-col items-center"> {/* <div className="flex flex-col items-center">
<button <button
onClick={() => handleMove('left')} onClick={() => handleMove('left')}
disabled={disabled} disabled={disabled}
@ -163,12 +163,12 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
<FaArrowLeft size={12} /> <FaArrowLeft size={12} />
</button> </button>
<span className="mt-1">Left</span> <span className="mt-1">Left</span>
</div> </div> */}
{/* Center Panel */} {/* Center Panel */}
<div className="relative flex items-center justify-center bg-white rounded-lg px-1 py-2 w-24 "> <div className="relative flex items-center justify-center bg-white rounded-lg px-1 py-2 w-24 ">
{/* Floating dimensions tooltip */} {/* Floating dimensions tooltip */}
<div className="absolute -top-2 left-1/2 -translate-x-1/2 px-2 py-[1px] text-[10px] font-mono text-black z-10 rounded-sm flex items-center gap-[2px]"> {/* <div className="absolute -top-2 left-1/2 -translate-x-1/2 px-2 py-[1px] text-[10px] font-mono text-black z-10 rounded-sm flex items-center gap-[2px]">
<input <input
type="number" type="number"
value={leftValue} value={leftValue}
@ -188,12 +188,12 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
disabled={disabled} disabled={disabled}
className="w-[40px] h-[16px] text-[10px] text-center border-none focus:outline-none text-black font-mono" className="w-[40px] h-[16px] text-[10px] text-center border-none focus:outline-none text-black font-mono"
/> />
</div> </div> */}
</div> </div>
{/* Right Button */} {/* Right Button */}
<div className="flex flex-col items-center"> {/* <div className="flex flex-col items-center">
<button <button
onClick={() => handleMove('right')} onClick={() => handleMove('right')}
disabled={disabled} disabled={disabled}
@ -204,11 +204,11 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
<FaArrowRight size={12} /> <FaArrowRight size={12} />
</button> </button>
<span className="mt-1">Right</span> <span className="mt-1">Right</span>
</div> </div> */}
</div> </div>
{/* Bottom Button */} {/* Bottom Button */}
<div className="flex justify-center me-4"> {/* <div className="flex justify-center me-4">
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
<button <button
onClick={() => handleMove('down')} onClick={() => handleMove('down')}
@ -221,7 +221,7 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
</button> </button>
<span className="mt-1">Bottom</span> <span className="mt-1">Bottom</span>
</div> </div>
</div> </div> */}
</div> </div>
); );
}; };

View File

@ -40,7 +40,7 @@ const Templates = () => {
const getAllTemplates = async () => { const getAllTemplates = async () => {
setLoading(true) setLoading(true)
try { try {
let res = await api.get(`/api/accounts/${activeAccountId}/templates`) let res = await api.get(`/api/accounts/${activeAccountId}/templates?sortBy=lastModifiedAt desc`)
let templateData = res?.data let templateData = res?.data
setTemplates(templateData) setTemplates(templateData)
} catch (error) { } catch (error) {
@ -84,7 +84,10 @@ const Templates = () => {
} }
} }
const handleEditClick = (templateId: string) => {
setShowMenu(null);
navigate(`/process-step-one/${accountId}/template/${templateId}`);
}
return ( return (
<div className="p-3 text-sm"> <div className="p-3 text-sm">
@ -146,7 +149,7 @@ const Templates = () => {
{/* <FaCheck className="text-purple-600 text-xs" /> */} {/* <FaCheck className="text-purple-600 text-xs" /> */}
</div> </div>
</td> </td>
<td className="px-4 py-2 text-sm text-center">{item.name || 'Dummy'}</td> <td className="px-4 py-2 text-sm text-center">{item.name || 'Untitled'}</td>
<td className="px-4 py-2 text-sm text-center">{formatDate(item.createdAt)}</td> <td className="px-4 py-2 text-sm text-center">{formatDate(item.createdAt)}</td>
<td className="px-4 py-2 text-sm text-center">{formatDate(item.lastModifiedAt)}</td> <td className="px-4 py-2 text-sm text-center">{formatDate(item.lastModifiedAt)}</td>
<td className="px-4 py-2 text-sm text-center" onClick={() => handleUseClick(item)}><span className="bg-[var(--lightGreyCol)] cursor-pointer border-gray-300 text-gray-600 hover:bg-gray-100 px-3 py-1 rounded-lg font-semibold text-[12px]">Use</span></td> <td className="px-4 py-2 text-sm text-center" onClick={() => handleUseClick(item)}><span className="bg-[var(--lightGreyCol)] cursor-pointer border-gray-300 text-gray-600 hover:bg-gray-100 px-3 py-1 rounded-lg font-semibold text-[12px]">Use</span></td>
@ -172,10 +175,7 @@ const Templates = () => {
> >
<button <button
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={() => { onClick={() => handleEditClick(item.id)}
setShowMenu(null);
navigate(`/process-step-two/${accountId}/template/${item.id}`);
}}
> >
Edit Edit
</button> </button>

View File

@ -135,7 +135,7 @@ const TransactionView = () => {
<AiFillSignature className='absolute left-[-30px] text-[1rem]' /> <AiFillSignature className='absolute left-[-30px] text-[1rem]' />
<p className='text-black font-semibold'>{recipient.status}</p> <p className='text-black font-semibold'>{recipient.status}</p>
</div> </div>
{recipient.email === user.email && {recipient.email === user.email && recipient.status !== "COMPLETED" &&
<p <p
onClick={() => handleNavigateToSign(recipient.id)} onClick={() => handleNavigateToSign(recipient.id)}
className='text-white rounded-sm bg-[var(--primary)] px-3 py-1 font-semibold'> className='text-white rounded-sm bg-[var(--primary)] px-3 py-1 font-semibold'>

View File

@ -213,7 +213,13 @@ export const fetchDocuments = createAsyncThunk<
`/api/accounts/${accountId}/${toggle}/${transactionId}/documents?include_images=true` `/api/accounts/${accountId}/${toggle}/${transactionId}/documents?include_images=true`
); );
return response.data as Document[]; const data = response.data as Document[];
if (data.length === 0 && window.location.pathname.includes('/process-step-two')) {
window.location.href = window.location.href.replace("process-step-two", "process-step-one") // ✅ navigate using window
}
return data;
} catch (error: any) { } catch (error: any) {
return rejectWithValue(error.response?.data || 'Failed to fetch documents'); return rejectWithValue(error.response?.data || 'Failed to fetch documents');
} }

View File

@ -463,7 +463,13 @@ export const fetchRecipients = createAsyncThunk<
const basePath = `/api/accounts/${accountId}/${toggle}/${transactionId}/recipients`; const basePath = `/api/accounts/${accountId}/${toggle}/${transactionId}/recipients`;
const response = await api.get(basePath); const response = await api.get(basePath);
return response.data as Recipient[]; const data = response.data as Recipient[];
if (data.length === 0 && window.location.pathname.includes('/process-step-two')) {
window.location.href = window.location.href.replace("process-step-two", "process-step-one") // ✅ navigate using window
}
return data as Recipient[];
} catch (err: any) { } catch (err: any) {
return rejectWithValue(err.response?.data || "Failed to fetch recipients"); return rejectWithValue(err.response?.data || "Failed to fetch recipients");
} }
@ -475,17 +481,20 @@ const selectedRecipientsSlice = createSlice({
initialState, initialState,
reducers: { reducers: {
addRecipient: (state, action: PayloadAction<Recipient>) => { addRecipient: (state, action: PayloadAction<Recipient>) => {
for (const recipient of state.recipients) {
if (recipient.email === action.payload.email) { // Checking if meail already exist - START
toast.dismiss(); // for (const recipient of state.recipients) {
toast.error("Email already exists !", { // if (recipient.email === action.payload.email) {
position: "bottom-right", // toast.dismiss();
autoClose: 5000, // toast.error("Email already exists !", {
theme: "light", // position: "bottom-right",
}); // autoClose: 5000,
return state; // theme: "light",
} // });
} // return state;
// }
// }
// Checking if meail already exist - END
if (action.payload.type === recipientType.INPERSONSIGNER) { if (action.payload.type === recipientType.INPERSONSIGNER) {
state.signingOrder = true; state.signingOrder = true;

View File

@ -200,6 +200,12 @@ const JoyrideController = () => {
} }
}; };
if(window.location.href.includes("template")) {
return (<></>)
} else {
return (<></>)
}
return ( return (
<Joyride <Joyride
steps={steps} steps={steps}