'use client' import { useEffect, useState } from 'react' import { fetchAdminNotifications, formatNotificationDate, type NotificationsPageResult, } from '@/lib/adminNotifications' import { ADMIN_API_BASE } from '@/lib/api' interface AdminInboxItem { id: string readAt: string | null createdAt: string notificationEvent: { title: string; body: string; type: string; locale: string; data?: Record } } const CHANNELS = ['EMAIL', 'SMS', 'WHATSAPP', 'IN_APP', 'PUSH'] const STATUSES = ['PENDING', 'QUEUED', 'SENT', 'DELIVERED', 'FAILED', 'SKIPPED', 'DEAD_LETTER', 'READ'] const CHANNEL_BADGE: Record = { EMAIL: 'text-sky-400 bg-sky-950/40', SMS: 'text-emerald-400 bg-emerald-950/40', WHATSAPP: 'text-teal-400 bg-teal-950/40', IN_APP: 'text-violet-400 bg-violet-950/40', PUSH: 'text-orange-400 bg-orange-950/40', } const STATUS_BADGE: Record = { PENDING: 'text-yellow-400 bg-yellow-950/40', SENT: 'text-emerald-400 bg-emerald-950/40', DELIVERED: 'text-emerald-400 bg-emerald-950/40', FAILED: 'text-red-400 bg-red-950/40', QUEUED: 'text-sky-400 bg-sky-950/40', SKIPPED: 'text-zinc-400 bg-zinc-800', DEAD_LETTER: 'text-red-300 bg-red-950/60', READ: 'text-zinc-400 bg-zinc-800', } function formatRecipient(item: { recipientType: string | null recipientName: string | null recipientEmail: string | null employeeId: string | null renterId: string | null }) { const fallbackId = item.employeeId ?? item.renterId const label = item.recipientName || item.recipientEmail || (fallbackId ? `${fallbackId.slice(0, 10)}...` : '-') return item.recipientType ? `${item.recipientType}: ${label}` : label } export default function AdminNotificationsPage() { const [result, setResult] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [filterChannel, setFilterChannel] = useState('') const [filterStatus, setFilterStatus] = useState('') const [filterCompany, setFilterCompany] = useState('') const [page, setPage] = useState(1) const [inbox, setInbox] = useState<{ data: AdminInboxItem[]; unread: number }>({ data: [], unread: 0 }) function loadInbox() { fetch(`${ADMIN_API_BASE}/admin/notifications/me`, { credentials: 'include', cache: 'no-store' }) .then((response) => response.ok ? response.json() : Promise.reject(new Error('Failed to load personal inbox'))) .then((json) => setInbox(json.data)) .catch(() => {}) } function load(p: number, signal?: AbortSignal) { setLoading(true) setError(null) fetchAdminNotifications( p, { channel: filterChannel, status: filterStatus, companyId: filterCompany }, signal, ) .then((data) => setResult(data)) .catch((err) => { if (err instanceof DOMException && err.name === 'AbortError') return setResult(null) setError(err instanceof Error ? err.message : 'Failed to load notifications') }) .finally(() => { if (!signal?.aborted) setLoading(false) }) } useEffect(() => { const controller = new AbortController() setPage(1) load(1, controller.signal) loadInbox() return () => controller.abort() }, [filterChannel, filterStatus, filterCompany]) function goToPage(p: number) { setPage(p) load(p) } const totalPages = result ? (result.totalPages ?? Math.ceil(result.total / result.pageSize)) : 0 async function markRead(recipientId: string) { const response = await fetch(`${ADMIN_API_BASE}/admin/notifications/me/${recipientId}/read`, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: '{}' }) if (response.ok) loadInbox() } return (

Platform

Notifications

Full audit log of every notification sent across all companies.

{result && ( {result.total.toLocaleString()} total )}

My inbox

Assigned operational notices

{inbox.unread} unread
{inbox.data.length === 0 ?

No assigned notices.

: (
{inbox.data.map((item) => ( ))}
)}
{/* Filters */}
setFilterCompany(e.target.value)} placeholder="Company ID" className="w-64 rounded-xl border border-zinc-700 bg-zinc-800 px-3 py-2 text-sm text-zinc-100 placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-orange-500" />
{error &&
{error}
}
{loading ? ( ) : !result || result.data.length === 0 ? ( ) : result.data.map((item) => ( ))}
Date Company Recipient Event Channel Title Status Sent at
Loading…
No notifications found.
{formatNotificationDate(item.createdAt)} {item.company?.name ?? (item.companyId ? item.companyId.slice(0, 10) + '…' : '—')}

{formatRecipient(item)}

{item.recipientEmail && item.recipientName ? (

{item.recipientEmail}

) : null}
{item.type.replaceAll('_', ' ')} {item.channel}

{item.title}

{item.body}

{item.status} {formatNotificationDate(item.sentAt)}
{/* Pagination */} {totalPages > 1 && (
Page {page} of {totalPages} — {result?.total.toLocaleString()} records
)}
) }