import { useMemo, useState } from 'react'; import { Link } from 'react-router'; import { Inbox } from 'lucide-react'; import { Button, ButtonGroup, DataTable, EmptyState, useToast, } from '@cameleer/design-system'; import type { ButtonGroupItem, Column } from '@cameleer/design-system'; import { PageLoader } from '../../components/PageLoader'; import { SeverityBadge } from '../../components/SeverityBadge'; import { AlertStateChip } from '../../components/AlertStateChip'; import { useAlerts, useAckAlert, useBulkReadAlerts, useMarkAlertRead, type AlertDto, } from '../../api/queries/alerts'; import { severityToAccent } from './severity-utils'; import { formatRelativeTime } from './time-utils'; import { renderAlertExpanded } from './alert-expanded'; import css from './alerts-page.module.css'; import tableStyles from '../../styles/table-section.module.css'; type Severity = NonNullable; const SEVERITY_ITEMS: ButtonGroupItem[] = [ { value: 'CRITICAL', label: 'Critical', color: 'var(--error)' }, { value: 'WARNING', label: 'Warning', color: 'var(--warning)' }, { value: 'INFO', label: 'Info', color: 'var(--text-muted)' }, ]; export default function InboxPage() { const [severitySel, setSeveritySel] = useState>(new Set()); const severityValues: Severity[] | undefined = severitySel.size === 0 ? undefined : [...severitySel] as Severity[]; const { data, isLoading, error } = useAlerts({ state: ['FIRING', 'ACKNOWLEDGED'], severity: severityValues, limit: 200, }); const bulkRead = useBulkReadAlerts(); const markRead = useMarkAlertRead(); const ack = useAckAlert(); const { toast } = useToast(); const [selected, setSelected] = useState>(new Set()); const rows = data ?? []; const unreadIds = useMemo( () => rows.filter((a) => a.state === 'FIRING').map((a) => a.id), [rows], ); const firingIds = unreadIds; // FIRING alerts are the ones that can be ack'd const allSelected = rows.length > 0 && rows.every((r) => selected.has(r.id)); const someSelected = selected.size > 0 && !allSelected; const toggleSelected = (id: string) => { setSelected((prev) => { const next = new Set(prev); if (next.has(id)) next.delete(id); else next.add(id); return next; }); }; const toggleSelectAll = () => { if (allSelected) { setSelected(new Set()); } else { setSelected(new Set(rows.map((r) => r.id))); } }; const onAck = async (id: string, title?: string) => { try { await ack.mutateAsync(id); toast({ title: 'Acknowledged', description: title, variant: 'success' }); } catch (e) { toast({ title: 'Ack failed', description: String(e), variant: 'error' }); } }; const onBulkAck = async (ids: string[]) => { if (ids.length === 0) return; try { await Promise.all(ids.map((id) => ack.mutateAsync(id))); setSelected(new Set()); toast({ title: `Acknowledged ${ids.length} alert${ids.length === 1 ? '' : 's'}`, variant: 'success' }); } catch (e) { toast({ title: 'Bulk ack failed', description: String(e), variant: 'error' }); } }; const onBulkRead = async (ids: string[]) => { if (ids.length === 0) return; try { await bulkRead.mutateAsync(ids); setSelected(new Set()); toast({ title: `Marked ${ids.length} as read`, variant: 'success' }); } catch (e) { toast({ title: 'Bulk read failed', description: String(e), variant: 'error' }); } }; const columns: Column[] = [ { key: 'select', header: '', width: '40px', render: (_, row) => ( toggleSelected(row.id)} aria-label={`Select ${row.title ?? row.id}`} onClick={(e) => e.stopPropagation()} /> ), }, { key: 'severity', header: 'Severity', width: '110px', render: (_, row) => row.severity ? : null, }, { key: 'state', header: 'Status', width: '140px', render: (_, row) => row.state ? : null, }, { key: 'title', header: 'Title', render: (_, row) => { const unread = row.state === 'FIRING'; return (
markRead.mutate(row.id)}> {row.title ?? '(untitled)'} {row.message && {row.message}}
); }, }, { key: 'age', header: 'Age', width: '100px', sortable: true, render: (_, row) => row.firedAt ? ( {formatRelativeTime(row.firedAt)} ) : '—', }, { key: 'ack', header: '', width: '120px', render: (_, row) => row.state === 'FIRING' ? ( ) : null, }, ]; if (isLoading) return ; if (error) return
Failed to load alerts: {String(error)}
; const selectedIds = Array.from(selected); const selectedFiringIds = rows .filter((r) => selected.has(r.id) && r.state === 'FIRING') .map((r) => r.id); const subtitle = selectedIds.length > 0 ? `${selectedIds.length} selected` : `${unreadIds.length} need attention · ${rows.length} total in inbox`; return (

Inbox

{subtitle}
{selectedIds.length > 0 ? ( <> ) : ( <> )}
{rows.length === 0 ? ( } title="All clear" description="No open alerts for you in this environment." /> ) : (
columns={columns as Column[]} data={rows as Array} sortable flush fillHeight pageSize={200} rowAccent={(row) => row.severity ? severityToAccent(row.severity) : undefined} expandedContent={renderAlertExpanded} />
)}
); }