import { useMemo } from 'react'; import { Link } from 'react-router-dom'; import { Bell } from 'lucide-react'; import { formatDistanceToNow } from 'date-fns'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; import { useNotifications, useUnreadCount, useMarkNotificationsRead, } from '../api/queries'; import type { Notification } from '../../../shared/types'; const KIND_LABELS: Record = { ASSIGNED: 'assigned to you', MENTION: 'mentioned you', RESOLVED: 'was resolved', TICKET_CREATED: 'was created', STATUS_CHANGED: 'status changed', COMMENT: 'new comment', }; interface NotifData { displayId?: string; title?: string; byName?: string; } function renderSummary(n: Notification): { label: string; href: string } { const data = (n.data ?? {}) as NotifData; const action = KIND_LABELS[n.kind] ?? n.kind.toLowerCase(); const label = data.title ? `${data.title} — ${action}` : `Ticket ${action}`; const href = data.displayId ? `/${data.displayId}` : '/notifications'; return { label, href }; } export default function NotificationsBell() { const { data: unreadCount = 0 } = useUnreadCount(); const { data: notifications = [] } = useNotifications(); const markRead = useMarkNotificationsRead(); const latest = useMemo(() => notifications.slice(0, 8), [notifications]); return (
Notifications {unreadCount > 0 && ( )}
{latest.length === 0 ? (

You're all caught up

) : ( latest.map((n) => { const { label, href } = renderSummary(n); const unread = !n.readAt; return ( unread && markRead.mutate({ ids: [n.id] })} className={`flex flex-col gap-1 px-3 py-2 text-sm border-b last:border-b-0 hover:bg-accent transition-colors ${ unread ? 'bg-primary/5' : '' }`} > {label} {formatDistanceToNow(new Date(n.createdAt), { addSuffix: true })} ); }) )}
View all notifications
); }