2025-08-22 20:28:50 +01:00

143 lines
3.9 KiB
JavaScript

import { createElement } from 'react'
import { Dropdown, Button } from 'antd'
import { getModelByName } from '../../../database/ObjectModels'
import PropTypes from 'prop-types'
import { useNavigate, useLocation } from 'react-router-dom'
// Recursively filter actions based on visibleActions
function filterActionsByVisibility(actions, visibleActions) {
if (!visibleActions) return actions
return actions.filter((action) => {
if (action.type === 'divider') {
return true // Always show dividers
}
const actionKey = action.key || action.name
const isVisible = visibleActions[actionKey] !== false
// If this action has children, filter them recursively
if (action.children && Array.isArray(action.children)) {
const filteredChildren = filterActionsByVisibility(
action.children,
visibleActions
)
action.children = filteredChildren
// Show parent if it has visible children or if it's explicitly visible
return (
isVisible &&
(filteredChildren.length > 0 || visibleActions[actionKey] === true)
)
}
return isVisible
})
}
// Recursively map actions to AntD Dropdown items
function mapActionsToMenuItems(actions, currentUrlWithActions, id) {
return actions.map((action) => {
if (action.type === 'divider') {
return { type: 'divider' }
}
const actionUrl = action.url ? action.url(id) : undefined
const item = {
key: action.key || action.name,
label: action.label,
danger: action?.danger || false,
icon: action.icon ? createElement(action.icon) : undefined,
disabled: actionUrl && actionUrl === currentUrlWithActions
}
if (action.children && Array.isArray(action.children)) {
item.children = mapActionsToMenuItems(
action.children,
currentUrlWithActions,
id
)
}
return item
})
}
const stripActionParam = (pathname, search) => {
const params = new URLSearchParams(search)
params.delete('action')
const query = params.toString()
return pathname + (query ? `?${query}` : '')
}
const ObjectActions = ({
type,
id,
disabled = false,
buttonProps = {},
visibleActions = {},
...dropdownProps
}) => {
const model = getModelByName(type)
const actions = model.actions || []
const navigate = useNavigate()
const location = useLocation()
// Get current url without 'action' param
const currentUrlWithoutActions = stripActionParam(
location.pathname,
location.search
)
// First filter by visibility, then by current URL
const visibilityFilteredActions = filterActionsByVisibility(
actions,
visibleActions
)
const filteredActions = visibilityFilteredActions.filter(
(action) =>
typeof action.url !== 'function' ||
action.url(id) !== currentUrlWithoutActions
)
const currentUrlWithActions = location.pathname + location.search
// Compose AntD Dropdown menu items
const menu = {
items: mapActionsToMenuItems(filteredActions, currentUrlWithActions, id),
onClick: (info) => {
// Find the action by key
const findAction = (acts, key) => {
for (const act of acts) {
if ((act.key || act.name) === key) return act
if (act.children) {
const found = findAction(act.children, key)
if (found) return found
}
}
return null
}
const action = findAction(filteredActions, info.key)
if (action && action.url) {
navigate(action.url(id))
}
}
}
return (
<Dropdown menu={menu} {...dropdownProps}>
<Button {...buttonProps} disabled={disabled}>
Actions
</Button>
</Dropdown>
)
}
ObjectActions.propTypes = {
type: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
disabled: PropTypes.bool,
buttonProps: PropTypes.object,
buttonLabel: PropTypes.string,
visibleActions: PropTypes.object
}
export default ObjectActions