feat(customers): Add filtering functionality to customer list table

- Create new CustomerListFilters component with status, role, and type filter options
- Integrate filter modal into customer list table with open/close functionality
- Add filter form handling with react-hook-form for managing filter state
- Implement search functionality triggered by filter form submission
- Add EmptyListWrapper component to display message when no customers found
- Replace inline create button with reusable ListHeader component
- Add clearable select fields for better filter UX
- Update customer table to safely handle optional customer properties
- Add new CustomerStatus and CustomerType enums to support filtering options
- Improve table organization with proper imports and component structure
This commit is contained in:
AmirReza Jamali
2025-11-22 15:22:44 +03:30
parent 1407d93704
commit 7005ab67b7
4 changed files with 278 additions and 105 deletions
+132 -100
View File
@@ -1,6 +1,11 @@
"use client";
import { ExclamationIcon, PencilSquareIcon, TrashIcon } from "@/assets/icons";
import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
import { Building } from "@/components/Layouts/sidebar/icons";
import ConfirmationModal from "@/components/ui/ConfirmationModal";
import ListHeader from "@/components/ui/ListHeader";
import Modal from "@/components/ui/modal";
import Status from "@/components/ui/Status";
import {
Table,
TableBody,
@@ -9,18 +14,14 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
import { _TooltipDefaultParams } from "@/constants/tooltip";
import Link from "next/link";
import useCustomerListPresenter from "./useCustomerListPresenter";
import { Building } from "@/components/Layouts/sidebar/icons";
import Modal from "@/components/ui/modal";
import Status from "@/components/ui/Status";
import TableSkeleton from "@/components/ui/TableSkeleton";
import { _TooltipDefaultParams } from "@/constants/tooltip";
import { unixToDate } from "@/utils/shared";
import { toast } from "react-toastify";
import { Tooltip } from "react-tooltip";
import AssignToCompanyModalContent from "./AssignToCompanyModalContent";
import CustomerListFilters from "./CustomerListFilters";
import useCustomerListPresenter from "./useCustomerListPresenter";
const CustomersTable = () => {
const {
@@ -39,12 +40,21 @@ const CustomersTable = () => {
setCurrentCustomer,
isAssignCompanyModalOpen,
setIsAssignCompanyModalOpen,
isFilterModalOpen,
setIsFilterModalOpen,
filterRegister,
handleFilterSubmit,
search,
watch,
setFilterValue,
isMutating,
} = useCustomerListPresenter();
return (
<div className="flex flex-col rounded-[10px] bg-white px-7.5 pb-4 pt-7.5 shadow-1 dark:bg-gray-dark dark:shadow-card">
<button className="mb-5 flex w-fit justify-center self-end rounded-lg bg-primary p-[13px] font-medium text-white hover:bg-opacity-90">
<Link href={`${pathName}/create`}>Create Customer </Link>
</button>
<ListHeader
onFilter={() => setIsFilterModalOpen(true)}
createButtonText="Create Customer"
/>
<Table>
<TableHeader>
<TableRow>
@@ -61,99 +71,121 @@ const CustomersTable = () => {
</TableHeader>
{isPending && <TableSkeleton column={columns.length} />}
<TableBody>
{allCustomers.map((customer, index) => (
<TableRow key={customer.id}>
<TableCell colSpan={100}>{index + 1}</TableCell>
<TableCell colSpan={100}>{customer.full_name}</TableCell>
<TableCell colSpan={100}>{customer.email}</TableCell>
<TableCell colSpan={100}>
{unixToDate({ unix: customer.created_at, hasTime: true })}
</TableCell>
<EmptyListWrapper
list={allCustomers ?? []}
columns={columns}
emptyMessage="No Customers were found"
>
{allCustomers?.map((customer, index) => (
<TableRow key={customer?.id ?? index}>
<TableCell colSpan={100}>{index + 1}</TableCell>
<TableCell colSpan={100}>{customer?.full_name}</TableCell>
<TableCell colSpan={100}>{customer?.email}</TableCell>
<TableCell colSpan={100}>
{unixToDate({ unix: customer?.created_at, hasTime: true })}
</TableCell>
<TableCell colSpan={100} className="uppercase">
{customer.type}
</TableCell>
<TableCell colSpan={100}>
<Status status={customer.status}>{customer.status}</Status>
</TableCell>
<TableCell colSpan={100}>
{customer.companies?.length ? (
customer.companies.map((c) => (
<span key={c.id}>{c.name}, </span>
))
) : (
<span>None</span>
)}
</TableCell>
<TableCell colSpan={100} className="uppercase">
{customer.role ?? "-"}
</TableCell>
<TableCell className="text-start xl:pr-7.5">
<div className="flex items-center justify-start gap-x-3.5">
<button
data-tooltip-id="delete"
data-tooltip-content="Delete"
data-tooltip-place="top"
className="hover:text-red-500"
onClick={() => {
setCurrentCustomer(customer);
setIsModalOpen(true);
}}
>
<Tooltip {..._TooltipDefaultParams({ id: "delete" })} />
<span className="sr-only">Delete Company </span>
<TrashIcon />
</button>
<button
data-tooltip-id="edit"
data-tooltip-content="Edit"
data-tooltip-place="top"
className="hover:text-primary"
onClick={() =>
router.push(`${pathName}/edit/${customer.id}`)
}
>
<Tooltip {..._TooltipDefaultParams({ id: "edit" })} />
<span className="sr-only">Edit Company </span>
<PencilSquareIcon />
</button>
<button
data-tooltip-id="assign-company"
data-tooltip-content="Assign to company"
data-tooltip-place="top"
className="hover:text-green-dark"
onClick={() => {
setCurrentCustomer(customer);
setIsAssignCompanyModalOpen(true);
setSelectedCompanies({
company_ids: [customer?.companies?.[0]?.id ?? ""],
});
}}
>
<Tooltip
{..._TooltipDefaultParams({ id: "assign-company" })}
/>
<span className="sr-only">Assign To Company </span>
<Building />
</button>
<button
data-tooltip-id="feedback"
data-tooltip-content="Feedback"
data-tooltip-place="top"
className="text-gray-40 rounded-full bg-gray-dark bg-opacity-5 hover:text-green-dark dark:bg-gray dark:bg-opacity-5"
onClick={() => {
router.push(`${pathName}/feedback/${customer.id}`);
}}
>
<Tooltip {..._TooltipDefaultParams({ id: "feedback" })} />
<ExclamationIcon />
</button>
</div>
</TableCell>
</TableRow>
))}
<TableCell colSpan={100} className="uppercase">
{customer?.type}
</TableCell>
<TableCell colSpan={100}>
<Status status={customer?.status}>{customer?.status}</Status>
</TableCell>
<TableCell colSpan={100}>
{customer?.companies?.length ? (
customer?.companies.map((c) => (
<span key={c.id}>{c.name}, </span>
))
) : (
<span>None</span>
)}
</TableCell>
<TableCell colSpan={100} className="uppercase">
{customer?.role ?? "-"}
</TableCell>
<TableCell className="text-start xl:pr-7.5">
<div className="flex items-center justify-start gap-x-3.5">
<button
data-tooltip-id="delete"
data-tooltip-content="Delete"
data-tooltip-place="top"
className="hover:text-red-500"
onClick={() => {
setCurrentCustomer(customer);
setIsModalOpen(true);
}}
>
<Tooltip {..._TooltipDefaultParams({ id: "delete" })} />
<span className="sr-only">Delete Company </span>
<TrashIcon />
</button>
<button
data-tooltip-id="edit"
data-tooltip-content="Edit"
data-tooltip-place="top"
className="hover:text-primary"
onClick={() =>
router.push(`${pathName}/edit/${customer.id}`)
}
>
<Tooltip {..._TooltipDefaultParams({ id: "edit" })} />
<span className="sr-only">Edit Company </span>
<PencilSquareIcon />
</button>
<button
data-tooltip-id="assign-company"
data-tooltip-content="Assign to company"
data-tooltip-place="top"
className="hover:text-green-dark"
onClick={() => {
setCurrentCustomer(customer);
setIsAssignCompanyModalOpen(true);
setSelectedCompanies({
company_ids: [customer?.companies?.[0]?.id ?? ""],
});
}}
>
<Tooltip
{..._TooltipDefaultParams({ id: "assign-company" })}
/>
<span className="sr-only">Assign To Company </span>
<Building />
</button>
<button
data-tooltip-id="feedback"
data-tooltip-content="Feedback"
data-tooltip-place="top"
className="text-gray-40 rounded-full bg-gray-dark bg-opacity-5 hover:text-green-dark dark:bg-gray dark:bg-opacity-5"
onClick={() => {
router.push(`${pathName}/feedback/${customer.id}`);
}}
>
<Tooltip {..._TooltipDefaultParams({ id: "feedback" })} />
<ExclamationIcon />
</button>
</div>
</TableCell>
</TableRow>
))}
</EmptyListWrapper>
</TableBody>
</Table>
<Modal
isOpen={isFilterModalOpen}
onClose={() => setIsFilterModalOpen(false)}
classNames="w-full xl:w-1/2"
showButtons={false}
>
<CustomerListFilters
setValue={setFilterValue}
filterRegister={filterRegister}
handleFilterSubmit={handleFilterSubmit}
isMutating={isMutating as number}
search={search}
setIsFilterModalOpen={setIsFilterModalOpen}
watch={watch}
/>
</Modal>
<div className="w-1/12">
<Modal
onConfirm={() => {