farmcontrol-ui/src/components/Dashboard/common/NotificationCenter.jsx

242 lines
6.2 KiB
JavaScript

import React, { useState, useEffect, useContext, useCallback } from 'react'
import {
Typography,
Space,
Button,
Empty,
Spin,
message,
Popconfirm,
Flex,
Badge,
Dropdown
} from 'antd'
import {
BellOutlined,
DeleteOutlined,
CheckOutlined,
ReloadOutlined
} from '@ant-design/icons'
import axios from 'axios'
import PropTypes from 'prop-types'
import { AuthContext } from '../context/AuthContext'
import config from '../../../config'
import Notification from './Notification'
const { Text } = Typography
const NotificationCenter = ({ visible }) => {
const [notifications, setNotifications] = useState([])
const [loading, setLoading] = useState(false)
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
const [messageApi, contextHolder] = message.useMessage()
const { authenticated } = useContext(AuthContext)
const fetchNotifications = useCallback(async () => {
if (!authenticated) return
setLoading(true)
try {
const response = await axios.get(`${config.backendUrl}/notifications`, {
headers: {
Accept: 'application/json'
},
withCredentials: true
})
setNotifications(response.data)
} catch (error) {
console.error('Error fetching notifications:', error)
messageApi.error('Failed to fetch notifications')
} finally {
setLoading(false)
}
}, [authenticated, messageApi])
const markAsRead = useCallback(
async (notificationId) => {
try {
await axios.put(
`${config.backendUrl}/notifications/${notificationId}/read`,
{},
{
headers: {
Accept: 'application/json'
},
withCredentials: true
}
)
// Update local state
setNotifications((prev) =>
prev.map((notification) => {
if (notification._id === notificationId) {
return { ...notification, read: true }
}
return notification
})
)
messageApi.success('Notification marked as read')
} catch (error) {
console.error('Error marking notification as read:', error)
messageApi.error('Failed to mark notification as read')
}
},
[messageApi]
)
const markAllAsRead = useCallback(async () => {
try {
await axios.put(
`${config.backendUrl}/notifications/read-all`,
{},
{
headers: {
Accept: 'application/json'
},
withCredentials: true
}
)
// Update local state
setNotifications((prev) =>
prev.map((notification) => ({ ...notification, read: true }))
)
messageApi.success('All notifications marked as read')
} catch (error) {
console.error('Error marking all notifications as read:', error)
messageApi.error('Failed to mark all notifications as read')
}
}, [messageApi])
const deleteAllNotifications = useCallback(async () => {
try {
await axios.delete(`${config.backendUrl}/notifications`, {
headers: {
Accept: 'application/json'
},
withCredentials: true
})
setNotifications([])
messageApi.success('All notifications deleted')
} catch (error) {
console.error('Error deleting all notifications:', error)
messageApi.error('Failed to delete all notifications')
} finally {
setShowDeleteConfirm(false)
}
}, [messageApi])
useEffect(() => {
if (visible && authenticated) {
fetchNotifications()
}
}, [visible, authenticated, fetchNotifications])
const unreadCount = notifications.filter(
(notification) => !notification.read
).length
const actionItems = {
items: [
{
label: 'Mark All Read',
key: 'markAllRead',
icon: <CheckOutlined />,
disabled: unreadCount === 0
},
{
label: 'Reload Notifications',
key: 'reloadNotifications',
icon: <ReloadOutlined />
},
{ type: 'divider' },
{
label: 'Delete All',
key: 'deleteAll',
icon: <DeleteOutlined />,
danger: true,
disabled: notifications.length === 0
}
],
onClick: ({ key }) => {
if (key === 'markAllRead') {
markAllAsRead()
} else if (key === 'reloadNotifications') {
fetchNotifications()
} else if (key === 'deleteAll') {
setShowDeleteConfirm(true)
}
}
}
if (!visible) {
return null
}
return (
<>
{contextHolder}
<Flex justify='space-between' align='center'>
<Space size='middle'>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
<Badge count={unreadCount} size='small'>
<BellOutlined style={{ fontSize: '18px' }} />
</Badge>
</Space>
<Space>
<Button icon={<DeleteOutlined />} danger />
</Space>
</Flex>
<div style={{ maxHeight: 500, overflow: 'auto' }}>
{loading ? (
<div style={{ padding: '40px', textAlign: 'center' }}>
<Spin size='large' />
<div style={{ marginTop: 16 }}>
<Text type='secondary'>Loading notifications...</Text>
</div>
</div>
) : notifications.length === 0 ? (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description='No notifications'
style={{ padding: '40px 20px' }}
/>
) : (
<Flex vertical gap='small' style={{ padding: '16px' }}>
{notifications.map((notification) => (
<Notification
key={notification._id}
notification={notification}
onMarkAsRead={markAsRead}
/>
))}
</Flex>
)}
</div>
<Popconfirm
title='Delete all notifications?'
description='This action cannot be undone.'
open={showDeleteConfirm}
onConfirm={deleteAllNotifications}
onCancel={() => setShowDeleteConfirm(false)}
okText='Yes'
cancelText='No'
/>
</>
)
}
NotificationCenter.propTypes = {
visible: PropTypes.bool.isRequired
}
export default NotificationCenter