import { CheckCircle, Error, Info, Undo, Warning } from '@mui/icons-material'
import { Box, Button, Snackbar, Typography } from '@mui/joy'
import React, { createContext, useContext, useState } from 'react'
const NotificationContext = createContext()
export const useNotification = () => useContext(NotificationContext)
// For backward compatibility
export const useError = () => {
const { showError } = useNotification()
return { showError }
}
// Notification types configuration with default titles
const NOTIFICATION_TYPES = {
error: {
color: 'danger',
icon: ,
autoHideDuration: 6000,
showDismissButton: true,
defaultTitle: 'Error',
},
success: {
color: 'success',
icon: ,
autoHideDuration: 5000,
showDismissButton: false,
defaultTitle: 'Success',
},
undo: {
color: 'success',
icon: ,
autoHideDuration: null,
showDismissButton: false,
defaultTitle: 'Undone Successfully',
},
warning: {
color: 'warning',
icon: ,
autoHideDuration: 4000,
showDismissButton: false,
defaultTitle: 'Warning',
},
info: {
color: 'primary',
icon: ,
autoHideDuration: 4000,
showDismissButton: false,
defaultTitle: 'Information',
},
custom: {
color: 'neutral',
icon: null,
autoHideDuration: null,
showDismissButton: false,
defaultTitle: 'Notification',
},
}
export const NotificationProvider = ({ children }) => {
const [notifications, setNotifications] = useState([])
const addNotification = notification => {
const id = Date.now() + Math.random()
const newNotification = {
id,
...notification,
timestamp: Date.now(),
}
setNotifications(prev => [...prev, newNotification])
// Auto-remove notification if it has a duration
const config =
NOTIFICATION_TYPES[notification.type] || NOTIFICATION_TYPES.info
if (config.autoHideDuration) {
setTimeout(() => {
removeNotification(id)
}, config.autoHideDuration)
}
return id
}
const removeNotification = id => {
setNotifications(prev => prev.filter(n => n.id !== id))
}
const clearAllNotifications = () => {
setNotifications([])
}
// Helper function to normalize notification input
const normalizeNotification = (input, type) => {
if (typeof input === 'string') {
return {
type,
message: input,
}
}
if (typeof input === 'object' && input !== null) {
// If it's already a properly structured notification
if (input.title || input.message) {
return {
type,
...input,
}
}
// If it's a simple object with just message content
return {
type,
message: input.message || input.toString(),
title: input.title,
...input,
}
}
return {
type,
message: input?.toString() || 'Unknown notification',
}
}
// Unified notification method
const showNotification = notification => {
// Handle different input formats
if (typeof notification === 'string') {
return addNotification(normalizeNotification(notification, 'info'))
}
return addNotification(
normalizeNotification(notification, notification.type || 'info'),
)
}
// Specific notification methods with enhanced language
const showError = error => {
return addNotification(normalizeNotification(error, 'error'))
}
const showUndo = message => {
return addNotification(normalizeNotification(message, 'undo'))
}
const showSuccess = message => {
return addNotification(normalizeNotification(message, 'success'))
}
const showWarning = message => {
return addNotification(normalizeNotification(message, 'warning'))
}
const showInfo = message => {
return addNotification(normalizeNotification(message, 'info'))
}
const renderNotification = notification => {
const config =
NOTIFICATION_TYPES[notification.type] || NOTIFICATION_TYPES.info
// Handle custom notifications with components
if (notification.type === 'custom' && notification.component) {
return (
removeNotification(notification.id)}
anchorOrigin={
notification.anchorOrigin || {
vertical: 'bottom',
horizontal: 'right',
}
}
{...(notification.snackbarProps || {})}
>
{React.cloneElement(notification.component, {
onClose: () => removeNotification(notification.id),
...notification.componentProps,
})}
)
}
// Handle standard notifications
// Determine the icon to use
const notificationIcon = notification.icon || config.icon
// Determine title and message
const title = notification.title || config.defaultTitle
const message = notification.message
return (
removeNotification(notification.id)}
startDecorator={notificationIcon}
endDecorator={
notification.undoAction ? (
) : config.showDismissButton ? (
) : null
}
anchorOrigin={
notification.anchorOrigin || {
vertical: 'bottom',
horizontal: 'right',
}
}
{...(notification.snackbarProps || {})}
>
{/* Enhanced structure like ErrorProvider - always show title and message for consistency */}
{title && message ? (
{title}
{message}
) : (
{message || title || 'Notification'}
)}
)
}
return (
{children}
{notifications.map(renderNotification)}
)
}