import React, { useState, useRef, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { Bell, Check, CheckCheck, Trash2, X, Ticket, Calendar, MessageSquare, Clock } from 'lucide-react'; import { useNotifications, useUnreadNotificationCount, useMarkNotificationRead, useMarkAllNotificationsRead, useClearAllNotifications, } from '../hooks/useNotifications'; import { Notification } from '../api/notifications'; interface NotificationDropdownProps { variant?: 'light' | 'dark'; onTicketClick?: (ticketId: string) => void; } const NotificationDropdown: React.FC = ({ variant = 'dark', onTicketClick }) => { const { t } = useTranslation(); const navigate = useNavigate(); const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); const { data: notifications = [], isLoading } = useNotifications({ limit: 20 }); const { data: unreadCount = 0 } = useUnreadNotificationCount(); const markReadMutation = useMarkNotificationRead(); const markAllReadMutation = useMarkAllNotificationsRead(); const clearAllMutation = useClearAllNotifications(); // Close dropdown when clicking outside 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 handleNotificationClick = (notification: Notification) => { // Mark as read if (!notification.read) { markReadMutation.mutate(notification.id); } // Handle ticket notifications specially - open modal instead of navigating if (notification.target_type === 'ticket' && onTicketClick) { const ticketId = notification.data?.ticket_id; if (ticketId) { onTicketClick(String(ticketId)); setIsOpen(false); return; } } // Handle time-off request notifications - navigate to time blocks page // Includes both new requests and modified requests that need re-approval if (notification.data?.type === 'time_off_request' || notification.data?.type === 'time_off_request_modified') { navigate('/dashboard/time-blocks'); setIsOpen(false); return; } // Navigate to target if available if (notification.target_url) { navigate(notification.target_url); setIsOpen(false); } }; const handleMarkAllRead = () => { markAllReadMutation.mutate(); }; const handleClearAll = () => { clearAllMutation.mutate(); }; const getNotificationIcon = (notification: Notification) => { // Check for time-off request type in data (new or modified) if (notification.data?.type === 'time_off_request' || notification.data?.type === 'time_off_request_modified') { return ; } switch (notification.target_type) { case 'ticket': return ; case 'event': case 'appointment': return ; default: return ; } }; const formatTimestamp = (timestamp: string) => { const date = new Date(timestamp); 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 t('notifications.justNow', 'Just now'); if (diffMins < 60) return t('notifications.minutesAgo', '{{count}}m ago', { count: diffMins }); if (diffHours < 24) return t('notifications.hoursAgo', '{{count}}h ago', { count: diffHours }); if (diffDays < 7) return t('notifications.daysAgo', '{{count}}d ago', { count: diffDays }); return date.toLocaleDateString(); }; const buttonClasses = variant === 'light' ? 'p-2 rounded-md text-white/80 hover:text-white hover:bg-white/10 transition-colors' : 'relative p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700'; return (
{/* Bell Button */} {/* Dropdown */} {isOpen && (
{/* Header */}

{t('notifications.title', 'Notifications')}

{unreadCount > 0 && ( )}
{/* Notification List */}
{isLoading ? (
{t('common.loading')}
) : notifications.length === 0 ? (

{t('notifications.noNotifications', 'No notifications yet')}

) : (
{notifications.map((notification) => ( ))}
)}
{/* Footer */} {notifications.length > 0 && (
)}
)}
); }; export default NotificationDropdown;