396 lines
17 KiB
TypeScript
396 lines
17 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import { FaPlus } from "react-icons/fa";
|
|
import { addRecipient, Recipient, recipientType, updateRecipient } from "../../../store/slices/selectedRecipientsSlice";
|
|
import { useDispatch } from "react-redux";
|
|
import { AppDispatch } from "../../../store/store";
|
|
import api from "../../../config/api";
|
|
import ContactModal from "./ContactModal";
|
|
import useDebouncedEffect from "../../../utlity/useDebounce";
|
|
import { IoIosSettings } from "react-icons/io";
|
|
import { FiEdit, FiTrash } from "react-icons/fi";
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
|
|
interface ContactType {
|
|
id: string;
|
|
email: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
}
|
|
|
|
export interface ContactTypeObj {
|
|
type: string;
|
|
contacts: ContactType[];
|
|
}
|
|
|
|
|
|
const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,setEditRecipient, setEditCase }) => {
|
|
|
|
const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] });
|
|
const [openContactModal, setOpenContactModal] = useState(false);
|
|
|
|
const dispatch = useDispatch<AppDispatch>();
|
|
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 [showAdvanceSettings, setShowAdvanceSettings] = useState(false)
|
|
console.log(recipient)
|
|
|
|
useEffect(() => {
|
|
if (editCase === "Edit" && editRecipient) {
|
|
setRecipients({
|
|
// id:editRecipient.id,
|
|
firstName: editRecipient.firstName || "",
|
|
lastName: editRecipient.lastName || "",
|
|
email: editRecipient.email || "",
|
|
hostName: editRecipient.hostName || "",
|
|
hostEmail: editRecipient.hostEmail || "",
|
|
roleName: editRecipient.roleName || "",
|
|
type: editRecipient.type || recipientType.SIGNER,
|
|
allowSenderToEdit: editRecipient.allowSenderToEdit ?? false,
|
|
allowSenderToDelete: editRecipient.allowSenderToDelete ?? false,
|
|
uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''),
|
|
newOne:editRecipient.newOne ?? false
|
|
});
|
|
}
|
|
}, [editCase, editRecipient]);
|
|
|
|
|
|
const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => {
|
|
e.preventDefault();
|
|
|
|
console.log("recipientrecipient",recipient)
|
|
|
|
if (!recipient.lastName || !recipient.email || !recipient.type) return;
|
|
|
|
// ✅ Email format validation
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(recipient.email)) {
|
|
console.log("Invalid email format");
|
|
return;
|
|
}
|
|
|
|
if (editCase === 'Edit') {
|
|
dispatch(updateRecipient(recipient));
|
|
} else {
|
|
dispatch(addRecipient({
|
|
...recipient,
|
|
uuid:uuidv4(),
|
|
newOne:true
|
|
}));
|
|
}
|
|
|
|
// ✅ Reset state and form
|
|
setRecipients({
|
|
firstName: "",
|
|
lastName: "",
|
|
email: "",
|
|
hostName: "",
|
|
hostEmail: "",
|
|
roleName: "",
|
|
type: recipientType.SIGNER,
|
|
allowSenderToEdit: false,
|
|
allowSenderToDelete: false,
|
|
uuid:"",
|
|
newOne:false
|
|
});
|
|
|
|
// setRecipients(null),
|
|
setEditCase("")
|
|
|
|
formRef.current?.reset();
|
|
};
|
|
|
|
|
|
|
|
|
|
// useEffect(() => {
|
|
// if (!recipient.firstName.length) return;
|
|
|
|
// const handler = setTimeout(() => {
|
|
// api.get(`/api/contact?firstName=${recipient.firstName}`)
|
|
// .then(res => {
|
|
// setContactData({ type: "firstName", contacts: res.data.content });
|
|
// })
|
|
// .catch(err => console.log(err));
|
|
// }, 500); // wait 500ms after the last change
|
|
|
|
// return () => {
|
|
// clearTimeout(handler); // cancel previous timeout if effect runs again
|
|
// };
|
|
|
|
// }, [recipient.firstName]);
|
|
|
|
// useDebouncedEffect(() => {
|
|
// if (!recipient.firstName.length) return;
|
|
// api.get(`/api/contact?firstName=${recipient.firstName}`)
|
|
// .then(res => {
|
|
// setContactData({ type: "firstName", contacts: res.data.content });
|
|
// })
|
|
// .catch(err => console.log(err));
|
|
// }, [recipient.firstName], 500);
|
|
|
|
// useDebouncedEffect(() => {
|
|
// if (!recipient.lastName.length) return
|
|
// api.get(`/api/contact?lastName=${recipient.lastName}`)
|
|
// .then(res => {
|
|
// setContactData({ type: "lastName", contacts: res.data.content });
|
|
// })
|
|
// .catch(err => console.log(err))
|
|
// }, [recipient.lastName], 500);
|
|
|
|
// useDebouncedEffect(() => {
|
|
// if (!recipient.email.length) return
|
|
// api.get(`/api/contact?email=${recipient.email}`)
|
|
// .then(res => {
|
|
// setContactData({ type: "email", contacts: res.data.content });
|
|
// })
|
|
// .catch(err => console.log(err))
|
|
// }, [recipient.email], 500);
|
|
|
|
const handleContactModalBlur = () => {
|
|
setTimeout(() => {
|
|
setOpenContactModal(false)
|
|
}, 350)
|
|
}
|
|
|
|
return (
|
|
<form
|
|
ref={formRef}
|
|
onSubmit={handleRecipientAdd}
|
|
className="w-full bg-white border border-gray-200 rounded-lg shadow-md px-6 py-4 space-y-3 relative"
|
|
>
|
|
{transactionTemplateToggle === 'templates' && (
|
|
<div className="space-y-1">
|
|
{/* Top Row: Role Input + Settings */}
|
|
<div className="flex justify-between items-center">
|
|
{/* Role Name Input */}
|
|
<input
|
|
required
|
|
type="text"
|
|
onFocus={() => setOpenContactModal(true)}
|
|
onBlur={handleContactModalBlur}
|
|
value={recipient.roleName}
|
|
placeholder="Role name"
|
|
className="w-64 px-3 py-2 me-3 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
onChange={(e) => setRecipients({ ...recipient, roleName: e.target.value })}
|
|
/>
|
|
|
|
{/* Settings + Permissions */}
|
|
<div className="flex items-center">
|
|
<div className="flex justify-between">
|
|
{/* Permission Status Text - Always Visible when settings open */}
|
|
<div
|
|
className={`transition-all duration-600 ease-in-out ${showAdvanceSettings ? 'max-h-10 opacity-100 me-3' : 'max-h-0 opacity-0 '
|
|
} overflow-hidden`}
|
|
>
|
|
<div className="text-sm text-gray-600">
|
|
{recipient.allowSenderToEdit && recipient.allowSenderToDelete && "Full permissions granted"}
|
|
{recipient.allowSenderToEdit && !recipient.allowSenderToDelete && "Edit permission granted"}
|
|
{!recipient.allowSenderToEdit && recipient.allowSenderToDelete && "Delete permission granted"}
|
|
{!recipient.allowSenderToEdit && !recipient.allowSenderToDelete && "No permissions granted"}
|
|
</div>
|
|
</div>
|
|
{/* Animated Permission Controls */}
|
|
<div className={`overflow-visible transition-all duration-900 ease-in-out ${showAdvanceSettings ? 'max-w-80 opacity-100' : 'max-w-0 opacity-0'
|
|
}`}>
|
|
<div className="flex items-center space-x-3 pr-1.5">
|
|
{/* Edit Permission */}
|
|
<div className="relative group">
|
|
<label
|
|
className={`relative flex items-center justify-center w-6 h-6 border-2 rounded-lg cursor-pointer transition-all duration-300 transform hover:scale-105 ${recipient.allowSenderToEdit
|
|
? 'bg-blue-100 border-blue-500 shadow-lg'
|
|
: 'bg-white border-gray-300 hover:border-blue-400'
|
|
}`}
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
checked={recipient.allowSenderToEdit}
|
|
onChange={(e) =>
|
|
setRecipients({ ...recipient, allowSenderToEdit: e.target.checked })
|
|
}
|
|
onFocus={() => setOpenContactModal(true)}
|
|
onBlur={handleContactModalBlur}
|
|
className="absolute w-full h-full opacity-0 cursor-pointer"
|
|
/>
|
|
<FiEdit
|
|
className={`w-3 h-3 pointer-events-none transition-all duration-300 ${recipient.allowSenderToEdit ? 'text-blue-600' : 'text-gray-400'
|
|
}`}
|
|
/>
|
|
</label>
|
|
{/* Tooltip */}
|
|
<div className="absolute bottom-0 -translate-y-full left-1/2 transform -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-20">
|
|
<div className="bg-gray-800 text-white text-xs rounded-lg py-2 px-3 whitespace-nowrap">
|
|
Allow Edit Permission
|
|
<div className="absolute bottom-[-5px] left-1/2 transform -translate-x-1/2 border-4 border-transparent border-b-gray-800"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Delete Permission */}
|
|
<div className="relative group">
|
|
<label
|
|
className={`relative flex items-center justify-center w-6 h-6 border-2 rounded-lg cursor-pointer transition-all duration-300 transform hover:scale-105 ${recipient.allowSenderToDelete
|
|
? 'bg-red-100 border-red-500 shadow-lg'
|
|
: 'bg-white border-gray-300 hover:border-red-400'
|
|
}`}
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
checked={recipient.allowSenderToDelete}
|
|
onChange={(e) =>
|
|
setRecipients({ ...recipient, allowSenderToDelete: e.target.checked })
|
|
}
|
|
onFocus={() => setOpenContactModal(true)}
|
|
onBlur={handleContactModalBlur}
|
|
className="absolute w-full h-full opacity-0 cursor-pointer"
|
|
/>
|
|
<FiTrash
|
|
className={`w-3 h-3 pointer-events-none transition-all duration-300 ${recipient.allowSenderToDelete ? 'text-red-600' : 'text-gray-400'
|
|
}`}
|
|
/>
|
|
</label>
|
|
{/* Tooltip */}
|
|
<div className="absolute bottom-0 -translate-y-full left-1/2 transform -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-20">
|
|
<div className="bg-gray-800 text-white text-xs rounded-lg py-2 px-3 whitespace-nowrap">
|
|
Allow Delete Permission
|
|
<div className="absolute bottom-[-5px] left-1/2 transform -translate-x-1/2 border-4 border-transparent border-b-gray-800"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Settings Icon */}
|
|
<div className="relative group">
|
|
<p
|
|
className="text-blue-600 hover:text-blue-700 cursor-pointer transition-colors duration-200 p-2 rounded-full hover:bg-blue-50"
|
|
onClick={() => setShowAdvanceSettings((prev) => !prev)}
|
|
>
|
|
<IoIosSettings
|
|
className={`h-6 w-6 transition-transform duration-300 ${showAdvanceSettings ? 'rotate-90' : 'rotate-0'
|
|
}`}
|
|
/>
|
|
</p>
|
|
{/* Tooltip */}
|
|
<div className="absolute top-0 -translate-y-full left-1/2 transform -translate-x-1/2 mb-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-20">
|
|
<div className="bg-gray-800 text-white text-xs rounded-lg py-2 px-3 whitespace-nowrap">
|
|
Advanced Settings
|
|
<div className="absolute bottom-[-6px] left-1/2 transform -translate-x-1/2 border-4 border-transparent border-b-gray-800"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Name Inputs */}
|
|
<div className="grid grid-cols-12 gap-4 mt-3">
|
|
<input
|
|
type="text"
|
|
onFocus={() => setOpenContactModal(true)}
|
|
onBlur={handleContactModalBlur}
|
|
value={recipient.firstName}
|
|
placeholder="First Name"
|
|
className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
onChange={(e) => setRecipients({ ...recipient, firstName: e.target.value })}
|
|
/>
|
|
<input
|
|
required
|
|
type="text"
|
|
onFocus={() => setOpenContactModal(true)}
|
|
onBlur={handleContactModalBlur}
|
|
value={recipient.lastName}
|
|
placeholder="Last Name"
|
|
className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
onChange={(e) => setRecipients({ ...recipient, lastName: e.target.value })}
|
|
/>
|
|
<input
|
|
required
|
|
type="email"
|
|
onFocus={() => setOpenContactModal(true)}
|
|
onBlur={handleContactModalBlur}
|
|
value={recipient.email}
|
|
placeholder="Recipient Email"
|
|
className="col-span-4 w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
onChange={(e) => setRecipients({ ...recipient, email: e.target.value })}
|
|
/>
|
|
</div>
|
|
|
|
|
|
{recipient.type === recipientType.INPERSONSIGNER && <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<input
|
|
required
|
|
type="text"
|
|
onFocus={() => setOpenContactModal(true)}
|
|
onBlur={handleContactModalBlur}
|
|
value={recipient.hostName}
|
|
placeholder="Host Name"
|
|
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
onChange={(e) => setRecipients({ ...recipient, hostName: e.target.value })}
|
|
/>
|
|
<input
|
|
required
|
|
type="email"
|
|
onFocus={() => setOpenContactModal(true)}
|
|
onBlur={handleContactModalBlur}
|
|
value={recipient.hostEmail}
|
|
placeholder="Host Email"
|
|
className="w-full px-4 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-[var(--primary)] transition"
|
|
onChange={(e) => setRecipients({ ...recipient, hostEmail: e.target.value })}
|
|
/>
|
|
</div>}
|
|
|
|
{/* Contact Modal */}
|
|
{contactData.contacts.length > 0 && openContactModal && (
|
|
<div className="absolute z-50 mt-1 w-full max-w-2xl bg-white shadow-lg rounded-md p-3 border border-gray-100">
|
|
<ContactModal
|
|
contactData={contactData}
|
|
inpFirstName={recipient.firstName}
|
|
inpLastName={recipient.lastName}
|
|
inpEmail={recipient.email}
|
|
openContactModal={openContactModal}
|
|
setRecipients={setRecipients}
|
|
setOpenContactModal={setOpenContactModal}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Signing options + Add button in one line */}
|
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
|
<div className="flex flex-wrap gap-6">
|
|
{[
|
|
{ label: "Signer", value: recipientType.SIGNER },
|
|
{ label: "In Person Signer", value: recipientType.INPERSONSIGNER },
|
|
{ label: "CC", value: recipientType.CC },
|
|
].map((option) => (
|
|
<label key={option.value} className="flex items-center gap-2 text-gray-700 text-sm cursor-pointer">
|
|
<input
|
|
type="radio"
|
|
name="recipient-type"
|
|
value={option.value}
|
|
defaultChecked={option.value === recipientType.SIGNER}
|
|
onClick={(e) => setRecipients({ ...recipient, type: e.target.value })}
|
|
className="accent-[var(--primary)] cursor-pointer"
|
|
/>
|
|
{option.label}
|
|
</label>
|
|
))}
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition"
|
|
>
|
|
<FaPlus size={14} /> {editCase === "Edit" ? "Edit Recipient" : "Add Recipient"}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
);
|
|
};
|
|
|
|
export default RecipientAddCard; |