diff --git a/src/components/dashBoard/DashboardSideNav.tsx b/src/components/dashBoard/DashboardSideNav.tsx index cccbd7a..a28db4e 100644 --- a/src/components/dashBoard/DashboardSideNav.tsx +++ b/src/components/dashBoard/DashboardSideNav.tsx @@ -38,6 +38,13 @@ const sideBarItems = [ path: '/dashboard/customers', name: 'Customers', icon: , + subItems: [ + { + name: 'All customers', + path: '/dashboard/customers', + role: ['Admin'], + }, + ], role: ['Admin'], }, { diff --git a/src/pages/customer.tsx b/src/pages/customer.tsx new file mode 100644 index 0000000..898eff7 --- /dev/null +++ b/src/pages/customer.tsx @@ -0,0 +1,654 @@ +import { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import axios from 'axios'; +import { useNavigate } from 'react-router-dom'; +import { FaRegTrashAlt, FaWindowClose } from 'react-icons/fa'; +import { MdOutlineEdit } from 'react-icons/md'; +import { + ChevronLeft, + ChevronRight, + Power, + RefreshCcw, + Search, +} from 'lucide-react'; +import PuffLoader from 'react-spinners/PuffLoader'; +import ConfirmationCard from '@/components/dashBoard/ConfirmationCard'; +import { RootState, AppDispatch } from '@/app/store'; +import { fetchBuyers } from '@/app/Dashboard/buyerSlice'; +import Button from '@/components/form/Button'; + +import { showErrorToast, showSuccessToast } from '@/utils/ToastConfig'; + +interface Buyer { + id: number; + firstName: string; + lastName: string; + email: string; + picture: string; + status: string; + updatedAt: string; +} + +interface UserRoleUpdate { + userId: number; + newRoleId: number; +} + +function Customer() { + const dispatch = useDispatch(); + const { buyers, status } = useSelector((state: RootState) => state.buyer); + + const navigate = useNavigate(); + + const [numberofItemPerPage] = useState(8); + const [currentPage, setCurrentPage] = useState(1); + const [searchTerm, setSearch] = useState(''); + const [filteredcustomers, setFilteredcustomer] = useState([]); + const [reRenderTrigger, setReRenderTrigger] = useState(false); + + const [clickedcustomer, setClickedcustomer] = useState(null); + const [deactivate, setDeactivate] = useState(false); + const [activate, setActivate] = useState(false); + const [mode, setmode] = useState(''); + const [isConfirmationModalVisible, setModalVisible] = useState(false); + const [updating, setupdating] = useState(false); + const [updateRole, setupRole] = useState(false); + const [selectedRole, setSelectedRole] = useState(4); + + useEffect(() => { + dispatch(fetchBuyers()); + }, [dispatch]); + + useEffect(() => { + setFilteredcustomer(buyers.filter((v) => v.userType.name === 'Buyer')); + }, [buyers, reRenderTrigger]); + + const customers = filteredcustomers; + const TotalPages = Math.ceil(customers.length / numberofItemPerPage); + const pages = [...Array(TotalPages + 1).keys()].slice(1); + const itemsOnNextPage = currentPage * numberofItemPerPage; + const itemsOnPreviousPage = itemsOnNextPage - numberofItemPerPage; + const visiblePage = customers.slice(itemsOnPreviousPage, itemsOnNextPage); + + const HandleEdit = (customer: Buyer | null) => { + setDeactivate(true); + setClickedcustomer(customer); + }; + + const HandleActive = (customer: Buyer | null) => { + setActivate(true); + setClickedcustomer(customer); + }; + + const HandleDelete = (customer: Buyer | null) => { + setClickedcustomer(customer); + setModalVisible(true); + setmode('delete'); + }; + const confirmDelete = async () => { + setModalVisible(false); + setupdating(true); + try { + await axios.delete( + `${import.meta.env.VITE_BASE_URL}/user/delete/${clickedcustomer?.id}` + ); + navigate(`/dashboard/customer`); + dispatch(fetchBuyers()); + setupdating(false); + showSuccessToast(`${clickedcustomer?.firstName} was Delete Successfully`); + } catch (error) { + if (axios.isAxiosError(error)) { + navigate(`/dashboard/customer`); + setupdating(false); + showErrorToast(`Deleting ${clickedcustomer?.firstName} failed`); + throw new Error( + `Error Deleting product with id ${clickedcustomer?.firstName}: ${error.message}` + ); + } else { + navigate(`/dashboard/customer`); + setupdating(false); + showErrorToast(`Deleting ${clickedcustomer?.firstName} failed`); + throw new Error(`Unexpected error occurred: ${error}`); + } + } + }; + const handleRole = (customer: Buyer | null) => { + setClickedcustomer(customer); + setupRole(true); + }; + const comformedit = async () => { + setupRole(false); + setupdating(true); + const formData: UserRoleUpdate = { + userId: clickedcustomer?.id ?? 0, + newRoleId: selectedRole, + }; + try { + await axios.patch( + `${import.meta.env.VITE_BASE_URL}/roles/change_user_role`, + formData + ); + navigate(`/dashboard/customers`); + dispatch(fetchBuyers()); + setupdating(false); + showSuccessToast( + `${clickedcustomer?.firstName} Role was Changed Successfully` + ); + } catch (error) { + if (axios.isAxiosError(error)) { + navigate(`/dashboard/customers`); + setupdating(false); + showErrorToast( + `Changing the Role of ${clickedcustomer?.firstName} ${clickedcustomer?.lastName} failed` + ); + throw new Error( + `Error Chenging Role of ${clickedcustomer?.firstName} ${clickedcustomer?.lastName}, ${error.message}` + ); + } else { + navigate(`/dashboard/customers`); + setupdating(false); + showErrorToast(`Changing Role of ${clickedcustomer?.firstName} failed`); + throw new Error(`Unexpected error occurred: ${error}`); + } + } + }; + + const closedit = () => { + setupRole(false); + }; + const activateVendor = `${import.meta.env.VITE_BASE_URL}/activate/${clickedcustomer?.id}`; + + const HandleActivate = (customer: Buyer | null) => { + if (customer?.status !== 'active') { + setupdating(true); + setActivate(false); + axios + .put(activateVendor, null, { + headers: { + Authorization: `Bearer ${localStorage.getItem('token')}`, + }, + }) + .then((res) => { + if (res.status === 200) { + setupdating(false); + dispatch(fetchBuyers()); + showSuccessToast(`${customer?.firstName} Activated Successfully`); + setReRenderTrigger((prev) => !prev); + } else { + setupdating(false); + showErrorToast('Failed to Activate the customer'); + } + }) + .catch((error) => { + setupdating(false); + showErrorToast(error.message); + }); + } else { + showErrorToast('User is Already Active'); + } + }; + + const updatecustomerStatus = `${import.meta.env.VITE_BASE_URL}/deactivate/${clickedcustomer?.id}`; + + const handleSuspend = (customer: Buyer | null) => { + if (customer?.status !== 'inactive') { + setupdating(true); + setDeactivate(false); + axios + .put(updatecustomerStatus, null, { + headers: { + Authorization: `Bearer ${localStorage.getItem('token')}`, + }, + }) + .then((res) => { + if (res.status === 200) { + setupdating(false); + dispatch(fetchBuyers()); + showSuccessToast(`${customer?.firstName} Suspended Successfully`); + setReRenderTrigger((prev) => !prev); + } else { + setupdating(false); + showErrorToast('Failed to Suspend the vendor'); + } + }) + .catch((error) => { + setupdating(false); + showErrorToast(error.message); + }); + } else { + showErrorToast('User is Already Inactive'); + } + }; + + const DateFormat = (udpdatedAt: string) => { + const date = new Date(udpdatedAt); + return date.toLocaleDateString(); + }; + + const HandleNext = () => { + if (currentPage < TotalPages) setCurrentPage(currentPage + 1); + }; + + const HandlePrev = () => { + if (currentPage > 1) setCurrentPage(currentPage - 1); + }; + + const HandleSearch = (value: string) => { + setSearch(value); + if (value.trim() === '') { + setFilteredcustomer(buyers.filter((v) => v.userType.name === 'Buyer')); + } else { + setFilteredcustomer( + buyers + .filter((v) => v.userType.name === 'Buyer') + .filter((v) => + `${v.firstName} ${v.lastName} ${v.email}` + .toLowerCase() + .includes(value.toLowerCase()) + ) + ); + } + }; + + return ( +
+ {mode === 'delete' && ( +
+ setModalVisible(false)} + onConfirm={confirmDelete} + message="Are you sure you want to Delete this customer ?" + /> +
+ )} + {deactivate && ( +
+
+
+ Are you sure you want to suspend? +
+ {clickedcustomer?.firstName} +
+
+
+
+
+
+ )} + + {activate && ( +
+
+
+ Are you sure you want to activate? +
+ {clickedcustomer?.firstName} +
+
+
+
+
+
+ )} + +
+
+
Customers
+
+
+
+

All ({customers.length})

+

+ Approved ({customers.filter((v) => v.status === 'active').length}) +

+

+ Suspended ( + {customers.filter((v) => v.status === 'inactive').length}) +

+
+
+ + HandleSearch(e.target.value)} + /> +
+
+
+
+ {status === 'loading' && ( +
+ +
+ )} +
+
+
+
+
Image
+
+ First Name +
+
+ Last Name +
+
Email
+
Date
+
Status
+
Role
+
Action
+
+ {customers.length > 0 ? ( + visiblePage.map((v, id) => ( +
+
+ +
+
+ {v.firstName} +
+
+ {v.lastName} +
+
+ {v.email} +
+
+ {DateFormat(v.updatedAt)} +
+
+ + {v.status} + +
+
+ Buyer +
+ handleRole(v)} + /> +
+
+
+ + + +
+
+ )) + ) : ( +
No customer Found
+ )} +
+ + {pages && + pages.map((page) => ( + + ))} + +
+
+
+ +
+ {customers && + visiblePage.map((v, id) => ( +
+
+ +
+

+ {v.firstName} {v.lastName} +

+

{v.email}

+
+
+
+

First Name: {v.firstName}

+

Date: {DateFormat(v.updatedAt)}

+

+ Status: + + {v.status} + +

+
+
+ + + +
+
+ ))} +
+ +
+
+ +
+
+ + {/* -------------------------------------------------------- */} + {updateRole && ( +
+
+
+
+

Change Role

+
+
Current Details:
+
+

Name:

+

+ {clickedcustomer?.firstName} {clickedcustomer?.lastName} +

+
+
+

Email:

+

{clickedcustomer?.email}

+
+
+

Status:

+ + {clickedcustomer?.status} + +
+
+

Current Role:

+ + Buyer + +
+
+
+
+ Make change: +
+
+
Select Role
+
+ +
+
+
+
+ +
+ +
+
+ +
+ + +
+
+
+ )} + {/* -------------------------------------------------------- */} +
+ ); +} + +export default Customer; diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index 25bcaa1..bb9c1d1 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -27,6 +27,7 @@ import AddCoupon from '@/pages/AddCoupon'; import Coupons from '@/pages/Coupons'; import EditCoupon from '@/pages/EditCoupon'; import TableUserRole from '@/components/dashBoard/UserRole'; +import Customer from '@/pages/customer'; function AppRoutes() { return ( @@ -66,6 +67,7 @@ function AppRoutes() { } /> } /> } /> + } />