Added Tag Attributes functionality

This commit is contained in:
=bhagwanSingh 2025-08-22 15:53:57 +05:30
parent ee6160ff71
commit 8024c76bd3
17 changed files with 267 additions and 125 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title> <title>Sign Made Easy</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -141,7 +141,6 @@ const fitObj = [
const SignDocument = () => { const SignDocument = () => {
const { recipientId } = useParams(); const { recipientId } = useParams();
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -232,8 +231,6 @@ const SignDocument = () => {
}, [droppedItems]) }, [droppedItems])
console.log(droppedItems, "{droppedItems[selectedTag.key][selectedTag.index]}")
useEffect(() => { useEffect(() => {
if (recSignStatus === "COMPLETED") { if (recSignStatus === "COMPLETED") {
// setHideTags(true); // setHideTags(true);
@ -372,9 +369,23 @@ const SignDocument = () => {
}); });
} }
const handleToastError = (msg: string) => {
toast.dismiss();
toast.error(msg, {
position: "top-center",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: false,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
transition: Bounce,
});
}
const handleFormSubmit = () => { const handleFormSubmit = () => {
// e.preventDefault(); // e.preventDefault();
const submitData: TagsSubmitDataObj = { const submitData: TagsSubmitDataObj = {
tags: [] tags: []
@ -398,37 +409,15 @@ const SignDocument = () => {
} }
} }
if (signInitialsCount !== Object.keys(signConfObj).length) { if (signInitialsCount !== Object.keys(signConfObj).length) {
toast.error("Please ensure all signature and initials are signed", { handleToastError("Please ensure all signature and initials are signed")
position: "bottom-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: false,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
transition: Bounce,
});
return; return;
} }
console.log(droppedItems, "Line 417")
for (const tag of droppedItems) { for (const tag of droppedItems) {
if ("required" in tag && tag.required && ((tag.type !== "SIGNATURE" && tag.type !== "INITIALS") && (tag.value === undefined || tag.value === null || tag.value === "" || !("value" in tag)))) {
// if ((tag.type === "SIGNATURE" || tag.subType === "INITIALS") && !tag.value) { handleToastError(`${tag.label} is required`)
if (("required" in tag && tag.required) && ((tag.type !== "SIGNATURE" && tag.type !== "INITIALS") && (tag.value === "" || !("value" in tag)))) {
toast.error(`${tag.label} is required`, {
position: "bottom-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: false,
pauseOnHover: true,
draggable: true,
progress: undefined,
theme: "light",
transition: Bounce,
});
return; return;
} }
@ -453,39 +442,8 @@ const SignDocument = () => {
} }
} }
// for (const tag of tagJson) { console.log(submitData, "submitData Line 491");
// if (data[tag.id as string]) { // return;
// const tagData: TagSubmitData = {
// id: tag.id as string,
// type: tag.type,
// subType: tag.subType ? tag.subType : "",
// signed: true,
// value: data[tag.id as string] as string
// }
// submitData.tags.push(tagData)
// }
// if (tag.type === "SIGNATURE") {
// submitData.tags.push({
// id: tag.id as string,
// type: "SIGNATURE",
// subType: "DRAW",
// signed: true,
// value: ""
// })
// }
// if (tag.type === "INITIALS") {
// submitData.tags.push({
// id: tag.id as string,
// type: "INITIALS",
// subType: "DRAW",
// signed: true,
// value: ""
// })
// }
// }
dispatch(showLoader("Signing ...")); dispatch(showLoader("Signing ..."));
axios.post(`${import.meta.env.VITE_BASE_URI}/api/sign?recipientId=${recipientId}`, submitData, { headers: { "ngrok-skip-browser-warning": "69420", } }) axios.post(`${import.meta.env.VITE_BASE_URI}/api/sign?recipientId=${recipientId}`, submitData, { headers: { "ngrok-skip-browser-warning": "69420", } })
@ -585,6 +543,7 @@ const SignDocument = () => {
const handleTagUpdate = (updateIndx: number, updateValue: string) => { const handleTagUpdate = (updateIndx: number, updateValue: string) => {
console.log(updateIndx, updateValue, "LINEE636")
if (droppedItems && droppedItems.length) { if (droppedItems && droppedItems.length) {
const updatedTag = droppedItems.map((val, indx) => { const updatedTag = droppedItems.map((val, indx) => {
if (indx === updateIndx) { if (indx === updateIndx) {
@ -608,6 +567,60 @@ const SignDocument = () => {
} }
const handlePrefillValues = () => {
const prefilledTags = ["NAME", "EMAIL", "TEXT", "COMPANY", "TEMPORAL"]
const updatedItems = droppedItems.map((item, index) => {
if (prefilledTags.includes(item.type)) {
let value = null;
let isUpdate = false
switch (item.type) {
case "NAME":
value = recipient?.firstName + " " + recipient?.lastName;
isUpdate = true;
break;
case "EMAIL":
value = recipient?.email;
isUpdate = true;
break;
case "TEXT":
value = item.value;
isUpdate = true;
break;
case "COMPANY":
value = item.value;
isUpdate = true;
break;
case "TEMPORAL":
if (item.currentTimeStamp) isUpdate = true;
if (isUpdate) {
value = dayjs(Date.now()).format(item.format);
}
break;
default:
break;
}
return { ...item, value }
} else {
return item
}
})
setDroppedItems(updatedItems);
}
console.log(droppedItems, "droppedItems Line 652")
useEffect(() => {
if (droppedItems.length > 0) {
console.log("Dropped Items handlePrefillValues:")
handlePrefillValues();
}
}, [droppedItems.length])
// HANDLE DROP - START // HANDLE DROP - START
const handleDrop = (event: React.DragEvent<HTMLDivElement>, docId: string, documentId: number, pageIndex: number) => { const handleDrop = (event: React.DragEvent<HTMLDivElement>, docId: string, documentId: number, pageIndex: number) => {
event.preventDefault(); event.preventDefault();
@ -689,7 +702,7 @@ const SignDocument = () => {
isDeleted: false isDeleted: false
} }
if(prepData.type === "NAME") { if (prepData.type === "NAME") {
prepData.firstName = true prepData.firstName = true
prepData.lastName = true prepData.lastName = true
} }
@ -848,18 +861,13 @@ const SignDocument = () => {
// Updated handleRemoveTag function with your existing API logic and group handling // Updated handleRemoveTag function with your existing API logic and group handling
const handleRemoveTag = (key: string, targetIndex: number) => { const handleRemoveTag = (key: string, targetIndex: number) => {
console.log("Removing tag:", key, targetIndex, droppedItems);
const tag: DroppedItem = droppedItems[targetIndex]; const tag: DroppedItem = droppedItems[targetIndex];
const recipientId = tag.recipientId; const recipientId = tag.recipientId;
console.log(tag, "line 776");
(async () => { (async () => {
if ("id" in tag) { if ("id" in tag) {
try { try {
const response = await api.delete(`/api/sign/tags?recipientId=${recipientId}`, { data: [ { tagId: tag.id } ] }) const response = await api.delete(`/api/sign/tags?recipientId=${recipientId}`, { data: [{ tagId: tag.id }] })
if (response.status !== 200) throw "Tag Deletion error" if (response.status !== 200) throw "Tag Deletion error"
} catch (err) { } catch (err) {
return; return;
@ -927,7 +935,6 @@ const SignDocument = () => {
// }, 100); // }, 100);
} }
} else { } else {
console.log("Listening to 895")
// Handle non-group item removal (your existing logic) // Handle non-group item removal (your existing logic)
setDroppedItems(prevState => prevState.filter((item, idx) => { setDroppedItems(prevState => prevState.filter((item, idx) => {
return idx !== targetIndex; return idx !== targetIndex;
@ -1032,10 +1039,11 @@ const SignDocument = () => {
{/* Send */} {/* Send */}
{(!transparentTags && !hideTags) ? <button {(!transparentTags && !hideTags) ? <button
onClick={handleFormSubmit}
// onClick={() => handleFormSubmitBtnClick()} // onClick={() => handleFormSubmitBtnClick()}
className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)] outline-none'> className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)] outline-none'>
<p <p
onClick={handleFormSubmit}
id="sign-client-side-btn" id="sign-client-side-btn"
className='tracking-wide text-white'> className='tracking-wide text-white'>
Sign Sign
@ -1162,7 +1170,7 @@ const SignDocument = () => {
<div <div
id={item.id} id={item.id}
tabIndex={1} tabIndex={1}
className={`h-full text-[14px] font-medium w-full border-slate-100 rounded ${(transparentTags || hideTags) ? "pointer-events-nones" : "bg-slate-200 shadow-sm opacity-80"} flex justify-start items-end`} className={`max-h-max text-[14px] font-medium w-full border-slate-100 rounded ${(transparentTags || hideTags) ? "pointer-events-nones" : "bg-slate-200 shadow-sm opacity-80"} flex justify-start items-end`}
// style={{ height: item.dimensions.height + "px" }} // style={{ height: item.dimensions.height + "px" }}
> >
@ -1176,7 +1184,8 @@ const SignDocument = () => {
!item.multiLine && !item.maskType && <p !item.multiLine && !item.maskType && <p
// name={item.id} // name={item.id}
// style={{ height: item.dimensions.height + "px" }} // style={{ height: item.dimensions.height + "px" }}
onInput={(e) => { onBlur={(e) => {
console.log("Text Input Event Triggered");
const val = e.currentTarget.textContent; const val = e.currentTarget.textContent;
handleTagUpdate(index, val as string); handleTagUpdate(index, val as string);
}} }}
@ -1185,24 +1194,25 @@ const SignDocument = () => {
}} }}
contentEditable={true} contentEditable={true}
className="outline-none whitespace-nowrap w-full max-h-max" className="outline-none whitespace-nowrap w-full max-h-max"
></p> >{item.value ?? ""}</p>
} }
{ {
item.multiLine && !item.maskType && <p item.multiLine && !item.maskType && <p
onInput={(e) => { suppressContentEditableWarning
onBlur={(e) => {
const val = e.currentTarget.textContent; const val = e.currentTarget.textContent;
handleTagUpdate(index, val as string); handleTagUpdate(index, val as string);
}} }}
contentEditable={true} contentEditable={true}
className="w-full outline-none max-h-max" className="w-full outline-none max-h-max"
></p> >{item.value ?? ""}</p>
} }
{ {
"maskType" in item && "maskCharacter" in item && item.maskType && <p "maskType" in item && "maskCharacter" in item && item.maskType && <p
onInput={(e) => { // onBlur={(e) => {
const val = e.currentTarget.textContent; // const val = e.currentTarget.textContent;
handleTagUpdate(index, val as string); // handleTagUpdate(index, val as string);
}} // }}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") e.preventDefault(); if (e.key === "Enter") e.preventDefault();
}} }}
@ -1210,6 +1220,7 @@ const SignDocument = () => {
// }} // }}
onBlur={(e) => { onBlur={(e) => {
let val = e.target.textContent; let val = e.target.textContent;
handleTagUpdate(index, val as string);
if (typeof item.maskType === "string" && typeof val === "string" && item.maskCharacter) { if (typeof item.maskType === "string" && typeof val === "string" && item.maskCharacter) {
val = val.replace(/ /g, ""); val = val.replace(/ /g, "");
e.target.textContent = maskUtils(item.maskType, val, item.maskCharacter) e.target.textContent = maskUtils(item.maskType, val, item.maskCharacter)
@ -1268,7 +1279,7 @@ const SignDocument = () => {
// contentEditable={true} // contentEditable={true}
className="outline-none whitespace-nowrap w-full max-h-max" className="outline-none whitespace-nowrap w-full max-h-max"
> >
{recipient?.email || ""} {item.value ?? ""}
</p> </p>
} }
</div> </div>
@ -1304,10 +1315,7 @@ const SignDocument = () => {
// contentEditable={true} // contentEditable={true}
className="outline-none whitespace-nowrap w-full max-h-max" className="outline-none whitespace-nowrap w-full max-h-max"
> >
{item.firstName && recipient?.firstName+" " } {item.value ?? ""}
{item.lastName && recipient?.lastName }
</p> </p>
} }
</div> </div>
@ -1325,6 +1333,10 @@ const SignDocument = () => {
> >
{ {
<input <input
onInput={(e) => {
const val = e.currentTarget.value;
handleTagUpdate(index, val as string);
}}
type="number" type="number"
className="outline-none h-full max-w-max" className="outline-none h-full max-w-max"
/> />
@ -1496,6 +1508,10 @@ const SignDocument = () => {
<select <select
name={item.id} name={item.id}
className="outline-none" className="outline-none"
onInput={(e) => {
const val = e.currentTarget.value;
handleTagUpdate(index, val as string);
}}
> >
<option value="">Select</option> <option value="">Select</option>
{item.options && item.options.map((obj, indx) => { {item.options && item.options.map((obj, indx) => {
@ -1529,7 +1545,7 @@ const SignDocument = () => {
<p <p
style={{ opacity: `${item.id === isDateOpen ? "0" : "100"}` }} style={{ opacity: `${item.id === isDateOpen ? "0" : "100"}` }}
className="w-full h-full rounded pl-1 pt-1 whitespace-nowrap"> className="w-full h-full rounded pl-1 pt-1 whitespace-nowrap">
{(item.id && item.id in dateValues) ? dateValues[item.id] : item.dateFormat?.toLowerCase()} {(item.id && item.id in dateValues) ? dateValues[item.id] : item.value ? item.value : item.dateFormat?.toLowerCase()}
</p> </p>
<input <input
name={item.id} name={item.id}
@ -1550,6 +1566,10 @@ const SignDocument = () => {
[item.id as string]: dayjs(val).format(item.format) [item.id as string]: dayjs(val).format(item.format)
}) })
}} }}
onInput={(e) => {
const val = e.currentTarget.value;
handleTagUpdate(index, val as string);
}}
style={{ opacity: `${item.id === isDateOpen ? "100" : "0"}` }} style={{ opacity: `${item.id === isDateOpen ? "100" : "0"}` }}
className="w-full px-2 outline-none absolute" className="w-full px-2 outline-none absolute"
onClick={() => { onClick={() => {

View File

@ -0,0 +1,25 @@
const prefilledTags = ["NAME", "EMAIL", "TEXT", "COMPANY", "TEMPORAL"]
const handlePrefillValues = () => {
for(const droppedItem of droppedItems) {
if (prefilledTags.includes(droppedItem.type) && "value" in droppedItem && droppedItem.value.length > 0) {
handleTagUpdate(droppedItem.index, droppedItem.value);
}
}
}
const handleTagUpdate = (updateIndx: number, updateValue: string) => {
if (droppedItems && droppedItems.length) {
const updatedTag = droppedItems.map((val, indx) => {
if (indx === updateIndx) {
return {
...val,
value: updateValue
}
}
return val;
})
setDroppedItems(updatedTag);
}
}

View File

@ -216,10 +216,17 @@ const ProcessStepOne = () => {
dispatch(showLoader("Processing Recipients...")); dispatch(showLoader("Processing Recipients..."));
let minRoutingOrder = 1
selectedRecipients.forEach((recipient) => {
if (recipient.routingOrder ?? 1 < minRoutingOrder) {
minRoutingOrder = recipient.routingOrder ?? 1;
}
});
const recipientsData = selectedRecipients.map((recipient, index) => ({ const recipientsData = selectedRecipients.map((recipient, index) => ({
id: "id" in recipient ? recipient.id : null, id: "id" in recipient ? recipient.id : null,
routingOrder: "routingOrder" in recipient && recipient.routingOrder ? recipient.routingOrder : 1, routingOrder: "routingOrder" in recipient && recipient.routingOrder ? recipient.routingOrder : 1,
provideOwnTags: userData.email === recipient.email ? true : false, provideOwnTags: userData.email === recipient.email && (recipient.routingOrder ?? 1) === minRoutingOrder ? true : false,
type: recipient.type, type: recipient.type,
firstName: recipient.firstName, firstName: recipient.firstName,
lastName: recipient.lastName, lastName: recipient.lastName,

View File

@ -235,7 +235,7 @@ const DocumentContainer: React.FC<DocumentContainerProps> = ({ transactionSaving
return tagsData; return tagsData;
}; };
const sendTagsToRecipients = async ( const sendTagsToRecipients = async (
tagsData: Record<number, any[]>, tagsData: Record<number, any[]>,
accountId: string, accountId: string,
transactionId: string transactionId: string

View File

@ -83,7 +83,10 @@ const FieldList: React.FC<FieldListProps> = ({ color, recipientId, recipientIndx
.then((res) => { .then((res) => {
const priorityOrder = ['SIGNATURE', 'INITIALS', 'STAMP']; const priorityOrder = ['SIGNATURE', 'INITIALS', 'STAMP'];
const data = res.data; const data = res.data.filter((obj, indx) => {
if (obj.type === "STAMP" || obj.type === "RADIO" || obj.type === "CHECKBOX" || obj.type === "ATTACHMENT") return false
return true
});
// Separate prioritized and other tags // Separate prioritized and other tags
const prioritized = data.filter((tag: any) => const prioritized = data.filter((tag: any) =>

View File

@ -3,7 +3,7 @@ import { RootState } from '../../../store/store';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import FieldList from './FieldList'; import FieldList from './FieldList';
import { addColorsToRecipient, fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'; import { addColorsToRecipient, fetchRecipients, recipientType, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice';
import { IoCheckmark } from 'react-icons/io5' import { IoCheckmark } from 'react-icons/io5'
import { MdKeyboardArrowDown } from 'react-icons/md' import { MdKeyboardArrowDown } from 'react-icons/md'
@ -94,6 +94,7 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIn
<div <div
className={`absolute w-full shadow-md rounded-md mt-1 px-2 py-1 z-20 bg-white ${dropDownToggle ? '' : 'hidden'}`}> className={`absolute w-full shadow-md rounded-md mt-1 px-2 py-1 z-20 bg-white ${dropDownToggle ? '' : 'hidden'}`}>
{selectedRecipients.length && selectedRecipients.map((recipient, indx) => { {selectedRecipients.length && selectedRecipients.map((recipient, indx) => {
if (recipient.type === recipientType.CC) return null; // Skip CC recipients
return (<div return (<div
key={indx} key={indx}
className='flex items-center justify-between gap-2 w-full p-2 text-xs rounded-sm cursor-pointer hover:bg-slate-100' className='flex items-center justify-between gap-2 w-full p-2 text-xs rounded-sm cursor-pointer hover:bg-slate-100'

View File

@ -8,7 +8,7 @@ import { IoIosDocument } from 'react-icons/io'
import { LuTableOfContents } from 'react-icons/lu' import { LuTableOfContents } from 'react-icons/lu'
import { MdKeyboardArrowDown } from 'react-icons/md' import { MdKeyboardArrowDown } from 'react-icons/md'
import { RiResetRightLine } from 'react-icons/ri' import { RiResetRightLine } from 'react-icons/ri'
import { fetchRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice' import { fetchRecipients, recipientType, 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' import { Bounce, toast } from 'react-toastify'
@ -91,7 +91,8 @@ const NavTwo: React.FC<NavTwoProps> = ({ transactionSaving, isFields, setIsField
let userEmailPresent = { let userEmailPresent = {
present: false, present: false,
routingOrder: 0, routingOrder: 0,
recipientId: 0 recipientId: 0,
type: recipientType.CC
}; };
// Getting Minimum RoutingOrder // Getting Minimum RoutingOrder
for (let i = 0; i < selectedRecipients.length; i++) { for (let i = 0; i < selectedRecipients.length; i++) {
@ -104,10 +105,11 @@ const NavTwo: React.FC<NavTwoProps> = ({ transactionSaving, isFields, setIsField
userEmailPresent.present = true; userEmailPresent.present = true;
userEmailPresent.routingOrder = recipient.routingOrder; userEmailPresent.routingOrder = recipient.routingOrder;
userEmailPresent.recipientId = recipient.id; userEmailPresent.recipientId = recipient.id;
userEmailPresent.type = recipient.type;
} }
} }
if (userEmailPresent.present && userEmailPresent.routingOrder === minRoutingOrder) { if (userEmailPresent.present && userEmailPresent.routingOrder === minRoutingOrder && userEmailPresent.type !== recipientType.CC && userEmailPresent.type !== recipientType.INPERSONSIGNER) {
setSelfUserSign({ sign: true, recipientId: userEmailPresent.recipientId }) setSelfUserSign({ sign: true, recipientId: userEmailPresent.recipientId })
} }
} }
@ -126,7 +128,7 @@ const NavTwo: React.FC<NavTwoProps> = ({ transactionSaving, isFields, setIsField
setTimeout(() => { setTimeout(() => {
if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) { if (transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) {
if (isSignSend) { if (isSignSend) {
navigate(`./../../../sign/${selfUserSign.recipientId}`,{ replace: true }); window.open(`./../../../sign/${selfUserSign.recipientId}`, "_self");
} }
else { else {
navigate(`/document-completion-screen/${accountId}/${transactionId}`, { replace: true }) navigate(`/document-completion-screen/${accountId}/${transactionId}`, { replace: true })
@ -281,7 +283,6 @@ const NavTwo: React.FC<NavTwoProps> = ({ transactionSaving, isFields, setIsField
<MdKeyboardArrowDown className={`${nav2DocToggle ? 'rotate-180' : ''} transition-all`} /> <MdKeyboardArrowDown className={`${nav2DocToggle ? 'rotate-180' : ''} transition-all`} />
</div> </div>
<div <div
className={`absolute max-w-max shadow rounded-md mt-1 px-2 py-1 z-20 bg-white ${nav2DocToggle ? '' : 'hidden'}`}> className={`absolute max-w-max shadow rounded-md mt-1 px-2 py-1 z-20 bg-white ${nav2DocToggle ? '' : 'hidden'}`}>
@ -312,7 +313,7 @@ const NavTwo: React.FC<NavTwoProps> = ({ transactionSaving, isFields, setIsField
return (<div key={indx} className='flex items-center justify-between gap-2 w-full p-2 text-xs rounded-sm cursor-default'> return (<div key={indx} className='flex items-center justify-between gap-2 w-full p-2 text-xs rounded-sm cursor-default'>
<div className='flex items-center gap-1.5'> <div className='flex items-center gap-1.5'>
<p className='w-3 h-3 rounded-sm' style={{ background: recipient.color }}></p> <p className='w-3 h-3 rounded-sm' style={{ background: recipient.color }}></p>
<p className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[12ch]'>{recipient.firstName} {recipient.lastName}</p> <p className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[22ch]'>{recipient.type + " | "} {recipient.firstName} {recipient.lastName}</p>
</div> </div>
</div>) </div>)
})} })}

View File

@ -34,9 +34,6 @@ const ProcessStepTwo = () => {
}, 800) }, 800)
}, []) }, [])
return ( return (
<div className={`min-w-full relative h-screen bg-slate-100 overflow-hidden ${(globalLoader.isLoading) ? 'pointer-events-none' : ''}`}> <div className={`min-w-full relative h-screen bg-slate-100 overflow-hidden ${(globalLoader.isLoading) ? 'pointer-events-none' : ''}`}>
<NavTwo transactionSaving={transactionSaving} isFields={isFields} setIsFields={setIsFields} isSend={isSend} setIsSend={setIsSend} /> <NavTwo transactionSaving={transactionSaving} isFields={isFields} setIsFields={setIsFields} isSend={isSend} setIsSend={setIsSend} />

View File

@ -2,6 +2,8 @@
import { MdKeyboardArrowDown } from 'react-icons/md'; import { MdKeyboardArrowDown } from 'react-icons/md';
import { DROPDOWNTOGGLE, MASKCHARACTER, MASKTYPE, NUMBER, SELECT, TEMPORAL, TEXT } from './RightSideBar'; import { DROPDOWNTOGGLE, MASKCHARACTER, MASKTYPE, NUMBER, SELECT, TEMPORAL, TEXT } from './RightSideBar';
import { DroppedItem, TagsTypes } from '../../../../utlity/types'; import { DroppedItem, TagsTypes } from '../../../../utlity/types';
import { dateFormats, timeFormats } from '../../../../utlity/dateTimeFormats';
import { FaCommentsDollar } from 'react-icons/fa';
interface CommonAttrDropDownInt { interface CommonAttrDropDownInt {
selectedType: any; selectedType: any;
@ -162,8 +164,27 @@ const CommonAttrDropDown: React.FC<CommonAttrDropDownInt> = ({ selectedType, sel
} }
handlePropertyChange(subTypeKey, value as boolean); handlePropertyChange(subTypeKey, value as boolean);
} } else if (selectedTag.type.toLowerCase() === TagsTypes.TEMPORAL) {
else { handlePropertyChange(subTypeKey, selectedType[val as keyof typeof selectedType]);
if (subTypeKey === "dateFormat") {
let format = null;
switch (value) {
case "date":
format = dateFormats[0].format;
break;
case "time":
format = timeFormats[0].format;
break;
case "date_time":
format = dateFormats[0].format + " " + timeFormats[0].format;
break;
default:
format = dateFormats[0].format;
}
console.log(format, "line 184");
handlePropertyChange("format", format as string);
}
} else {
handlePropertyChange(subTypeKey, selectedType[val as keyof typeof selectedType]); handlePropertyChange(subTypeKey, selectedType[val as keyof typeof selectedType]);
} }
}} }}

View File

@ -3,6 +3,7 @@ import { IoCheckmark } from 'react-icons/io5';
import { MdKeyboardArrowDown } from 'react-icons/md'; import { MdKeyboardArrowDown } from 'react-icons/md';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { RootState } from '../../../../store/store'; import { RootState } from '../../../../store/store';
import { recipientType } from '../../../../store/slices/selectedRecipientsSlice';
interface SelectedRecipientsIntr { interface SelectedRecipientsIntr {
@ -67,6 +68,7 @@ const RecipientsAttrCon: React.FC<RecipientsAttrConIntr> = ({ selectedRecipientI
<div <div
className={`absolute w-full shadow-md rounded-md mt-1 px-2 py-1 z-20 bg-white ${dropDownToggle ? '' : 'hidden'}`}> className={`absolute w-full shadow-md rounded-md mt-1 px-2 py-1 z-20 bg-white ${dropDownToggle ? '' : 'hidden'}`}>
{selectedRecipients.length && selectedRecipients.map((recipient, indx) => { {selectedRecipients.length && selectedRecipients.map((recipient, indx) => {
if (recipient.type === recipientType.CC) return null; // Skip CC recipients
return (<div return (<div
key={indx} key={indx}
className='flex items-center justify-between gap-2 w-full p-2 text-xs rounded-sm cursor-pointer hover:bg-slate-100' className='flex items-center justify-between gap-2 w-full p-2 text-xs rounded-sm cursor-pointer hover:bg-slate-100'

View File

@ -18,6 +18,7 @@ import MaxMinLenAttr from "./MaxMinLenAttr";
import RecipientsAttrCon from "./RecipientsAttrCon"; import RecipientsAttrCon from "./RecipientsAttrCon";
import MovableFieldContainer from "./MovableField"; import MovableFieldContainer from "./MovableField";
import DirectionalControls from "./DirectionalControls"; import DirectionalControls from "./DirectionalControls";
import TextValueFIll from "./TextValueFIll";
// interface DirectionalControlsProps { // interface DirectionalControlsProps {
// onMove: (direction: 'up' | 'down' | 'left' | 'right') => void; // onMove: (direction: 'up' | 'down' | 'left' | 'right') => void;
@ -292,7 +293,7 @@ const RightSideBar: React.FC<DroppedItemProps> = ({ droppedItems, selectedTag, s
if (selectedTag.label === TagsTypes.TEXT && !("multiLine" in selectedTag)) { if (selectedTag.label === TagsTypes.TEXT && !("multiLine" in selectedTag)) {
handlePropertyChange("multiLine", true); handlePropertyChange("multiLine", true);
} }
}, []) }, [selectedTag])
const handleDecimalPlace = (e: React.ChangeEvent<HTMLInputElement>) => { const handleDecimalPlace = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = Number(e.target.value) const val = Number(e.target.value)
@ -527,6 +528,15 @@ const RightSideBar: React.FC<DroppedItemProps> = ({ droppedItems, selectedTag, s
handlePropertyChange={handlePropertyChange} /> handlePropertyChange={handlePropertyChange} />
} }
{/* Mask Type */}
{
selectedTag.type.toLowerCase() === TagsTypes.TEXT &&
<TextValueFIll
value={selectedTag.value ?? ""}
multiLine={selectedTag.multiLine ?? false}
handlePropertyChange={handlePropertyChange}
/>
}
{/* TEMPORAL DATE FORMAT - START */} {/* TEMPORAL DATE FORMAT - START */}
{ {

View File

@ -27,18 +27,29 @@ const TemporalDateAttr: React.FC<TemporalDateAttrInt> = ({ dateFormat, format, s
console.log(dateFormat, format, "dateFormat Line 26", selectedTag) console.log(dateFormat, format, "dateFormat Line 26", selectedTag)
const [is24Hrs, setIs24Hrs] = useState<boolean>(true); let is24HourFormat = true
if (selectedTag.format && selectedTag.format.split(/hh|mm/).length > 0) {
if ((selectedTag.format.split(/:mm/)[0]).at(-1) === "h") {
is24HourFormat = false;
}
}
const [is24Hrs, setIs24Hrs] = useState<boolean>(is24HourFormat);
const handleIs24HrsClick = (checked: boolean) => { const handleIs24HrsClick = (checked: boolean) => {
if (dateFormat === TEMPORAL.TIME) { if (dateFormat === TEMPORAL.TIME) {
if (!checked) { if (!checked) {
handlePropertyChange("format", timeFormats[1].format, is24Hrs) handlePropertyChange("format", timeFormats[1].format, !is24Hrs)
} else { } else {
handlePropertyChange("format", timeFormats[0].format, is24Hrs) handlePropertyChange("format", timeFormats[0].format, is24Hrs)
} }
} }
else if (dateFormat === TEMPORAL.DATE_TIME) { else if (dateFormat === TEMPORAL.DATE_TIME) {
handlePropertyChange("format", (format.split(" ").slice(0, -1).join(" ")), !is24Hrs) const timeFormat = format = timeFormats[checked ? 0 : 1].format;
const dateFormat = selectedTag.format && selectedTag.format.split(/hh|HH/)[0]
handlePropertyChange("format", dateFormat+" "+timeFormat, checked ? is24Hrs : !is24Hrs)
} }
setIs24Hrs(checked) setIs24Hrs(checked)
} }
@ -87,12 +98,12 @@ const TemporalDateAttr: React.FC<TemporalDateAttrInt> = ({ dateFormat, format, s
let val = valObj["format"] let val = valObj["format"]
if (dateFormat === TEMPORAL.DATE_TIME) { if (dateFormat === TEMPORAL.DATE_TIME) {
if (is24Hrs) { if (is24Hrs) {
val = val + " " + timeFormats[0].format; val = val+" "+timeFormats[0].format;
} else { } else {
val = val + " " + timeFormats[1].format; val = val+" "+timeFormats[1].format;
} }
} }
console.log('line 89', val)
return ( return (
<div <div
key={indx} key={indx}

View File

@ -0,0 +1,35 @@
import React, { useState } from 'react'
interface TextValueFIllProps {
value: string | number;
multiLine: boolean;
handlePropertyChange: (changeTo: string, changeWith: string | boolean | number) => void;
}
const TextValueFIll: React.FC<TextValueFIllProps> = ({ value = "", multiLine, handlePropertyChange }) => {
const [textValue, setTextValue] = useState<string | number>(value);
return (
<div className="flex flex-col">
<label htmlFor="textValue">Value</label>
{multiLine ?
<textarea
onBlur={() => handlePropertyChange("value", textValue)}
id="textValue"
value={textValue}
className="outline-none border-1 rounded px-2 py-1 border-slate-200"
onChange={e => setTextValue(e.target.value)}
></textarea>
: <input
onBlur={() => handlePropertyChange("value", textValue)}
id="textValue"
value={textValue}
className="outline-none border-1 rounded px-2 py-1 border-slate-200"
onChange={e => setTextValue(e.target.value)}
/>
}
</div>
)
}
export default TextValueFIll

View File

@ -467,9 +467,9 @@ export const fetchRecipients = createAsyncThunk<
let data = response.data as Recipient[]; let data = response.data as Recipient[];
data = data.filter((recipient, indx) => { // data = data.filter((recipient, indx) => {
return recipient.type !== "CC"; // return recipient.type !== "CC";
}) // })
if (data.length === 0 && window.location.pathname.includes('/process-step-two')) { 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 window.location.href = window.location.href.replace("process-step-two", "process-step-one") // ✅ navigate using window

View File

@ -10,12 +10,21 @@ const type_last_four = (str: string, changeWith: string): string => {
}; };
const type_credit_card = (str: string, changeWith: string): string => { const type_credit_card = (str: string, changeWith: string): string => {
if (str.length !== 16) return str; if (str.length !== 15 && str.length !== 16) return str;
// console.log(str.replace(/\d(?=\d{4})/g, "*"), "line 11", str)
// return str.replace(/\d(?=\d{4})/g, `${changeWith}`); const last4 = str.slice(-4);
return `${changeWith.repeat(4)} ${changeWith.repeat(4)} ${changeWith.repeat(4)} ${str.slice(-4)}` const masked = str.slice(0, -4).replace(/\d/g, changeWith);
if (str.length === 16) {
// Format: XXXX XXXX XXXX 1234
return masked.slice(0, 4) + " " + masked.slice(4, 8) + " " + masked.slice(8, 12) + " " + last4;
} else {
// 15-digit (Amex): XXXX XXXXXX X1234
return masked.slice(0, 4) + " " + masked.slice(4, 10) + " " + masked.slice(10, 11) + last4;
}
}; };
const type_national_insurance = (str: string, changeWith: string): string => { const type_national_insurance = (str: string, changeWith: string): string => {
// str = str.slice(0, -1).replace(/[A-Za-z0-9]/g, "*") + str.slice(-1); // str = str.slice(0, -1).replace(/[A-Za-z0-9]/g, "*") + str.slice(-1);
// return str.replace(/\*/g, changeWith); // return str.replace(/\*/g, changeWith);
@ -40,7 +49,7 @@ const type_social_insurance = (str: string, changeWith: string): string => {
}; };
const maskUtils = (type: string, original: string, changeWith: string): string => { const maskUtils = (type: string, original: string, changeWith: string): string => {
switch (changeWith) { switch (changeWith) {
case MASKCHARACTER.ASTERISK: case MASKCHARACTER.ASTERISK:
changeWith = "*"; changeWith = "*";

View File

@ -137,13 +137,13 @@ export interface StylesInterface {
textSize: number textSize: number
} }
export interface TagOptionAttrType{ export interface TagOptionAttrType {
label: string; label: string;
value: string; value: string;
pageIndex?: number; pageIndex?: number;
selected?: boolean; selected?: boolean;
readOnly?: boolean; readOnly?: boolean;
dimensions?: Record<string, any>; dimensions?: Record<string, any>;
} }