'use client' import { useEffect, useState, useCallback } from 'react' import { useTranslations } from 'next-intl' import { Button } from '@/components/ui/button' import { Loader } from '@/components/ui/loader' import { useSmartPoll } from '@/lib/use-smart-poll' interface Notification { id: number recipient: string type: string title: string message: string source_type?: string source_id?: number read_at?: number delivered_at?: number created_at: number } export function NotificationsPanel() { const t = useTranslations('notifications') const [recipient, setRecipient] = useState(() => { if (typeof window === 'undefined') return '' return window.localStorage.getItem('mc.notifications.recipient') || '' }) const [notifications, setNotifications] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const fetchNotifications = useCallback(async () => { if (!recipient) return try { setLoading(true) setError(null) const response = await fetch(`/api/notifications?recipient=${encodeURIComponent(recipient)}`) if (!response.ok) throw new Error('Failed to fetch notifications') const data = await response.json() setNotifications(data.notifications || []) } catch (err) { setError('Failed to fetch notifications') } finally { setLoading(false) } }, [recipient]) useEffect(() => { if (recipient) { window.localStorage.setItem('mc.notifications.recipient', recipient) fetchNotifications() } }, [recipient, fetchNotifications]) useSmartPoll(fetchNotifications, 30000, { enabled: !!recipient, pauseWhenSseConnected: true }) const markAllRead = async () => { if (!recipient) return try { const res = await fetch('/api/notifications', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ recipient, markAllRead: true }) }) if (!res.ok) throw new Error('Failed to mark all as read') fetchNotifications() } catch { // Silent — notification state will resync on next poll } } const markRead = async (id: number) => { try { const res = await fetch('/api/notifications', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: [id] }) }) if (!res.ok) throw new Error('Failed to mark as read') fetchNotifications() } catch { // Silent — notification state will resync on next poll } } return (

{t('title')}

setRecipient(e.target.value)} className="w-full bg-surface-1 text-foreground rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary/50" placeholder={t('recipientPlaceholder')} />
{error && (
{error}
)}
{loading ? (
) : notifications.length === 0 ? (
{t('noNotifications')}
) : ( notifications.map((n) => (
{n.title}
{n.type}
{!n.read_at && ( )}
{n.message}
{new Date(n.created_at * 1000).toLocaleString()}
)) )}
) }