Files
dmail-front/src/pages/notification/Notification.tsx
T
hamid zarghami d0496c2bd7 notification
2025-08-23 16:34:11 +03:30

182 lines
8.9 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 { CloseCircle, Notification } from 'iconsax-react';
import { FC, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getIconColor } from '@/utils/colorUtils';
import { clx } from '../../helpers/utils';
import StatusCircle from '../../components/StatusCircle';
import { useOutsideClick } from '../../hooks/useOutSideClick';
import { useGetNotification, useReadAll } from './hooks/useNotificationData';
import { NotificationItemType, NotificationTypeEnum } 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 '../../components/Toast';
// import { useNavigate } from 'react-router-dom';
import { ErrorType } from '../../helpers/types';
import { useSharedStore } from '@/shared/store/sharedStore';
const Notifications: FC = () => {
// const navigate = useNavigate()
const { t } = useTranslation();
const ref = useOutsideClick(() => setShowModal(false));
const [activeTab, setActiveTab] = useState<'read' | 'unread'>('unread');
const [showModal, setShowModal] = useState<boolean>(false);
const { data, fetchNextPage, hasNextPage, refetch } = useGetNotification(activeTab);
const readAll = useReadAll()
const { emailWidth } = useSharedStore()
const posts = data?.pages.flatMap((page) => page.data?.notifications) || [];
const handleAllRead = () => {
readAll.mutate(undefined, {
onSuccess: () => {
refetch()
toast(t('success'), 'success')
},
onError: (error: ErrorType) => {
toast(error.response?.data?.error?.message[0], 'error')
}
})
}
const handleRedirect = (type: NotificationTypeEnum) => {
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={getIconColor('primary')} />
<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">
{data?.pages[0]?.data?.notificationCount}
</div>
</div>
{showModal && (
<div
id='notificationsContainer'
className={clx(
"xl:h-[calc(100%-110px)] h-[70%] p-6 z-[300] 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",
emailWidth > 0 ? 'xl:left-auto' : 'w-full'
)}
style={emailWidth > 0 && window.innerWidth > 1280 ? { left: `${emailWidth - 50}px` } : {}}
>
<div className="flex justify-between items-center">
<div>{t('notif.natification')}</div>
<div className="size-7 rounded-full bg-white/30 dark:bg-white/10 flex justify-center items-center">
<CloseCircle size={20} color={getIconColor('primary')} onClick={() => setShowModal(false)} />
</div>
</div>
<div className="mt-6 flex h-8 border border-white border-opacity-35 dark:border-white/20 text-xs rounded-lg overflow-hidden">
<div
onClick={() => setActiveTab('unread')}
className={clx(
'flex-1 border-l cursor-pointer text-white border-white border-opacity-70 bg-transparent flex justify-center items-center',
activeTab === 'unread' ? 'bg-white/70 text-black dark:bg-white/15 dark:text-white' : ''
)}
>
خوانده نشده
</div>
<div
onClick={() => setActiveTab('read')}
className={clx(
'flex-1 cursor-pointer text-white border-white bg-transparent flex justify-center items-center',
activeTab === 'read' ? 'bg-white/70 text-black dark:bg-white/15 dark:text-white' : ''
)}
>
خوانده شده
</div>
</div>
<div className='flex mt-4 justify-end'>
<Button
loading={readAll.isPending}
onClick={handleAllRead}
className={clx(
'flex-1 border bg-transparent text-xs h-7 rounded-md cursor-pointer text-white dark:text-white border-white border-opacity-70 dark:border-white/40 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={getIconColor('primary')} size={20} /></div>}
scrollableTarget="notificationsContainer"
>
{
posts.map((item: NotificationItemType) => (
<div onClick={() => handleRedirect(item.type)} className="bg-white/60 dark:bg-white/10 dark:text-white cursor-pointer h-fit gap-4 items-start rounded-xl p-3 mb-4 flex" key={item.id}>
{
!item.isRead &&
<div className="mt-1">
<StatusCircle color="#00BA4B" />
</div>
}
<div>
<div className="truncate max-w-[200px]">{item.message}</div>
<div className="mt-2 flex gap-2 text-[10px] text-description dark:text-gray-400">
<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;