Files
dmenu-admin/src/pages/notification/Notification.tsx
T
2025-12-21 15:05:23 +03:30

172 lines
8.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Notification } from 'iconsax-react';
import { type FC, useState } from 'react';
import XIcon from '../../assets/images/close-circle.svg';
import { useTranslation } from 'react-i18next';
import { clx } from '../../helpers/utils';
import StatusCircle from '../../components/StatusCircle';
import { useOutsideClick } from '../../hooks/useOutSideClick';
import { useGetNotification, useGetUnseenCount, useReadAll } from './hooks/useNotificationData';
import { type NotificationItemType } from './types/NotificationTypes';
import { timeAgo } from '../../config/func';
import InfiniteScroll from 'react-infinite-scroll-component';
import MoonLoader from "react-spinners/MoonLoader"
import Button from '../../components/Button';
import { toast } from 'react-toastify';
const Notifications: FC = () => {
// const navigate = useNavigate()
const { t } = useTranslation('global');
const ref = useOutsideClick(() => setShowModal(false));
const [activeTab, setActiveTab] = useState<'seen' | 'unseen'>('unseen');
const [showModal, setShowModal] = useState<boolean>(false);
const { data: unseenCount } = useGetUnseenCount();
const { data, fetchNextPage, hasNextPage, refetch } = useGetNotification(activeTab);
const readAll = useReadAll()
const posts = data?.pages.flatMap((page: { data: NotificationItemType[] }) => page.data || []) || [];
// const getDashboard = useGetDashboard()
const handleAllRead = () => {
readAll.mutate(undefined, {
onSuccess: () => {
// getDashboard.refetch()
refetch()
toast.success(t('success'))
}
})
}
const handleRedirect = () => {
// switch (type) {
// case NotificationTypeEnum.USER_LOGIN:
// break;
// case NotificationTypeEnum.ANNOUNCEMENT:
// navigate(Pages.announcement.list)
// break;
// case NotificationTypeEnum.WALLET_CHARGE:
// navigate(Pages.transactions)
// break;
// case NotificationTypeEnum.WALLET_DEDUCTION:
// navigate(Pages.transactions)
// break;
// case NotificationTypeEnum.BILL_INVOICE_REMINDER:
// navigate(Pages.receipts.index)
// break;
// case NotificationTypeEnum.BILL_INVOICE:
// navigate(Pages.receipts.index)
// break;
// case NotificationTypeEnum.CREATE_INVOICE:
// navigate(Pages.receipts.index)
// break;
// case NotificationTypeEnum.CREATE_SERVICE:
// navigate(Pages.services.other)
// break;
// case NotificationTypeEnum.UNBLOCK_SERVICE:
// navigate(Pages.services.other)
// break;
// case NotificationTypeEnum.BLOCK_SERVICE:
// navigate(Pages.services.other)
// break;
// case NotificationTypeEnum.ANSWER_TICKET:
// navigate(Pages.ticket.list)
// break;
// case NotificationTypeEnum.CREATE_TICKET:
// navigate(Pages.ticket.list)
// break;
// default:
// break
// }
setShowModal(false)
}
return (
<div ref={ref}>
<div onClick={() => setShowModal(!showModal)} className='relative cursor-pointer'>
<Notification size={18} color="black" />
<div className="absolute top-0 right-0 -mt-1 -mr-1 rounded-full bg-red-500 text-white text-[8px] size-3 flex pt-0.5 pl-[1px] justify-center items-center">
{unseenCount?.data?.count}
</div>
</div>
{showModal && (
<div id='notificationsContainer' className="xl:h-[calc(100%-110px)] h-[70%] p-6 z-50 xl:w-[300px] w-full xl:left-7 left-0 xl:top-[90px] top-0 overflow-y-auto xl:rounded-3xl rounded-b-3xl rounded-t-none fixed modalGlass3 ">
<div className="flex justify-between items-center">
<div>{t('notif.natification')}</div>
<div className="size-7 rounded-full bg-white bg-opacity-35 flex justify-center items-center">
<img src={XIcon} alt="close" className="w-4 h-4" onClick={() => setShowModal(false)} />
</div>
</div>
<div className="mt-6 flex h-8 border border-white border-opacity-35 text-xs rounded-lg overflow-hidden">
<div
onClick={() => setActiveTab('unseen')}
className={clx(
'flex-1 border-l cursor-pointer text-white border-white border-opacity-70 bg-transparent flex justify-center items-center',
activeTab === 'unseen' ? 'bg-white bg-opacity-70 text-black' : ''
)}
>
خوانده نشده
</div>
<div
onClick={() => setActiveTab('seen')}
className={clx(
'flex-1 cursor-pointer text-white border-white bg-transparent flex justify-center items-center',
activeTab === 'seen' ? 'bg-white bg-opacity-70 text-black' : ''
)}
>
خوانده شده
</div>
</div>
<div className='flex mt-4 justify-end'>
<Button
isloading={readAll.isPending}
onClick={handleAllRead}
className={clx(
'flex-1 border bg-transparent text-xs h-7 rounded-md cursor-pointer text-white border-white border-opacity-70 flex justify-center items-center',
)}
>
{t('notif.all_read')}
</Button>
</div>
<div className="mt-6 flex flex-col gap-4 text-xs overflow-auto">
<InfiniteScroll
dataLength={posts.length}
next={fetchNextPage}
hasMore={hasNextPage}
loader={<div className='flex justify-center'><MoonLoader color="black" size={20} /></div>}
scrollableTarget="notificationsContainer"
>
{
posts.map((item: NotificationItemType) => (
<div onClick={() => handleRedirect()} className="bg-white cursor-pointer h-fit gap-4 items-start rounded-xl bg-opacity-60 p-3 mb-4 flex" key={item.id}>
{
!item.seenAt &&
<div className="mt-1">
<StatusCircle color="#00BA4B" />
</div>
}
<div>
<div className="truncate max-w-[200px]">{item.content}</div>
<div className="mt-2 flex gap-2 text-[10px] text-description">
<div>{timeAgo(item.createdAt)}</div>
<div className="flex gap-1 items-center">
<StatusCircle color="#8C90A3" />
<div>{item.title}</div>
</div>
</div>
</div>
</div>
))
}
</InfiniteScroll>
</div>
</div>
)}
</div>
);
};
export default Notifications;