src/pages/AdminNotifications/AdminNotifications.tsx
import React, { ChangeEvent, useEffect, useState } from "react";
import Notification from "./Notifications";
import * as icons from "react-icons/ai";
import SelectField from "../../components/ReusableComponents/Select";
import Loader from "../../components/ReusableComponents/Loaders";
import {
deleteNotification,
fetchAdminNotifications,
markNotificationAsRead,
} from "../../redux/actions/adminNotifications";
import { toast } from "react-toastify";
import {
useCustomPagination,
DOTS,
} from "../../components/Pagination/useCustomPagination";
import Pagination from "./Pagination";
interface NotificationType {
_id: string;
message: string;
createdAt: string;
read: boolean;
}
enum FilterOptions {
All = "all",
Unread = "unread",
}
enum OrderOptions {
Recent = "recent",
Oldest = "oldest",
}
const PAGE_SIZE = 4;
const AdminNotification: React.FC = () => {
const [notifications, setNotifications] = useState<NotificationType[]>([]);
const [filter, setFilter] = useState<FilterOptions>(FilterOptions.All);
const [orderBy, setOrderBy] = useState<OrderOptions>(OrderOptions.Recent);
const [currentPage, setCurrentPage] = useState(1);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const fetchedNotifications = await fetchAdminNotifications();
setNotifications(fetchedNotifications);
} catch (error) {
toast.error("Failed to fetch notifications");
}
setLoading(false);
};
fetchData();
}, []);
const handleMarkAsRead = async (id: string) => {
try {
const updatedNotification = await markNotificationAsRead(id);
if (updatedNotification) {
setNotifications((prev) =>
prev.map((notification) =>
notification._id === id
? { ...notification, read: true }
: notification
)
);
toast.success("Notification marked as read");
} else {
throw new Error("Failed to mark notification as read");
}
} catch (error: any) {
toast.error(`Error marking notification as read: ${error.message}`);
}
};
const handleDelete = async (id: string) => {
try {
await deleteNotification(id);
setNotifications((prev) =>
prev.filter((notification) => notification._id !== id)
);
} catch (error: any) {
toast.error(`Error deleting notification: ${error.message}`);
}
};
const filteredNotifications = getFilteredNotifications(notifications, filter);
const sortedNotifications = sortNotifications(filteredNotifications, orderBy);
const totalPageCount = Math.ceil(sortedNotifications.length / PAGE_SIZE);
const paginatedNotifications = sortedNotifications.slice(
(currentPage - 1) * PAGE_SIZE,
currentPage * PAGE_SIZE
);
const paginationRange = useCustomPagination({
totalPageCount,
siblingCount: 1,
currentPage,
});
const handleOrderChange = (e: ChangeEvent<HTMLSelectElement>) => {
setOrderBy(e.target.value as OrderOptions);
};
const onPageChange = (page: number) => {
setCurrentPage(page);
};
return (
<div className="h-screen w-full px-4 md:px-8 ">
{loading ? (
<div className="flex justify-center items-center h-full">
<Loader />
</div>
) : (
<>
<NotificationFilter
filter={filter}
setFilter={setFilter}
orderBy={orderBy}
onOrderChange={handleOrderChange}
/>
<NotificationList
notifications={paginatedNotifications}
onMarkAsRead={handleMarkAsRead}
onDelete={handleDelete}
/>
<Pagination
paginationRange={paginationRange}
currentPage={currentPage}
totalPageCount={totalPageCount}
onPageChange={onPageChange}
/>
</>
)}
</div>
);
};
const getFilteredNotifications = (
notifications: NotificationType[],
filter: FilterOptions
) => {
return notifications.filter((n) =>
filter === FilterOptions.All ? true : !n.read
);
};
const sortNotifications = (
notifications: NotificationType[],
orderBy: OrderOptions
) => {
return [...notifications].sort((a, b) => {
const dateA = new Date(Number(a.createdAt)).getTime();
const dateB = new Date(Number(b.createdAt)).getTime();
return orderBy === OrderOptions.Recent ? dateB - dateA : dateA - dateB;
});
};
const NotificationFilter: React.FC<{
filter: FilterOptions;
setFilter: (filter: FilterOptions) => void;
orderBy: OrderOptions;
onOrderChange: (e: ChangeEvent<HTMLSelectElement>) => void;
}> = ({ filter, setFilter, orderBy, onOrderChange }) => (
<div className="flex mt-10 space-x-6 mb-10 items-center ">
<div className="flex">
<button
className={`rounded-s-md w-20 py-2 px-4 border transition-colors ${
filter === FilterOptions.All
? "bg-primary text-white dark:bg-[#56C870] dark:text-white"
: "bg-white text-primary dark:bg-white dark:text-primary"
}`}
onClick={() => setFilter(FilterOptions.All)}
>
All
</button>
<button
className={`rounded-e-md w-20 py-2 px-4 transition-colors ${
filter === FilterOptions.Unread
? "text-white bg-primary dark:bg-[#56C870] dark:text-white"
: "bg-white text-primary dark:bg-white dark:text-primary"
}`}
onClick={() => setFilter(FilterOptions.Unread)}
>
Unread
</button>
</div>
<div className="ml-8 flex flex-row gap-2 items-center">
<span className="text-primary mr-3 dark:text-white">OrderBy:</span>
<SelectField
value={orderBy}
onChange={onOrderChange}
options={[
{ value: OrderOptions.Recent, label: "Newest" },
{ value: OrderOptions.Oldest, label: "Oldest" },
]}
className="rounded py-2 px-4 bg-primary dark:bg-[#56C870] text-white focus:outline-none cursor-pointer"
/>
</div>
</div>
);
const NotificationList: React.FC<{
notifications: NotificationType[];
onMarkAsRead: (id: string) => void;
onDelete: (id: string) => void;
}> = ({ notifications, onMarkAsRead, onDelete }) => (
<div className="space-y-4">
{notifications.length ? (
notifications.map((notification) => (
<Notification
key={notification._id}
notification={notification}
onMarkAsRead={onMarkAsRead}
onDelete={onDelete}
/>
))
) : (
<p className="text-primary dark:text-white text-center">
No notifications available.
</p>
)}
</div>
);
export default AdminNotification;