import { createContext, useContext, useState, useCallback, useRef, useEffect, type ReactNode } from 'react'; import { CheckCircle, XCircle, AlertCircle, Info, X, Loader2 } from 'lucide-react'; type ToastType = 'success' | 'error' | 'warning' | 'info' | 'loading'; interface Toast { id: string; message: string; type: ToastType; persistent?: boolean; } interface ToastContextType { showToast: (message: string, type?: ToastType) => void; showPersistentToast: (id: string, message: string, type?: ToastType) => void; dismissToast: (id: string) => void; } const ToastContext = createContext(undefined); export function useToast() { const context = useContext(ToastContext); if (!context) { throw new Error('useToast must be used within a ToastProvider'); } return context; } const icons = { success: , error: , warning: , info: , loading: , }; const bgColors = { success: 'bg-green-500/10 border-green-500/30', error: 'bg-red-500/10 border-red-500/30', warning: 'bg-yellow-500/10 border-yellow-500/30', info: 'bg-blue-500/10 border-blue-500/30', loading: 'bg-bambu-green/10 border-bambu-green/30', }; export function ToastProvider({ children }: { children: ReactNode }) { const [toasts, setToasts] = useState([]); const timeoutRefs = useRef>>(new Map()); // Clean up all timeouts on unmount useEffect(() => { const timeouts = timeoutRefs.current; return () => { timeouts.forEach((timeout) => clearTimeout(timeout)); timeouts.clear(); }; }, []); const showToast = useCallback((message: string, type: ToastType = 'success') => { const id = Math.random().toString(36).substr(2, 9); setToasts((prev) => [...prev, { id, message, type }]); // Auto-dismiss after 3 seconds const timeout = setTimeout(() => { setToasts((prev) => prev.filter((t) => t.id !== id)); timeoutRefs.current.delete(id); }, 3000); timeoutRefs.current.set(id, timeout); }, []); const showPersistentToast = useCallback((id: string, message: string, type: ToastType = 'info') => { setToasts((prev) => { // Update existing toast if same id, otherwise add new one const exists = prev.find((t) => t.id === id); if (exists) { return prev.map((t) => (t.id === id ? { ...t, message, type, persistent: true } : t)); } return [...prev, { id, message, type, persistent: true }]; }); }, []); const dismissToast = useCallback((id: string) => { // Clear any pending auto-dismiss timeout const timeout = timeoutRefs.current.get(id); if (timeout) { clearTimeout(timeout); timeoutRefs.current.delete(id); } setToasts((prev) => prev.filter((t) => t.id !== id)); }, []); return ( {children} {/* Toast Container */}
{toasts.map((toast) => (
{icons[toast.type]} {toast.message}
))}
); }