- Updated AlertsDisplay to receive an object and its type instead of specific alerts and printerId, enhancing reusability for different object types. - Modified ControlPrinter and ObjectProperty components to pass the new props structure, ensuring consistent alert handling across various contexts.
241 lines
6.9 KiB
JavaScript
241 lines
6.9 KiB
JavaScript
import PropTypes from 'prop-types'
|
|
import { createElement, useContext } from 'react'
|
|
import { Flex, Alert, Button, Dropdown } from 'antd'
|
|
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
|
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
|
import XMarkIcon from '../../Icons/XMarkIcon'
|
|
import { getModelByName } from '../../../database/ObjectModels'
|
|
import { useNavigate } from 'react-router-dom'
|
|
import ActionsIcon from '../../Icons/ActionsIcon'
|
|
import { ApiServerContext } from '../context/ApiServerContext'
|
|
|
|
const AlertsDisplay = ({
|
|
object,
|
|
objectType,
|
|
showDismiss = true,
|
|
showActions = true
|
|
}) => {
|
|
const alerts = object?.alerts ?? []
|
|
const objectId = object?._id
|
|
const getAlertType = (type, priority) => {
|
|
if (type === 'error' || priority === '9') return 'error'
|
|
if (type === 'warning' || priority === '8') return 'warning'
|
|
return 'info'
|
|
}
|
|
|
|
const model = objectType ? getModelByName(objectType) : null
|
|
const navigate = useNavigate()
|
|
const { updateObject } = useContext(ApiServerContext)
|
|
|
|
const handleDismissAlert = (alertId) => {
|
|
const updatedAlerts = alerts.filter((a) => a._id !== alertId)
|
|
updateObject(objectId, objectType, { alerts: updatedAlerts })
|
|
}
|
|
const getAlertIcon = (type, priority) => {
|
|
if (type === 'error' || priority === '9') return <ExclamationOctagonIcon />
|
|
if (type === 'warning' || priority === '8')
|
|
return <ExclamationOctagonIcon />
|
|
return <InfoCircleIcon />
|
|
}
|
|
|
|
// Recursively filter the printer model actions by a set of allowed action keys
|
|
const filterActionsByKeys = (actions, allowedKeys) => {
|
|
if (!Array.isArray(actions)) return []
|
|
|
|
const filtered = actions
|
|
.map((action) => {
|
|
if (action.type === 'divider') {
|
|
return { type: 'divider' }
|
|
}
|
|
|
|
const actionKey = action.key || action.name
|
|
let children = []
|
|
|
|
if (Array.isArray(action.children)) {
|
|
children = filterActionsByKeys(action.children, allowedKeys)
|
|
}
|
|
|
|
const isAllowed = actionKey && allowedKeys.has(actionKey)
|
|
|
|
if (!isAllowed && children.length === 0) {
|
|
return null
|
|
}
|
|
|
|
return {
|
|
...action,
|
|
children
|
|
}
|
|
})
|
|
.filter((action) => action !== null)
|
|
|
|
// Clean up dividers: remove leading/trailing and consecutive dividers
|
|
const cleaned = []
|
|
for (const action of filtered) {
|
|
if (action.type === 'divider') {
|
|
if (cleaned.length === 0) continue
|
|
if (cleaned[cleaned.length - 1].type === 'divider') continue
|
|
}
|
|
cleaned.push(action)
|
|
}
|
|
if (cleaned[cleaned.length - 1]?.type === 'divider') {
|
|
cleaned.pop()
|
|
}
|
|
|
|
return cleaned
|
|
}
|
|
|
|
// Map filtered printer actions to AntD Dropdown menu items (including children)
|
|
const mapActionsToMenuItems = (actions) => {
|
|
if (!Array.isArray(actions)) return []
|
|
|
|
return actions.map((action) => {
|
|
if (action.type === 'divider') {
|
|
return { type: 'divider' }
|
|
}
|
|
|
|
const item = {
|
|
key: action.key || action.name,
|
|
label: action.label,
|
|
icon: action.icon ? createElement(action.icon) : undefined
|
|
}
|
|
|
|
if (Array.isArray(action.children) && action.children.length > 0) {
|
|
item.children = mapActionsToMenuItems(action.children)
|
|
}
|
|
|
|
return item
|
|
})
|
|
}
|
|
|
|
if (alerts.length == 0) {
|
|
return null
|
|
}
|
|
|
|
const alertElements = alerts.map((alert, index) => {
|
|
const objectActions = model?.actions || []
|
|
|
|
const alertActionKeys = Array.isArray(alert?.actions)
|
|
? alert.actions
|
|
.map((action) =>
|
|
typeof action === 'string'
|
|
? action
|
|
: action?.key || action?.name || null
|
|
)
|
|
.filter((key) => key != null)
|
|
: []
|
|
|
|
const allowedKeys = new Set(alertActionKeys)
|
|
const filteredActions = filterActionsByKeys(objectActions, allowedKeys)
|
|
|
|
const findActionByKey = (actions, key) => {
|
|
if (!Array.isArray(actions)) return null
|
|
|
|
for (const action of actions) {
|
|
if (action.type === 'divider') continue
|
|
|
|
const actionKey = action.key || action.name
|
|
if (actionKey === key) {
|
|
return action
|
|
}
|
|
|
|
if (Array.isArray(action.children) && action.children.length > 0) {
|
|
const found = findActionByKey(action.children, key)
|
|
if (found) return found
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
const menu = {
|
|
items: mapActionsToMenuItems(filteredActions),
|
|
onClick: ({ key }) => {
|
|
const action = findActionByKey(filteredActions, key)
|
|
|
|
if (action?.url) {
|
|
navigate(action.url(objectId))
|
|
} else {
|
|
console.warn('No action found for key:', key)
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Alert
|
|
key={`${alert.createdAt}-${index}-${alert._id}`}
|
|
message={alert.message}
|
|
style={{ padding: '4px 5px 4px 10px', minHeight: '35px' }}
|
|
type={getAlertType(alert.type, alert.priority)}
|
|
icon={getAlertIcon(alert.type, alert.priority)}
|
|
showIcon
|
|
closable={false}
|
|
onClose={() => {}}
|
|
action={
|
|
<Flex gap='1px'>
|
|
{showActions && filteredActions.length >= 0 && (
|
|
<Dropdown menu={menu} on>
|
|
<Button
|
|
size='small'
|
|
type='text'
|
|
style={{ marginLeft: '5px' }}
|
|
icon={
|
|
<ActionsIcon
|
|
style={{ fontSize: '12px', marginBottom: '3.5px' }}
|
|
/>
|
|
}
|
|
/>
|
|
</Dropdown>
|
|
)}
|
|
{showDismiss && alert.canDismiss && (
|
|
<Button
|
|
size='small'
|
|
type='text'
|
|
style={{ marginLeft: '5px' }}
|
|
onClick={() => handleDismissAlert(alert._id)}
|
|
icon={
|
|
<XMarkIcon
|
|
style={{
|
|
fontSize: '10px',
|
|
marginBottom: '5px',
|
|
marginLeft: '0.5px'
|
|
}}
|
|
/>
|
|
}
|
|
/>
|
|
)}
|
|
</Flex>
|
|
}
|
|
/>
|
|
)
|
|
})
|
|
|
|
return (
|
|
<Flex gap='small' style={{ width: '100%' }} vertical align='stretch'>
|
|
{alertElements}
|
|
</Flex>
|
|
)
|
|
}
|
|
|
|
AlertsDisplay.propTypes = {
|
|
object: PropTypes.shape({
|
|
_id: PropTypes.string.isRequired,
|
|
alerts: PropTypes.arrayOf(
|
|
PropTypes.shape({
|
|
canDismiss: PropTypes.bool.isRequired,
|
|
_id: PropTypes.string.isRequired,
|
|
code: PropTypes.string,
|
|
type: PropTypes.string.isRequired,
|
|
createdAt: PropTypes.string.isRequired,
|
|
updatedAt: PropTypes.string.isRequired,
|
|
message: PropTypes.string,
|
|
actions: PropTypes.arrayOf(PropTypes.string)
|
|
})
|
|
)
|
|
}).isRequired,
|
|
objectType: PropTypes.string.isRequired,
|
|
showActions: PropTypes.bool,
|
|
showDismiss: PropTypes.bool
|
|
}
|
|
|
|
export default AlertsDisplay
|