diff --git a/src/components/accounts/Account.tsx b/src/components/accounts/Account.tsx index 8a6c996..90c091b 100644 --- a/src/components/accounts/Account.tsx +++ b/src/components/accounts/Account.tsx @@ -245,6 +245,7 @@ const Account = () => { return ( <> + diff --git a/src/components/accounts/TransactionDataTable.tsx b/src/components/accounts/TransactionDataTable.tsx index c5178fd..4784589 100644 --- a/src/components/accounts/TransactionDataTable.tsx +++ b/src/components/accounts/TransactionDataTable.tsx @@ -3,23 +3,24 @@ import { PiDotsThreeOutlineVerticalFill } from 'react-icons/pi'; import api from '../../config/api'; import { Transaction } from '../transactions/Transactions'; import { useNavigate } from 'react-router-dom'; + import { FaCheck } from 'react-icons/fa'; + const formatDate = (timestamp: number) => { return new Date(timestamp * 1000).toLocaleString(); }; const TransactionDataTable = () => { + const navigate = useNavigate() const [currentEmailId, setCurrentEmailId] = useState('null'); const [selectedTransactions, setSelectedTransactions] = useState([]); const [activeAccountId, setActiveAccountId] = useState( JSON.parse(sessionStorage.getItem('activeAccountId') || 'null') ); - const navigate = useNavigate(); - const fetchTransactions = async () => { console.log("fetchTransactions") try { @@ -47,6 +48,9 @@ const TransactionDataTable = () => { setCurrentEmailId(userDetails?.email) }, []); + + + return ( <> {transactions.length > 0 && ( diff --git a/src/components/contacts/Contacts.tsx b/src/components/contacts/Contacts.tsx index da45268..b4cce43 100644 --- a/src/components/contacts/Contacts.tsx +++ b/src/components/contacts/Contacts.tsx @@ -1,20 +1,32 @@ import { useEffect, useState, useRef } from 'react'; import { FaEllipsisV } from 'react-icons/fa'; import api from '../../config/api'; -import ContactFormModal from './ContactsFormModal'; +import ContactFormModal, { ContactData } from './ContactsFormModal'; import { Bounce, toast } from 'react-toastify'; import { IoMdAdd } from 'react-icons/io'; +import LoaderUi from '../../helper/LoaderUi'; + +type ContactSchema = [{ + id: number, + firstName: string + lastName: string + email: string +}] const Contacts = () => { const activeAccountId = JSON.parse(sessionStorage.getItem('activeAccountId') || 'null'); - const [contacts, setContacts] = useState([]); + const [contacts, setContacts] = useState([]); 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 || []); @@ -33,17 +45,27 @@ const Contacts = () => { 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) => { + const handleDelete = async (contactId) => { console.log('Delete contact:', contactId); - setOpenMenuId(null); + try { - let res = await api.delete(`/api/accounts/{accountId}/contacts/{contactId}`) - if (res?.data) { - - } + 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) } @@ -61,10 +83,15 @@ const Contacts = () => { }, 100); }; - const handleContactSubmit = async (data: ContactData) => { + const handleContactSubmit = async (data: ContactData, type: "") => { try { + let res; setCreateAndInviteLoader(true); - const res = await api.post(`/api/accounts/${activeAccountId}/contacts`, [data]); + 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", @@ -72,16 +99,16 @@ const Contacts = () => { transition: Bounce, }); setShowModal(false) - fetchContacts(); + fetchContacts(); } // return res; } catch (err) { console.error("Error creating contact", err); toast.error("Failed to create contact", { - position: "top-center", - autoClose: 1500, - transition: Bounce, - }); + position: "top-center", + autoClose: 1500, + transition: Bounce, + }); throw err; } finally { setCreateAndInviteLoader(false); @@ -90,82 +117,84 @@ const Contacts = () => { return ( <> -
-
setShowModal((prev) => !prev)} - className="flex items-center gap-2 bg-[var(--primary)] px-3 py-1.5 rounded-sm hover:opacity-90 "> - - +
+
+
setShowModal((prev) => !prev)} + className="flex items-center gap-2 bg-[var(--primary)] px-3 py-1.5 rounded-sm hover:opacity-90 "> + + +
-
-
- - - - - - - - - - - {loading ? ( +
+
First NameLast NameEmailActions
+ - + + + + - ) : contacts.length === 0 ? ( - - - - ) : ( - contacts.map((contact) => ( - - - - - + + + {loading ? ( + + - )) - )} - -
Loading...First NameLast NameEmailActions
No contacts found.
{contact.firstName}{contact.lastName}{contact.email} -
- - - {openMenuId === contact.id && ( -
- - -
- )} -
-
- {showModal && - - } + ) : contacts.length === 0 ? ( + + No contacts found. + + ) : ( + contacts.map((contact) => ( + + {contact.firstName} + {contact.lastName} + {contact.email} + +
+ + + {openMenuId === contact.id && ( +
+ + +
+ )} +
+ + + )) + )} + + + {showModal && + + } +
); diff --git a/src/components/contacts/ContactsFormModal.tsx b/src/components/contacts/ContactsFormModal.tsx index ea6416a..fcc25cc 100644 --- a/src/components/contacts/ContactsFormModal.tsx +++ b/src/components/contacts/ContactsFormModal.tsx @@ -1,5 +1,5 @@ // ContactForm.tsx -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { FaSpinner } from 'react-icons/fa'; export interface ContactData { @@ -17,9 +17,11 @@ interface Props { onSubmit: (data: ContactData) => void; loading: boolean; setShowModal: () => React.Dispatch> + actionStatus: string, + editJsonData: any } -const ContactFormModal = ({ onSubmit, loading, setShowModal }: Props) => { +const ContactFormModal = ({ onSubmit, loading, setShowModal, actionStatus, editJsonData }: Props) => { const [formData, setFormData] = useState({ email: '', firstName: '', @@ -32,6 +34,21 @@ const ContactFormModal = ({ onSubmit, loading, setShowModal }: Props) => { const [errors, setErrors] = useState<{ [key: string]: string }>({}); + useEffect(() => { + if (actionStatus === 'Edit' && editJsonData) { + setFormData({ + email: editJsonData.email, + firstName: editJsonData.firstName, + lastName: editJsonData.lastName, + title: editJsonData.title, + company: editJsonData.company, + website: editJsonData.website, + phones: editJsonData.phones, + }) + } + }, [actionStatus, editJsonData]) + + const handleChange = (field: keyof ContactData, value: string) => { setFormData((prev) => ({ ...prev, [field]: value })); if (errors[field]) { @@ -115,13 +132,22 @@ const ContactFormModal = ({ onSubmit, loading, setShowModal }: Props) => { const handleSubmit = async () => { const validationErrors = validate(); + let type = 'Add' + let payload = formData if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); return; } - try { - await onSubmit(formData); + if (actionStatus === 'Edit') { + type = 'Edit' + payload = { + ...formData, + contactId: editJsonData.id + } + } + await onSubmit(payload, type); + setFormData({ email: '', @@ -158,7 +184,7 @@ const ContactFormModal = ({ onSubmit, loading, setShowModal }: Props) => { handleChange(field as keyof ContactData, e.target.value)} required={true} className="px-3 py-1.5 border border-gray-400 rounded-md focus:outline-none focus:ring-1 focus:ring-[var(--primary)] text-sm" @@ -172,7 +198,7 @@ const ContactFormModal = ({ onSubmit, loading, setShowModal }: Props) => {
- {formData.phones.map((phone, index) => ( + {formData?.phones?.map((phone, index) => (
{ SAVING... ) : ( - 'SAVE CONTACT' + `${actionStatus?.toUpperCase() || 'SAVE'} CONTACT` )} + diff --git a/src/components/dashboard/Loader.tsx b/src/components/dashboard/Loader.tsx index 3f1093e..8e094ca 100644 --- a/src/components/dashboard/Loader.tsx +++ b/src/components/dashboard/Loader.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { LuSignature } from "react-icons/lu"; import { useSelector } from 'react-redux'; import { RootState } from '../../store/store'; diff --git a/src/components/templates/Templates.tsx b/src/components/templates/Templates.tsx index 2d1bba3..140abf1 100644 --- a/src/components/templates/Templates.tsx +++ b/src/components/templates/Templates.tsx @@ -139,7 +139,15 @@ const Templates = () => { - {templates.length > 0 ? ( + {loading ? ( + + + + + + + + ) : templates.length > 0 ? ( {templates.map((item, index) => { return ( @@ -200,18 +208,12 @@ const Templates = () => { ) : ( - -
- {/* Pulsing background div */} -
- - {/* Loader stays on top, no animation */} - -
+ + No templates found. - + )}
diff --git a/src/helper/LoaderUi.tsx b/src/helper/LoaderUi.tsx index c118cb4..0113300 100644 --- a/src/helper/LoaderUi.tsx +++ b/src/helper/LoaderUi.tsx @@ -1,19 +1,22 @@ +import { LuSignature } from "react-icons/lu"; interface StyleProps { - classStyle : string + classStyle?: string; } -const LoaderUi = ({ classStyle }:StyleProps) => { - return ( -
- - - - - - -
- ) -} +const LoaderUi = ({ classStyle = '' }: StyleProps) => { + return ( +
+ {/* Overlay */} +
-export default LoaderUi \ No newline at end of file + {/* Loader box */} +
+ +

Loading...!

+
+
+ ); +}; + +export default LoaderUi;