'use client'; import { useEffect, useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import type { ToastItem } from '@/hooks/useAlertToasts'; const TOAST_LIFETIME_MS = 5_000; function getRiskColor(score: number): string { if (score >= 9) return '#ef4444'; if (score >= 7) return '#f97316'; if (score >= 4) return '#eab308'; return '#22d3ee'; } function getRiskLabel(score: number): string { if (score >= 9) return 'CRITICAL'; if (score >= 7) return 'HIGH'; return 'ELEVATED'; } /** * ToastCard — renders a single toast with hover-to-pause auto-dismiss. * * Each card owns its own 5s dismiss timer. Hovering the card pauses the * timer; the timer restarts (full duration) on mouse leave. All visual * styling, the progress bar animation, the click-to-fly behavior, and * the dismiss button match the previous inline implementation — the * only behavioral change is the pause-on-hover. */ function ToastCard({ toast, onDismiss, onFlyTo, }: { toast: ToastItem; onDismiss: (id: string) => void; onFlyTo?: (lat: number, lng: number) => void; }) { const [isPaused, setIsPaused] = useState(false); const color = getRiskColor(toast.risk_score); const label = getRiskLabel(toast.risk_score); // Per-toast auto-dismiss timer. Restarts whenever the pause flag flips // off — so hovering resets the clock back to a full lifetime when the // user moves the mouse away, giving them time to actually read it. useEffect(() => { if (isPaused) return; const timer = setTimeout(() => { onDismiss(toast.id); }, TOAST_LIFETIME_MS); return () => clearTimeout(timer); }, [isPaused, toast.id, onDismiss]); return ( setIsPaused(true)} onMouseLeave={() => setIsPaused(false)} onClick={() => { if (onFlyTo && toast.lat && toast.lng) { onFlyTo(toast.lat, toast.lng); } onDismiss(toast.id); }} >
{/* Progress bar — animation pauses while the card is hovered. */}
{/* Header */}
⚠ {label} LVL {toast.risk_score}/10
{/* Title */}
{toast.title}
{/* Source */}
{toast.source}
{/* Dismiss button */}
); } export default function AlertToast({ toasts, onDismiss, onFlyTo, }: { toasts: ToastItem[]; onDismiss: (id: string) => void; onFlyTo?: (lat: number, lng: number) => void; }) { return (
{toasts.map((toast) => ( ))}
); }