forked from ddrilling/asb_cloud_front
Методы работы с правами переработаны, стили исправлены
This commit is contained in:
parent
6ce6ba04ea
commit
d9abe87ba4
@ -1,13 +1,11 @@
|
||||
import React from 'react'
|
||||
import { Role, Permissions, hasAccess, PermissionMixingType } from '../../utils/permissions'
|
||||
import { Role, Permission, hasPermission, isInRole } from '../../utils/PermissionService'
|
||||
|
||||
type PrivateContentProps = {
|
||||
permissions?: Permissions
|
||||
roles?: Role[]
|
||||
mixing?: PermissionMixingType
|
||||
children?: any
|
||||
roles?: Role[] | Role
|
||||
permission?: Permission
|
||||
children?: React.ReactElement<any, any>
|
||||
}
|
||||
|
||||
export const PrivateContent: React.FC<PrivateContentProps> = ({ permissions, roles, mixing, children }) => {
|
||||
return (hasAccess({ permissions, roles, mixing }) && children) || null
|
||||
}
|
||||
export const PrivateContent: React.FC<PrivateContentProps> = ({ permission, roles, children = null }) =>
|
||||
hasPermission(permission) || isInRole(roles) ? children : null
|
||||
|
@ -1,15 +1,13 @@
|
||||
import React from 'react'
|
||||
import { Menu } from 'antd'
|
||||
import { hasAccess, Role, Permissions, PermissionMixingType } from '../../utils/permissions'
|
||||
import { Role, Permission, hasPermission, isInRole } from '../../utils/PermissionService'
|
||||
|
||||
|
||||
type PrivateMenuItemProps = {
|
||||
roles?: Role[]
|
||||
permissions?: Permissions
|
||||
mixing?: PermissionMixingType
|
||||
roles?: Role[] | Role
|
||||
permission?: Permission
|
||||
[props: string]: any
|
||||
}
|
||||
|
||||
export const PrivateMenuItem: React.FC<PrivateMenuItemProps> = ({ roles, permissions, mixing, ...props }) => {
|
||||
return hasAccess({ permissions, roles, mixing }) ? <Menu.Item {...props}/> : null
|
||||
}
|
||||
export const PrivateMenuItem: React.FC<PrivateMenuItemProps> = ({ roles, permission, mixing, ...props }) =>
|
||||
hasPermission(permission) || isInRole(roles) ? <Menu.Item {...props}/> : null
|
||||
|
@ -1,21 +1,22 @@
|
||||
import React from 'react'
|
||||
import { Route, Redirect } from 'react-router-dom'
|
||||
import { Role, Permissions, hasAccess } from '../../utils/permissions'
|
||||
import { StaticContext } from 'react-router'
|
||||
import { Route, Redirect, RouteComponentProps } from 'react-router-dom'
|
||||
import { Role, Permission, hasPermission, isInRole } from '../../utils/PermissionService'
|
||||
|
||||
type PrivateRouteProps = {
|
||||
permissions?: Permissions
|
||||
roles: Role[]
|
||||
component?: any
|
||||
children?: any
|
||||
roles: Role[] | Role
|
||||
permission?: Permission
|
||||
component?: React.ComponentType<any> | React.ComponentType<RouteComponentProps<any, StaticContext, unknown>>
|
||||
children?: React.ReactNode
|
||||
[other: string]: any
|
||||
}
|
||||
|
||||
export const PrivateRoute: React.FC<PrivateRouteProps> = ({ permissions, roles, component, children, ...other }) => {
|
||||
const available = localStorage['token'] && hasAccess({ permissions, roles })
|
||||
export const PrivateRoute: React.FC<PrivateRouteProps> = ({ permission, roles, component, children, ...other }) => {
|
||||
const available = localStorage.getItem('token') && (hasPermission(permission) || isInRole(roles))
|
||||
|
||||
return (
|
||||
<Route {...other}
|
||||
component={available && component}
|
||||
component={available ? component : undefined}
|
||||
render={({ location }) => available ? children : (
|
||||
<Redirect to={{ pathname: '/login', state: { from: location } }} />
|
||||
)}
|
||||
|
@ -38,8 +38,8 @@ export const EditableTable = ({
|
||||
dataSource,
|
||||
onChange, // Метод вызывается со всем dataSource с измененными элементами после любого действия
|
||||
onRowAdd, // Метод вызывается с новой добавленной записью. Если метод не определен, то кнопка добавления строки не показывается
|
||||
onRowEdit,// Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается
|
||||
onRowDelete,// Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается
|
||||
onRowEdit, // Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается
|
||||
onRowDelete, // Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается
|
||||
...otherTableProps
|
||||
}) => {
|
||||
|
||||
@ -73,10 +73,6 @@ export const EditableTable = ({
|
||||
...form.initialValues,
|
||||
key:newRowKeyValue
|
||||
}
|
||||
// const newRow = { key: newRowKeyValue }
|
||||
|
||||
// for (let column of columns)
|
||||
// newRow[column.dataIndex] = form.initialValues?.[column.dataIndex] ?? column.initialValue
|
||||
|
||||
const newData = [newRow, ...data]
|
||||
setData(newData)
|
||||
|
@ -27,15 +27,15 @@ export const makeNumericRender = (fixed?: number) => (value: any, row: object):
|
||||
)
|
||||
}
|
||||
|
||||
export const makeNumericColumnOptions = (fixed?: number, sorterKey?: string ) => ({
|
||||
export const makeNumericColumnOptions = (fixed?: number, sorterKey?: string) => ({
|
||||
editable: true,
|
||||
initialValue: 0,
|
||||
width:100,
|
||||
sorter: sorterKey? makeNumericSorter(sorterKey) : null,
|
||||
width: 100,
|
||||
sorter: sorterKey ? makeNumericSorter(sorterKey) : null,
|
||||
formItemRules: [
|
||||
{
|
||||
required: true,
|
||||
message: `Введите число`,
|
||||
message: 'Введите число',
|
||||
pattern: RegExpIsFloat,
|
||||
},
|
||||
],
|
||||
@ -58,7 +58,7 @@ interface columnPropsOther {
|
||||
// массив правил валидации значений https://ant.design/components/form/#Rule
|
||||
formItemRules?: Rule[]
|
||||
// дефолтное значение при добавлении новой строки
|
||||
initialValue?: string|number
|
||||
initialValue?: string | number
|
||||
|
||||
render?: (...attributes: any) => any
|
||||
}
|
||||
@ -71,13 +71,13 @@ export const makeColumn = (title: string | ReactNode, key: string, other?: colum
|
||||
})
|
||||
|
||||
export const makeColumnsPlanFact = (
|
||||
title:string | ReactNode,
|
||||
key:string | string[],
|
||||
title: string | ReactNode,
|
||||
key: string | string[],
|
||||
columsOther?: any | any[],
|
||||
gruopOther?: any
|
||||
) => {
|
||||
let keyPlanLocal = key
|
||||
let keyFactLocal = key
|
||||
let keyPlanLocal: string
|
||||
let keyFactLocal: string
|
||||
|
||||
if (key instanceof Array) {
|
||||
keyPlanLocal = key[0]
|
||||
@ -87,29 +87,27 @@ export const makeColumnsPlanFact = (
|
||||
keyFactLocal = key + 'Fact'
|
||||
}
|
||||
|
||||
let columsOtherLoacl : any[2]
|
||||
let columsOtherLocal : any[2]
|
||||
if (columsOther instanceof Array)
|
||||
columsOtherLoacl = [columsOther[0], columsOther[1]]
|
||||
columsOtherLocal = [columsOther[0], columsOther[1]]
|
||||
else
|
||||
columsOtherLoacl = [columsOther, columsOther]
|
||||
columsOtherLocal = [columsOther, columsOther]
|
||||
|
||||
return {
|
||||
title: title,
|
||||
...gruopOther,
|
||||
children: [
|
||||
makeColumn('план', keyPlanLocal, columsOtherLoacl[0]),
|
||||
makeColumn('факт', keyFactLocal, columsOtherLoacl[1]),
|
||||
makeColumn('план', keyPlanLocal, columsOtherLocal[0]),
|
||||
makeColumn('факт', keyFactLocal, columsOtherLocal[1]),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const makeFilterTextMatch = (key: string | number) => (filterValue: string | number, dataItem: any) =>
|
||||
dataItem[key] === filterValue
|
||||
export const makeFilterTextMatch = (key: string | number) => (
|
||||
(filterValue: string | number, dataItem: any) => dataItem[key] === filterValue
|
||||
)
|
||||
|
||||
export const makeGroupColumn = (title: string, children: object[]) => ({
|
||||
title: title,
|
||||
children: children,
|
||||
})
|
||||
export const makeGroupColumn = (title: string, children: object[]) => ({ title, children })
|
||||
|
||||
export const makeTextColumn = (
|
||||
title: string,
|
||||
|
@ -1,123 +0,0 @@
|
||||
import { Button, Modal, Select } from 'antd'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { EditableTable, makeColumn } from '../../components/Table'
|
||||
|
||||
export const toHexString = (num, size) => '0x' + ('0'.repeat(size) + num.toString(16).toUpperCase()).slice(-size)
|
||||
|
||||
const bitCount = 32
|
||||
const bitOptions = [...Array(bitCount).keys()].map((n) => ({
|
||||
label: toHexString(1 << n, bitCount / 4),
|
||||
value: n,
|
||||
}))
|
||||
|
||||
const columns = [
|
||||
makeColumn('Бит', 'bit', {
|
||||
// initialValue: bitOptions[0]?.value ?? '--',
|
||||
input: <Select options={bitOptions} />,
|
||||
render: (value) => bitOptions.find(option => option?.value === value)?.label ?? '--',
|
||||
width: 200,
|
||||
editable: true,
|
||||
formItemRules: [{
|
||||
required: true,
|
||||
message: 'Пожалуйста, выберите бит'
|
||||
}],
|
||||
}),
|
||||
makeColumn('Описание', 'description', {
|
||||
width: 400,
|
||||
editable: true,
|
||||
formItemRules: [{
|
||||
required: true,
|
||||
message: 'Пожалуйста, введите описание'
|
||||
}],
|
||||
}),
|
||||
]
|
||||
|
||||
export const PermissionBits = React.memo(({ value, onChange }) => {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false)
|
||||
const [bits, setBits] = useState([])
|
||||
// const [columns, setColumns] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
setBits(Object.entries(value ?? {}).map(([bit, description]) => ({ bit, description })))
|
||||
}, [value])
|
||||
|
||||
// useEffect(() => {
|
||||
// const options = bitOptions.filter((option) => !(bits?.length && bits.find((row) => row.bit === option.value)))
|
||||
// const defaultValue = options?.[0]?.label ?? '--'
|
||||
|
||||
// setColumns([
|
||||
// makeColumn('Бит', 'bit', {
|
||||
// initialValue: options?.[0]?.value ?? 0,
|
||||
// input: <Select options={options} />,
|
||||
// render: (value) => bitOptions.find(option => option?.value === value)?.label ?? defaultValue,
|
||||
// width: 200,
|
||||
// editable: true,
|
||||
// formItemRules: [{ required: true, message: 'Пожалуйста, выберите бит' }],
|
||||
// }),
|
||||
// makeColumn('Описание', 'description', {
|
||||
// width: 400,
|
||||
// editable: true,
|
||||
// formItemRules: [{ required: true, message: 'Пожалуйста, введите описание' }],
|
||||
// }),
|
||||
// ])
|
||||
// }, [bits])
|
||||
|
||||
const saveBits = () => {
|
||||
const newValue = {}
|
||||
bits.forEach(({bit, description}) => (newValue[bit] = description))
|
||||
if(!onChange(newValue))
|
||||
setIsModalVisible(false)
|
||||
}
|
||||
|
||||
const addNewBit = (bit) => {
|
||||
bit.key = Date.now()
|
||||
setBits((prevBits) => [...(prevBits ?? []), bit])
|
||||
}
|
||||
|
||||
const editBit = (bit) => {
|
||||
if (!bit.key) return
|
||||
const idx = bits.findIndex(v => v.key === bit.key)
|
||||
if (idx < 0) return
|
||||
setBits((prevBits) => {
|
||||
prevBits[idx] = bit
|
||||
return prevBits
|
||||
})
|
||||
}
|
||||
|
||||
const removeBit = (bit) => {
|
||||
if (!bit.key) return
|
||||
const idx = bits.findIndex(v => v.bit === bit.bit)
|
||||
if (idx < 0) return
|
||||
setBits((prevBits) => {
|
||||
prevBits.splice(idx, 1)
|
||||
return prevBits
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type={'link'} onClick={() => setIsModalVisible(true)}>Редактировать</Button>
|
||||
<Modal
|
||||
title={'Описание битов'}
|
||||
centered
|
||||
visible={isModalVisible}
|
||||
width={750}
|
||||
onCancel={() => setIsModalVisible(false)}
|
||||
onOk={saveBits}
|
||||
okText={'Сохранить'}
|
||||
>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={bits}
|
||||
onRowAdd={addNewBit}
|
||||
onRowEdit={editBit}
|
||||
onRowDelete={removeBit}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default PermissionBits
|
@ -1,60 +0,0 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { EditableTable, makeActionHandler, makeColumn } from '../../components/Table'
|
||||
import { AdminPermissionInfoService } from '../../services/api'
|
||||
import PermissionBits, { toHexString } from './PermissionBits'
|
||||
|
||||
export const PermissionController = () => {
|
||||
const [permissions, setPermissions] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
const columns = [
|
||||
makeColumn('Название', 'name', { width: 200, editable: true }),
|
||||
makeColumn('Описание', 'description', { width: 200, editable: true }),
|
||||
makeColumn('Значения битов', 'bitDescription', {
|
||||
width: 200,
|
||||
editable: true,
|
||||
input: <PermissionBits />, // TODO: Дописать колонку для описания битов права
|
||||
render: (bits) => {
|
||||
if (!bits) return '--'
|
||||
const sum = Object.keys(bits).reduce((sum, key) => sum + (1 << parseInt(key)), 0)
|
||||
return sum && toHexString(sum, 16)
|
||||
},
|
||||
})
|
||||
]
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const permissions = await AdminPermissionInfoService.getAll()
|
||||
setPermissions(permissions)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список прав`
|
||||
)
|
||||
|
||||
useEffect(updateTable, [])
|
||||
|
||||
const handlerProps = {
|
||||
service: AdminPermissionInfoService,
|
||||
setLoader: setShowLoader,
|
||||
errorMsg: `Не удалось выполнить операцию`,
|
||||
onComplete: updateTable
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={permissions}
|
||||
onRowAdd={makeActionHandler('insert', handlerProps)}
|
||||
onRowEdit={makeActionHandler('update', handlerProps)}
|
||||
onRowDelete={makeActionHandler('delete', handlerProps)}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
||||
|
||||
export default PermissionController
|
141
src/pages/AdminPanel/RoleController.jsx
Normal file
141
src/pages/AdminPanel/RoleController.jsx
Normal file
@ -0,0 +1,141 @@
|
||||
import { Button, Modal } from 'antd'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { AdminUserRoleService } from '../../services/api'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import { EditableTable, makeActionHandler, makeColumn, makeSelectColumn } from '../../components/Table'
|
||||
|
||||
export const toHexString = (num, size) => '0x' + ('0'.repeat(size) + num.toString(16).toUpperCase()).slice(-size)
|
||||
|
||||
const columns = [
|
||||
makeColumn('Имена прав', 'name', {
|
||||
width: 400,
|
||||
editable: true,
|
||||
formItemRules: [{
|
||||
required: true,
|
||||
message: 'Пожалуйста, введите имя права'
|
||||
}],
|
||||
}),
|
||||
]
|
||||
|
||||
export const RolePermissions = ({ value, onChange }) => {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false)
|
||||
const [list, setList] = useState([])
|
||||
|
||||
const save = () => {
|
||||
const newValue = list.map((value) => value.name)
|
||||
if(!onChange(newValue))
|
||||
setIsModalVisible(false)
|
||||
}
|
||||
|
||||
const add = (permission) => {
|
||||
permission.key = Date.now()
|
||||
setList((prevList) => {
|
||||
if (!prevList) prevList = []
|
||||
prevList.push(permission)
|
||||
return prevList
|
||||
})
|
||||
}
|
||||
|
||||
const edit = (permission) => {
|
||||
if (!permission.key) return
|
||||
const idx = list.findIndex(v => v.key === permission.key)
|
||||
if (idx < 0) return
|
||||
setList((prevList) => {
|
||||
prevList[idx] = permission
|
||||
return prevList
|
||||
})
|
||||
}
|
||||
|
||||
const remove = (permission) => {
|
||||
if (!permission.key) return
|
||||
const idx = list.findIndex(v => v.key === permission.key)
|
||||
if (idx < 0) return
|
||||
setList((prevList) => {
|
||||
prevList.splice(idx, 1)
|
||||
return prevList
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type={'link'} onClick={() => setIsModalVisible(true)}>Редактировать</Button>
|
||||
<Modal
|
||||
title={'Права доступа'}
|
||||
centered
|
||||
visible={isModalVisible}
|
||||
width={750}
|
||||
onCancel={() => setIsModalVisible(false)}
|
||||
onOk={save}
|
||||
okText={'Сохранить'}
|
||||
>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={list}
|
||||
onRowAdd={add}
|
||||
onRowEdit={edit}
|
||||
onRowDelete={remove}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const RoleController = () => {
|
||||
const [roles, setRoles] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [columns, setColumns] = useState([])
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const roles = await AdminUserRoleService.getAll()
|
||||
setRoles(roles)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список прав`
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const options = roles?.map((r) => ({ value: r.id, label: r.caption })) ?? []
|
||||
setColumns([
|
||||
makeColumn('Название', 'caption', { width: 200, editable: true }),
|
||||
makeSelectColumn('Роль-родитель', 'idParent', options, options[0], {
|
||||
width: 200,
|
||||
editable: true
|
||||
}),
|
||||
makeColumn('Права доступа', 'permissions', {
|
||||
width: 200,
|
||||
editable: true,
|
||||
input: <RolePermissions />,
|
||||
render: (permissions) => permissions?.join(', ') ?? '',
|
||||
})
|
||||
])
|
||||
}, [roles])
|
||||
|
||||
useEffect(updateTable, [])
|
||||
|
||||
const handlerProps = {
|
||||
service: AdminUserRoleService,
|
||||
setLoader: setShowLoader,
|
||||
errorMsg: `Не удалось выполнить операцию`,
|
||||
onComplete: updateTable
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={roles}
|
||||
onRowAdd={makeActionHandler('insert', handlerProps)}
|
||||
onRowEdit={makeActionHandler('update', handlerProps)}
|
||||
onRowDelete={makeActionHandler('delete', handlerProps)}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
||||
|
||||
export default RoleController
|
@ -2,14 +2,13 @@ import React, { Suspense } from 'react'
|
||||
import { Layout, Menu } from 'antd'
|
||||
import { Switch, Link, useParams, Redirect, Route } from 'react-router-dom'
|
||||
import { PrivateMenuItem, PrivateRoute } from '../../components/Private'
|
||||
import { PermissionNames, PermissionValue } from '../../utils/permissions'
|
||||
|
||||
const ClusterController = React.lazy(() => import('./ClusterController'))
|
||||
const CompanyController = React.lazy(() => import('./CompanyController'))
|
||||
const DepositController = React.lazy(() => import('./DepositController'))
|
||||
const UserController = React.lazy(() => import('./UserController'))
|
||||
const WellController = React.lazy(() => import('./WellController'))
|
||||
const PermissionController = React.lazy(() => import('./PermissionController'))
|
||||
const RoleController = React.lazy(() => import('./RoleController'))
|
||||
|
||||
export const AdminPanel = () => {
|
||||
const { tab } = useParams()
|
||||
@ -17,28 +16,24 @@ export const AdminPanel = () => {
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Menu
|
||||
mode={'horizontal'}
|
||||
selectable={true}
|
||||
selectedKeys={[tab]}
|
||||
>
|
||||
<PrivateMenuItem key={'deposit'} permissions={[[PermissionNames.admin.deposit, PermissionValue.Read]]}>
|
||||
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]}>
|
||||
<PrivateMenuItem key={'deposit'} permission={'deposit_editor'}>
|
||||
<Link to={`${rootPath}/deposit`}>Месторождения</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem key={'cluster'} permissions={[[PermissionNames.admin.cluster, PermissionValue.Read]]}>
|
||||
<PrivateMenuItem key={'cluster'} permission={'cluster_editor'}>
|
||||
<Link to={`${rootPath}/cluster`}>Кусты</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem key={'well'} permissions={[[PermissionNames.admin.well, PermissionValue.Read]]}>
|
||||
<PrivateMenuItem key={'well'} permission={'well_editor'}>
|
||||
<Link to={`${rootPath}/well`}>Скважины</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem key={'user'} permissions={[[PermissionNames.admin.user, PermissionValue.Read]]}>
|
||||
<PrivateMenuItem key={'user'} permission={'user_editor'}>
|
||||
<Link to={`${rootPath}/user`}>Пользователи</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem key={'company'} permissions={[[PermissionNames.admin.company, PermissionValue.Read]]}>
|
||||
<PrivateMenuItem key={'company'} permission={'company_editor'}>
|
||||
<Link to={`${rootPath}/company`}>Компании</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem key={'permission'} permissions={[[PermissionNames.admin.permissions, PermissionValue.Read]]}>
|
||||
<Link to={`${rootPath}/permission`}>Права</Link>
|
||||
<PrivateMenuItem key={'role'} permission={'role_editor'}>
|
||||
<Link to={`${rootPath}/role`}>Права</Link>
|
||||
</PrivateMenuItem>
|
||||
</Menu>
|
||||
|
||||
@ -46,12 +41,12 @@ export const AdminPanel = () => {
|
||||
<Layout.Content className={'site-layout-background'}>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Switch>
|
||||
<PrivateRoute roles={['deposit_admin']} path={`${rootPath}/deposit`} component={DepositController} />
|
||||
<PrivateRoute roles={['cluster_admin']} path={`${rootPath}/cluster`} component={ClusterController} />
|
||||
<PrivateRoute roles={[ 'well_admin']} path={`${rootPath}/well` } component={ WellController} />
|
||||
<PrivateRoute roles={[ 'user_admin']} path={`${rootPath}/user` } component={ UserController} />
|
||||
<PrivateRoute roles={['company_admin']} path={`${rootPath}/company`} component={CompanyController} />
|
||||
<PrivateRoute path={`${rootPath}/permission`} component={PermissionController} />
|
||||
<PrivateRoute permission={'deposit_editor'} path={`${rootPath}/deposit`} component={DepositController} />
|
||||
<PrivateRoute permission={'cluster_editor'} path={`${rootPath}/cluster`} component={ClusterController} />
|
||||
<PrivateRoute permission={ 'well_editor'} path={`${rootPath}/well` } component={ WellController} />
|
||||
<PrivateRoute permission={ 'user_editor'} path={`${rootPath}/user` } component={ UserController} />
|
||||
<PrivateRoute permission={'company_editor'} path={`${rootPath}/company`} component={CompanyController} />
|
||||
<PrivateRoute permission={ 'role_editor'} path={`${rootPath}/role` } component={ RoleController} />
|
||||
<Route path={'/'}>
|
||||
<Redirect to={`${rootPath}/deposit`}/>
|
||||
</Route>
|
||||
|
@ -1,59 +1,50 @@
|
||||
//import { Menu } from "antd";
|
||||
import { FolderOutlined } from "@ant-design/icons";
|
||||
import { Link, Route} from "react-router-dom";
|
||||
import path from 'path'
|
||||
import { FolderOutlined } from '@ant-design/icons'
|
||||
import { Link, Route } from 'react-router-dom'
|
||||
import { PrivateMenuItem } from '../../components/Private'
|
||||
import { getUserRoles, isInRole } from "../../utils/PermissionService";
|
||||
import DocumentsTemplate from './DocumentsTemplate'
|
||||
import {PrivateMenuItem} from '../../components/Private'
|
||||
|
||||
export const documentCategories = [
|
||||
{id:1, key:'fluidService', title:'Растворный сервис'/*, roles:['ff']*/},
|
||||
{id:2, key:'cementing', title:'Цементирование'},
|
||||
{id:3, key:'nnb', title:'ННБ'},
|
||||
{id:4, key:'gti', title:'ГТИ'},
|
||||
{id:5, key:'documentsForWell', title:'Документы по скважине'},
|
||||
{id:6, key:'supervisor', title:'Супервайзер'},
|
||||
{id:7, key:'master', title:'Мастер'},
|
||||
{ id: 1, key: 'fluidService', title: 'Растворный сервис' },
|
||||
{ id: 2, key: 'cementing', title: 'Цементирование' },
|
||||
{ id: 3, key: 'nnb', title: 'ННБ' },
|
||||
{ id: 4, key: 'gti', title: 'ГТИ' },
|
||||
{ id: 5, key: 'documentsForWell', title: 'Документы по скважине' },
|
||||
{ id: 6, key: 'supervisor', title: 'Супервайзер' },
|
||||
{ id: 7, key: 'master', title: 'Мастер' },
|
||||
]
|
||||
|
||||
const makeMenuItem = (keyValue, rootPath, title, other) => (
|
||||
<PrivateMenuItem className="ant-menu-item"
|
||||
key={`${keyValue}`}
|
||||
{...other}>
|
||||
<PrivateMenuItem className={'ant-menu-item'} key={`${keyValue}`} {...other}>
|
||||
<Link to={{pathname: `${rootPath}/${keyValue}`}}>{title}</Link>
|
||||
</PrivateMenuItem>)
|
||||
</PrivateMenuItem>
|
||||
)
|
||||
|
||||
const makeRouteItem = (keyValue, rootPath, other) => (
|
||||
<Route
|
||||
path={`${rootPath}/${keyValue}`}
|
||||
key={`${keyValue}`}>
|
||||
key={`${keyValue}`}
|
||||
>
|
||||
<DocumentsTemplate {...other}/>
|
||||
</Route>)
|
||||
</Route>
|
||||
)
|
||||
|
||||
const formatRoutePath = (rootPath) =>{
|
||||
let root = rootPath.endsWith('/')
|
||||
? rootPath.slice(0,-1)
|
||||
: rootPath
|
||||
|
||||
root = root.endsWith('/document')
|
||||
? root
|
||||
: `${root}/document`
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
const getCategoriesByUserRole = (role) => documentCategories
|
||||
.filter(cat => !cat.roles || cat.roles.includes('*') || cat.roles.includes(role))
|
||||
const getCategoriesByUserRole = (role) => documentCategories.filter(cat => isInRole(cat.roles))
|
||||
|
||||
export const makeMenuItems = (rootPath) => {
|
||||
const root = formatRoutePath(rootPath)
|
||||
const categories = getCategoriesByUserRole(localStorage['roleName'])
|
||||
const root = path.join(rootPath, '/document')
|
||||
const categories = getCategoriesByUserRole(getUserRoles())
|
||||
return categories.map(category =>
|
||||
makeMenuItem(category.key, root, category.title, {icon:<FolderOutlined/>}))
|
||||
}
|
||||
|
||||
export const makeRouteItems = (rootPath, idWell) => {
|
||||
const root = formatRoutePath(rootPath)
|
||||
const categories = getCategoriesByUserRole(localStorage['roleName'])
|
||||
const routes = categories.map(category =>
|
||||
makeRouteItem(category.key, root, {idCategory: category.id, idWell: idWell}))
|
||||
return routes;
|
||||
const root = path.join(rootPath, '/document')
|
||||
const categories = getCategoriesByUserRole(getUserRoles())
|
||||
const routes = categories.map(category => makeRouteItem(category.key, root, {
|
||||
idCategory: category.id,
|
||||
idWell: idWell
|
||||
}))
|
||||
return routes
|
||||
}
|
62
src/utils/PermissionService.ts
Normal file
62
src/utils/PermissionService.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { getArrayFromLocalStorage } from './storage'
|
||||
|
||||
export type Role = string
|
||||
export type Permission = string
|
||||
|
||||
export const getUserRoles = (): Role[] => getArrayFromLocalStorage<Role>('roles') ?? []
|
||||
export const getUserPermissions = (): Permission[] =>
|
||||
getArrayFromLocalStorage<Permission>('permissions') ?? []
|
||||
|
||||
export const hasPermission = (permission?: Permission): boolean => {
|
||||
if (typeof permission !== 'string') return true
|
||||
return permission in getUserPermissions()
|
||||
}
|
||||
|
||||
export const isInRole = (roles?: Role[] | Role): boolean => {
|
||||
if (typeof roles === 'string' && !Array.isArray(roles))
|
||||
roles = [roles]
|
||||
if (!roles?.length) return true
|
||||
|
||||
if (localStorage.getItem('login') === 'dev') return true // TODO: Удалить строку
|
||||
|
||||
const user_roles = getUserRoles()
|
||||
return roles.some((role) => role in user_roles)
|
||||
}
|
||||
|
||||
/*
|
||||
deposit
|
||||
cluster
|
||||
well (R)
|
||||
well/archive (R)
|
||||
well/message (R)
|
||||
well/report (R)
|
||||
well/measure (RW)
|
||||
well/drillingProgram (RU согласовать)
|
||||
well/telemetry (R)
|
||||
well/telemetry:status (RW)
|
||||
well/telemetry:rop (RW)
|
||||
well/operations (R)
|
||||
well/operations/tvd (R)
|
||||
well/operations/sections (R)
|
||||
well/operations/plan (RW)
|
||||
well/operations/fact (RW)
|
||||
well/operations/drillProccesFlow (RW)
|
||||
well/operations/params (RW)
|
||||
well/operations/composite (R)
|
||||
well/operations/composite/wells (R)
|
||||
well/operations/composite/sections (RW)
|
||||
well/document (R)
|
||||
well/document/fluidService (RU)
|
||||
well/document/cementing (RU)
|
||||
well/document/nnb (RU)
|
||||
well/document/gti (RU)
|
||||
well/document/documentsForWell (RU)
|
||||
well/document/supervisor (RU)
|
||||
well/document/master (RU)
|
||||
admin (R)
|
||||
admin/deposit (RAED)
|
||||
admin/cluster (RAED)
|
||||
admin/well (RAED)
|
||||
admin/user (RAED)
|
||||
admin/company (RAED)
|
||||
*/
|
@ -1,140 +0,0 @@
|
||||
export type Role = string
|
||||
export type PermissionName = string
|
||||
export enum PermissionValue {
|
||||
Nothing = 0,
|
||||
Read = 1 << 0,
|
||||
Write = 1 << 1,
|
||||
Aprove = 1 << 1,
|
||||
Update = 1 << 1,
|
||||
Insert = 1 << 2,
|
||||
Delete = 1 << 3,
|
||||
}
|
||||
export type PermissionMixingType = 'and' | 'or'
|
||||
export type Permissions = [PermissionName, PermissionValue][]
|
||||
|
||||
export const PermissionNames = {
|
||||
deposit: 'deposit',
|
||||
cluster: 'cluster',
|
||||
well: {
|
||||
_self: 'well',
|
||||
archive: 'well/archive',
|
||||
measure: 'well/measure',
|
||||
message: 'well/message',
|
||||
report: 'well/report',
|
||||
drillingProgram: 'well/drillingProgram',
|
||||
telemetry: {
|
||||
_self: 'well/telemetry',
|
||||
status: 'well/telemetry/status',
|
||||
rop: 'well/telemetry/rop',
|
||||
},
|
||||
operations: {
|
||||
_self: 'well/operations',
|
||||
tvd: 'well/operations/tvd',
|
||||
sections: 'well/operations/sections',
|
||||
plan: 'well/operations/plan',
|
||||
fact: 'well/operations/fact',
|
||||
drillProccesFlow: 'well/operations/drillProccesFlow',
|
||||
params: 'well/operations/params',
|
||||
composite: {
|
||||
_self: 'well/operations/composite',
|
||||
wells: 'well/operations/composite/wells',
|
||||
sections: 'well/operations/composite/sections',
|
||||
}
|
||||
},
|
||||
document: {
|
||||
_self: 'well/document',
|
||||
fluidService: 'well/document/fluidService',
|
||||
cementing: 'well/document/cementing',
|
||||
nnb: 'well/document/nnb',
|
||||
gti: 'well/document/gti',
|
||||
documentsForWell: 'well/document/documentsForWell',
|
||||
supervisor: 'well/document/supervisor',
|
||||
master: 'well/document/master'
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
_self: 'admin',
|
||||
deposit: 'admin/deposit',
|
||||
cluster: 'admin/cluster',
|
||||
well: 'admin/well',
|
||||
user: 'admin/user',
|
||||
company: 'admin/company',
|
||||
}
|
||||
}
|
||||
|
||||
const admins: Role[] = ['администратор', 'админ', 'admin']
|
||||
|
||||
export const hasPermissions = (permissions?: Permissions): boolean => {
|
||||
if (!permissions?.length)
|
||||
return true
|
||||
return true // TODO: Написать проверку на доступ через права
|
||||
return false
|
||||
}
|
||||
|
||||
export const isInRole = (roles?: Role[]): boolean => {
|
||||
if (localStorage['login'] === 'dev') return true // TODO: Удалить строку
|
||||
if (!roles?.length)
|
||||
return true
|
||||
// TODO: Переписать проверку на доступ через роли
|
||||
const role: Role = localStorage['roleName']?.toLowerCase()
|
||||
if (admins.indexOf(role) > -1)
|
||||
return true
|
||||
for (const r of roles)
|
||||
if (r.toLowerCase() === role)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
export type hasAccessProps = {
|
||||
permissions?: Permissions
|
||||
roles?: Role[]
|
||||
mixing?: PermissionMixingType
|
||||
}
|
||||
|
||||
export const hasAccess = ({permissions, roles, mixing}: hasAccessProps): boolean => {
|
||||
switch (mixing) {
|
||||
case 'or':
|
||||
return hasPermissions(permissions) || isInRole(roles)
|
||||
case 'and':
|
||||
default:
|
||||
return hasPermissions(permissions) && isInRole(roles)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
deposit
|
||||
cluster
|
||||
well (R)
|
||||
well/archive (R)
|
||||
well/message (R)
|
||||
well/report (R)
|
||||
well/measure (RW)
|
||||
well/drillingProgram (RU согласовать)
|
||||
well/telemetry (R)
|
||||
well/telemetry:status (RW)
|
||||
well/telemetry:rop (RW)
|
||||
well/operations (R)
|
||||
well/operations/tvd (R)
|
||||
well/operations/sections (R)
|
||||
well/operations/plan (RW)
|
||||
well/operations/fact (RW)
|
||||
well/operations/drillProccesFlow (RW)
|
||||
well/operations/params (RW)
|
||||
well/operations/composite (R)
|
||||
well/operations/composite/wells (R)
|
||||
well/operations/composite/sections (RW)
|
||||
well/document (R)
|
||||
well/document/fluidService (RU)
|
||||
well/document/cementing (RU)
|
||||
well/document/nnb (RU)
|
||||
well/document/gti (RU)
|
||||
well/document/documentsForWell (RU)
|
||||
well/document/supervisor (RU)
|
||||
well/document/master (RU)
|
||||
admin (R)
|
||||
admin/deposit (RAED)
|
||||
admin/cluster (RAED)
|
||||
admin/well (RAED)
|
||||
admin/user (RAED)
|
||||
admin/company (RAED)
|
||||
*/
|
5
src/utils/storage.ts
Normal file
5
src/utils/storage.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const getArrayFromLocalStorage = <T extends string = string>(name: string, sep: string | RegExp = ','): T[] | null => {
|
||||
const raw = localStorage.getItem(name)
|
||||
if (!raw) return null
|
||||
return raw.split(sep).map<T>(elm => elm as T)
|
||||
}
|
Loading…
Reference in New Issue
Block a user