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",
"lint": "eslint .",
"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": {
"@dnd-kit/core": "^6.3.1",

View File

@ -87,6 +87,7 @@ const ProcessStepOne = () => {
const [templateName, setTemplateName] = useState('Untitled');
const [isTyping, setIsTyping] = useState(false);
const [signingOrder, setSigningOrder] = useState(false)
// Optional: use a timer to detect pause in typing
let typingTimeout = null;
@ -512,6 +513,24 @@ const ProcessStepOne = () => {
<label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600">
I AM THE ONLY SIGNER</label>
</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>
}
@ -526,6 +545,7 @@ const ProcessStepOne = () => {
// setEditRecipientsModal={setEditRecipientsModal}
submitError={submitError}
isModal={false}
signingOrder={signingOrder}
/>
</>
}

View File

@ -53,6 +53,7 @@ type CardsData = {
transactionTemplateToggle: string;
editRecipientCurrentEmail: string;
setEditRecipientCurrentEmail: (email: string) => void;
signingOrder: boolean;
};
const RecipientCards = ({
@ -62,6 +63,7 @@ const RecipientCards = ({
editRecipient,
editRecipientCurrentEmail,
setEditRecipientCurrentEmail,
signingOrder // signing order - Routing order
}: CardsData) => {
const templateId = useParams<{ templateId: string }>().templateId;
let { accountId, transactionId } = useParams<{ accountId: string; transactionId: string }>();
@ -389,230 +391,239 @@ const RecipientCards = ({
>
<div
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"
: ""
}`}
>
{/* 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">
Signing order: Recipients receive emails one after another.
Same order means they all receive at once.
</span>
</p>}
</div>
) : (
<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"
/>
)}
<>
{/* Main Content Section */}
<div className="flex items-start sm:items-center gap-2 sm:gap-3 min-w-0 flex-1 pl-8">
{/* Order Number */}
{recipients.length > 1 && signingOrder ? (
<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" />
{/* 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">
{/* 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">
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 || ""}`)}
</div> */}
{/* Form Fields Section */}
<div className="min-w-0 flex-1">
{/* Role Name and Permissions Row */}
<div className="flex flex-col lg:flex-row lg:items-center gap-2 mb-2">
{<div className="flex lg:w-1/2">
{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">
{/* Form Fields Section */}
<div className="min-w-0 flex-1">
{/* Role Name and Permissions Row */}
<div className="flex flex-col lg:flex-row lg:items-center gap-2 mb-2">
{<div className="flex lg:w-1/2">
{transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES ?
<div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "firstName", "First Name")}
{renderEditableField(recipient, "roleName", "Role 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 className="font-semibold text-gray-800 text-sm px-2">
{recipient.roleName}
</div>
</div>
</div>
<div className="w-full sm:flex-1">
<div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "email", "Recipient Email", "", "email")}
</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>
</div>
{/* Host Fields (for In-Person Signers) */}
{recipient.type == recipientType.INPERSONSIGNER && (
<div className="mb-2">
{/* 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="w-full sm:w-1/2">
<div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "hostName", "Host Name")}
<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">
{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 className="w-full sm:w-1/2">
<div className="w-full sm:flex-1">
<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>
)}
{/* Recipient Type Radio Buttons */}
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 my-1.5 mx-1 sm:mx-2">
<label className="flex items-center gap-1">
<input
type="radio"
name={`recipient-type-${getRecipientIdentifier(recipient)}`}
value={recipientType.SIGNER}
checked={recipient.type == recipientType.SIGNER}
onChange={() => handleTypeChange(recipient, recipientType.SIGNER)}
onBlur={() => setEditingField(null)}
className="accent-[var(--primary)] w-3 h-3"
disabled={!isEditable(recipient)}
/>
<span className="text-xs sm:text-sm">Signer</span>
</label>
<label className="flex items-center gap-1">
<input
type="radio"
name={`recipient-type-${getRecipientIdentifier(recipient)}`}
value={recipientType.INPERSONSIGNER}
checked={recipient.type == recipientType.INPERSONSIGNER}
onChange={() => handleTypeChange(recipient, recipientType.INPERSONSIGNER)}
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>
{/* Host Fields (for In-Person Signers) */}
{recipient.type == recipientType.INPERSONSIGNER && (
<div className="mb-2">
<div className="flex flex-col sm:flex-row gap-1 sm:gap-2">
<div className="w-full sm:w-1/2">
<div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "hostName", "Host Name")}
</div>
</div>
<div className="w-full sm:w-1/2">
<div className="font-semibold text-gray-800 text-xs">
{renderEditableField(recipient, "hostEmail", "Host Email", "", "email")}
</div>
</div>
</div>
</div>
)}
{/* Recipient Type Radio Buttons */}
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 my-1.5 mx-1 sm:mx-2">
<label className="flex items-center gap-1">
<input
type="radio"
name={`recipient-type-${getRecipientIdentifier(recipient)}`}
value={recipientType.SIGNER}
checked={recipient.type == recipientType.SIGNER}
onChange={() => handleTypeChange(recipient, recipientType.SIGNER)}
onBlur={() => setEditingField(null)}
className="accent-[var(--primary)] w-3 h-3"
disabled={!isEditable(recipient)}
/>
<span className="text-xs sm:text-sm">Signer</span>
</label>
<label className="flex items-center gap-1">
<input
type="radio"
name={`recipient-type-${getRecipientIdentifier(recipient)}`}
value={recipientType.INPERSONSIGNER}
checked={recipient.type == recipientType.INPERSONSIGNER}
onChange={() => handleTypeChange(recipient, recipientType.INPERSONSIGNER)}
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>
{/* Action Buttons */}
{signType != "self-sign" && (
<div className="lg:ml-2 absolute top-[-8px] right-[-5px] px-1.5 py-1.5">
<button
type="button"
title="Remove recipient"
aria-label={`Remove recipient ${recipient.firstName}`}
onClick={() => handleRemoveRecipient(recipient)}
disabled={
transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES
? false
: !recipient.newOne && !recipient.allowSenderToDelete
}
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" />
</button>
</div>
)}
{/* Action Buttons */}
{signType != "self-sign" && (
<div className="lg:ml-2 absolute top-[-8px] right-[-5px] px-1.5 py-1.5">
<button
type="button"
title="Remove recipient"
aria-label={`Remove recipient ${recipient.firstName}`}
onClick={() => handleRemoveRecipient(recipient)}
disabled={
transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES
? false
: !recipient.newOne && !recipient.allowSenderToDelete
}
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" />
</button>
</div>
)}
</>
</div>
</SortableRecipient>

View File

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

View File

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