import React, { useState, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Bell, MessageSquare, DollarSign, CheckCircle2, XCircle, AlertTriangle, Star, Loader2, CheckCheck, } from 'lucide-react'; import { useAuth } from '@/contexts/AuthContext'; import { supabase } from '@/lib/supabase'; interface Notification { id: string; user_id: string; type: string; title: string; message: string; reference_type?: string; reference_id?: string; is_read: boolean; created_at: string; } export function NotificationBell() { const navigate = useNavigate(); const { user } = useAuth(); const [notifications, setNotifications] = useState([]); const [unreadCount, setUnreadCount] = useState(0); const [loading, setLoading] = useState(true); const [isOpen, setIsOpen] = useState(false); // Fetch notifications const fetchNotifications = useCallback(async () => { if (!user) return; try { const { data, error } = await supabase .from('p2p_notifications') .select('*') .eq('user_id', user.id) .order('created_at', { ascending: false }) .limit(20); if (error) throw error; setNotifications(data || []); setUnreadCount(data?.filter(n => !n.is_read).length || 0); } catch (error) { console.error('Fetch notifications error:', error); } finally { setLoading(false); } }, [user]); // Initial fetch useEffect(() => { fetchNotifications(); }, [fetchNotifications]); // Real-time subscription useEffect(() => { if (!user) return; const channel = supabase .channel(`notifications-${user.id}`) .on( 'postgres_changes', { event: 'INSERT', schema: 'public', table: 'p2p_notifications', filter: `user_id=eq.${user.id}`, }, (payload) => { const newNotif = payload.new as Notification; setNotifications(prev => [newNotif, ...prev.slice(0, 19)]); setUnreadCount(prev => prev + 1); } ) .subscribe(); return () => { supabase.removeChannel(channel); }; }, [user]); // Mark as read const markAsRead = async (notificationId: string) => { try { await supabase .from('p2p_notifications') .update({ is_read: true }) .eq('id', notificationId); setNotifications(prev => prev.map(n => n.id === notificationId ? { ...n, is_read: true } : n) ); setUnreadCount(prev => Math.max(0, prev - 1)); } catch (error) { console.error('Mark as read error:', error); } }; // Mark all as read const markAllAsRead = async () => { if (!user) return; try { await supabase .from('p2p_notifications') .update({ is_read: true }) .eq('user_id', user.id) .eq('is_read', false); setNotifications(prev => prev.map(n => ({ ...n, is_read: true }))); setUnreadCount(0); } catch (error) { console.error('Mark all as read error:', error); } }; // Handle notification click const handleClick = (notification: Notification) => { // Mark as read if (!notification.is_read) { markAsRead(notification.id); } // Navigate to reference if (notification.reference_type === 'trade' && notification.reference_id) { navigate(`/p2p/trade/${notification.reference_id}`); setIsOpen(false); } }; // Get icon for notification type const getIcon = (type: string) => { switch (type) { case 'new_message': return ; case 'payment_sent': return ; case 'payment_confirmed': return ; case 'trade_cancelled': return ; case 'dispute_opened': return ; case 'new_rating': return ; case 'new_order': return ; default: return ; } }; // Format time ago const formatTimeAgo = (dateString: string) => { const seconds = Math.floor((Date.now() - new Date(dateString).getTime()) / 1000); if (seconds < 60) return 'Just now'; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h ago`; const days = Math.floor(hours / 24); return `${days}d ago`; }; if (!user) return null; return ( Notifications {unreadCount > 0 && ( )} {loading ? (
) : notifications.length === 0 ? (

No notifications

) : ( notifications.map((notification) => ( handleClick(notification)} className={` flex items-start gap-3 p-3 cursor-pointer ${!notification.is_read ? 'bg-gray-800/50' : ''} hover:bg-gray-800 `} >
{getIcon(notification.type)}

{notification.title}

{notification.message && (

{notification.message}

)}

{formatTimeAgo(notification.created_at)}

{!notification.is_read && (
)} )) )} {notifications.length > 0 && ( <> { navigate('/p2p/orders'); setIsOpen(false); }} className="justify-center text-gray-400 hover:text-white cursor-pointer" > View all trades )} ); }