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.
This commit is contained in:
@@ -38,6 +38,35 @@ export function CurrencyIcon(props: IconProps) {
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
export function FilterIcon(props: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M5.40002 2.09998H18.6C19.7 2.09998 20.6 2.99998 20.6 4.09998V6.29998C20.6 7.09998 20.1 8.09998 19.6 8.59998L15.3 12.4C14.7 12.9 14.3 13.9 14.3 14.7V19C14.3 19.6 13.9 20.4 13.4 20.7L12 21.6C10.7 22.4 8.90002 21.5 8.90002 19.9V14.6C8.90002 13.9 8.50002 13 8.10002 12.5L4.30002 8.49998C3.80002 7.99998 3.40002 7.09998 3.40002 6.49998V4.19998C3.40002 2.99998 4.30002 2.09998 5.40002 2.09998Z"
|
||||
stroke="#9299A6"
|
||||
strokeWidth="1.5"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.93 2.09998L6 9.99998"
|
||||
stroke="#9299A6"
|
||||
strokeWidth="1.5"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
export function MoneyIcon(props: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import InputGroup from "@/components/FormElements/InputGroup";
|
||||
import { Select } from "@/components/FormElements/select";
|
||||
import { AdminRoles, NotificationChannels } from "@/constants/enums";
|
||||
import { getEnumAsArray } from "@/utils/shared";
|
||||
import useAdminListFilterPresenter from "./useAdminListFilterPresenter";
|
||||
|
||||
interface IProps {}
|
||||
|
||||
const AdminListFilters = ({}: IProps) => {
|
||||
const { register } = useAdminListFilterPresenter();
|
||||
return (
|
||||
<form className="grid !min-w-full grid-cols-3 gap-4">
|
||||
<Select
|
||||
{...register("method")}
|
||||
items={getEnumAsArray(NotificationChannels)}
|
||||
label="Method"
|
||||
name="method"
|
||||
placeholder="Method"
|
||||
/>
|
||||
<Select
|
||||
{...register("status")}
|
||||
items={getEnumAsArray(NotificationChannels)}
|
||||
label="status"
|
||||
name="status"
|
||||
placeholder="Status"
|
||||
/>
|
||||
<InputGroup
|
||||
{...register("email")}
|
||||
name="email"
|
||||
label="email"
|
||||
placeholder="email"
|
||||
type="email"
|
||||
/>
|
||||
<Select
|
||||
{...register("role")}
|
||||
name="role"
|
||||
items={getEnumAsArray(AdminRoles)}
|
||||
label="Role"
|
||||
placeholder="Role"
|
||||
/>
|
||||
<InputGroup
|
||||
{...register("username")}
|
||||
label="username"
|
||||
name="username"
|
||||
placeholder="username"
|
||||
type="text"
|
||||
/>
|
||||
<InputGroup
|
||||
{...register("full_name")}
|
||||
label="full name"
|
||||
name="full_name"
|
||||
placeholder="full name"
|
||||
type="text"
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminListFilters;
|
||||
@@ -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 (
|
||||
<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 Admin</Link>
|
||||
</button>
|
||||
<ListHeader
|
||||
onFilter={() => setIsFilterModalOpen(true)}
|
||||
createButtonText="Create Admin"
|
||||
/>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-none uppercase [&>th]:text-start">
|
||||
@@ -143,6 +146,14 @@ const AdminsTable = () => {
|
||||
setStatus={setStatus}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
isOpen={isFilterModalOpen}
|
||||
onClose={() => setIsFilterModalOpen(false)}
|
||||
classNames="w-full"
|
||||
>
|
||||
<AdminListFilters />
|
||||
</Modal>
|
||||
{isModalOpen && (
|
||||
<ConfirmationModal
|
||||
isOpen={isModalOpen}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
const useAdminListFilterPresenter = () => {
|
||||
const { register } = useForm();
|
||||
return {
|
||||
register,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAdminListFilterPresenter;
|
||||
@@ -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<IUser | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [status, setStatus] = useState<string>(
|
||||
@@ -79,5 +80,7 @@ export const useAdminsPresenter = () => {
|
||||
register,
|
||||
errors,
|
||||
handleSubmit,
|
||||
isFilterModalOpen,
|
||||
setIsFilterModalOpen,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -154,6 +154,7 @@ const CustomersTable = () => {
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<div className="w-1/12">
|
||||
<Modal
|
||||
onConfirm={() => {
|
||||
if (!selectedCompanies || !currentCustomer) {
|
||||
@@ -173,6 +174,7 @@ const CustomersTable = () => {
|
||||
defaultValues={selectedCompanies}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
{isModalOpen && (
|
||||
<ConfirmationModal
|
||||
isOpen={isModalOpen}
|
||||
|
||||
@@ -66,6 +66,7 @@ const NotificationHistoryTable = () => {
|
||||
unix: item.created_at,
|
||||
})}
|
||||
</TableCell>
|
||||
<TableCell colSpan={100}>{item.recipient?.full_name}</TableCell>
|
||||
<TableCell colSpan={100} className="truncate">
|
||||
{truncateString(item.message, 20, 20)}
|
||||
</TableCell>
|
||||
|
||||
@@ -15,6 +15,7 @@ const useNotificationHistoryTablePresenter = () => {
|
||||
"title",
|
||||
"channel",
|
||||
"created at",
|
||||
"receiver",
|
||||
"message",
|
||||
"priority",
|
||||
"type",
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex items-center justify-between">
|
||||
<IsVisible condition={hasFilter}>
|
||||
<button
|
||||
onClick={onFilter}
|
||||
className="mb-5 flex w-fit justify-center self-end rounded-2xl border bg-gray-5 bg-opacity-20 px-6 py-2 font-medium transition duration-300 hover:bg-opacity-30 dark:border-gray-7 dark:bg-black dark:bg-opacity-40 dark:text-white dark:hover:bg-opacity-90"
|
||||
>
|
||||
<span>
|
||||
<FilterIcon />
|
||||
</span>
|
||||
{filterButtonText}
|
||||
</button>
|
||||
</IsVisible>
|
||||
<IsVisible condition={hasCreate}>
|
||||
<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`}>{createButtonText}</Link>
|
||||
</button>
|
||||
</IsVisible>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListHeader;
|
||||
@@ -12,6 +12,7 @@ interface ModalProps {
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
showButtons?: boolean;
|
||||
classNames?: string;
|
||||
}
|
||||
|
||||
const Modal: React.FC<ModalProps> = ({
|
||||
@@ -22,6 +23,7 @@ const Modal: React.FC<ModalProps> = ({
|
||||
confirmText = "confirm",
|
||||
cancelText = "close",
|
||||
showButtons = true,
|
||||
classNames,
|
||||
}) => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const isMutating = useIsMutating();
|
||||
@@ -49,7 +51,7 @@ const Modal: React.FC<ModalProps> = ({
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="max-h-[90vh] min-w-75 max-w-[90vw] overflow-auto rounded-lg bg-white p-5 shadow-4 dark:bg-dark-2"
|
||||
className={`max-h-[90vh] max-w-[90vw] overflow-auto rounded-lg bg-white p-5 shadow-4 dark:bg-dark-2 ${classNames}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
|
||||
@@ -6,6 +6,7 @@ import { toast } from "react-toastify";
|
||||
|
||||
export const useGetNotificationHistoryQuery = (
|
||||
params?: Record<string, any>,
|
||||
options?: { enabled: boolean },
|
||||
) => {
|
||||
const queryKey = useMemo(
|
||||
() => [API_ENDPOINTS.NOTIFICATIONS.HISTORY, params],
|
||||
@@ -14,6 +15,7 @@ export const useGetNotificationHistoryQuery = (
|
||||
return useQuery({
|
||||
queryKey,
|
||||
queryFn: () => notificationService.getNotifications(params),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
export const useGetMyNotifications = (params?: Record<string, any>) => {
|
||||
|
||||
Reference in New Issue
Block a user