/** * AlertToast — Toast notification component for real-time alerts (Story 12.5) * * This component can be wired up to SSE events when Epic 14 is implemented. * For now, it provides the UI and a simple imperative API for showing toasts. */ import React, { useState, useCallback, useEffect, useRef } from 'react'; export interface Toast { id: string; type: 'alert' | 'info' | 'success'; title: string; message: string; timestamp: string; } interface AlertToastProps { /** Optional: external toasts to display (e.g., from SSE) */ toasts?: Toast[]; /** Auto-dismiss after this many ms (default: 8000) */ dismissAfterMs?: number; } /** * Toast container — renders toasts in the top-right corner. * Will be connected to EventBus/SSE in Epic 14. */ export function AlertToastContainer({ toasts: externalToasts = [], dismissAfterMs = 8000, }: AlertToastProps): React.ReactElement { const [internalToasts, setInternalToasts] = useState([]); const timersRef = useRef>>(new Map()); const allToasts = [...externalToasts, ...internalToasts]; const dismiss = useCallback((id: string) => { setInternalToasts((prev) => prev.filter((t) => t.id !== id)); const timer = timersRef.current.get(id); if (timer) { clearTimeout(timer); timersRef.current.delete(id); } }, []); // Auto-dismiss useEffect(() => { for (const toast of allToasts) { if (!timersRef.current.has(toast.id)) { const timer = setTimeout(() => dismiss(toast.id), dismissAfterMs); timersRef.current.set(toast.id, timer); } } }, [allToasts, dismiss, dismissAfterMs]); // Cleanup on unmount useEffect(() => { return () => { for (const timer of timersRef.current.values()) { clearTimeout(timer); } }; }, []); if (allToasts.length === 0) return <>; return (
{allToasts.map((toast) => (
{toast.type === 'alert' ? '🔴' : toast.type === 'success' ? '✅' : 'â„šī¸'}

{toast.title}

{toast.message}

))}
); }