// ─── Toast notification system ────────────────────────────────────────────── import { createContext, type ReactNode, useCallback, useContext, useState, } from "react"; import { cn } from "../lib/utils"; interface Toast { id: string; message: string; type: "info" | "success" | "error" | "warning"; } interface ToastContextType { toasts: Toast[]; addToast: (message: string, type?: Toast["type"]) => void; removeToast: (id: string) => void; } const ToastContext = createContext({ toasts: [], addToast: () => {}, removeToast: () => {}, }); export function ToastProvider({ children }: { children: ReactNode }) { const [toasts, setToasts] = useState([]); const addToast = useCallback( (message: string, type: Toast["type"] = "info") => { const id = `toast-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; setToasts((prev) => [...prev, { id, message, type }]); setTimeout( () => setToasts((prev) => prev.filter((t) => t.id !== id)), 4000, ); }, [], ); const removeToast = useCallback((id: string) => { setToasts((prev) => prev.filter((t) => t.id !== id)); }, []); return ( {children} ); } export function useToast() { return useContext(ToastContext); } const typeStyles: Record = { info: "border-l-primary bg-white text-foreground", success: "border-l-green-500 bg-white text-foreground", error: "border-l-red-500 bg-white text-foreground", warning: "border-l-amber-500 bg-white text-foreground", }; const typeIcons: Record = { info: "💠", success: "🌸", error: "😿", warning: "⚠️", }; function ToastContainer() { const { toasts, removeToast } = useContext(ToastContext); if (toasts.length === 0) return null; return (
{toasts.map((toast) => (
removeToast(toast.id)} > {typeIcons[toast.type]} {toast.message}
))}
); }