SME-Web/src/components/process/processStepOne/RecipientAddCard.tsx

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;