'use client'; import { useEffect, useState, useRef } from 'react'; import { Bell, Settings, X } from 'lucide-react'; import { useNotifications } from '@/hooks/useApi'; import { useWebPush } from '@/hooks/useWebPush'; import { formatDistanceToNow } from 'date-fns'; import { ru } from 'date-fns/locale/ru'; interface NotificationCenterProps { variant?: 'default' | 'mobile'; isOpen?: boolean; onToggle?: () => void; } export function NotificationCenter({ variant = 'default', isOpen: externalIsOpen, onToggle }: NotificationCenterProps) { const [internalIsOpen, setInternalIsOpen] = useState(false); const isControlled = externalIsOpen !== undefined; const isOpen = isControlled ? externalIsOpen : internalIsOpen; const [showSettings, setShowSettings] = useState(false); const dropdownRef = useRef(null); const { notifications: notificationsData, unreadCount, markAsRead, markAllAsRead, loading } = useNotifications(); const { isSupported, isSubscribed, subscribe, unsubscribe, permission, isLoading: pushLoading } = useWebPush(); const notifications = Array.isArray(notificationsData) ? notificationsData : []; const toggleOpen = () => { if (onToggle) { onToggle(); } else { setInternalIsOpen((v) => !v); } }; const close = () => { if (onToggle && isOpen) { onToggle(); } else { setInternalIsOpen(false); } setShowSettings(false); }; // Закрытие при клике вне компонента useEffect(() => { function handleClickOutside(event: MouseEvent) { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { close(); } } document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [isOpen]); const handleNotificationClick = async (notification: any) => { if (!notification.is_read) { await markAsRead(notification.id); } // Навигация если есть action_url if (notification.action_url) { window.location.href = notification.action_url; } }; const handleTogglePush = async () => { if (isSubscribed) { await unsubscribe(); } else { await subscribe(); } }; return (
{/* Кнопка уведомлений */} {/* Dropdown — только для desktop */} {isOpen && variant === 'default' && (
{/* Заголовок */}

Уведомления

{unreadCount > 0 && ( )}
{/* Настройки Push */} {showSettings && (

Push уведомления

{!isSupported ? (

Push уведомления не поддерживаются вашим браузером

) : permission === 'denied' ? (

Вы запретили уведомления. Измените настройки браузера.

) : (
{isSubscribed ? 'Включены' : 'Отключены'}
)}
)} {/* Список уведомлений */}
{loading ? (
Загрузка...
) : notifications.length === 0 ? (

Нет уведомлений

) : (
    {notifications.slice(0, 20).map((notification) => (
  • handleNotificationClick(notification)} className={`p-3 border-b border-slate-100 hover:bg-slate-100 cursor-pointer transition-colors ${ !notification.is_read ? 'bg-sky-50/50' : '' }`} >
    {!notification.is_read && (
    )}

    {notification.title}

    {notification.short_message || notification.message}

    {formatDistanceToNow(new Date(notification.created_at), { addSuffix: true, locale: ru, })}

  • ))}
)}
{/* Футер */} {notifications.length > 0 && (
Показать все уведомления
)}
)}
); } /** Встраиваемая панель уведомлений (для выдвижных блоков) */ export function NotificationPanel({ onClose }: { onClose?: () => void }) { const { notifications: notificationsData, unreadCount, markAsRead, markAllAsRead, loading } = useNotifications(); const { isSupported, isSubscribed, subscribe, unsubscribe, permission, isLoading: pushLoading } = useWebPush(); const [showSettings, setShowSettings] = useState(false); const notifications = Array.isArray(notificationsData) ? notificationsData : []; const handleNotificationClick = async (notification: any) => { if (!notification.is_read) { await markAsRead(notification.id); } if (notification.action_url) { window.location.href = notification.action_url; } }; const handleTogglePush = async () => { if (isSubscribed) { await unsubscribe(); } else { await subscribe(); } }; return (

Уведомления

{unreadCount > 0 && ( )} {onClose && ( )}
{showSettings && (

Push уведомления

{!isSupported ? (

Не поддерживаются браузером

) : permission === 'denied' ? (

Запрещены. Измените настройки браузера.

) : (
{isSubscribed ? 'Включены' : 'Отключены'}
)}
)}
{loading ? (
Загрузка...
) : notifications.length === 0 ? (

Нет уведомлений

) : (
    {notifications.slice(0, 20).map((notification) => (
  • handleNotificationClick(notification)} className={`p-3 border-b border-slate-100 hover:bg-slate-100 cursor-pointer transition-colors ${ !notification.is_read ? 'bg-sky-50/50' : '' }`} >
    {!notification.is_read && (
    )}

    {notification.title}

    {notification.short_message || notification.message}

    {formatDistanceToNow(new Date(notification.created_at), { addSuffix: true, locale: ru })}

  • ))}
)}
{notifications.length > 0 && (
Показать все
)}
); }