From 5b73389d649f8edb14c82da7c2dbdb587578fbfb Mon Sep 17 00:00:00 2001 From: AmirReza Jamali Date: Sun, 12 Oct 2025 15:20:52 +0330 Subject: [PATCH] feat(admins): add filter functionality to admins list This commit introduces filtering capabilities to the administrators table, allowing users to refine the list based on specific criteria. A new `ListHeader` component has been implemented, which now contains the "Create Admin" button and a new "Filter" button. Clicking the filter button opens a modal containing the `AdminListFilters` form. The state for managing the filter modal's visibility is handled within the `useAdminsPresenter` hook. A new `FilterIcon` has also been added to support this feature. --- src/assets/icons.tsx | 29 +++++++++ .../Tables/admins/AdminListFilters.tsx | 59 +++++++++++++++++++ src/components/Tables/admins/index.tsx | 23 ++++++-- .../admins/useAdminListFilterPresenter.ts | 10 ++++ .../Tables/admins/useAdminsPresenter.ts | 3 + src/components/Tables/customers/index.tsx | 40 +++++++------ .../Tables/notification-history/index.tsx | 1 + .../useNotificationHistoryTablePresenter.ts | 1 + src/components/ui/ListHeader.tsx | 45 ++++++++++++++ src/components/ui/modal.tsx | 4 +- src/hooks/queries/useNotificationQueries.ts | 2 + 11 files changed, 191 insertions(+), 26 deletions(-) create mode 100644 src/components/Tables/admins/AdminListFilters.tsx create mode 100644 src/components/Tables/admins/useAdminListFilterPresenter.ts create mode 100644 src/components/ui/ListHeader.tsx diff --git a/src/assets/icons.tsx b/src/assets/icons.tsx index a8e3570..4ac130f 100644 --- a/src/assets/icons.tsx +++ b/src/assets/icons.tsx @@ -38,6 +38,35 @@ export function CurrencyIcon(props: IconProps) { ); } +export function FilterIcon(props: IconProps) { + return ( + + + + + ); +} export function MoneyIcon(props: IconProps) { return ( { + const { register } = useAdminListFilterPresenter(); + return ( +
+ + + + + + +
+ ); +}; + +export default AdminListFilters; diff --git a/src/components/Tables/admins/index.tsx b/src/components/Tables/admins/index.tsx index 8afe283..1735331 100644 --- a/src/components/Tables/admins/index.tsx +++ b/src/components/Tables/admins/index.tsx @@ -2,8 +2,8 @@ import { PencilSquareIcon, TrashIcon, UserSettingIcon } from "@/assets/icons"; import ConfirmationModal from "@/components/ui/ConfirmationModal"; +import ListHeader from "@/components/ui/ListHeader"; import Modal from "@/components/ui/modal"; -import { _TooltipDefaultParams } from "@/constants/tooltip"; import Status from "@/components/ui/Status"; import { Table, @@ -15,8 +15,9 @@ import { } from "@/components/ui/table"; import TableSkeleton from "@/components/ui/TableSkeleton"; import { AdminStatus } from "@/constants/enums"; -import Link from "next/link"; +import { _TooltipDefaultParams } from "@/constants/tooltip"; import { Tooltip } from "react-tooltip"; +import AdminListFilters from "./AdminListFilters"; import ChangeStatusModalContent from "./ChangeStatusModalContent"; import { useAdminsPresenter } from "./useAdminsPresenter"; @@ -32,7 +33,8 @@ const AdminsTable = () => { setIsChangeStatusModalOpen, columns, setCurrentUser, - pathName, + isFilterModalOpen, + setIsFilterModalOpen, changeStatus, currentUser, setStatus, @@ -41,9 +43,10 @@ const AdminsTable = () => { return (
- + setIsFilterModalOpen(true)} + createButtonText="Create Admin" + /> @@ -143,6 +146,14 @@ const AdminsTable = () => { setStatus={setStatus} /> + + setIsFilterModalOpen(false)} + classNames="w-full" + > + + {isModalOpen && ( { + const { register } = useForm(); + return { + register, + }; +}; + +export default useAdminListFilterPresenter; diff --git a/src/components/Tables/admins/useAdminsPresenter.ts b/src/components/Tables/admins/useAdminsPresenter.ts index 8f63241..1f4cca8 100644 --- a/src/components/Tables/admins/useAdminsPresenter.ts +++ b/src/components/Tables/admins/useAdminsPresenter.ts @@ -14,6 +14,7 @@ import { useForm } from "react-hook-form"; export const useAdminsPresenter = () => { const [isChangeStatusModalOpen, setIsChangeStatusModalOpen] = useState(false); + const [isFilterModalOpen, setIsFilterModalOpen] = useState(false); const [currentUser, setCurrentUser] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [status, setStatus] = useState( @@ -79,5 +80,7 @@ export const useAdminsPresenter = () => { register, errors, handleSubmit, + isFilterModalOpen, + setIsFilterModalOpen, }; }; diff --git a/src/components/Tables/customers/index.tsx b/src/components/Tables/customers/index.tsx index 3797bc1..057de63 100644 --- a/src/components/Tables/customers/index.tsx +++ b/src/components/Tables/customers/index.tsx @@ -154,25 +154,27 @@ const CustomersTable = () => { ))}
- { - if (!selectedCompanies || !currentCustomer) { - toast.error("Invalid credentials"); - return; - } - assignSelectedCompanies({ - credentials: selectedCompanies, - id: currentCustomer?.id ?? "", - }); - }} - isOpen={isAssignCompanyModalOpen} - onClose={() => setIsAssignCompanyModalOpen(false)} - > - - +
+ { + if (!selectedCompanies || !currentCustomer) { + toast.error("Invalid credentials"); + return; + } + assignSelectedCompanies({ + credentials: selectedCompanies, + id: currentCustomer?.id ?? "", + }); + }} + isOpen={isAssignCompanyModalOpen} + onClose={() => setIsAssignCompanyModalOpen(false)} + > + + +
{isModalOpen && ( { unix: item.created_at, })} + {item.recipient?.full_name} {truncateString(item.message, 20, 20)} diff --git a/src/components/Tables/notification-history/useNotificationHistoryTablePresenter.ts b/src/components/Tables/notification-history/useNotificationHistoryTablePresenter.ts index f8068d1..139fb5c 100644 --- a/src/components/Tables/notification-history/useNotificationHistoryTablePresenter.ts +++ b/src/components/Tables/notification-history/useNotificationHistoryTablePresenter.ts @@ -15,6 +15,7 @@ const useNotificationHistoryTablePresenter = () => { "title", "channel", "created at", + "receiver", "message", "priority", "type", diff --git a/src/components/ui/ListHeader.tsx b/src/components/ui/ListHeader.tsx new file mode 100644 index 0000000..adfa251 --- /dev/null +++ b/src/components/ui/ListHeader.tsx @@ -0,0 +1,45 @@ +"use client"; +import { FilterIcon } from "@/assets/icons"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import IsVisible from "./IsVisible"; + +interface IProps { + hasCreate?: boolean; + hasFilter?: boolean; + createButtonText?: string; + filterButtonText?: string; + onFilter?: () => void; +} + +const ListHeader = ({ + hasCreate = true, + hasFilter = true, + createButtonText = "Create", + filterButtonText = "Filter", + onFilter, +}: IProps) => { + const pathName = usePathname(); + return ( +
+ + + + + + +
+ ); +}; + +export default ListHeader; diff --git a/src/components/ui/modal.tsx b/src/components/ui/modal.tsx index ae6fe36..e065c6b 100644 --- a/src/components/ui/modal.tsx +++ b/src/components/ui/modal.tsx @@ -12,6 +12,7 @@ interface ModalProps { confirmText?: string; cancelText?: string; showButtons?: boolean; + classNames?: string; } const Modal: React.FC = ({ @@ -22,6 +23,7 @@ const Modal: React.FC = ({ confirmText = "confirm", cancelText = "close", showButtons = true, + classNames, }) => { const [mounted, setMounted] = useState(false); const isMutating = useIsMutating(); @@ -49,7 +51,7 @@ const Modal: React.FC = ({ onClick={onClose} >
e.stopPropagation()} >