import { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import { notificationsApi, NotificationHistoryEntry } from '../api/client'; 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 / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 1) return 'Just now'; if (diffMins < 60) return `${diffMins}m ago`; if (diffHours < 24) return `${diffHours}h ago`; if (diffDays === 1) return 'Yesterday'; if (diffDays < 7) return `${diffDays}d ago`; return date.toLocaleDateString(); } function getNotificationIcon(type: string): string { switch (type) { case 'price_drop': return '\u{1F4C9}'; // Chart decreasing case 'price_target': return '\u{1F3AF}'; // Target case 'stock_change': return '\u{1F4E6}'; // Package default: return '\u{1F514}'; // Bell } } function getNotificationTitle(notification: NotificationHistoryEntry): string { switch (notification.notification_type) { case 'price_drop': const percent = notification.price_change_percent ? `${Math.abs(notification.price_change_percent).toFixed(0)}%` : ''; return `Price dropped ${percent}`; case 'price_target': return 'Target price reached'; case 'stock_change': return 'Back in stock'; default: return 'Notification'; } } function formatPrice(price: number | null, currency: string | null): string { if (price === null) return ''; const symbol = currency === 'EUR' ? '\u20AC' : currency === 'GBP' ? '\u00A3' : '$'; return `${symbol}${price.toFixed(2)}`; } export default function NotificationBell() { const [isOpen, setIsOpen] = useState(false); const [notifications, setNotifications] = useState([]); const [recentCount, setRecentCount] = useState(0); const [loading, setLoading] = useState(false); const dropdownRef = useRef(null); useEffect(() => { fetchNotifications(); // Poll for new notifications every 60 seconds const interval = setInterval(fetchNotifications, 60000); return () => clearInterval(interval); }, []); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const fetchNotifications = async () => { try { const response = await notificationsApi.getRecent(5); setNotifications(response.data.notifications); setRecentCount(response.data.recentCount); } catch (error) { console.error('Failed to fetch notifications:', error); } }; const handleOpen = async () => { setIsOpen(!isOpen); if (!isOpen) { setLoading(true); await fetchNotifications(); setLoading(false); } }; return (
{isOpen && (
Notifications
{loading ? (
Loading...
) : notifications.length === 0 ? (
{'\u{1F514}'}
No notifications yet
You'll be notified when prices drop
) : (
{notifications.map((notification) => ( setIsOpen(false)} > {getNotificationIcon(notification.notification_type)}
{getNotificationTitle(notification)}
{notification.product_name || 'Unknown Product'}
{notification.new_price && ( {formatPrice(notification.new_price, notification.currency)} )} {formatTimeAgo(notification.triggered_at)}
))}
)}
setIsOpen(false)}> View All History
)}
); }