import { useEffect, useState, useRef } from 'react'; import { Link } from 'react-router-dom'; import { api } from '@/lib/api'; import { Bell, AlertTriangle, Calendar, Users, Mail, Clock, X } from 'lucide-react'; import { cn } from '@/lib/utils'; interface Notification { id: string; type: 'overdue' | 'upcoming' | 'stale' | 'drafts'; title: string; description: string; date: string; link: string; priority: 'high' | 'medium' | 'low'; } interface NotifCounts { total: number; high: number; overdue: number; upcoming: number; stale: number; drafts: number; } const typeIcons: Record = { overdue: AlertTriangle, upcoming: Calendar, stale: Users, drafts: Mail, }; const typeColors: Record = { overdue: 'text-red-500 bg-red-50 dark:bg-red-900/30', upcoming: 'text-blue-500 bg-blue-50 dark:bg-blue-900/30', stale: 'text-amber-500 bg-amber-50 dark:bg-amber-900/30', drafts: 'text-purple-500 bg-purple-50 dark:bg-purple-900/30', }; const priorityDot: Record = { high: 'bg-red-500', medium: 'bg-amber-400', low: 'bg-slate-300 dark:bg-slate-500', }; export default function NotificationBell() { const [notifications, setNotifications] = useState([]); const [counts, setCounts] = useState(null); const [open, setOpen] = useState(false); const [dismissed, setDismissed] = useState>(new Set()); const ref = useRef(null); useEffect(() => { fetchNotifications(); const interval = setInterval(fetchNotifications, 5 * 60 * 1000); return () => clearInterval(interval); }, []); useEffect(() => { const handleClick = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) { setOpen(false); } }; if (open) document.addEventListener('mousedown', handleClick); return () => document.removeEventListener('mousedown', handleClick); }, [open]); const fetchNotifications = async () => { try { const data = await api.getNotifications(); setNotifications(data.notifications || []); setCounts(data.counts || null); } catch { // Silently fail } }; const dismiss = (id: string) => { setDismissed(prev => new Set(prev).add(id)); }; const visibleNotifs = notifications.filter(n => !dismissed.has(n.id)); const activeCount = visibleNotifs.length; const highCount = visibleNotifs.filter(n => n.priority === 'high').length; return (
{open && (

Notifications

{counts && (
{counts.overdue > 0 && ( {counts.overdue} overdue )} {counts.upcoming > 0 && ( {counts.upcoming} upcoming )}
)}
{visibleNotifs.length === 0 ? (

All caught up!

) : ( visibleNotifs.map(n => { const Icon = typeIcons[n.type] || Calendar; return (
setOpen(false)} className="flex-1 min-w-0">

{n.title}

{n.description}

); }) )}
{visibleNotifs.length > 0 && (
setOpen(false)} className="text-xs text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-medium" > View Reports →
)}
)}
); }