Enhance PermissionsMatrix functionality and UI
- Introduced new utility functions for normalizing and aggregating permission states, improving the management of user permissions. - Added row and column cycling functionality to allow users to easily toggle permission states. - Enhanced the rendering of checkboxes with tooltips for better user feedback on permission states. - Updated the layout of the PermissionsMatrix to improve visual clarity and user interaction.
This commit is contained in:
parent
e54106eb6d
commit
daf2e1aab4
@ -53,6 +53,77 @@ const cyclePermissionState = (current, inherit) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizePermissionState = (state, inherit) => {
|
||||||
|
if (!inherit && state === null) return false
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAggregatePermissionState = (states) => {
|
||||||
|
if (states.length === 0) return null
|
||||||
|
const first = states[0]
|
||||||
|
for (let i = 1; i < states.length; i++) {
|
||||||
|
if (states[i] !== first) return 'mixed'
|
||||||
|
}
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRowPermissionStates = (permissions, model, actions, inherit) =>
|
||||||
|
actions
|
||||||
|
.filter((action) => modelHasPermissionAction(model, action.name))
|
||||||
|
.map((action) =>
|
||||||
|
normalizePermissionState(
|
||||||
|
getPermissionState(permissions, model.name, action.name),
|
||||||
|
inherit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const getColumnPermissionStates = (permissions, models, action, inherit) =>
|
||||||
|
models
|
||||||
|
.filter((model) => modelHasPermissionAction(model, action.name))
|
||||||
|
.map((model) =>
|
||||||
|
normalizePermissionState(
|
||||||
|
getPermissionState(permissions, model.name, action.name),
|
||||||
|
inherit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const setRowPermissionState = (permissions, model, actions, next) => {
|
||||||
|
let nextPermissions = permissions
|
||||||
|
actions.forEach((action) => {
|
||||||
|
if (modelHasPermissionAction(model, action.name)) {
|
||||||
|
nextPermissions = setPermissionState(
|
||||||
|
nextPermissions,
|
||||||
|
model.name,
|
||||||
|
action.name,
|
||||||
|
next
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nextPermissions
|
||||||
|
}
|
||||||
|
|
||||||
|
const setColumnPermissionState = (permissions, models, action, next) => {
|
||||||
|
let nextPermissions = permissions
|
||||||
|
models.forEach((model) => {
|
||||||
|
if (modelHasPermissionAction(model, action.name)) {
|
||||||
|
nextPermissions = setPermissionState(
|
||||||
|
nextPermissions,
|
||||||
|
model.name,
|
||||||
|
action.name,
|
||||||
|
next
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return nextPermissions
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCheckboxTitle = (state, inherit) => {
|
||||||
|
if (state === 'mixed') return 'Mixed'
|
||||||
|
if (inherit && state === null) return 'Inherit'
|
||||||
|
if (state === true) return 'Allow'
|
||||||
|
return 'Deny'
|
||||||
|
}
|
||||||
|
|
||||||
const PermissionsMatrix = ({
|
const PermissionsMatrix = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@ -88,58 +159,140 @@ const PermissionsMatrix = ({
|
|||||||
[disabled, inherit, onChange, permissions]
|
[disabled, inherit, onChange, permissions]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleCycleRow = useCallback(
|
||||||
|
(model) => {
|
||||||
|
if (disabled || typeof onChange !== 'function') return
|
||||||
|
const aggregate = getAggregatePermissionState(
|
||||||
|
getRowPermissionStates(permissions, model, actions, inherit)
|
||||||
|
)
|
||||||
|
const next = cyclePermissionState(
|
||||||
|
aggregate === 'mixed' ? null : aggregate,
|
||||||
|
inherit
|
||||||
|
)
|
||||||
|
onChange(setRowPermissionState(permissions, model, actions, next))
|
||||||
|
},
|
||||||
|
[actions, disabled, inherit, onChange, permissions]
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleCycleColumn = useCallback(
|
||||||
|
(action) => {
|
||||||
|
if (disabled || typeof onChange !== 'function') return
|
||||||
|
const aggregate = getAggregatePermissionState(
|
||||||
|
getColumnPermissionStates(permissions, models, action, inherit)
|
||||||
|
)
|
||||||
|
const next = cyclePermissionState(
|
||||||
|
aggregate === 'mixed' ? null : aggregate,
|
||||||
|
inherit
|
||||||
|
)
|
||||||
|
onChange(setColumnPermissionState(permissions, models, action, next))
|
||||||
|
},
|
||||||
|
[disabled, inherit, models, onChange, permissions]
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderCheckbox = useCallback(
|
||||||
|
(state, onCycle) => {
|
||||||
|
const mixed = state === 'mixed'
|
||||||
|
const checked = state === true
|
||||||
|
const indeterminate = inherit && state === null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={getCheckboxTitle(state, inherit)} arrow={false}>
|
||||||
|
<span>
|
||||||
|
<PermissionCheckbox
|
||||||
|
checked={checked}
|
||||||
|
indeterminate={indeterminate}
|
||||||
|
mixed={mixed}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={onCycle}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[disabled, inherit]
|
||||||
|
)
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
const modelColumn = {
|
const modelColumn = {
|
||||||
title: '',
|
title: '',
|
||||||
key: 'model',
|
key: 'model',
|
||||||
dataIndex: 'label',
|
dataIndex: 'label',
|
||||||
fixed: isMobile ? undefined : 'left',
|
fixed: isMobile ? undefined : 'left',
|
||||||
width: 220,
|
width: 248,
|
||||||
render: (label, record) => (
|
render: (label, record) => {
|
||||||
<Flex align='center' gap='small'>
|
const aggregate = getAggregatePermissionState(
|
||||||
{record.icon ? createElement(record.icon) : null}
|
getRowPermissionStates(permissions, record.model, actions, inherit)
|
||||||
{label}
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const actionColumns = actions.map((action) => ({
|
|
||||||
title: <span className='permissions-matrix-header'>{action.label}</span>,
|
|
||||||
key: action.name,
|
|
||||||
width: 48,
|
|
||||||
align: 'center',
|
|
||||||
render: (_, record) => {
|
|
||||||
if (!modelHasPermissionAction(record.model, action.name)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const state = getPermissionState(
|
|
||||||
permissions,
|
|
||||||
record.model.name,
|
|
||||||
action.name
|
|
||||||
)
|
)
|
||||||
const checked = state === true
|
|
||||||
const indeterminate = inherit && state === null
|
|
||||||
const title = indeterminate ? 'Inherit' : checked ? 'Allow' : 'Deny'
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={title} arrow={false}>
|
<Flex
|
||||||
<span>
|
align='center'
|
||||||
<PermissionCheckbox
|
gap='small'
|
||||||
checked={checked}
|
justify='space-between'
|
||||||
indeterminate={indeterminate}
|
style={{ width: '100%', paddingRight: '10px' }}
|
||||||
disabled={disabled}
|
>
|
||||||
onChange={() => {
|
<Flex align='center' gap='small' style={{ marginLeft: '6px' }}>
|
||||||
handleCycle(record.model.name, action.name)
|
{record.icon ? createElement(record.icon) : null}
|
||||||
}}
|
{label}
|
||||||
/>
|
</Flex>
|
||||||
</span>
|
<div style={{ paddingBottom: '1px' }}>
|
||||||
</Tooltip>
|
{renderCheckbox(aggregate, () => {
|
||||||
|
handleCycleRow(record.model)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}))
|
}
|
||||||
|
|
||||||
|
const actionColumns = actions.map((action) => {
|
||||||
|
const columnAggregate = getAggregatePermissionState(
|
||||||
|
getColumnPermissionStates(permissions, models, action, inherit)
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: (
|
||||||
|
<Flex vertical align='center' gap={12} justify='flex-end'>
|
||||||
|
<span className='permissions-matrix-header'>{action.label}</span>
|
||||||
|
<div style={{ paddingBottom: '2px' }}>
|
||||||
|
{renderCheckbox(columnAggregate, () => {
|
||||||
|
handleCycleColumn(action)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
),
|
||||||
|
key: action.name,
|
||||||
|
width: 48,
|
||||||
|
align: 'center',
|
||||||
|
render: (_, record) => {
|
||||||
|
if (!modelHasPermissionAction(record.model, action.name)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const state = getPermissionState(
|
||||||
|
permissions,
|
||||||
|
record.model.name,
|
||||||
|
action.name
|
||||||
|
)
|
||||||
|
|
||||||
|
return renderCheckbox(state, () => {
|
||||||
|
handleCycle(record.model.name, action.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return [modelColumn, ...actionColumns]
|
return [modelColumn, ...actionColumns]
|
||||||
}, [actions, disabled, handleCycle, inherit, isMobile, permissions])
|
}, [
|
||||||
|
actions,
|
||||||
|
handleCycle,
|
||||||
|
handleCycleColumn,
|
||||||
|
handleCycleRow,
|
||||||
|
inherit,
|
||||||
|
isMobile,
|
||||||
|
models,
|
||||||
|
permissions,
|
||||||
|
renderCheckbox
|
||||||
|
])
|
||||||
|
|
||||||
const dataSource = useMemo(
|
const dataSource = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user