import { useState, useEffect, createContext, useContext, useCallback } from 'react'; import { Check, X, AlertCircle, Info } from 'lucide-react'; type ToastType = 'success' | 'error' | 'warning' | 'info'; interface ToastData { id: string; type: ToastType; message: string; duration?: number; } interface NotificationToastContextType { toasts: ToastData[]; showToast: (type: ToastType, message: string, duration?: number) => void; hideToast: (id: string) => void; } const NotificationToastContext = createContext(undefined); export function NotificationToastProvider({ children }: { children: React.ReactNode }) { const [toasts, setToasts] = useState([]); const showToast = useCallback((type: ToastType, message: string, duration = 4000) => { const id = `toast_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; setToasts(prev => [...prev, { id, type, message, duration }]); }, []); const hideToast = useCallback((id: string) => { setToasts(prev => prev.filter(toast => toast.id !== id)); }, []); return ( {children} ); } export function useNotificationToast() { const context = useContext(NotificationToastContext); if (!context) { throw new Error('useNotificationToast must be used within a NotificationToastProvider'); } return context; } function NotificationToastContainer() { const { toasts, hideToast } = useContext(NotificationToastContext)!; if (toasts.length === 0) return null; return (
{toasts.map(toast => ( hideToast(toast.id)} /> ))}
); } function NotificationToastItem({ toast, onClose }: { toast: ToastData; onClose: () => void }) { const [isVisible, setIsVisible] = useState(false); const [isLeaving, setIsLeaving] = useState(false); useEffect(() => { // Trigger enter animation const enterTimer = setTimeout(() => { setIsVisible(true); }, 10); // Auto dismiss let dismissTimer: ReturnType; if (toast.duration && toast.duration > 0) { dismissTimer = setTimeout(() => { handleClose(); }, toast.duration); } return () => { clearTimeout(enterTimer); if (dismissTimer) clearTimeout(dismissTimer); }; }, [toast.duration]); const handleClose = () => { setIsLeaving(true); setTimeout(onClose, 300); }; const getIcon = () => { switch (toast.type) { case 'success': return ; case 'error': return ; case 'warning': return ; case 'info': return ; } }; const getStyles = () => { switch (toast.type) { case 'success': return { iconColor: '#10b981', iconBg: '#ecfdf5', borderColor: '#d1fae5', textColor: '#065f46', }; case 'error': return { iconColor: '#ef4444', iconBg: '#fef2f2', borderColor: '#fecaca', textColor: '#991b1b', }; case 'warning': return { iconColor: '#f59e0b', iconBg: '#fffbeb', borderColor: '#fde68a', textColor: '#92400e', }; case 'info': return { iconColor: '#3b82f6', iconBg: '#eff6ff', borderColor: '#bfdbfe', textColor: '#1e40af', }; } }; const styles = getStyles(); return (
{getIcon()}

{toast.message}

); }