/** * @ai-summary Bell icon with dropdown for in-app notifications * @ai-context Displays in header with unread count badge */ import React, { useState } from 'react'; import { IconButton, Badge, Popover, Box, Typography, List, ListItem, ListItemText, Button, Divider, CircularProgress, } from '@mui/material'; import NotificationsIcon from '@mui/icons-material/Notifications'; import CloseIcon from '@mui/icons-material/Close'; import CheckIcon from '@mui/icons-material/Check'; import { useInAppNotifications } from '../hooks/useInAppNotifications'; // Helper function for relative time function formatTimeAgo(dateString: string): string { const date = new Date(dateString); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / (1000 * 60)); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); if (diffMins < 1) return 'just now'; if (diffMins < 60) return `${diffMins}m ago`; if (diffHours < 24) return `${diffHours}h ago`; if (diffDays < 7) return `${diffDays}d ago`; return date.toLocaleDateString(); } export const NotificationBell: React.FC = () => { const [anchorEl, setAnchorEl] = useState(null); const { notifications, unreadCount, isLoading, markAsRead, markAllAsRead, deleteNotification, } = useInAppNotifications(); const handleClick = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; const handleMarkAsRead = async (id: string) => { try { await markAsRead(id); } catch (error) { console.error('Failed to mark as read:', error); } }; const handleMarkAllAsRead = async () => { try { await markAllAsRead(); } catch (error) { console.error('Failed to mark all as read:', error); } }; const handleDelete = async (id: string) => { try { await deleteNotification(id); } catch (error) { console.error('Failed to delete notification:', error); } }; const open = Boolean(anchorEl); return ( <> Notifications {unreadCount > 0 && ( )} {isLoading ? ( ) : notifications.length === 0 ? ( No notifications You're all caught up ) : ( {notifications.map((notification) => ( {!notification.isRead && ( handleMarkAsRead(notification.id)} title="Mark as read" sx={{ minWidth: 36, minHeight: 36 }} > )} handleDelete(notification.id)} title="Delete" sx={{ minWidth: 36, minHeight: 36 }} > } > {notification.title} } secondary={ <> {notification.message} {formatTimeAgo(notification.createdAt)} } /> ))} )} ); };