'use client' import { useState } from 'react' import Link from 'next/link' import type { Route } from 'next' import { usePathname } from 'next/navigation' import { trpc } from '@/lib/trpc/client' import { cn, formatRelativeTime } from '@/lib/utils' import { Button } from '@/components/ui/button' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' import { ScrollArea } from '@/components/ui/scroll-area' import { Bell, CheckCheck, Settings, AlertTriangle, FileText, Files, Upload, ClipboardList, PlayCircle, Clock, AlertCircle, Lock, Users, TrendingUp, Trophy, CheckCircle, Star, GraduationCap, Vote, Brain, Download, AlertOctagon, RefreshCw, CalendarPlus, Heart, BarChart, Award, UserPlus, UserCheck, UserMinus, FileCheck, Eye, MessageSquare, MessageCircle, Info, Calendar, Newspaper, UserX, Lightbulb, BookOpen, XCircle, Edit, FileUp, } from 'lucide-react' // Icon mapping for notification types const ICON_MAP: Record> = { Brain, AlertTriangle, FileText, Files, Upload, ClipboardList, PlayCircle, Clock, AlertCircle, Lock, Users, TrendingUp, Trophy, CheckCircle, Star, GraduationCap, Vote, Download, AlertOctagon, RefreshCw, CalendarPlus, Heart, BarChart, Award, UserPlus, UserCheck, UserMinus, FileCheck, Eye, MessageSquare, MessageCircle, Info, Calendar, Newspaper, UserX, Lightbulb, BookOpen, XCircle, Edit, FileUp, Bell, } // Priority styles const PRIORITY_STYLES = { low: { iconBg: 'bg-slate-100 dark:bg-slate-800', iconColor: 'text-slate-500', }, normal: { iconBg: 'bg-blue-100 dark:bg-blue-900/30', iconColor: 'text-blue-600 dark:text-blue-400', }, high: { iconBg: 'bg-amber-100 dark:bg-amber-900/30', iconColor: 'text-amber-600 dark:text-amber-400', }, urgent: { iconBg: 'bg-red-100 dark:bg-red-900/30', iconColor: 'text-red-600 dark:text-red-400', }, } type Notification = { id: string type: string priority: string icon: string | null title: string message: string linkUrl: string | null linkLabel: string | null isRead: boolean createdAt: Date } function NotificationItem({ notification, onRead, }: { notification: Notification onRead: () => void }) { const IconComponent = ICON_MAP[notification.icon || 'Bell'] || Bell const priorityStyle = PRIORITY_STYLES[notification.priority as keyof typeof PRIORITY_STYLES] || PRIORITY_STYLES.normal const content = (
{/* Icon with colored background */}
{/* Content */}

{notification.title}

{notification.message}

{formatRelativeTime(notification.createdAt)} {notification.linkLabel && ( {notification.linkLabel} → )}
{/* Unread dot */} {!notification.isRead && (
)}
) if (notification.linkUrl) { return ( {content} ) } return content } export function NotificationBell() { const [filter, setFilter] = useState<'all' | 'unread'>('all') const [open, setOpen] = useState(false) const pathname = usePathname() // Derive the role-based path prefix from the current route const pathPrefix = pathname.startsWith('/admin') ? '/admin' : pathname.startsWith('/jury') ? '/jury' : pathname.startsWith('/mentor') ? '/mentor' : pathname.startsWith('/observer') ? '/observer' : '' const { data: countData } = trpc.notification.getUnreadCount.useQuery( undefined, { refetchInterval: 30000, // Refetch every 30 seconds } ) const { data: hasUrgent } = trpc.notification.hasUrgent.useQuery(undefined, { refetchInterval: 30000, }) const { data: notificationData, refetch } = trpc.notification.list.useQuery( { unreadOnly: filter === 'unread', limit: 20, }, { enabled: open, // Only fetch when popover is open } ) const markAsReadMutation = trpc.notification.markAsRead.useMutation({ onSuccess: () => refetch(), }) const markAllAsReadMutation = trpc.notification.markAllAsRead.useMutation({ onSuccess: () => refetch(), }) const unreadCount = countData ?? 0 const notifications = notificationData?.notifications ?? [] return ( {/* Header */}

Notifications

{unreadCount > 0 && ( )}
{/* Filter tabs */}
{/* Notification list */}
{notifications.map((notification) => ( { if (!notification.isRead) { markAsReadMutation.mutate({ id: notification.id }) } }} /> ))} {notifications.length === 0 && (

{filter === 'unread' ? 'No unread notifications' : 'No notifications yet'}

)}
{/* Footer */} {notifications.length > 0 && (
)}
) }