218 lines
6.0 KiB
JavaScript

import PropTypes from 'prop-types'
import { createElement } from 'react'
import { Flex, Alert, Button, Dropdown, Popover } from 'antd'
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import { CaretDownOutlined } from '@ant-design/icons'
import { useMediaQuery } from 'react-responsive'
import { getModelByName } from '../../../database/ObjectModels'
import { useNavigate } from 'react-router-dom'
const AlertsDisplay = ({
alerts = [],
printerId,
showDismiss = true,
showActions = true
}) => {
const isMobile = useMediaQuery({ maxWidth: 768 })
const getAlertType = (type, priority) => {
if (type === 'error' || priority === '9') return 'error'
if (type === 'warning' || priority === '8') return 'warning'
return 'info'
}
const printerModel = getModelByName('printer')
const navigate = useNavigate()
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 printerActions = printerModel?.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(printerActions, 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(printerId))
} else {
console.warn('No action found for key:', key)
}
}
}
return (
<Alert
key={`${alert.createdAt}-${index}-${alert._id}`}
message={alert.message}
style={{ padding: '4px 10px 4px 8px' }}
type={getAlertType(alert.type, alert.priority)}
icon={getAlertIcon(alert.type, alert.priority)}
showIcon
closable={showDismiss && alert.canDismiss}
onClose={() => {
console.log('Closing alert:', alert._id)
}}
action={
showActions ? (
<Dropdown menu={menu} on>
<Button size='small' type='text' style={{ marginLeft: '5px' }}>
<CaretDownOutlined />
</Button>
</Dropdown>
) : null
}
/>
)
})
if (isMobile) {
return (
<Popover
content={alertElements}
trigger='hover'
arrow={false}
placement='bottom'
classNames={{
root: 'printer-alerts-display-popover'
}}
>
<Button>Alerts</Button>
</Popover>
)
}
return <Flex gap='small'>{alertElements}</Flex>
}
AlertsDisplay.propTypes = {
printerId: PropTypes.string.isRequired,
showActions: PropTypes.bool.isRequired,
showDismiss: PropTypes.bool.isRequired,
alerts: PropTypes.arrayOf(
PropTypes.shape({
canDismiss: PropTypes.bool.isRequired,
_id: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
createdAt: PropTypes.string.isRequired,
updatedAt: PropTypes.string.isRequired,
message: PropTypes.string,
actions: PropTypes.arrayOf(PropTypes.string)
})
).isRequired
}
export default AlertsDisplay