import { useCallback, useEffect, useState } from 'react' import type { AlertState, RiskAlert } from './api' import { alertsFor } from './demo/ops' /* * Officer actions on the alert queue. * * The queue itself is derived from the portfolio; only the state changes an * officer makes (acknowledge / escalate / snooze / close) and their audit trail * are persisted, which is exactly the split the deck describes — the model * proposes, the officer disposes, and every disposal is auditable. */ const KEY = 'ul-alert-states' const AUDIT_KEY = 'ul-alert-audit' export interface AuditEntry { alertId: string from: AlertState to: AlertState actor: string note: string | null at: string } function readStates(): Record { try { return JSON.parse(localStorage.getItem(KEY) ?? '{}') as Record } catch { return {} } } export function readAudit(): AuditEntry[] { try { return JSON.parse(localStorage.getItem(AUDIT_KEY) ?? '[]') as AuditEntry[] } catch { return [] } } export function setAlertState(alert: RiskAlert, to: AlertState, actor: string, note?: string) { const states = readStates() const from = states[alert.alertId] ?? alert.state states[alert.alertId] = to localStorage.setItem(KEY, JSON.stringify(states)) const audit: AuditEntry = { alertId: alert.alertId, from, to, actor, note: note ?? null, at: new Date().toISOString() } localStorage.setItem(AUDIT_KEY, JSON.stringify([audit, ...readAudit()].slice(0, 200))) window.dispatchEvent(new Event('ul-alerts-changed')) } /** The alert queue for a block, with any officer actions applied on top. */ export function useAlerts(block: string) { const [version, setVersion] = useState(0) const bump = useCallback(() => setVersion((v) => v + 1), []) useEffect(() => { window.addEventListener('ul-alerts-changed', bump) return () => window.removeEventListener('ul-alerts-changed', bump) }, [bump]) const states = readStates() // `version` is read so the memo-free derivation re-runs after every action. void version const alerts = alertsFor(block).map((a) => ({ ...a, state: states[a.alertId] ?? a.state })) return { alerts, audit: readAudit() } }