'use client'; import React, { useState, useEffect, useRef } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import { useEffectiveDriverId } from '@/lib/currentDriver'; import type { Notification } from '@core/notifications/application'; import { Bell, AlertTriangle, Shield, Vote, Trophy, Users, Flag, X, Check, CheckCheck, ExternalLink, } from 'lucide-react'; const notificationIcons: Record = { protest_filed: AlertTriangle, protest_defense_requested: Shield, protest_vote_required: Vote, penalty_issued: AlertTriangle, race_results_posted: Trophy, league_invite: Users, race_reminder: Flag, }; const notificationColors: Record = { protest_filed: 'text-red-400 bg-red-400/10', protest_defense_requested: 'text-warning-amber bg-warning-amber/10', protest_vote_required: 'text-primary-blue bg-primary-blue/10', penalty_issued: 'text-red-400 bg-red-400/10', race_results_posted: 'text-performance-green bg-performance-green/10', league_invite: 'text-primary-blue bg-primary-blue/10', race_reminder: 'text-warning-amber bg-warning-amber/10', }; export default function NotificationCenter() { const [isOpen, setIsOpen] = useState(false); const [notifications, setNotifications] = useState([]); const [loading, setLoading] = useState(false); const panelRef = useRef(null); const router = useRouter(); const currentDriverId = useEffectiveDriverId(); // Polling for new notifications // TODO // useEffect(() => { // const loadNotifications = async () => { // try { // const repo = getNotificationRepository(); // const allNotifications = await repo.findByRecipientId(currentDriverId); // setNotifications(allNotifications); // } catch (error) { // console.error('Failed to load notifications:', error); // } // }; // loadNotifications(); // // Poll every 5 seconds // const interval = setInterval(loadNotifications, 5000); // return () => clearInterval(interval); // }, [currentDriverId]); // Close panel when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (panelRef.current && !panelRef.current.contains(event.target as Node)) { setIsOpen(false); } }; if (isOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isOpen]); const unreadCount = notifications.filter((n) => n.isUnread()).length; const handleMarkAsRead = async (notification: Notification) => { if (!notification.isUnread()) return; try { const markRead = getMarkNotificationReadUseCase(); await markRead.execute({ notificationId: notification.id, recipientId: currentDriverId, }); // Update local state setNotifications((prev) => prev.map((n) => (n.id === notification.id ? n.markAsRead() : n)) ); } catch (error) { console.error('Failed to mark notification as read:', error); } }; const handleMarkAllAsRead = async () => { try { const repo = getNotificationRepository(); await repo.markAllAsReadByRecipientId(currentDriverId); // Update local state setNotifications((prev) => prev.map((n) => n.markAsRead())); } catch (error) { console.error('Failed to mark all as read:', error); } }; const handleNotificationClick = async (notification: Notification) => { await handleMarkAsRead(notification); if (notification.actionUrl) { router.push(notification.actionUrl); setIsOpen(false); } }; const formatTime = (date: Date) => { const now = new Date(); const diff = now.getTime() - new Date(date).getTime(); const minutes = Math.floor(diff / (1000 * 60)); const hours = Math.floor(diff / (1000 * 60 * 60)); const days = Math.floor(diff / (1000 * 60 * 60 * 24)); if (minutes < 1) return 'Just now'; if (minutes < 60) return `${minutes}m ago`; if (hours < 24) return `${hours}h ago`; if (days < 7) return `${days}d ago`; return new Date(date).toLocaleDateString(); }; return (
{/* Bell button */} {/* Notification panel */} {isOpen && (
{/* Header */}
Notifications {unreadCount > 0 && ( {unreadCount} new )}
{unreadCount > 0 && ( )}
{/* Notifications list */}
{notifications.length === 0 ? (

No notifications yet

You'll be notified about protests, races, and more

) : (
{notifications.map((notification) => { const Icon = notificationIcons[notification.type] || Bell; const colorClass = notificationColors[notification.type] || 'text-gray-400 bg-gray-400/10'; return ( ); })}
)}
{/* Footer */} {notifications.length > 0 && (

Showing {notifications.length} notification{notifications.length !== 1 ? 's' : ''}

)}
)}
); }