234 lines
10 KiB
TypeScript
234 lines
10 KiB
TypeScript
import { useEffect, useState, useRef } from 'react';
|
|
import { FaEllipsisV } from 'react-icons/fa';
|
|
import api from '../../config/api';
|
|
import ContactFormModal, { ContactData } from './ContactsFormModal';
|
|
import { Bounce, toast } from 'react-toastify';
|
|
import { IoMdAdd } from 'react-icons/io';
|
|
import LoaderUi from '../../helper/LoaderUi';
|
|
import logoRecord from '../../assets/record.svg'
|
|
|
|
type ContactSchema = [{
|
|
id: number,
|
|
firstName: string
|
|
lastName: string
|
|
email: string
|
|
phone: string
|
|
}]
|
|
|
|
const Contacts = () => {
|
|
const activeAccountId = JSON.parse(sessionStorage.getItem('activeAccountId') || 'null');
|
|
const [contacts, setContacts] = useState<ContactSchema>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [openMenuId, setOpenMenuId] = useState(null);
|
|
const blurTimeoutRef = useRef(null);
|
|
const [createAndInviteLoader, setCreateAndInviteLoader] = useState(false);
|
|
const [showModal, setShowModal] = useState(false)
|
|
const [editJsonData, setEditJsonData] = useState({})
|
|
const [actionStatus, setActionStatus] = useState("")
|
|
console.log("editJsonData", editJsonData)
|
|
|
|
const fetchContacts = async () => {
|
|
setLoading(true)
|
|
try {
|
|
const res = await api.get(`/api/accounts/${activeAccountId}/contacts`);
|
|
setContacts(res.data.content || []);
|
|
} catch (error) {
|
|
console.error('Failed to fetch contacts:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (activeAccountId) {
|
|
fetchContacts();
|
|
}
|
|
}, [activeAccountId]);
|
|
|
|
const handleEdit = (contactId) => {
|
|
console.log('Edit contact:', contactId);
|
|
let jsonEditData = contacts.find((prev) => prev.id === contactId)
|
|
setEditJsonData(jsonEditData)
|
|
setActionStatus("Edit")
|
|
setShowModal((prev) => !prev)
|
|
setOpenMenuId(null);
|
|
};
|
|
|
|
const handleDelete = async (contactId) => {
|
|
console.log('Delete contact:', contactId);
|
|
|
|
try {
|
|
let res = await api.delete(`/api/accounts/${activeAccountId}/contacts/${contactId}`)
|
|
if (res?.data) {
|
|
toast.error("Contact delete successfully!", {
|
|
position: "top-center",
|
|
autoClose: 1500,
|
|
transition: Bounce,
|
|
});
|
|
fetchContacts()
|
|
setOpenMenuId(null);
|
|
}
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
};
|
|
|
|
const toggleMenu = (contactId) => {
|
|
// Cancel any pending blur timeout (prevents blinking)
|
|
clearTimeout(blurTimeoutRef.current);
|
|
setOpenMenuId((prevId) => (prevId === contactId ? null : contactId));
|
|
};
|
|
|
|
const handleBlur = () => {
|
|
blurTimeoutRef.current = setTimeout(() => {
|
|
setOpenMenuId(null);
|
|
}, 100);
|
|
};
|
|
|
|
const handleContactSubmit = async (data: ContactData, type: "") => {
|
|
try {
|
|
let res;
|
|
setCreateAndInviteLoader(true);
|
|
if (type === 'Edit') {
|
|
res = await api.put(`/api/accounts/${activeAccountId}/contacts`, [data])
|
|
} else {
|
|
res = await api.post(`/api/accounts/${activeAccountId}/contacts`, [data]);
|
|
}
|
|
if (res.status === 200) {
|
|
toast.success('Contact created successfully!', {
|
|
position: "top-center",
|
|
autoClose: 1500,
|
|
transition: Bounce,
|
|
});
|
|
setShowModal(false)
|
|
fetchContacts();
|
|
}
|
|
// return res;
|
|
} catch (err) {
|
|
console.error("Error creating contact", err);
|
|
toast.error("Failed to create contact", {
|
|
position: "top-center",
|
|
autoClose: 1500,
|
|
transition: Bounce,
|
|
});
|
|
throw err;
|
|
} finally {
|
|
setCreateAndInviteLoader(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className='px-1.5'>
|
|
<div className='flex justify-end my-2 cursor-pointer'>
|
|
<div
|
|
onClick={() => {
|
|
setShowModal((prev) => !prev);
|
|
setActionStatus("Add")
|
|
}}
|
|
className="flex items-center gap-1 bg-gradient-to-br from-[var(--primary)] to-blue-600 px-3 py-1.5 rounded-sm hover:opacity-90 ">
|
|
<IoMdAdd className="text-white" />
|
|
<button
|
|
className="text-white rounded text-sm hover:opacity-90 cursor-pointer">
|
|
Add Contact
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="overflow-y-auto mx-2 min-h-[76vh] max-h-[76vh] rounded-xl shadow-sm border bg-white border-gray-100 no-scrollbar">
|
|
<table className="min-w-full text-sm bg-gray-50">
|
|
<thead className="text-gray-600 text-xs uppercase tracking-wider border-b border-gray-200">
|
|
<tr>
|
|
<th className="px-6 py-4 font-medium text-left">First Name</th>
|
|
<th className="px-6 py-4 font-medium text-left">Last Name</th>
|
|
<th className="px-6 py-4 font-medium text-left">Email</th>
|
|
<th className="px-6 py-4 font-medium text-center">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-gray-100 bg-white">
|
|
{loading ? (
|
|
<tr>
|
|
<td colSpan="4" className="relative h-100">
|
|
<LoaderUi />
|
|
</td>
|
|
</tr>
|
|
) : contacts.length === 0 ? (
|
|
<tr>
|
|
<td colSpan="4" className="text-center py-10 text-gray-500">
|
|
<div className="flex flex-col items-center justify-center">
|
|
<img
|
|
src={logoRecord}
|
|
alt="Record Not Found"
|
|
className="w-70 h-70 mb-4 opacity-70"
|
|
/>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
) : (
|
|
contacts.map((contact, index) => (
|
|
<tr
|
|
key={contact.id}
|
|
className="hover:bg-gray-50 transition-colors"
|
|
>
|
|
<td className="px-6 py-4 text-gray-800">{contact.firstName}</td>
|
|
<td className="px-6 py-4 text-gray-800">{contact.lastName}</td>
|
|
<td className="px-6 py-4 text-gray-800">{contact.email}</td>
|
|
<td className="px-6 py-4 text-center">
|
|
<div
|
|
className="relative inline-block"
|
|
tabIndex={0}
|
|
onBlur={handleBlur}
|
|
>
|
|
<button
|
|
className="text-gray-400 hover:text-gray-600 p-1 focus:outline-none"
|
|
onClick={() => toggleMenu(contact.id)}
|
|
>
|
|
<FaEllipsisV />
|
|
</button>
|
|
|
|
{openMenuId === contact.id && (
|
|
<div
|
|
className={`absolute mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-20 min-w-[100px] ${contacts.length - 1 === index
|
|
? "bottom-1 right-4"
|
|
: "bottom-0 right-5"
|
|
}`}
|
|
>
|
|
<button
|
|
onMouseDown={() => handleEdit(contact.id)}
|
|
className="block w-full text-left px-4 py-2 text-sm hover:bg-gray-100 text-blue-600"
|
|
>
|
|
Edit
|
|
</button>
|
|
<button
|
|
onMouseDown={() => handleDelete(contact.id)}
|
|
className="block w-full text-left px-4 py-2 text-sm hover:bg-gray-100 text-red-600"
|
|
>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
|
|
{showModal && (
|
|
<ContactFormModal
|
|
onSubmit={handleContactSubmit}
|
|
loading={createAndInviteLoader}
|
|
setShowModal={setShowModal}
|
|
actionStatus={actionStatus}
|
|
editJsonData={editJsonData}
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Contacts;
|