Skip to main content
Glama
northernvariables

FedMCP - Federal Parliamentary Information

NotificationBell.tsx6.21 kB
'use client'; import { useState, useRef, useEffect } from 'react'; import { Bell, Check, X } from 'lucide-react'; import { useRealtimeNotifications } from '@/hooks/useRealtimeNotifications'; import { useRouter } from 'next/navigation'; export function NotificationBell() { const router = useRouter(); const { notifications, unreadCount, markAsRead, markAllAsRead, clearNotifications } = useRealtimeNotifications(); const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef<HTMLDivElement>(null); // Close dropdown when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setIsOpen(false); } }; if (isOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isOpen]); const handleNotificationClick = (postId: string, notificationId: string) => { markAsRead(notificationId); setIsOpen(false); router.push(`/forum/posts/${postId}`); }; const formatTime = (date: Date) => { const now = new Date(); const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000); if (diffInSeconds < 60) return 'just now'; if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`; if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`; if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`; return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', }); }; return ( <div className="relative" ref={dropdownRef}> {/* Bell button */} <button onClick={() => setIsOpen(!isOpen)} className="relative p-2 rounded-lg hover:bg-background-secondary transition-colors" aria-label="Notifications" > <Bell size={20} className="text-text-secondary" /> {unreadCount > 0 && ( <span className="absolute top-0 right-0 bg-accent-red text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center"> {unreadCount > 9 ? '9+' : unreadCount} </span> )} </button> {/* Dropdown */} {isOpen && ( <div className=" absolute right-0 mt-2 w-80 max-h-96 overflow-y-auto bg-background-secondary border-2 border-border-primary rounded-lg shadow-2xl z-50 " > {/* Header */} <div className="flex items-center justify-between p-4 border-b border-border-primary sticky top-0 bg-background-secondary"> <h3 className="font-bold text-text-primary">Notifications</h3> {notifications.length > 0 && ( <div className="flex gap-2"> {unreadCount > 0 && ( <button onClick={markAllAsRead} className="text-xs text-accent-red hover:underline" > Mark all read </button> )} <button onClick={clearNotifications} className="text-xs text-text-tertiary hover:text-text-primary" > Clear all </button> </div> )} </div> {/* Notifications list */} {notifications.length === 0 ? ( <div className="p-8 text-center"> <Bell size={48} className="mx-auto text-text-tertiary mb-3 opacity-50" /> <p className="text-text-secondary text-sm">No notifications yet</p> <p className="text-text-tertiary text-xs mt-1"> We'll notify you when someone replies to your posts </p> </div> ) : ( <div className="divide-y divide-border-primary"> {notifications.map((notification) => ( <button key={notification.id} onClick={() => handleNotificationClick(notification.postId, notification.id)} className={` w-full p-4 text-left hover:bg-background-primary transition-colors ${!notification.read ? 'bg-accent-red/5' : ''} `} > <div className="flex items-start gap-3"> {/* Icon */} <div className={` w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${notification.read ? 'bg-background-primary' : 'bg-accent-red/20'} `} > {notification.type === 'reply' && ( <span className="text-lg">💬</span> )} {notification.type === 'vote' && ( <span className="text-lg">👍</span> )} {notification.type === 'mention' && ( <span className="text-lg">@</span> )} </div> {/* Content */} <div className="flex-1 min-w-0"> <p className={`text-sm ${ notification.read ? 'text-text-secondary' : 'text-text-primary font-medium' }`} > {notification.message} </p> <p className="text-xs text-text-tertiary mt-1"> {formatTime(notification.createdAt)} </p> </div> {/* Unread indicator */} {!notification.read && ( <div className="w-2 h-2 rounded-full bg-accent-red flex-shrink-0 mt-2" /> )} </div> </button> ))} </div> )} </div> )} </div> ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/northernvariables/FedMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server