119 lines
3.8 KiB
TypeScript
119 lines
3.8 KiB
TypeScript
import { updateReadedNotification } from '@/service/notify.api';
|
|
import { notificationQueries } from '@/service/queries';
|
|
import useNotificationStore from '@/store/useNotificationStore';
|
|
import { formatTimeAgo } from '@/utils/helper';
|
|
import { cn } from '@lib/utils';
|
|
import { m } from '@paraglide/messages';
|
|
import { BellIcon } from '@phosphor-icons/react';
|
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
|
import { Link } from '@tanstack/react-router';
|
|
import { Button } from '@ui/button';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuGroup,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from '@ui/dropdown-menu';
|
|
import { useEffect, useState } from 'react';
|
|
import { toast } from 'sonner';
|
|
import { Item, ItemContent, ItemDescription, ItemTitle } from './ui/item';
|
|
|
|
const Notification = () => {
|
|
const [open, _setOpen] = useState(false);
|
|
const { hasNew, setHasNew } = useNotificationStore((state) => state);
|
|
const { data } = useQuery(notificationQueries.topFive());
|
|
|
|
const { mutate: updateReaded } = useMutation({
|
|
mutationFn: () => updateReadedNotification(),
|
|
onError: (error: ReturnError) => {
|
|
const code = error.code as Parameters<
|
|
typeof m.backend_message
|
|
>[0]['code'];
|
|
toast.error(m.backend_message({ code }), {
|
|
richColors: true,
|
|
});
|
|
},
|
|
});
|
|
|
|
const onOpenNotification = (isOpen: boolean) => {
|
|
_setOpen(isOpen);
|
|
updateReaded();
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (data) {
|
|
setHasNew(data.hasNewNotify);
|
|
}
|
|
}, [data]);
|
|
|
|
if (!data) return null;
|
|
|
|
return (
|
|
<DropdownMenu open={open} onOpenChange={onOpenNotification}>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
size="icon-lg"
|
|
variant="ghost"
|
|
className="relative rounded-full"
|
|
>
|
|
<BellIcon
|
|
size={32}
|
|
className={cn('origin-top', { 'animate-bell-ring': hasNew })}
|
|
/>
|
|
{hasNew && (
|
|
<span className="absolute top-1 right-1 rounded-full w-2 h-2 bg-red-600"></span>
|
|
)}
|
|
<span className="sr-only">{m.ui_label_notifications()}</span>
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent className="w-sm min-w-56 rounded-lg">
|
|
<DropdownMenuLabel className="font-bold text-black">
|
|
{m.ui_label_notifications()}
|
|
</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuGroup>
|
|
{data.list && data.list.length > 0 ? (
|
|
data.list.map((notify) => {
|
|
return (
|
|
<DropdownMenuItem key={notify.id}>
|
|
<Item className="p-0">
|
|
<ItemContent>
|
|
<ItemTitle className="text-sm">
|
|
{m.templates_title_notification({
|
|
title: notify.title as Parameters<
|
|
typeof m.templates_title_notification
|
|
>[0]['title'],
|
|
})}
|
|
</ItemTitle>
|
|
<ItemDescription>
|
|
{formatTimeAgo(new Date(notify.createdAt))}
|
|
</ItemDescription>
|
|
</ItemContent>
|
|
</Item>
|
|
</DropdownMenuItem>
|
|
);
|
|
})
|
|
) : (
|
|
<DropdownMenuItem className="py-10 justify-center">
|
|
{m.common_no_notify()}
|
|
</DropdownMenuItem>
|
|
)}
|
|
</DropdownMenuGroup>
|
|
<DropdownMenuGroup>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem asChild>
|
|
<Link to="/management/notifications" className="cursor-pointer">
|
|
{m.ui_view_all_notifications()}
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuGroup>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
);
|
|
};
|
|
|
|
export default Notification;
|