This commit is contained in:
Bhagwan Singh 2026-05-14 12:18:50 +05:30
parent aed2aee60b
commit f527539b96
5 changed files with 234 additions and 200 deletions

View File

@ -9,7 +9,7 @@
"build:skip-check": "tsc --noEmit && vite build", "build:skip-check": "tsc --noEmit && vite build",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview", "preview": "vite preview",
"deploy": "npm run build:skip-check && scp -i private\\SME-Web.pem -r dist root@52.10.89.255:/var/www/sme" "deploy": "npm run build:skip-check && scp -i private\\SME-Web.pem -r dist root@100.22.185.166:/var/www/sme"
}, },
"dependencies": { "dependencies": {
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",

View File

@ -87,6 +87,7 @@ const ProcessStepOne = () => {
const [templateName, setTemplateName] = useState('Untitled'); const [templateName, setTemplateName] = useState('Untitled');
const [isTyping, setIsTyping] = useState(false); const [isTyping, setIsTyping] = useState(false);
const [signingOrder, setSigningOrder] = useState(false)
// Optional: use a timer to detect pause in typing // Optional: use a timer to detect pause in typing
let typingTimeout = null; let typingTimeout = null;
@ -512,6 +513,24 @@ const ProcessStepOne = () => {
<label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600"> <label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600">
I AM THE ONLY SIGNER</label> I AM THE ONLY SIGNER</label>
</div> */} </div> */}
{/* HERE WE AREEEEEEEE */}
<div className="flex items-center">
<input
// checked={signType == SignTypeEnum.selfSign}
onChange={() => setSigningOrder(!signingOrder)}
id="default-checkbox" type="checkbox" value="" className="w-4 h-4 checked:bg-[var(--mainPrimaryBtn)] bg-gray-100 border-gray-300 rounded-sm outline-none"
/>
<label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600">
Set signing order
</label>
</div>
{/* HERE WE AREEEEEEEE */}
</div> </div>
} }
@ -526,6 +545,7 @@ const ProcessStepOne = () => {
// setEditRecipientsModal={setEditRecipientsModal} // setEditRecipientsModal={setEditRecipientsModal}
submitError={submitError} submitError={submitError}
isModal={false} isModal={false}
signingOrder={signingOrder}
/> />
</> </>
} }

View File

@ -53,6 +53,7 @@ type CardsData = {
transactionTemplateToggle: string; transactionTemplateToggle: string;
editRecipientCurrentEmail: string; editRecipientCurrentEmail: string;
setEditRecipientCurrentEmail: (email: string) => void; setEditRecipientCurrentEmail: (email: string) => void;
signingOrder: boolean;
}; };
const RecipientCards = ({ const RecipientCards = ({
@ -62,6 +63,7 @@ const RecipientCards = ({
editRecipient, editRecipient,
editRecipientCurrentEmail, editRecipientCurrentEmail,
setEditRecipientCurrentEmail, setEditRecipientCurrentEmail,
signingOrder // signing order - Routing order
}: CardsData) => { }: CardsData) => {
const templateId = useParams<{ templateId: string }>().templateId; const templateId = useParams<{ templateId: string }>().templateId;
let { accountId, transactionId } = useParams<{ accountId: string; transactionId: string }>(); let { accountId, transactionId } = useParams<{ accountId: string; transactionId: string }>();
@ -389,230 +391,239 @@ const RecipientCards = ({
> >
<div <div
key={recipient.email + idx} key={recipient.email + idx}
className={`relative flex flex-col lg:flex-row lg:items-center justify-between px-2 sm:px-3 lg:px-2 py-3 transition-colors gap-3 lg:gap-0 ${(recipient.uuid || recipient.id) == editRecipientCurrentEmail className={`relative flex flex-col lg:flex-row lg:items-center justify-between transition-colors gap-3 lg:gap-0 ${(recipient.uuid || recipient.id) == editRecipientCurrentEmail
? "bg-neutral-100 opacity-40" ? "bg-neutral-100 opacity-40"
: "" : ""
}`} }`}
> >
{/* Main Content Section */}
<div className="flex items-start sm:items-center gap-2 sm:gap-3 min-w-0 flex-1">
{/* Order Number */}
{recipients.length > 1 ? (
<div className="relative flex-shrink-0">
<input
type="text"
value={recipient.routingOrder == 0 ? "" : recipient.routingOrder}
onChange={(e) => {
const value = e.target.value;
if (/^\d*$/.test(value)) {
const num = Number(value);
handleOrderChange(recipient.id || recipient.uuid, num);
}
}}
onKeyDown={(e) => {
if (!/[0-9]/.test(e.key) && e.key != "Backspace" && e.key != "Tab") {
e.preventDefault();
}
}}
className="w-8 sm:w-10 h-6 text-center rounded text-sm text-gray-600 focus:outline-none focus:ring-[var(--primary)] border border-dashed border-gray-400"
/>
{idx == 0 && recipients.length > 1 && <p className="group">
<BiSolidHelpCircle className="absolute top-[-12px] right-[-10px] text-gray-400 cursor-pointer" />
{/* Tooltip */} <>
<span className="absolute top-[2px] left-[48px] hidden group-hover:block w-56 p-2 rounded-lg text-sm bg-white text-slate-600 shadow-lg z-10"> {/* Main Content Section */}
Signing order: Recipients receive emails one after another. <div className="flex items-start sm:items-center gap-2 sm:gap-3 min-w-0 flex-1 pl-8">
Same order means they all receive at once. {/* Order Number */}
</span> {recipients.length > 1 && signingOrder ? (
</p>} <div className="relative flex-shrink-0">
</div> <input
) : ( type="text"
<input value={recipient.routingOrder == 0 ? "" : recipient.routingOrder}
type="number" onChange={(e) => {
disabled const value = e.target.value;
value={recipient.routingOrder} if (/^\d*$/.test(value)) {
min={1} const num = Number(value);
onChange={(e) => handleOrderChange(recipient.id || recipient.uuid, num);
handleOrderChange( }
recipient.id || recipient.uuid, }}
Number(e.target.value) onKeyDown={(e) => {
) if (!/[0-9]/.test(e.key) && e.key != "Backspace" && e.key != "Tab") {
} e.preventDefault();
className="w-6 sm:w-8 text-center rounded text-sm text-gray-700 border-none focus:outline-none focus:ring-[var(--primary)] flex-shrink-0" }
/> }}
)} className="w-8 sm:w-10 h-6 text-center rounded text-sm text-gray-600 focus:outline-none focus:ring-[var(--primary)] border border-dashed border-gray-400"
/>
{idx == 0 && recipients.length > 1 && <p className="group">
<BiSolidHelpCircle className="absolute top-[-12px] right-[-10px] text-gray-400 cursor-pointer" />
{/* Avatar */} {/* Tooltip */}
{/* <div className="flex-shrink-0 w-7 h-7 sm:w-8 sm:h-8 rounded-full bg-[var(--primary)] text-white flex items-center justify-center font-semibold text-xs select-none"> <span className="absolute top-[2px] left-[48px] hidden group-hover:block w-56 p-2 rounded-lg text-sm bg-white text-slate-600 shadow-lg z-10">
Signing order: Recipients receive emails one after another.
Same order means they all receive at once.
</span>
</p>}
</div>
) : (
<>{signingOrder &&
<input
type="number"
disabled
value={recipient.routingOrder}
min={1}
onChange={(e) =>
handleOrderChange(
recipient.id || recipient.uuid,
Number(e.target.value)
)
}
className="w-6 sm:w-8 text-center rounded text-sm text-gray-700 border-none focus:outline-none focus:ring-[var(--primary)] flex-shrink-0"
/>}
</>
)}
{/* Avatar */}
{/* <div className="flex-shrink-0 w-7 h-7 sm:w-8 sm:h-8 rounded-full bg-[var(--primary)] text-white flex items-center justify-center font-semibold text-xs select-none">
{getInitials(`${recipient.firstName} ${recipient.lastName || ""}`)} {getInitials(`${recipient.firstName} ${recipient.lastName || ""}`)}
</div> */} </div> */}
{/* Form Fields Section */} {/* Form Fields Section */}
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
{/* Role Name and Permissions Row */} {/* Role Name and Permissions Row */}
<div className="flex flex-col lg:flex-row lg:items-center gap-2 mb-2"> <div className="flex flex-col lg:flex-row lg:items-center gap-2 mb-2">
{<div className="flex lg:w-1/2"> {<div className="flex lg:w-1/2">
{transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES ? {transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES ?
<div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "roleName", "Role name")}
</div>
:
<div className="font-semibold text-gray-800 text-sm px-2">
{recipient.roleName}
</div>
}
</div>}
{/* Permissions - Mobile: Below role, Desktop: Right side */}
<div className="lg:w-1/2 lg:flex lg:items-center lg:justify-end">
{transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES && (
<div className="flex ps-2.5 sm:flex-row items-start sm:items-center gap-2 sm:gap-4">
<label className="flex items-center gap-1 text-xs sm:text-sm text-gray-700">
<input
type="checkbox"
checked={recipient.allowSenderToEdit}
onChange={() =>
dispatch(
updateRecipient({
...recipient,
allowSenderToEdit: !recipient.allowSenderToEdit,
uuid: getRecipientIdentifier(recipient),
})
)
}
className="accent-blue-500 w-3 h-3 sm:w-4 sm:h-4"
/>
<span className="whitespace-nowrap">Allow Edit</span>
</label>
<label className="flex items-center gap-1 text-xs sm:text-sm text-gray-700">
<input
type="checkbox"
checked={recipient.allowSenderToDelete}
onChange={() =>
dispatch(
updateRecipient({
...recipient,
allowSenderToDelete: !recipient.allowSenderToDelete,
uuid: getRecipientIdentifier(recipient),
})
)
}
className="accent-red-500 w-3 h-3 sm:w-4 sm:h-4"
/>
<span className="whitespace-nowrap">Allow Delete</span>
</label>
</div>
)}
</div>
</div>
{/* Name and Email Fields */}
<div className="mb-2 w-[92%]">
{/* Mobile: Stack vertically, Tablet+: Side by side */}
<div className="flex flex-col sm:flex-row gap-1 sm:gap-2">
<div className="flex flex-col sm:flex-row gap-1 sm:gap-2 sm:flex-1">
<div className="w-full sm:w-1/2">
<div className="font-semibold text-gray-800 text-xs"> <div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "firstName", "First Name")} {renderEditableField(recipient, "roleName", "Role name")}
</div> </div>
</div> :
<div className="w-full sm:w-1/2"> <div className="font-semibold text-gray-800 text-sm px-2">
<div className="font-semibold text-gray-800 text-xs"> {recipient.roleName}
{renderEditableField(recipient, "lastName", "Last Name")}
</div> </div>
</div> }
</div> </div>}
<div className="w-full sm:flex-1">
<div className="font-semibold text-gray-800 text-xs"> {/* Permissions - Mobile: Below role, Desktop: Right side */}
{renderEditableField(recipient, "email", "Recipient Email", "", "email")} <div className="lg:w-1/2 lg:flex lg:items-center lg:justify-end">
</div> {transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES && (
<div className="flex ps-2.5 sm:flex-row items-start sm:items-center gap-2 sm:gap-4">
<label className="flex items-center gap-1 text-xs sm:text-sm text-gray-700">
<input
type="checkbox"
checked={recipient.allowSenderToEdit}
onChange={() =>
dispatch(
updateRecipient({
...recipient,
allowSenderToEdit: !recipient.allowSenderToEdit,
uuid: getRecipientIdentifier(recipient),
})
)
}
className="accent-blue-500 w-3 h-3 sm:w-4 sm:h-4"
/>
<span className="whitespace-nowrap">Allow Edit</span>
</label>
<label className="flex items-center gap-1 text-xs sm:text-sm text-gray-700">
<input
type="checkbox"
checked={recipient.allowSenderToDelete}
onChange={() =>
dispatch(
updateRecipient({
...recipient,
allowSenderToDelete: !recipient.allowSenderToDelete,
uuid: getRecipientIdentifier(recipient),
})
)
}
className="accent-red-500 w-3 h-3 sm:w-4 sm:h-4"
/>
<span className="whitespace-nowrap">Allow Delete</span>
</label>
</div>
)}
</div> </div>
</div> </div>
</div>
{/* Host Fields (for In-Person Signers) */} {/* Name and Email Fields */}
{recipient.type == recipientType.INPERSONSIGNER && ( <div className="mb-2 w-[92%]">
<div className="mb-2"> {/* Mobile: Stack vertically, Tablet+: Side by side */}
<div className="flex flex-col sm:flex-row gap-1 sm:gap-2"> <div className="flex flex-col sm:flex-row gap-1 sm:gap-2">
<div className="w-full sm:w-1/2"> <div className="flex flex-col sm:flex-row gap-1 sm:gap-2 sm:flex-1">
<div className="font-semibold text-gray-800 text-xs"> <div className="w-full sm:w-1/2">
{renderEditableField(recipient, "hostName", "Host Name")} <div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "firstName", "First Name")}
</div>
</div>
<div className="w-full sm:w-1/2">
<div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "lastName", "Last Name")}
</div>
</div> </div>
</div> </div>
<div className="w-full sm:w-1/2"> <div className="w-full sm:flex-1">
<div className="font-semibold text-gray-800 text-xs"> <div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "hostEmail", "Host Email", "", "email")} {renderEditableField(recipient, "email", "Recipient Email", "", "email")}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
)}
{/* Recipient Type Radio Buttons */} {/* Host Fields (for In-Person Signers) */}
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 my-1.5 mx-1 sm:mx-2"> {recipient.type == recipientType.INPERSONSIGNER && (
<label className="flex items-center gap-1"> <div className="mb-2">
<input <div className="flex flex-col sm:flex-row gap-1 sm:gap-2">
type="radio" <div className="w-full sm:w-1/2">
name={`recipient-type-${getRecipientIdentifier(recipient)}`} <div className="font-semibold text-gray-800 text-xs">
value={recipientType.SIGNER} {renderEditableField(recipient, "hostName", "Host Name")}
checked={recipient.type == recipientType.SIGNER} </div>
onChange={() => handleTypeChange(recipient, recipientType.SIGNER)} </div>
onBlur={() => setEditingField(null)} <div className="w-full sm:w-1/2">
className="accent-[var(--primary)] w-3 h-3" <div className="font-semibold text-gray-800 text-xs">
disabled={!isEditable(recipient)} {renderEditableField(recipient, "hostEmail", "Host Email", "", "email")}
/> </div>
<span className="text-xs sm:text-sm">Signer</span> </div>
</label> </div>
<label className="flex items-center gap-1"> </div>
<input )}
type="radio"
name={`recipient-type-${getRecipientIdentifier(recipient)}`} {/* Recipient Type Radio Buttons */}
value={recipientType.INPERSONSIGNER} <div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 my-1.5 mx-1 sm:mx-2">
checked={recipient.type == recipientType.INPERSONSIGNER} <label className="flex items-center gap-1">
onChange={() => handleTypeChange(recipient, recipientType.INPERSONSIGNER)} <input
onBlur={() => setEditingField(null)} type="radio"
className="accent-[var(--primary)] w-3 h-3" name={`recipient-type-${getRecipientIdentifier(recipient)}`}
disabled={!isEditable(recipient)} value={recipientType.SIGNER}
/> checked={recipient.type == recipientType.SIGNER}
<span className="text-xs sm:text-sm whitespace-nowrap">In Person Signer</span> onChange={() => handleTypeChange(recipient, recipientType.SIGNER)}
</label> onBlur={() => setEditingField(null)}
<label className="flex items-center gap-1"> className="accent-[var(--primary)] w-3 h-3"
<input disabled={!isEditable(recipient)}
type="radio" />
name={`recipient-type-${getRecipientIdentifier(recipient)}`} <span className="text-xs sm:text-sm">Signer</span>
value={recipientType.CC} </label>
checked={recipient.type == recipientType.CC} <label className="flex items-center gap-1">
onChange={() => handleTypeChange(recipient, recipientType.CC)} <input
onBlur={() => setEditingField(null)} type="radio"
className="accent-[var(--primary)] w-3 h-3" name={`recipient-type-${getRecipientIdentifier(recipient)}`}
disabled={!isEditable(recipient)} value={recipientType.INPERSONSIGNER}
/> checked={recipient.type == recipientType.INPERSONSIGNER}
<span className="text-xs sm:text-sm">CC</span> onChange={() => handleTypeChange(recipient, recipientType.INPERSONSIGNER)}
</label> onBlur={() => setEditingField(null)}
className="accent-[var(--primary)] w-3 h-3"
disabled={!isEditable(recipient)}
/>
<span className="text-xs sm:text-sm whitespace-nowrap">In Person Signer</span>
</label>
<label className="flex items-center gap-1">
<input
type="radio"
name={`recipient-type-${getRecipientIdentifier(recipient)}`}
value={recipientType.CC}
checked={recipient.type == recipientType.CC}
onChange={() => handleTypeChange(recipient, recipientType.CC)}
onBlur={() => setEditingField(null)}
className="accent-[var(--primary)] w-3 h-3"
disabled={!isEditable(recipient)}
/>
<span className="text-xs sm:text-sm">CC</span>
</label>
</div>
</div> </div>
</div> </div>
</div>
{/* Action Buttons */} {/* Action Buttons */}
{signType != "self-sign" && ( {signType != "self-sign" && (
<div className="lg:ml-2 absolute top-[-8px] right-[-5px] px-1.5 py-1.5"> <div className="lg:ml-2 absolute top-[-8px] right-[-5px] px-1.5 py-1.5">
<button <button
type="button" type="button"
title="Remove recipient" title="Remove recipient"
aria-label={`Remove recipient ${recipient.firstName}`} aria-label={`Remove recipient ${recipient.firstName}`}
onClick={() => handleRemoveRecipient(recipient)} onClick={() => handleRemoveRecipient(recipient)}
disabled={ disabled={
transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES
? false ? false
: !recipient.newOne && !recipient.allowSenderToDelete : !recipient.newOne && !recipient.allowSenderToDelete
} }
className="text-gray-400 hover:text-red-600 transition-colors cursor-pointer disabled:opacity-50 p-1" className="text-gray-400 hover:text-red-600 transition-colors cursor-pointer disabled:opacity-50 p-1"
> >
<ImBin size={14} className="sm:w-4 sm:h-4" /> <ImBin size={14} className="sm:w-4 sm:h-4" />
</button> </button>
</div> </div>
)} )}
</>
</div> </div>
</SortableRecipient> </SortableRecipient>

View File

@ -19,10 +19,11 @@ interface RecipientsConIntProps {
userData: UserState; userData: UserState;
isModal: boolean; isModal: boolean;
submitError?: FormSubmitInt; submitError?: FormSubmitInt;
setEditRecipientsModal?: React.Dispatch<boolean> setEditRecipientsModal?: React.Dispatch<boolean>;
signingOrder?: boolean;
} }
const RecipientsCon: React.FC<RecipientsConIntProps> = ({ transactionTemplateToggle, accountId, transactionId, userData, submitError, isModal = true, setEditRecipientsModal }) => { const RecipientsCon: React.FC<RecipientsConIntProps> = ({ transactionTemplateToggle, accountId, transactionId, userData, submitError, isModal = true, setEditRecipientsModal, signingOrder }) => {
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
const topRef = useRef<HTMLDivElement | null>(null); const topRef = useRef<HTMLDivElement | null>(null);
@ -144,6 +145,7 @@ const RecipientsCon: React.FC<RecipientsConIntProps> = ({ transactionTemplateTog
editRecipient={editRecipient} editRecipient={editRecipient}
setEditRecipientCurrentEmail={setEditRecipientCurrentEmail} setEditRecipientCurrentEmail={setEditRecipientCurrentEmail}
editRecipientCurrentEmail={editRecipientCurrentEmail} editRecipientCurrentEmail={editRecipientCurrentEmail}
signingOrder={signingOrder}
/> />
{submitError && submitError.showError && <p {submitError && submitError.showError && <p
className="text-xs text-right px-2 pt-2 text-red-500"> className="text-xs text-right px-2 pt-2 text-red-500">

View File

@ -7,6 +7,7 @@ export default defineConfig({
base: '/', base: '/',
plugins: [react(), tailwindcss()], plugins: [react(), tailwindcss()],
server: { server: {
port: 5174,
allowedHosts: [ allowedHosts: [
'quality-friendly-goldfish.ngrok-free.app', 'quality-friendly-goldfish.ngrok-free.app',
'officially-powerful-goat.ngrok-free.app', 'officially-powerful-goat.ngrok-free.app',