import React, { createContext, useContext, useState, useRef, ReactNode, useCallback } from 'react'; import { Snackbar, Alert, AlertColor } from '@mui/material'; interface Notification { id: number; message: string; severity: AlertColor; } interface NotificationContextType { showSuccess: (message: string) => void; showError: (message: string) => void; showWarning: (message: string) => void; showInfo: (message: string) => void; showNotificationToast: (message: string, severity?: AlertColor) => void; } const NotificationContext = createContext(undefined); interface NotificationProviderProps { children: ReactNode; } export const NotificationProvider: React.FC = ({ children }) => { const [currentNotification, setCurrentNotification] = useState(null); const queueRef = useRef([]); // Left-side toast queue for new backend notifications const [toastQueue, setToastQueue] = useState([]); const addNotification = useCallback((message: string, severity: AlertColor) => { const id = Date.now(); const notification: Notification = { id, message, severity }; // Use functional update to avoid stale closure over currentNotification setCurrentNotification((prev) => { if (prev) { queueRef.current.push(notification); return prev; } return notification; }); }, []); const showSuccess = useCallback((message: string) => { addNotification(message, 'success'); }, [addNotification]); const showError = useCallback((message: string) => { addNotification(message, 'error'); }, [addNotification]); const showWarning = useCallback((message: string) => { addNotification(message, 'warning'); }, [addNotification]); const showInfo = useCallback((message: string) => { addNotification(message, 'info'); }, [addNotification]); const showNotificationToast = useCallback((message: string, severity: AlertColor = 'info') => { const id = Date.now() + Math.random(); setToastQueue((prev) => [...prev, { id, message, severity }]); }, []); const handleClose = (_event?: React.SyntheticEvent | Event, reason?: string) => { if (reason === 'clickaway') { return; } setCurrentNotification(null); // Show next queued notification after a short delay setTimeout(() => { const next = queueRef.current.shift(); if (next) { setCurrentNotification(next); } }, 200); }; const handleToastClose = (_event?: React.SyntheticEvent | Event, reason?: string) => { if (reason === 'clickaway') return; setToastQueue((prev) => prev.slice(1)); }; const value: NotificationContextType = { showSuccess, showError, showWarning, showInfo, showNotificationToast, }; return ( {children} {/* Right-side: action feedback */} {currentNotification?.message} {/* Left-side: new backend notification toasts */} 0} autoHideDuration={5000} onClose={handleToastClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }} > {toastQueue[0]?.message} ); }; export const useNotification = (): NotificationContextType => { const context = useContext(NotificationContext); if (context === undefined) { throw new Error('useNotification must be used within a NotificationProvider'); } return context; };