Compare commits
No commits in common. "0b501a11c6858bd92b5421bb887383c0d3c52638" and "de1bd53f530c936fe28fbdf578bf5f4ab7b6bb0c" have entirely different histories.
0b501a11c6
...
de1bd53f53
@ -1337,64 +1337,6 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.permissions-matrix-table .permissions-matrix-group-row > .ant-table-cell {
|
|
||||||
font-weight: 600;
|
|
||||||
background-color: color-mix(
|
|
||||||
in srgb,
|
|
||||||
var(--color-text) 6%,
|
|
||||||
var(--layout-header-bg)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.permissions-matrix-table
|
|
||||||
.permissions-matrix-group-row
|
|
||||||
> .ant-table-cell.ant-table-cell-row-hover {
|
|
||||||
background: color-mix(
|
|
||||||
in srgb,
|
|
||||||
var(--color-text) 10%,
|
|
||||||
var(--layout-header-bg)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.permissions-matrix-table .ant-table-row-expand-icon,
|
|
||||||
.permissions-matrix-table .ant-table-row-indent {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.permissions-matrix-expand-button.ant-btn {
|
|
||||||
width: 22px;
|
|
||||||
min-width: 22px;
|
|
||||||
height: 22px;
|
|
||||||
padding: 0;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.permissions-matrix-expand-button.ant-btn:hover,
|
|
||||||
.permissions-matrix-expand-button.ant-btn:focus,
|
|
||||||
.permissions-matrix-expand-button.ant-btn:focus-visible {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.permissions-matrix-expand-spacer {
|
|
||||||
display: inline-block;
|
|
||||||
width: 18px;
|
|
||||||
height: 22px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.permissions-matrix-expand-icon {
|
|
||||||
font-size: 12px !important;
|
|
||||||
color: inherit;
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.permissions-matrix-expand-icon-open {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.permissions-matrix-table .ant-table-thead > tr > th {
|
.permissions-matrix-table .ant-table-thead > tr > th {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Space, Typography } from 'antd'
|
|||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const ObjectList = ({ value, objectType, style, showHyperlink = false }) => {
|
const ObjectList = ({ value, objectType, style }) => {
|
||||||
if (!value || !Array.isArray(value) || value.length === 0) {
|
if (!value || !Array.isArray(value) || value.length === 0) {
|
||||||
return <Text type='secondary'>n/a</Text>
|
return <Text type='secondary'>n/a</Text>
|
||||||
}
|
}
|
||||||
@ -12,12 +12,7 @@ const ObjectList = ({ value, objectType, style, showHyperlink = false }) => {
|
|||||||
return (
|
return (
|
||||||
<Space size={'small'} wrap style={style}>
|
<Space size={'small'} wrap style={style}>
|
||||||
{value.map((item) => (
|
{value.map((item) => (
|
||||||
<ObjectDisplay
|
<ObjectDisplay object={item} objectType={objectType} key={item._id} />
|
||||||
object={item}
|
|
||||||
objectType={objectType}
|
|
||||||
key={item._id}
|
|
||||||
showHyperlink={showHyperlink}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
@ -26,8 +21,7 @@ const ObjectList = ({ value, objectType, style, showHyperlink = false }) => {
|
|||||||
ObjectList.propTypes = {
|
ObjectList.propTypes = {
|
||||||
value: PropTypes.array,
|
value: PropTypes.array,
|
||||||
objectType: PropTypes.string,
|
objectType: PropTypes.string,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object
|
||||||
showHyperlink: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectList
|
export default ObjectList
|
||||||
|
|||||||
@ -475,13 +475,7 @@ const ObjectProperty = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'objectList': {
|
case 'objectList': {
|
||||||
return (
|
return <ObjectList value={value} objectType={objectType} />
|
||||||
<ObjectList
|
|
||||||
value={value}
|
|
||||||
objectType={objectType}
|
|
||||||
showHyperlink={showHyperlink}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
case 'objectChildren': {
|
case 'objectChildren': {
|
||||||
return (
|
return (
|
||||||
@ -964,14 +958,7 @@ const ObjectProperty = ({
|
|||||||
<ObjectTypeSelect masterFilter={masterFilter} {...inputProps} />
|
<ObjectTypeSelect masterFilter={masterFilter} {...inputProps} />
|
||||||
)
|
)
|
||||||
case 'objectList':
|
case 'objectList':
|
||||||
return (
|
return <ObjectSelect type={objectType} multiple {...inputProps} />
|
||||||
<ObjectSelect
|
|
||||||
type={objectType}
|
|
||||||
multiple
|
|
||||||
showHyperlink={showHyperlink}
|
|
||||||
{...inputProps}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'tags':
|
case 'tags':
|
||||||
return (
|
return (
|
||||||
<TagsInput
|
<TagsInput
|
||||||
|
|||||||
@ -1,23 +1,15 @@
|
|||||||
import {
|
import { useMemo, useCallback, createElement } from 'react'
|
||||||
useMemo,
|
|
||||||
useCallback,
|
|
||||||
useState,
|
|
||||||
createElement,
|
|
||||||
useContext
|
|
||||||
} from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Table, Flex, Tooltip, Button } from 'antd'
|
import { Table, Flex, Tooltip } from 'antd'
|
||||||
import { useMediaQuery } from 'react-responsive'
|
import { useMediaQuery } from 'react-responsive'
|
||||||
import { ElectronContext } from '../context/ElectronContext'
|
import { ElectronContext } from '../context/ElectronContext'
|
||||||
|
import { useContext } from 'react'
|
||||||
import {
|
import {
|
||||||
getPermissionMatrixModels,
|
getPermissionMatrixModels,
|
||||||
getPermissionMatrixActions,
|
getPermissionMatrixActions,
|
||||||
modelHasPermissionAction
|
modelHasPermissionAction
|
||||||
} from '../../../database/ObjectModels'
|
} from '../../../database/ObjectModels'
|
||||||
import { getSidebar, getSidebarMenuSections } from '../../../database/Sidebars'
|
|
||||||
import { getSidebarIconNode } from '../../Icons/sidebarIconMap'
|
|
||||||
import PermissionCheckbox from './PermissionCheckbox'
|
import PermissionCheckbox from './PermissionCheckbox'
|
||||||
import { CaretRightOutlined } from '@ant-design/icons'
|
|
||||||
|
|
||||||
const DEFAULT_SCROLL_HEIGHT = 'calc(var(--unit-100vh) - 258px)'
|
const DEFAULT_SCROLL_HEIGHT = 'calc(var(--unit-100vh) - 258px)'
|
||||||
|
|
||||||
@ -132,167 +124,6 @@ const getCheckboxTitle = (state, inherit) => {
|
|||||||
return 'Deny'
|
return 'Deny'
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizePath = (path) => {
|
|
||||||
if (!path || typeof path !== 'string') return ''
|
|
||||||
return path.split('?')[0].replace(/\/+$/, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
const collectSidebarMeta = (items, acc = { paths: [], iconKeys: [] }) => {
|
|
||||||
items.forEach((item) => {
|
|
||||||
if (item?.type === 'divider') return
|
|
||||||
if (item.path) acc.paths.push(normalizePath(item.path))
|
|
||||||
if (item.iconKey) acc.iconKeys.push(item.iconKey)
|
|
||||||
if (item.children?.length) collectSidebarMeta(item.children, acc)
|
|
||||||
})
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
const getModelPaths = (model) => {
|
|
||||||
const paths = []
|
|
||||||
const addPath = (url) => {
|
|
||||||
if (typeof url === 'string') {
|
|
||||||
paths.push(normalizePath(url))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (typeof url === 'function') {
|
|
||||||
try {
|
|
||||||
const sample = url('')
|
|
||||||
if (typeof sample === 'string') {
|
|
||||||
paths.push(normalizePath(sample.split('?')[0]))
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore uncallable url builders
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addPath(model?.url)
|
|
||||||
;(model?.actions || []).forEach((action) => addPath(action?.url))
|
|
||||||
return [...new Set(paths.filter(Boolean))]
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathMatches = (modelPath, sidebarPath) =>
|
|
||||||
modelPath === sidebarPath || modelPath.startsWith(`${sidebarPath}/`)
|
|
||||||
|
|
||||||
const getModelSectionOrder = (model, section) => {
|
|
||||||
const modelPaths = getModelPaths(model)
|
|
||||||
const pathIndex = section.paths.findIndex((sidebarPath) =>
|
|
||||||
modelPaths.some((modelPath) => pathMatches(modelPath, sidebarPath))
|
|
||||||
)
|
|
||||||
if (pathIndex !== -1) return pathIndex
|
|
||||||
const iconIndex = section.iconKeys.findIndex(
|
|
||||||
(iconKey) => iconKey === model.name
|
|
||||||
)
|
|
||||||
if (iconIndex !== -1) return iconIndex
|
|
||||||
return Number.MAX_SAFE_INTEGER
|
|
||||||
}
|
|
||||||
|
|
||||||
const modelMatchesSection = (model, section) => {
|
|
||||||
const modelPaths = getModelPaths(model)
|
|
||||||
if (
|
|
||||||
modelPaths.some((modelPath) =>
|
|
||||||
section.paths.some((sidebarPath) => pathMatches(modelPath, sidebarPath))
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (section.iconKeys.includes(model.name)) return true
|
|
||||||
const prefix = `/dashboard/${section.key}`
|
|
||||||
return modelPaths.some((modelPath) => pathMatches(modelPath, prefix))
|
|
||||||
}
|
|
||||||
|
|
||||||
const getRecordModels = (record) => {
|
|
||||||
if (record?.isGroup) {
|
|
||||||
return (record.children || []).map((child) => child.model).filter(Boolean)
|
|
||||||
}
|
|
||||||
return record?.model ? [record.model] : []
|
|
||||||
}
|
|
||||||
|
|
||||||
const getModelsRowPermissionStates = (permissions, models, actions, inherit) =>
|
|
||||||
models.flatMap((model) =>
|
|
||||||
getRowPermissionStates(permissions, model, actions, inherit)
|
|
||||||
)
|
|
||||||
|
|
||||||
const setModelsRowPermissionState = (permissions, models, actions, next) => {
|
|
||||||
let nextPermissions = permissions
|
|
||||||
models.forEach((model) => {
|
|
||||||
nextPermissions = setRowPermissionState(
|
|
||||||
nextPermissions,
|
|
||||||
model,
|
|
||||||
actions,
|
|
||||||
next
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return nextPermissions
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildPermissionMatrixDataSource = (models) => {
|
|
||||||
const assigned = new Set()
|
|
||||||
const sections = getSidebarMenuSections({ includeDev: false }).map(
|
|
||||||
(section) => {
|
|
||||||
const sidebar = getSidebar(section.key)
|
|
||||||
const meta = collectSidebarMeta(section.items || [])
|
|
||||||
Object.keys(sidebar?.routeAliases || {}).forEach((path) => {
|
|
||||||
meta.paths.push(normalizePath(path))
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
key: section.key,
|
|
||||||
label: section.label,
|
|
||||||
iconKey: section.iconKey,
|
|
||||||
paths: meta.paths,
|
|
||||||
iconKeys: meta.iconKeys
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const dataSource = []
|
|
||||||
sections.forEach((section) => {
|
|
||||||
const sectionModels = models
|
|
||||||
.filter(
|
|
||||||
(model) =>
|
|
||||||
!assigned.has(model.name) && modelMatchesSection(model, section)
|
|
||||||
)
|
|
||||||
.sort(
|
|
||||||
(a, b) =>
|
|
||||||
getModelSectionOrder(a, section) - getModelSectionOrder(b, section)
|
|
||||||
)
|
|
||||||
|
|
||||||
sectionModels.forEach((model) => assigned.add(model.name))
|
|
||||||
if (sectionModels.length === 0) return
|
|
||||||
|
|
||||||
dataSource.push({
|
|
||||||
key: `section-${section.key}`,
|
|
||||||
isGroup: true,
|
|
||||||
label: section.label,
|
|
||||||
iconNode: getSidebarIconNode(section.iconKey),
|
|
||||||
children: sectionModels.map((model) => ({
|
|
||||||
key: model.name,
|
|
||||||
label: model.label,
|
|
||||||
icon: model.icon,
|
|
||||||
model
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const leftover = models.filter((model) => !assigned.has(model.name))
|
|
||||||
if (leftover.length > 0) {
|
|
||||||
dataSource.push({
|
|
||||||
key: 'section-other',
|
|
||||||
isGroup: true,
|
|
||||||
label: 'Other',
|
|
||||||
iconNode: null,
|
|
||||||
children: leftover.map((model) => ({
|
|
||||||
key: model.name,
|
|
||||||
label: model.label,
|
|
||||||
icon: model.icon,
|
|
||||||
model
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataSource
|
|
||||||
}
|
|
||||||
|
|
||||||
const PermissionsMatrix = ({
|
const PermissionsMatrix = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@ -305,50 +136,7 @@ const PermissionsMatrix = ({
|
|||||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
const models = useMemo(() => getPermissionMatrixModels(), [])
|
const models = useMemo(() => getPermissionMatrixModels(), [])
|
||||||
const actions = useMemo(() => getPermissionMatrixActions(), [])
|
const actions = useMemo(() => getPermissionMatrixActions(), [])
|
||||||
const permissions = useMemo(
|
const permissions = value && typeof value === 'object' ? value : {}
|
||||||
() => (value && typeof value === 'object' ? value : {}),
|
|
||||||
[value]
|
|
||||||
)
|
|
||||||
const dataSource = useMemo(
|
|
||||||
() => buildPermissionMatrixDataSource(models),
|
|
||||||
[models]
|
|
||||||
)
|
|
||||||
const [expandedRowKeys, setExpandedRowKeys] = useState(null)
|
|
||||||
const [hoveredColumnKey, setHoveredColumnKey] = useState(null)
|
|
||||||
|
|
||||||
const resolvedExpandedRowKeys = useMemo(
|
|
||||||
() => expandedRowKeys ?? dataSource.map((row) => row.key),
|
|
||||||
[dataSource, expandedRowKeys]
|
|
||||||
)
|
|
||||||
|
|
||||||
const isSectionExpanded = useCallback(
|
|
||||||
(record) => resolvedExpandedRowKeys.includes(record.key),
|
|
||||||
[resolvedExpandedRowKeys]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleToggleSection = useCallback(
|
|
||||||
(record) => {
|
|
||||||
if (!record.isGroup) return
|
|
||||||
setExpandedRowKeys((current) => {
|
|
||||||
const keys = current ?? dataSource.map((row) => row.key)
|
|
||||||
return keys.includes(record.key)
|
|
||||||
? keys.filter((key) => key !== record.key)
|
|
||||||
: [...keys, record.key]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[dataSource]
|
|
||||||
)
|
|
||||||
|
|
||||||
const getColumnHoverProps = useCallback(
|
|
||||||
(columnKey, isHeader = false) => ({
|
|
||||||
onMouseEnter: () => setHoveredColumnKey(columnKey),
|
|
||||||
className:
|
|
||||||
!isHeader && hoveredColumnKey === columnKey
|
|
||||||
? 'ant-table-cell-row-hover'
|
|
||||||
: undefined
|
|
||||||
}),
|
|
||||||
[hoveredColumnKey]
|
|
||||||
)
|
|
||||||
|
|
||||||
var adjustedScrollHeight = scrollHeight
|
var adjustedScrollHeight = scrollHeight
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
@ -371,53 +159,21 @@ const PermissionsMatrix = ({
|
|||||||
[disabled, inherit, onChange, permissions]
|
[disabled, inherit, onChange, permissions]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleCycleModels = useCallback(
|
const handleCycleRow = useCallback(
|
||||||
(recordModels) => {
|
(model) => {
|
||||||
if (
|
if (disabled || typeof onChange !== 'function') return
|
||||||
disabled ||
|
|
||||||
typeof onChange !== 'function' ||
|
|
||||||
recordModels.length === 0
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const aggregate = getAggregatePermissionState(
|
const aggregate = getAggregatePermissionState(
|
||||||
getModelsRowPermissionStates(
|
getRowPermissionStates(permissions, model, actions, inherit)
|
||||||
permissions,
|
|
||||||
recordModels,
|
|
||||||
actions,
|
|
||||||
inherit
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
const next = cyclePermissionState(
|
const next = cyclePermissionState(
|
||||||
aggregate === 'mixed' ? null : aggregate,
|
aggregate === 'mixed' ? null : aggregate,
|
||||||
inherit
|
inherit
|
||||||
)
|
)
|
||||||
onChange(
|
onChange(setRowPermissionState(permissions, model, actions, next))
|
||||||
setModelsRowPermissionState(permissions, recordModels, actions, next)
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
[actions, disabled, inherit, onChange, permissions]
|
[actions, disabled, inherit, onChange, permissions]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleCycleModelsAction = useCallback(
|
|
||||||
(recordModels, action) => {
|
|
||||||
if (disabled || typeof onChange !== 'function') return
|
|
||||||
const applicable = recordModels.filter((model) =>
|
|
||||||
modelHasPermissionAction(model, action.name)
|
|
||||||
)
|
|
||||||
if (applicable.length === 0) return
|
|
||||||
const aggregate = getAggregatePermissionState(
|
|
||||||
getColumnPermissionStates(permissions, applicable, action, inherit)
|
|
||||||
)
|
|
||||||
const next = cyclePermissionState(
|
|
||||||
aggregate === 'mixed' ? null : aggregate,
|
|
||||||
inherit
|
|
||||||
)
|
|
||||||
onChange(setColumnPermissionState(permissions, applicable, action, next))
|
|
||||||
},
|
|
||||||
[disabled, inherit, onChange, permissions]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleCycleColumn = useCallback(
|
const handleCycleColumn = useCallback(
|
||||||
(action) => {
|
(action) => {
|
||||||
if (disabled || typeof onChange !== 'function') return
|
if (disabled || typeof onChange !== 'function') return
|
||||||
@ -462,18 +218,10 @@ const PermissionsMatrix = ({
|
|||||||
key: 'model',
|
key: 'model',
|
||||||
dataIndex: 'label',
|
dataIndex: 'label',
|
||||||
fixed: isMobile ? undefined : 'left',
|
fixed: isMobile ? undefined : 'left',
|
||||||
width: 280,
|
width: 248,
|
||||||
onHeaderCell: () => getColumnHoverProps('model', true),
|
|
||||||
onCell: () => getColumnHoverProps('model'),
|
|
||||||
render: (label, record) => {
|
render: (label, record) => {
|
||||||
const recordModels = getRecordModels(record)
|
|
||||||
const aggregate = getAggregatePermissionState(
|
const aggregate = getAggregatePermissionState(
|
||||||
getModelsRowPermissionStates(
|
getRowPermissionStates(permissions, record.model, actions, inherit)
|
||||||
permissions,
|
|
||||||
recordModels,
|
|
||||||
actions,
|
|
||||||
inherit
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -483,52 +231,13 @@ const PermissionsMatrix = ({
|
|||||||
justify='space-between'
|
justify='space-between'
|
||||||
style={{ width: '100%', paddingRight: '10px' }}
|
style={{ width: '100%', paddingRight: '10px' }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex align='center' gap='small' style={{ marginLeft: '6px' }}>
|
||||||
align='center'
|
{record.icon ? createElement(record.icon) : null}
|
||||||
gap='small'
|
|
||||||
style={{
|
|
||||||
marginLeft: record.isGroup ? '0px' : '6px',
|
|
||||||
fontWeight: record.isGroup ? 600 : undefined
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{record.isGroup ? (
|
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
size='small'
|
|
||||||
className='permissions-matrix-expand-button'
|
|
||||||
aria-expanded={isSectionExpanded(record)}
|
|
||||||
aria-label={
|
|
||||||
isSectionExpanded(record)
|
|
||||||
? 'Collapse section'
|
|
||||||
: 'Expand section'
|
|
||||||
}
|
|
||||||
icon={
|
|
||||||
<CaretRightOutlined
|
|
||||||
className={
|
|
||||||
isSectionExpanded(record)
|
|
||||||
? 'permissions-matrix-expand-icon permissions-matrix-expand-icon-open'
|
|
||||||
: 'permissions-matrix-expand-icon'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation()
|
|
||||||
handleToggleSection(record)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span className='permissions-matrix-expand-spacer' />
|
|
||||||
)}
|
|
||||||
{record.iconNode
|
|
||||||
? record.iconNode
|
|
||||||
: record.icon
|
|
||||||
? createElement(record.icon)
|
|
||||||
: null}
|
|
||||||
{label}
|
{label}
|
||||||
</Flex>
|
</Flex>
|
||||||
<div style={{ paddingBottom: '1px' }}>
|
<div style={{ paddingBottom: '1px' }}>
|
||||||
{renderCheckbox(aggregate, () => {
|
{renderCheckbox(aggregate, () => {
|
||||||
handleCycleModels(recordModels)
|
handleCycleRow(record.model)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -543,13 +252,7 @@ const PermissionsMatrix = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
title: (
|
title: (
|
||||||
<Flex
|
<Flex vertical align='center' gap={12} justify='flex-end'>
|
||||||
vertical
|
|
||||||
align='center'
|
|
||||||
gap={12}
|
|
||||||
justify='flex-end'
|
|
||||||
style={{ paddingTop: '8px' }}
|
|
||||||
>
|
|
||||||
<span className='permissions-matrix-header'>{action.label}</span>
|
<span className='permissions-matrix-header'>{action.label}</span>
|
||||||
<div style={{ paddingBottom: '2px' }}>
|
<div style={{ paddingBottom: '2px' }}>
|
||||||
{renderCheckbox(columnAggregate, () => {
|
{renderCheckbox(columnAggregate, () => {
|
||||||
@ -561,31 +264,18 @@ const PermissionsMatrix = ({
|
|||||||
key: action.name,
|
key: action.name,
|
||||||
width: 48,
|
width: 48,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
onHeaderCell: () => getColumnHoverProps(action.name, true),
|
|
||||||
onCell: () => getColumnHoverProps(action.name),
|
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
const recordModels = getRecordModels(record)
|
if (!modelHasPermissionAction(record.model, action.name)) {
|
||||||
const applicable = recordModels.filter((model) =>
|
return null
|
||||||
modelHasPermissionAction(model, action.name)
|
}
|
||||||
)
|
|
||||||
if (applicable.length === 0) return null
|
|
||||||
|
|
||||||
if (!record.isGroup && applicable.length === 1) {
|
|
||||||
const state = getPermissionState(
|
const state = getPermissionState(
|
||||||
permissions,
|
permissions,
|
||||||
applicable[0].name,
|
record.model.name,
|
||||||
action.name
|
action.name
|
||||||
)
|
)
|
||||||
return renderCheckbox(state, () => {
|
|
||||||
handleCycle(applicable[0].name, action.name)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = getAggregatePermissionState(
|
|
||||||
getColumnPermissionStates(permissions, applicable, action, inherit)
|
|
||||||
)
|
|
||||||
return renderCheckbox(state, () => {
|
return renderCheckbox(state, () => {
|
||||||
handleCycleModelsAction(applicable, action)
|
handleCycle(record.model.name, action.name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -594,22 +284,28 @@ const PermissionsMatrix = ({
|
|||||||
return [modelColumn, ...actionColumns]
|
return [modelColumn, ...actionColumns]
|
||||||
}, [
|
}, [
|
||||||
actions,
|
actions,
|
||||||
getColumnHoverProps,
|
|
||||||
handleCycle,
|
handleCycle,
|
||||||
handleCycleColumn,
|
handleCycleColumn,
|
||||||
handleCycleModels,
|
handleCycleRow,
|
||||||
handleCycleModelsAction,
|
|
||||||
handleToggleSection,
|
|
||||||
inherit,
|
inherit,
|
||||||
isMobile,
|
isMobile,
|
||||||
isSectionExpanded,
|
|
||||||
models,
|
models,
|
||||||
permissions,
|
permissions,
|
||||||
renderCheckbox
|
renderCheckbox
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const dataSource = useMemo(
|
||||||
|
() =>
|
||||||
|
models.map((model) => ({
|
||||||
|
key: model.name,
|
||||||
|
label: model.label,
|
||||||
|
icon: model.icon,
|
||||||
|
model
|
||||||
|
})),
|
||||||
|
[models]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onMouseLeave={() => setHoveredColumnKey(null)}>
|
|
||||||
<Table
|
<Table
|
||||||
className='dashboard-table permissions-matrix-table'
|
className='dashboard-table permissions-matrix-table'
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
@ -619,17 +315,7 @@ const PermissionsMatrix = ({
|
|||||||
bordered={true}
|
bordered={true}
|
||||||
size={size}
|
size={size}
|
||||||
scroll={{ x: 'max-content', y: adjustedScrollHeight }}
|
scroll={{ x: 'max-content', y: adjustedScrollHeight }}
|
||||||
rowClassName={(record) =>
|
|
||||||
record.isGroup ? 'permissions-matrix-group-row' : ''
|
|
||||||
}
|
|
||||||
expandable={{
|
|
||||||
expandedRowKeys: resolvedExpandedRowKeys,
|
|
||||||
onExpandedRowsChange: setExpandedRowKeys,
|
|
||||||
indentSize: 0,
|
|
||||||
expandIcon: () => null
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1015,16 +1015,6 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
|
|
||||||
const showError = useCallback(
|
const showError = useCallback(
|
||||||
(error, callback = null) => {
|
(error, callback = null) => {
|
||||||
console.error(error)
|
|
||||||
console.error(error.response)
|
|
||||||
console.error(error.response.data)
|
|
||||||
console.error(error.response.data.code)
|
|
||||||
console.error(error.response.data.error)
|
|
||||||
console.error(error.response.data.message)
|
|
||||||
console.error(error.response.data.status)
|
|
||||||
console.error(error.response.data.timestamp)
|
|
||||||
console.error(error.response.data.path)
|
|
||||||
console.error(error.response.data.method)
|
|
||||||
const code = error.response.data.code || 'UNKNOWN'
|
const code = error.response.data.code || 'UNKNOWN'
|
||||||
if (code == 'UNAUTHORIZED') {
|
if (code == 'UNAUTHORIZED') {
|
||||||
setUnauthenticated()
|
setUnauthenticated()
|
||||||
|
|||||||
@ -208,8 +208,7 @@ export const DocumentTemplate = {
|
|||||||
label: 'Document Printers',
|
label: 'Document Printers',
|
||||||
required: false,
|
required: false,
|
||||||
type: 'objectList',
|
type: 'objectList',
|
||||||
objectType: 'documentPrinter',
|
objectType: 'documentPrinter'
|
||||||
showHyperlink: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'content',
|
name: 'content',
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { createElement, lazy } from 'react'
|
import { createElement, lazy } from 'react'
|
||||||
|
|
||||||
const FilamentProfileInfo = lazy(
|
const FilamentProfileInfo = lazy(
|
||||||
() =>
|
() => import('../../components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo')
|
||||||
import('../../components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo')
|
|
||||||
)
|
)
|
||||||
import FilamentProfileIcon from '../../components/Icons/FilamentProfileIcon'
|
import FilamentProfileIcon from '../../components/Icons/FilamentProfileIcon'
|
||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
@ -76,14 +75,7 @@ export const FilamentProfile = {
|
|||||||
content: () => createElement(FilamentProfileInfo)
|
content: () => createElement(FilamentProfileInfo)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
columns: [
|
columns: ['_reference', 'name', 'filamentType', 'filament', 'createdAt', 'updatedAt'],
|
||||||
'_reference',
|
|
||||||
'name',
|
|
||||||
'filamentType',
|
|
||||||
'filament',
|
|
||||||
'createdAt',
|
|
||||||
'updatedAt'
|
|
||||||
],
|
|
||||||
filters: ['_id','name','createdAt','updatedAt','_reference'],
|
filters: ['_id','name','createdAt','updatedAt','_reference'],
|
||||||
sorters: ['name', 'createdAt', 'updatedAt'],
|
sorters: ['name', 'createdAt', 'updatedAt'],
|
||||||
properties: [
|
properties: [
|
||||||
@ -1211,8 +1203,7 @@ export const FilamentProfile = {
|
|||||||
type: 'objectList',
|
type: 'objectList',
|
||||||
objectType: 'printerProfile',
|
objectType: 'printerProfile',
|
||||||
span: 2,
|
span: 2,
|
||||||
columnWidth: 220,
|
columnWidth: 220
|
||||||
showHyperlink: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'compatiblePrintersCondition',
|
name: 'compatiblePrintersCondition',
|
||||||
|
|||||||
@ -270,8 +270,7 @@ export const Host = {
|
|||||||
objectType: 'file',
|
objectType: 'file',
|
||||||
required: false,
|
required: false,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 200,
|
columnWidth: 200
|
||||||
showHyperlink: true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -245,8 +245,7 @@ export const Job = {
|
|||||||
type: 'objectList',
|
type: 'objectList',
|
||||||
objectType: 'printer',
|
objectType: 'printer',
|
||||||
required: true,
|
required: true,
|
||||||
columnWidth: 200,
|
columnWidth: 200
|
||||||
showHyperlink: true
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
stats: [
|
stats: [
|
||||||
|
|||||||
@ -4,8 +4,7 @@ const ListingInfo = lazy(
|
|||||||
() => import('../../components/Dashboard/Sales/Listings/ListingInfo')
|
() => import('../../components/Dashboard/Sales/Listings/ListingInfo')
|
||||||
)
|
)
|
||||||
const NewListingVarient = lazy(
|
const NewListingVarient = lazy(
|
||||||
() =>
|
() => import('../../components/Dashboard/Sales/ListingVarients/NewListingVarient')
|
||||||
import('../../components/Dashboard/Sales/ListingVarients/NewListingVarient')
|
|
||||||
)
|
)
|
||||||
const PublishListing = lazy(
|
const PublishListing = lazy(
|
||||||
() => import('../../components/Dashboard/Sales/Listings/PublishListing')
|
() => import('../../components/Dashboard/Sales/Listings/PublishListing')
|
||||||
@ -77,11 +76,7 @@ export const Listing = {
|
|||||||
label: 'New Listing Varient',
|
label: 'New Listing Varient',
|
||||||
icon: PlusIcon,
|
icon: PlusIcon,
|
||||||
content: (objectData, { onOk } = {}) => {
|
content: (objectData, { onOk } = {}) => {
|
||||||
return createElement(NewListingVarient, {
|
return createElement(NewListingVarient, { defaultValues: { listing: objectData }, onOk, reset: true })
|
||||||
defaultValues: { listing: objectData },
|
|
||||||
onOk,
|
|
||||||
reset: true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
@ -313,7 +308,7 @@ export const Listing = {
|
|||||||
name: 'courierServices',
|
name: 'courierServices',
|
||||||
label: 'Courier Services',
|
label: 'Courier Services',
|
||||||
type: 'objectList',
|
type: 'objectList',
|
||||||
showHyperlink: true,
|
multiple: true,
|
||||||
objectType: 'courierService',
|
objectType: 'courierService',
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@ -482,7 +482,6 @@ export const Printer = {
|
|||||||
name: 'queue',
|
name: 'queue',
|
||||||
label: 'Queue',
|
label: 'Queue',
|
||||||
type: 'objectList',
|
type: 'objectList',
|
||||||
showHyperlink: true,
|
|
||||||
objectType: 'subJob',
|
objectType: 'subJob',
|
||||||
required: false,
|
required: false,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
|
|||||||
@ -185,7 +185,6 @@ export const User = {
|
|||||||
name: 'groups',
|
name: 'groups',
|
||||||
label: 'Groups',
|
label: 'Groups',
|
||||||
type: 'objectList',
|
type: 'objectList',
|
||||||
showHyperlink: true,
|
|
||||||
objectType: 'userGroup',
|
objectType: 'userGroup',
|
||||||
span: 2,
|
span: 2,
|
||||||
columnWidth: 250
|
columnWidth: 250
|
||||||
|
|||||||
@ -75,20 +75,8 @@ export const UserGroup = {
|
|||||||
content: () => createElement(UserGroupInfo)
|
content: () => createElement(UserGroupInfo)
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
columns: [
|
columns: ['_reference', 'name', 'permissionSettings', 'createdAt', 'updatedAt'],
|
||||||
'_reference',
|
filters: ['name', 'permissionSettings', 'createdAt', 'updatedAt', '_reference'],
|
||||||
'name',
|
|
||||||
'permissionSettings',
|
|
||||||
'createdAt',
|
|
||||||
'updatedAt'
|
|
||||||
],
|
|
||||||
filters: [
|
|
||||||
'name',
|
|
||||||
'permissionSettings',
|
|
||||||
'createdAt',
|
|
||||||
'updatedAt',
|
|
||||||
'_reference'
|
|
||||||
],
|
|
||||||
sorters: ['name', 'createdAt', '_id', 'updatedAt'],
|
sorters: ['name', 'createdAt', '_id', 'updatedAt'],
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
@ -138,8 +126,7 @@ export const UserGroup = {
|
|||||||
type: 'objectList',
|
type: 'objectList',
|
||||||
objectType: 'permissionSetting',
|
objectType: 'permissionSetting',
|
||||||
span: 2,
|
span: 2,
|
||||||
columnWidth: 250,
|
columnWidth: 250
|
||||||
showHyperlink: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'permissions',
|
name: 'permissions',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user