"use client" import { useState, useEffect } from "react" import { Bell, Check, ExternalLink } from "lucide-react" import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator } from "@/components/ui/dropdown-menu" import { Badge } from "@/components/ui/badge" import { ScrollArea } from "@/components/ui/scroll-area" import { getApiUrl } from "@/lib/api-config" import Link from "next/link" import { formatDistanceToNow } from "date-fns" import { cn } from "@/lib/utils" interface Notification { id: number type: string title: string message: string link: string | null is_read: boolean created_at: string } export function NotificationBell() { const [notifications, setNotifications] = useState([]) const [unreadCount, setUnreadCount] = useState(0) const [isOpen, setIsOpen] = useState(false) const [loading, setLoading] = useState(false) const fetchNotifications = async () => { const token = localStorage.getItem("token") if (!token) return setLoading(true) try { const res = await fetch(`${getApiUrl()}/notifications/`, { headers: { Authorization: `Bearer ${token}` } }) if (res.ok) { const data = await res.json() setNotifications(data) setUnreadCount(data.filter((n: Notification) => !n.is_read).length) } } catch (error) { console.error("Failed to fetch notifications", error) } finally { setLoading(false) } } // Determine when to fetch: Mount and when opened useEffect(() => { fetchNotifications() // Optional: Poll every 60s const interval = setInterval(fetchNotifications, 60000) return () => clearInterval(interval) }, []) const handleMarkAsRead = async (id: number) => { const token = localStorage.getItem("token") if (!token) return try { await fetch(`${getApiUrl()}/notifications/${id}/read`, { method: "POST", headers: { Authorization: `Bearer ${token}` } }) // Optimistic update setNotifications(prev => prev.map(n => n.id === id ? { ...n, is_read: true } : n)) setUnreadCount(prev => Math.max(0, prev - 1)) } catch (error) { console.error("Failed to mark as read", error) } } const handleMarkAllRead = async () => { const token = localStorage.getItem("token") if (!token) return try { await fetch(`${getApiUrl()}/notifications/mark-all-read`, { method: "POST", headers: { Authorization: `Bearer ${token}` } }) setNotifications(prev => prev.map(n => ({ ...n, is_read: true }))) setUnreadCount(0) } catch (error) { console.error("Failed to mark all as read", error) } } return ( { setIsOpen(open) if (open) fetchNotifications() }}>
Notifications {unreadCount > 0 && ( )}
{notifications.length === 0 ? (
No notifications yet.
) : (
{notifications.map((notification) => (
handleMarkAsRead(notification.id)} >
{notification.title} {formatDistanceToNow(new Date(notification.created_at), { addSuffix: true })}

{notification.message}

{notification.link && ( e.stopPropagation()} > View )}
))}
)}
) }