diff --git a/src/components/Dashboard/common/Notification.jsx b/src/components/Dashboard/common/Notification.jsx
index d7bd2cc..162f0a2 100644
--- a/src/components/Dashboard/common/Notification.jsx
+++ b/src/components/Dashboard/common/Notification.jsx
@@ -1,12 +1,15 @@
-import { useState } from 'react'
+import { useState, memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { Typography, Button, Tag, Badge, Flex, Card, Divider } from 'antd'
import {
- BellOutlined,
- InfoCircleOutlined,
- ExclamationCircleOutlined,
- CheckCircleOutlined
-} from '@ant-design/icons'
+ Typography,
+ Button,
+ Tag,
+ Badge,
+ Flex,
+ Card,
+ Divider,
+ Popover
+} from 'antd'
import TimeDisplay from './TimeDisplay'
import EditIcon from '../../Icons/EditIcon'
import PropertyChanges from './PropertyChanges'
@@ -15,9 +18,194 @@ import ObjectDisplay from './ObjectDisplay'
import BinIcon from '../../Icons/BinIcon'
import NoteItem from './NoteItem'
import PlusIcon from '../../Icons/PlusIcon'
+import InfoCircleIcon from '../../Icons/InfoCircleIcon'
+import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
+import CheckCircleIcon from '../../Icons/CheckCircleIcon'
+import BellIcon from '../../Icons/BellIcon'
+import { getModelByName } from '../../../database/ObjectModels'
const { Text, Paragraph } = Typography
+const getNotificationMessage = (type, metadata) => {
+ const objectType = metadata?.objectType ?? metadata?.note?.parentType
+ const model = objectType ? getModelByName(objectType) : null
+ const objectLabel = model?.label?.toLowerCase() ?? 'object'
+
+ switch (type) {
+ case 'editObject':
+ return `A ${objectLabel} was updated.`
+ case 'deleteObject':
+ return `A ${objectLabel} was deleted.`
+ case 'newNote':
+ return 'A new note was added.'
+ case 'info':
+ return 'You have a new notification.'
+ case 'error':
+ return 'An error occurred.'
+ case 'success':
+ return 'Action completed successfully.'
+ default:
+ return 'You have a new notification.'
+ }
+}
+
+const getParagraphProps = (largeSpacing) => ({
+ ellipsis: {
+ rows: 2,
+ expandable: true,
+ symbol: 'Show more'
+ },
+ style: {
+ margin: largeSpacing ? '10px 0 0 0' : 0,
+ lineHeight: 1.7,
+ marginBottom: 2
+ }
+})
+
+const NotificationBody = memo(function NotificationBody({
+ type,
+ message,
+ metadata,
+ largeSpacing
+}) {
+ const paragraph = getParagraphProps(largeSpacing)
+
+ switch (type) {
+ case 'editObject':
+ if (metadata?.old || metadata?.new) {
+ return (
+
+ )
+ }
+ return {message}
+ case 'newNote':
+ return (
+
+ )
+ case 'deleteObject':
+ return {message}
+ case 'info':
+ case 'error':
+ case 'success':
+ return {message}
+ default:
+ return {message}
+ }
+})
+
+NotificationBody.propTypes = {
+ type: PropTypes.string,
+ message: PropTypes.string,
+ metadata: PropTypes.object,
+ largeSpacing: PropTypes.bool
+}
+
+const NotificationDetails = memo(function NotificationDetails({
+ type,
+ message,
+ metadata,
+ largeSpacing
+}) {
+ const paragraph = getParagraphProps(largeSpacing)
+ const object = metadata?.object
+ const user = metadata?.user
+ const objectType = metadata?.objectType
+ const note = metadata?.note
+
+ switch (type) {
+ case 'editObject':
+ return (
+
+
+ Object:
+
+
+
+ User:
+
+
+
+ )
+ case 'newNote':
+ return (
+
+
+
+ )
+ case 'deleteObject':
+ return (
+
+
+ Object:
+
+
+
+ User:
+
+
+
+ )
+ case 'info':
+ case 'error':
+ case 'success':
+ return {message}
+ default:
+ return {message}
+ }
+})
+
+NotificationDetails.propTypes = {
+ type: PropTypes.string,
+ message: PropTypes.string,
+ metadata: PropTypes.object,
+ largeSpacing: PropTypes.bool
+}
+
const Notification = ({
notification,
onMarkAsRead,
@@ -27,14 +215,16 @@ const Notification = ({
showExtraInfo = true,
inlineIcon = false,
largeSpacing = false,
- showSince = true
+ showSince = true,
+ showDetails = true,
+ largeTitleSpacing = false
}) => {
const [deleting, setDeleting] = useState(false)
- const getNotificationIcon = (type) => {
+ const getNotificationTagIcon = (type) => {
switch (type) {
case 'info':
- return
+ return
case 'editObject':
return
case 'newNote':
@@ -42,16 +232,29 @@ const Notification = ({
case 'deleteObject':
return
case 'error':
- return
+ return
case 'success':
- return
+ return
default:
- return
+ return
}
}
+ const getNotificationIcon = (type, metadata) => {
+ if (['editObject', 'newNote', 'deleteObject'].includes(type)) {
+ const objectType = metadata?.objectType ?? metadata?.note?.parentType
+ const model = objectType ? getModelByName(objectType) : null
+ if (model?.icon) {
+ const Icon = model.icon
+ return
+ }
+ }
+
+ return getNotificationTagIcon(type)
+ }
+
const getNotificationTag = (type) => {
- const icon = getNotificationIcon(type)
+ const icon = getNotificationTagIcon(type)
switch (type) {
case 'info':
return (
@@ -98,71 +301,6 @@ const Notification = ({
}
}
- const getMetadataDisplay = (metadata, type) => {
- if (metadata.old && metadata.new && type === 'editObject') {
- return (
-
- )
- }
- return null
- }
-
- const getNotificationMessage = (metadata, type) => {
- const paragraph = {
- ellipsis: {
- rows: 2,
- expandable: true,
- symbol: 'Show more'
- },
- style: { margin: largeSpacing ? '10px 0 0 0' : 0 }
- }
- switch (type) {
- case 'editObject': {
- return (
-
- Object:
-
- User:
-
-
- )
- }
- case 'newNote': {
- return (
-
-
-
- )
- }
- default:
- return {notification.message}
- }
- }
-
const handleMarkAsRead = () => {
if (onMarkAsRead && !notification.read) {
onMarkAsRead(notification._id)
@@ -181,46 +319,114 @@ const Notification = ({
}
}
+ const message = useMemo(
+ () => getNotificationMessage(notification.type, notification.metadata),
+ [notification.type, notification.metadata]
+ )
+
+ const notificationDetails = useMemo(
+ () => (
+
+ ),
+ [notification.type, message, notification.metadata, largeSpacing]
+ )
+
+ const notificationBody = useMemo(
+ () => (
+
+ ),
+ [notification.type, message, notification.metadata, largeSpacing]
+ )
+
const content = (
-
-
-
-
- {inlineIcon && getNotificationIcon(notification.type)}
-
- {notification.title}
-
+
+
+
+
+
+ {inlineIcon &&
+ getNotificationIcon(notification.type, notification.metadata)}
+
+
+ {notification.title}
+
+
+
+ {showDetails && (
+
+ {notificationDetails}
+
+ }
+ >
+
+ }
+ >
+
+ )}
+ {showDelete && (
+
+
+ }
+ onClick={handleDelete}
+ />
+
+ )}
+
- {showDelete && (
-
- }
- onClick={handleDelete}
- />
- )}
+
+ {notificationBody}
-
- {getNotificationMessage(notification.metadata, notification.type)}
-
- {notification.metadata &&
- getMetadataDisplay(notification.metadata, notification.type)}
{showExtraInfo && (
<>
-
+
{getNotificationTag(notification.type)}
) : (
- {content}
+ {content}
)
}
@@ -254,9 +463,16 @@ Notification.propTypes = {
notification: PropTypes.shape({
_id: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
- message: PropTypes.string.isRequired,
- type: PropTypes.oneOf(['info', 'warning', 'error', 'success', 'default'])
- .isRequired,
+ type: PropTypes.oneOf([
+ 'info',
+ 'warning',
+ 'error',
+ 'success',
+ 'default',
+ 'editObject',
+ 'deleteObject',
+ 'newNote'
+ ]).isRequired,
read: PropTypes.bool.isRequired,
createdAt: PropTypes.string.isRequired,
metadata: PropTypes.object
@@ -268,7 +484,9 @@ Notification.propTypes = {
showExtraInfo: PropTypes.bool,
inlineIcon: PropTypes.bool,
largeSpacing: PropTypes.bool,
- showSince: PropTypes.bool
+ showSince: PropTypes.bool,
+ showDetails: PropTypes.bool,
+ largeTitleSpacing: PropTypes.bool
}
export default Notification
diff --git a/src/components/Dashboard/common/NotificationCenter.jsx b/src/components/Dashboard/common/NotificationCenter.jsx
index 1b59ef8..dc565c3 100644
--- a/src/components/Dashboard/common/NotificationCenter.jsx
+++ b/src/components/Dashboard/common/NotificationCenter.jsx
@@ -56,7 +56,9 @@ const NotificationCenter = ({ visible }) => {
handleMarkAllAsRead()
} else if (key === 'clearAll') {
setClearNotificationsLoading(true)
- deleteAllNotifications().finally(() => setClearNotificationsLoading(false))
+ deleteAllNotifications().finally(() =>
+ setClearNotificationsLoading(false)
+ )
}
}
}
@@ -95,6 +97,8 @@ const NotificationCenter = ({ visible }) => {
{notifications.map((notification) => (
{
showDelete={false}
showExtraInfo={false}
inlineIcon={true}
+ showDetails={false}
+ largeTitleSpacing={true}
/>
),
icon: null,
- duration: 3,
+ duration: 5,
key: notif._id
})
}