* Добавлен компонент для получения Route по умолчанию

* Доблена страница "Доступ запрещён"
* Актуализирована работа с правами
* Добавлен переключатель оси X TVD
This commit is contained in:
Александр Сироткин 2022-02-07 14:58:38 +05:00
parent 439df9401a
commit 85f98044c6
31 changed files with 740 additions and 617 deletions

View File

@ -1,11 +1,13 @@
import React from 'react'
import { Role, Permission, hasPermission, isInRole } from '../../utils/permissions'
type PrivateContentProps = {
roles?: Role[] | Role
permission?: Permission
import { isURLAvailable } from '@utils/permissions'
export type PrivateContentProps = {
absolutePath: string
children?: React.ReactElement<any, any>
}
export const PrivateContent: React.FC<PrivateContentProps> = ({ permission, roles, children = null }) =>
hasPermission(permission) && isInRole(roles) ? children : null
export const PrivateContent: React.FC<PrivateContentProps> = ({ absolutePath, children = null }) =>
isURLAvailable(absolutePath) ? children : null
export default PrivateContent

View File

@ -0,0 +1,17 @@
import { memo } from 'react'
import { Redirect, Route, RouteProps } from 'react-router-dom'
import { isURLAvailable } from '@utils/permissions'
export type PrivateDefaultRouteProps = RouteProps & {
urls: string[]
elseRedirect?: string
}
export const PrivateDefaultRoute = memo<PrivateDefaultRouteProps>(({ elseRedirect, urls, ...other }) => (
<Route {...other} path={'/'}>
<Redirect to={{ pathname: urls.find((url) => isURLAvailable(url)) ?? elseRedirect ?? '/access_denied' }} />
</Route>
))
export default PrivateDefaultRoute

View File

@ -1,14 +1,34 @@
import React from 'react'
import { join } from 'path'
import { Menu, MenuItemProps } from 'antd'
import { Role, Permission, hasPermission, isInRole } from '../../utils/permissions'
import { memo, NamedExoticComponent } from 'react'
import { isURLAvailable } from '@utils/permissions'
import { Link } from 'react-router-dom'
type PrivateMenuItemProps = MenuItemProps & {
roles?: Role[] | Role
permission?: Permission
export type PrivateMenuItemProps = MenuItemProps & {
root: string
path: string
}
export const PrivateMenuItem: React.FC<PrivateMenuItemProps> = ({ roles, permission, ...props }) =>
hasPermission(permission) && isInRole(roles) ? <Menu.Item {...props}/> : null
export type PrivateMenuLinkProps = MenuItemProps & {
root?: string
path: string
title: string
}
export const PrivateMenuItemLink = memo<PrivateMenuLinkProps>(({ root = '', path, title, ...other }) => (
<PrivateMenuItem key={path} root={root} path={path} {...other}>
<Link to={join(root, path)}>{title}</Link>
</PrivateMenuItem>
))
export const PrivateMenuItem: NamedExoticComponent<PrivateMenuItemProps> & {
Link: NamedExoticComponent<PrivateMenuLinkProps>
} = Object.assign(memo<PrivateMenuItemProps>(({ root, path, ...other }) =>
isURLAvailable(join(root, path)) ? <Menu.Item key={path} {...other}/> : null
), {
Link: PrivateMenuItemLink
})
export default PrivateMenuItem

View File

@ -1,30 +1,32 @@
import { FC, ReactNode } from 'react'
import { Location } from 'history'
import { memo, ReactNode } from 'react'
import { Redirect, Route, RouteProps } from 'react-router-dom'
import { join } from 'path'
import { Role, Permission, hasPermission, isInRole } from '../../utils/permissions'
import { getUserToken } from '../../utils/storage'
import { isURLAvailable } from '@utils/permissions'
export type PrivateRouteProps = RouteProps & {
roles: Role[] | Role
permission?: Permission
root: string
path: string
children?: ReactNode
redirect?: (location?: Location<unknown>) => ReactNode
}
export const defaultRedirect = (location?: Location<unknown>) => (
<Redirect to={{ pathname: '/login', state: { from: location } }} />
<Redirect to={{ pathname: '/access_denied', state: { from: location } }} />
)
export const PrivateRoute: FC<PrivateRouteProps> = ({ permission, roles, component, children, redirect = defaultRedirect, ...other }) => {
const available = getUserToken() && (hasPermission(permission) && isInRole(roles))
export const PrivateRoute = memo<PrivateRouteProps>(({ root = '', path, component, children, redirect = defaultRedirect, ...other }) => {
const available = isURLAvailable(join(root, path))
return (
<Route {...other}
<Route
{...other}
path={path}
component={available ? component : undefined}
render={({ location }) => available ? children : redirect(location)}
/>
)
}
})
export default PrivateRoute

View File

@ -1,3 +1,9 @@
export { PrivateRoute } from './PrivateRoute'
export { PrivateRoute, defaultRedirect } from './PrivateRoute'
export { PrivateContent } from './PrivateContent'
export { PrivateMenuItem } from './PrivateMenuItem'
export { PrivateMenuItem, PrivateMenuItemLink } from './PrivateMenuItem'
export { PrivateDefaultRoute } from './PrivateDefaultRoute'
export type { PrivateRouteProps } from './PrivateRoute'
export type { PrivateContentProps } from './PrivateContent'
export type { PrivateMenuItemProps, PrivateMenuLinkProps } from './PrivateMenuItem'
export type { PrivateDefaultRouteProps } from './PrivateDefaultRoute'

View File

@ -1,18 +1,16 @@
import { MouseEventHandler, useState } from 'react'
import { memo, MouseEventHandler, useState } from 'react'
import { Link, useHistory } from 'react-router-dom'
import { Button, Dropdown, Menu } from 'antd'
import { Button, Dropdown, DropDownProps, Menu } from 'antd'
import { UserOutlined } from '@ant-design/icons'
import { getUserLogin, removeUser } from '../utils/storage'
import { getUserLogin, removeUser } from '@utils/storage'
import { PrivateMenuItem } from './Private'
import { ChangePassword } from './ChangePassword'
import { PrivateMenuItemLink } from './Private/PrivateMenuItem'
type UserMenuProps = {
isAdmin?: boolean
}
type UserMenuProps = Omit<DropDownProps, 'overlay'> & { isAdmin?: boolean }
export const UserMenu: React.FC<UserMenuProps> = ({ isAdmin }) => {
export const UserMenu = memo<UserMenuProps>(({ isAdmin, ...other }) => {
const [isModalVisible, setIsModalVisible] = useState<boolean>(false)
const history = useHistory()
@ -30,16 +28,15 @@ export const UserMenu: React.FC<UserMenuProps> = ({ isAdmin }) => {
return (
<>
<Dropdown
{...other}
placement={'bottomRight'}
overlay={(
<Menu style={{ textAlign: 'right' }}>
<PrivateMenuItem permission={'admin_panel'}>
{isAdmin ? (
<Link to={'/'}>Вернуться на сайт</Link>
) : (
<Link to={'/admin'}>Панель администратора</Link>
)}
</PrivateMenuItem>
{isAdmin ? (
<PrivateMenuItemLink path={'/'} title={'Вернуться на сайт'}/>
) : (
<PrivateMenuItemLink path={'/admin'} title={'Панель администратора'}/>
)}
<Menu.Item>
<Link to={'/'} onClick={onChangePasswordClick}>Сменить пароль</Link>
</Menu.Item>
@ -58,4 +55,4 @@ export const UserMenu: React.FC<UserMenuProps> = ({ isAdmin }) => {
/>
</>
)
}
})

View File

@ -0,0 +1,15 @@
import { memo } from 'react'
export const AccessDenied = memo(() => (
<div style={{
width: '100vw',
height: '100vh',
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}>
Доступ запрещён
</div>
))
export default AccessDenied

View File

@ -1,7 +1,5 @@
import { useEffect, useState } from 'react'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import LoaderPortal from '../../components/LoaderPortal'
import {
EditableTable,
makeColumn,
@ -9,10 +7,13 @@ import {
makeActionHandler,
makeStringSorter,
defaultPagination
} from '../../components/Table'
import { AdminClusterService, AdminDepositService } from '../../services/api'
import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules'
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminClusterService, AdminDepositService } from '@api'
import { arrayOrDefault } from '@utils'
import { min1 } from '@utils/validationRules'
import { hasPermission } from '@utils/permissions'
import { coordsFixed } from './DepositController'
@ -73,9 +74,9 @@ export const ClusterController = () => {
dataSource={clusters}
columns={clusterColumns}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('AdminCluster.edit') && makeActionHandler('insert', handlerProps)}
onRowEdit={hasPermission('AdminCluster.edit') && makeActionHandler('put', handlerProps)}
onRowDelete={hasPermission('AdminCluster.delete') && makeActionHandler('delete', handlerProps)}
/>
</LoaderPortal>
)

View File

@ -1,7 +1,5 @@
import { useEffect, useState } from 'react'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import LoaderPortal from '../../components/LoaderPortal'
import {
EditableTable,
makeColumn,
@ -9,10 +7,13 @@ import {
makeStringSorter,
makeSelectColumn,
defaultPagination
} from '../../components/Table'
import { AdminCompanyService, AdminCompanyTypeService } from '../../services/api'
import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules'
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminCompanyService, AdminCompanyTypeService } from '@api'
import { arrayOrDefault } from '@utils'
import { min1 } from '@utils/validationRules'
import { hasPermission } from '@utils/permissions'
export const CompanyController = () => {
@ -70,9 +71,9 @@ export const CompanyController = () => {
columns={columns}
dataSource={companies}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('AdminCompany.edit') && makeActionHandler('insert', handlerProps)}
onRowEdit={hasPermission('AdminCompany.edit') && makeActionHandler('put', handlerProps)}
onRowDelete={hasPermission('AdminCompany.delete') && makeActionHandler('delete', handlerProps)}
/>
</LoaderPortal>
)

View File

@ -1,17 +1,18 @@
import { useEffect, useState } from 'react'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import LoaderPortal from '../../components/LoaderPortal'
import {
EditableTable,
makeColumn,
makeActionHandler,
makeStringSorter,
defaultPagination
} from '../../components/Table'
import { AdminCompanyTypeService } from '../../services/api'
import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules'
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminCompanyTypeService } from '@api'
import { arrayOrDefault } from '@utils'
import { min1 } from '@utils/validationRules'
import { hasPermission } from '@asb/utils/permissions'
const columns = [
makeColumn('Название', 'caption', {
@ -52,9 +53,9 @@ export const CompanyTypeController = () => {
columns={columns}
dataSource={companyTypes}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('AdminCompanyType.edit') && makeActionHandler('insert', handlerProps)}
onRowEdit={hasPermission('AdminCompanyType.edit') && makeActionHandler('put', handlerProps)}
onRowDelete={hasPermission('AdminCompanyType.delete') && makeActionHandler('delete', handlerProps)}
/>
</LoaderPortal>
)

View File

@ -1,11 +1,12 @@
import { useEffect, useState } from 'react'
import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { EditableTable, makeColumn, makeActionHandler, defaultPagination } from '../../components/Table'
import { AdminDepositService } from '../../services/api'
import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { EditableTable, makeColumn, makeActionHandler, defaultPagination } from '@components/Table'
import { AdminDepositService } from '@api'
import { arrayOrDefault } from '@utils'
import { min1 } from '@utils/validationRules'
import { hasPermission } from '@utils/permissions'
export const coordsFixed = (coords) => coords && isFinite(coords) ? (+coords).toPrecision(10) : '-'
@ -45,9 +46,9 @@ export const DepositController = () => {
dataSource={deposits}
columns={depositColumns}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('AdminDeposit.edit') && makeActionHandler('insert', handlerProps)}
onRowEdit={hasPermission('AdminDeposit.edit') && makeActionHandler('put', handlerProps)}
onRowDelete={hasPermission('AdminDeposit.delete') && makeActionHandler('delete', handlerProps)}
/>
</LoaderPortal>
)

View File

@ -1,16 +1,17 @@
import { useEffect, useState } from 'react'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import LoaderPortal from '../../components/LoaderPortal'
import {
EditableTable,
makeActionHandler,
makeColumn,
makeStringSorter
} from '../../components/Table'
import { AdminPermissionService } from '../../services/api'
import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules'
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminPermissionService } from '@api'
import { arrayOrDefault } from '@utils'
import { min1 } from '@utils/validationRules'
import { hasPermission } from '@utils/permissions'
const columns = [
makeColumn('Название', 'name', {
@ -55,9 +56,9 @@ export const PermissionController = () => {
columns={columns}
dataSource={permissions}
pagination={{ showSizeChanger: true }}
onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('AdminPermission.edit') && makeActionHandler('insert', handlerProps)}
onRowEdit={hasPermission('AdminPermission.edit') && makeActionHandler('put', handlerProps)}
onRowDelete={hasPermission('AdminPermission.delete') && makeActionHandler('delete', handlerProps)}
/>
</LoaderPortal>
)

View File

@ -1,12 +1,13 @@
import { memo, useEffect, useState } from 'react'
import LoaderPortal from '../../components/LoaderPortal'
import { PermissionView, RoleView } from '../../components/views'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { EditableTable, makeActionHandler, makeColumn, makeTagColumn } from '../../components/Table'
import { AdminPermissionService, AdminUserRoleService } from '../../services/api'
import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules'
import LoaderPortal from '@components/LoaderPortal'
import { PermissionView, RoleView } from '@components/views'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { EditableTable, makeActionHandler, makeColumn, makeTagColumn } from '@components/Table'
import { AdminPermissionService, AdminUserRoleService } from '@api'
import { arrayOrDefault } from '@utils'
import { min1 } from '@utils/validationRules'
import { hasPermission } from '@utils/permissions'
export const RoleController = memo(() => {
const [permissions, setPermissions] = useState([])
@ -62,9 +63,9 @@ export const RoleController = memo(() => {
size={'small'}
columns={columns}
dataSource={roles}
onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('AdminUserRole.edit') && makeActionHandler('insert', handlerProps)}
onRowEdit={hasPermission('AdminUserRole.edit') && makeActionHandler('put', handlerProps)}
onRowDelete={hasPermission('AdminUserRole.delete') && makeActionHandler('delete', handlerProps)}
/>
</LoaderPortal>
)

View File

@ -2,10 +2,6 @@ import { Button, Tag } from 'antd'
import { UserSwitchOutlined } from '@ant-design/icons'
import { useEffect, useState } from 'react'
import { RoleView } from '../../../components/views'
import LoaderPortal from '../../../components/LoaderPortal'
import { ChangePassword } from '../../../components/ChangePassword'
import { invokeWebApiWrapperAsync } from '../../../components/factory'
import {
EditableTable,
makeColumn,
@ -14,14 +10,19 @@ import {
makeStringSorter,
makeNumericSorter,
defaultPagination
} from '../../../components/Table'
import { AdminCompanyService, AdminUserRoleService, AdminUserService } from '../../../services/api'
import { createLoginRules, nameRules, phoneRules, emailRules } from '../../../utils/validationRules'
import { arrayOrDefault } from '../../../utils'
} from '@components/Table'
import { RoleView } from '@components/views'
import LoaderPortal from '@components/LoaderPortal'
import { ChangePassword } from '@components/ChangePassword'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { AdminCompanyService, AdminUserRoleService, AdminUserService } from '@api'
import { createLoginRules, nameRules, phoneRules, emailRules } from '@utils/validationRules'
import { makeTextOnFilter, makeTextFilters } from '@utils/table'
import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils'
import RoleTag from './RoleTag'
import { makeTextOnFilter, makeTextFilters } from '../../../utils/table'
export const UserController = () => {
const [users, setUsers] = useState([])
@ -153,9 +154,9 @@ export const UserController = () => {
bordered
columns={columns}
dataSource={users}
onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('AdminUser.edit') && makeActionHandler('insert', handlerProps)}
onRowEdit={hasPermission('AdminUser.edit') && makeActionHandler('put', handlerProps)}
onRowDelete={hasPermission('AdminUser.delete') && makeActionHandler('delete', handlerProps)}
additionalButtons={additionalButtons}
buttonsWidth={120}
pagination={defaultPagination}

View File

@ -3,26 +3,20 @@ import { Select } from 'antd'
import { getTelemetryLabel } from '@components/views'
export const TelemetrySelect = memo(({ telemetry, value, onChange }) => {
const onSelectChange = (id) => {
onChange?.(telemetry.find((row) => row.id === id))
}
return (
<Select
value={value?.id}
onChange={onSelectChange}
dropdownClassName={'telemetry_select'}
>
{telemetry.map((row, i) => (
<Select.Option key={i} value={row.id}>
<span className={row?.info?.well ? 'telemetry_used' : 'telemetry_unused'}>
{getTelemetryLabel(row)}
</span>
</Select.Option>
))}
</Select>
)
})
export const TelemetrySelect = memo(({ telemetry, value, onChange }) => (
<Select
value={value?.id}
onChange={(id) => onChange?.(telemetry.find((row) => row.id === id))}
dropdownClassName={'telemetry_select'}
>
{telemetry.map((row, i) => (
<Select.Option key={i} value={row.id}>
<span className={row?.info?.well ? 'telemetry_used' : 'telemetry_unused'}>
{getTelemetryLabel(row)}
</span>
</Select.Option>
))}
</Select>
))
export default TelemetrySelect

View File

@ -2,9 +2,12 @@ import { useEffect, useState } from 'react'
import { Button } from 'antd'
import { CopyOutlined } from '@ant-design/icons'
import LoaderPortal from '../../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../../components/factory'
import { TelemetryView, CompanyView } from '../../../components/views'
import {
AdminClusterService,
AdminCompanyService,
AdminTelemetryService,
AdminWellService,
} from '@api'
import {
EditableTable,
makeColumn,
@ -14,19 +17,17 @@ import {
makeNumericSorter,
makeTagColumn,
defaultPagination,
} from '../../../components/Table'
import {
AdminClusterService,
AdminCompanyService,
AdminTelemetryService,
AdminWellService,
} from '../../../services/api'
import { arrayOrDefault } from '../../../utils'
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { TelemetryView, CompanyView } from '@components/views'
import { arrayOrDefault } from '@utils'
import { coordsFixed } from '../DepositController'
import TelemetrySelect from './TelemetrySelect'
import '../../../styles/admin.css'
import '@styles/admin.css'
import { hasPermission } from '@asb/utils/permissions'
const wellTypes = [
{ value: 1, label: 'Наклонно-направленная' },
@ -124,9 +125,9 @@ export const WellController = () => {
columns={columns}
dataSource={wells}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps, recordParser)}
onRowEdit={makeActionHandler('put', handlerProps, recordParser)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('AdminWell.edit') && makeActionHandler('insert', handlerProps, recordParser)}
onRowEdit={hasPermission('AdminWell.edit') && makeActionHandler('put', handlerProps, recordParser)}
onRowDelete={hasPermission('AdminWell.delete') && makeActionHandler('delete', handlerProps)}
//additionalButtons={addititonalButtons}
buttonsWidth={95}
/>

View File

@ -1,76 +1,68 @@
import { Layout, Menu } from 'antd'
import { lazy, Suspense } from 'react'
import { Switch, Link, useParams, Redirect, Route } from 'react-router-dom'
import { PrivateMenuItem, PrivateRoute } from '../../components/Private'
import { SuspenseFallback } from '../SuspenseFallback'
import { Switch, useParams } from 'react-router-dom'
const ClusterController = lazy(() => import('./ClusterController'))
const CompanyController = lazy(() => import('./CompanyController'))
const DepositController = lazy(() => import('./DepositController'))
const UserController = lazy(() => import('./UserController'))
const WellController = lazy(() => import('./WellController'))
const RoleController = lazy(() => import('./RoleController'))
import { PrivateMenuItem, PrivateRoute, PrivateDefaultRoute } from '@components/Private'
import { SuspenseFallback } from '@pages/SuspenseFallback'
const ClusterController = lazy(() => import( './ClusterController'))
const CompanyController = lazy(() => import( './CompanyController'))
const DepositController = lazy(() => import( './DepositController'))
const UserController = lazy(() => import( './UserController'))
const WellController = lazy(() => import( './WellController'))
const RoleController = lazy(() => import( './RoleController'))
const CompanyTypeController = lazy(() => import('./CompanyTypeController'))
const PermissionController = lazy(() => import('./PermissionController'))
const TelemetryController = lazy(() => import('./TelemetryController'))
const VisitLog = lazy(() => import('./VisitLog'))
const PermissionController = lazy(() => import( './PermissionController'))
const TelemetryController = lazy(() => import( './TelemetryController'))
const VisitLog = lazy(() => import( './VisitLog'))
const rootPath = '/admin'
export const AdminPanel = () => {
const { tab } = useParams()
const rootPath = '/admin'
return (
<Layout>
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]}>
<PrivateMenuItem key={'deposit'} permission={'deposit_editor'}>
<Link to={`${rootPath}/deposit`}>Месторождения</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'cluster'} permission={'cluster_editor'}>
<Link to={`${rootPath}/cluster`}>Кусты</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'well'} permission={'well_editor'}>
<Link to={`${rootPath}/well`}>Скважины</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'user'} permission={'user_editor'}>
<Link to={`${rootPath}/user`}>Пользователи</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'company'} permission={'company_editor'}>
<Link to={`${rootPath}/company`}>Компании</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'company_type'}>
<Link to={`${rootPath}/company_type`}>Типы компаний</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'role'} permission={'role_editor'}>
<Link to={`${rootPath}/role`}>Роли</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'permission'} permission={'permission_editor'}>
<Link to={`${rootPath}/permission`}>Разрешения</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'telemetry'}>
<Link to={`${rootPath}/telemetry`}>Телеметрия</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'visit_log'}>
<Link to={`${rootPath}/visit_log`}>Журнал посещений</Link>
</PrivateMenuItem>
<PrivateMenuItem.Link root={rootPath} key={'deposit' } path={'deposit' } title={'Месторождения' } />
<PrivateMenuItem.Link root={rootPath} key={'cluster' } path={'cluster' } title={'Кусты' } />
<PrivateMenuItem.Link root={rootPath} key={'well' } path={'well' } title={'Скважины' } />
<PrivateMenuItem.Link root={rootPath} key={'user' } path={'user' } title={'Пользователи' } />
<PrivateMenuItem.Link root={rootPath} key={'company' } path={'company' } title={'Компании' } />
<PrivateMenuItem.Link root={rootPath} key={'company_type'} path={'company_type'} title={'Типы компаний' } />
<PrivateMenuItem.Link root={rootPath} key={'role' } path={'role' } title={'Роли' } />
<PrivateMenuItem.Link root={rootPath} key={'permission' } path={'permission' } title={'Разрешения' } />
<PrivateMenuItem.Link root={rootPath} key={'telemetry' } path={'telemetry' } title={'Телеметрии' } />
<PrivateMenuItem.Link root={rootPath} key={'visit_log' } path={'visit_log' } title={'Журнал посещений'} />
</Menu>
<Layout>
<Layout.Content className={'site-layout-background'}>
<Suspense fallback={<SuspenseFallback />}>
<Switch>
<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 path={`${rootPath}/deposit` } component={ DepositController} />
<PrivateRoute path={`${rootPath}/cluster` } component={ ClusterController} />
<PrivateRoute path={`${rootPath}/well` } component={ WellController} />
<PrivateRoute path={`${rootPath}/user` } component={ UserController} />
<PrivateRoute path={`${rootPath}/company` } component={ CompanyController} />
<PrivateRoute path={`${rootPath}/company_type`} component={CompanyTypeController} />
<PrivateRoute permission={ 'role_editor'} path={`${rootPath}/role` } component={ RoleController} />
<PrivateRoute permission={'permission_editor'} path={`${rootPath}/permission`} component={PermissionController} />
<PrivateRoute path={`${rootPath}/telemetry`} component={TelemetryController} />
<PrivateRoute path={`${rootPath}/visit_log`} component={VisitLog} />
<Route path={'/'}>
<Redirect to={`${rootPath}/visit_log`}/>
</Route>
<PrivateRoute path={`${rootPath}/role` } component={ RoleController} />
<PrivateRoute path={`${rootPath}/permission` } component={ PermissionController} />
<PrivateRoute path={`${rootPath}/telemetry` } component={ TelemetryController} />
<PrivateRoute path={`${rootPath}/visit_log` } component={VisitLog} />
<PrivateDefaultRoute urls={[
`${rootPath}/deposit`,
`${rootPath}/cluster`,
`${rootPath}/well`,
`${rootPath}/user`,
`${rootPath}/company`,
`${rootPath}/company_type`,
`${rootPath}/role`,
`${rootPath}/permission`,
`${rootPath}/telemetry`,
`${rootPath}/visit_log`,
]}/>
</Switch>
</Suspense>
</Layout.Content>

View File

@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'
import { DatePicker, Button, Input } from 'antd'
import { FileService } from '@api'
import { hasPermission } from '@utils/permissions'
import LoaderPortal from '@components/LoaderPortal'
import { UploadForm } from '@components/UploadForm'
import { CompanyView, UserView } from '@components/views'
@ -145,15 +146,17 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
</datalist>
</div>
&nbsp;&nbsp;
<div>
<span>Загрузка</span>
<UploadForm
url={uploadUrl}
accept={accept}
onUploadStart={() => setShowLoader(true)}
onUploadComplete={handleUploadComplete}
/>
</div>
{hasPermission(`File.edit${idCategory}`) && (
<div>
<span>Загрузка</span>
<UploadForm
url={uploadUrl}
accept={accept}
onUploadStart={() => setShowLoader(true)}
onUploadComplete={handleUploadComplete}
/>
</div>
)}
&nbsp;&nbsp;
{headerChild}
</div>
@ -167,7 +170,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
showSizeChanger: false,
onChange: (page) => setPage(page),
}}
onRowDelete={handleFileDelete}
onRowDelete={hasPermission(`File.edit${idCategory}`) && handleFileDelete}
rowKey={(record) => record.id}
/>
</LoaderPortal>

View File

@ -1,10 +1,10 @@
import path from 'path'
import { memo } from 'react'
import { Layout, Menu } from 'antd'
import { FolderOutlined } from '@ant-design/icons'
import { Switch, useParams, Link, Route } from 'react-router-dom'
import { PrivateMenuItem } from '../../components/Private'
import { isInRole } from '../../utils/permissions'
import { Switch, useParams } from 'react-router-dom'
import { PrivateDefaultRoute, PrivateMenuItem, PrivateRoute } from '@components/Private'
import DocumentsTemplate from './DocumentsTemplate'
const { Content } = Layout
@ -22,45 +22,33 @@ export const documentCategories = [
{ id: 9, key: 'closingService', title: 'Сервис по заканчиванию скважины' },
]
const getUserCategories = () => documentCategories.filter(cat => isInRole(cat.roles))
export const makeMenuItems = (root, cats) => (cats ?? getUserCategories()).map(category => (
<PrivateMenuItem
key={`${category.key}`}
className={'ant-menu-item'}
icon={<FolderOutlined/>}
permission={category.permission}
roles={category.roles}
>
<Link to={{ pathname: `${root}/${category.key}` }}>{category.title}</Link>
</PrivateMenuItem>
))
export const MenuDocuments = memo(({ idWell }) => {
const { category } = useParams()
const rootPath = `/well/${idWell}`
const root = path.join(rootPath, '/document')
const categories = getUserCategories()
const root = `/well/${idWell}/document`
return (
<>
<Menu
mode={'horizontal'}
selectable={true}
className={'well_menu'}
selectedKeys={[category]}
>
{makeMenuItems(root, categories)}
<Menu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[category]}>
{documentCategories.map(category => (
<PrivateMenuItem.Link
root={root}
key={`${category.key}`}
path={`${category.key}`}
className={'ant-menu-item'}
icon={<FolderOutlined/>}
title={category.title}
/>
))}
</Menu>
<Layout>
<Content className={'site-layout-background'}>
<Switch>
{categories.map(category => (
<Route path={`${root}/${category.key}`} key={`${category.key}`}>
{documentCategories.map(category => (
<PrivateRoute path={`${root}/${category.key}`} key={`${category.key}`}>
<DocumentsTemplate idCategory={category.id} idWell={idWell}/>
</Route>
</PrivateRoute>
))}
<PrivateDefaultRoute urls={documentCategories.map((cat) => `${root}/${cat.key}`)}/>
</Switch>
</Content>
</Layout>

View File

@ -1,37 +1,42 @@
import { Redirect, Route, Switch } from 'react-router-dom'
import { PrivateRoute } from '../components/Private'
import { AdminLayoutPortal, LayoutPortal } from '../components/Layout'
import Deposit from './Deposit'
import Cluster from './Cluster'
import Well from './Well'
import AdminPanel from './AdminPanel'
import { memo } from 'react'
import { Route, Switch } from 'react-router-dom'
export const Main = () => (
import { AdminLayoutPortal, LayoutPortal } from '@components/Layout'
import { PrivateDefaultRoute, PrivateRoute } from '@components/Private'
import Well from './Well'
import Cluster from './Cluster'
import Deposit from './Deposit'
import AdminPanel from './AdminPanel'
import AccessDenied from './AccessDenied'
export const Main = memo(() => (
<Switch>
<PrivateRoute path={'/admin/:tab?'} permission={'admin_panel'}>
<PrivateRoute path={'/admin/:tab?'}>
<AdminLayoutPortal title={'Администраторская панель'}>
<AdminPanel />
</AdminLayoutPortal>
</PrivateRoute>
<Route path={'/deposit'}>
<PrivateRoute path={'/deposit'}>
<LayoutPortal noSheet title='Месторождение'>
<Deposit />
</LayoutPortal>
</Route>
<Route path={'/cluster/:idClaster/:tab?'}>
</PrivateRoute>
<PrivateRoute path={'/cluster/:idCluster'}>
<LayoutPortal title={'Анализ скважин куста'}>
<Cluster />
</LayoutPortal>
</Route>
<Route path={'/well/:idWell/:tab?'}>
</PrivateRoute>
<PrivateRoute path={'/well/:idWell/:tab?'}>
<LayoutPortal>
<Well />
</LayoutPortal>
</PrivateRoute>
<Route path={'/access_denied'}>
<AccessDenied />
</Route>
<Route path={'/'}>
<Redirect to={{ pathname: `/deposit` }} />
</Route>
<PrivateDefaultRoute urls={['/deposit']} />
</Switch>
)
))
export default Main

View File

@ -1,56 +1,57 @@
import {Layout, Menu} from "antd";
import {Switch, Link, Route, useParams, Redirect} from "react-router-dom";
import { FolderOutlined } from "@ant-design/icons";
import TelemetryAnalysisOperationsSummary from "./TelemetryAnalysisOperationsSummary";
import TelemetryAnalysisOperationsToInterval from "./TelemetryAnalysisOperationsToInterval";
import { memo } from 'react'
import { Layout, Menu } from 'antd'
import { FolderOutlined } from '@ant-design/icons'
import { Switch, useParams } from 'react-router-dom'
import { PrivateDefaultRoute, PrivateRoute } from '@components/Private'
import { PrivateMenuItemLink } from '@components/Private/PrivateMenuItem'
import TelemetryAnalysisDepthToDay from './TelemetryAnalysisDepthToDay'
import TelemetryAnalysisDepthToInterval from './TelemetryAnalysisDepthToInterval'
import TelemetryAnalysisOperationsSummary from './TelemetryAnalysisOperationsSummary'
import TelemetryAnalysisOperationsToInterval from './TelemetryAnalysisOperationsToInterval'
const { Content } = Layout
export default function TelemetryAnalysis({idWell}) {
let {tab} = useParams()
const rootPath = `/well/${idWell}/telemetryAnalysis`;
export const TelemetryAnalysis = memo(({ idWell }) => {
const { tab } = useParams()
const rootPath = `/well/${idWell}/telemetryAnalysis`
return (<>
<Menu
mode={'horizontal'}
selectable={true}
className={'well_menu'}
selectedKeys={[tab]}>
<Menu.Item key={'depthToDay'} icon={<FolderOutlined />}>
<Link to={`${rootPath}/depthToDay`}>Глубина-день</Link>
</Menu.Item>
<Menu.Item key={'depthToInterval'} icon={<FolderOutlined />}>
<Link to={`${rootPath}/depthToInterval`}>Глубина-интервал</Link>
</Menu.Item>
<Menu.Item key={'operationsSummary'} icon={<FolderOutlined />}>
<Link to={`${rootPath}/operationsSummary`}>Все операции</Link>
</Menu.Item>
<Menu.Item key={'operationsToInterval'} icon={<FolderOutlined />}>
<Link to={`${rootPath}/operationsToInterval`}>Операции-интервал</Link>
</Menu.Item>
</Menu>
<Layout>
<Content className={'site-layout-background'}>
<Switch>
<Route path={`${rootPath}/depthToDay`}>
<TelemetryAnalysisDepthToDay idWell={idWell}/>
</Route>
<Route path={`${rootPath}/depthToInterval`}>
<TelemetryAnalysisDepthToInterval idWell={idWell}/>
</Route>
<Route path={`${rootPath}/operationsSummary`}>
<TelemetryAnalysisOperationsSummary idWell={idWell}/>
</Route>
<Route path={`${rootPath}/operationsToInterval`}>
<TelemetryAnalysisOperationsToInterval idWell={idWell}/>
</Route>
<Route path={`/`}>
<Redirect to={`${rootPath}/depthToDay`} />
</Route>
</Switch>
</Content>
</Layout>
</>)
}
return (
<>
<Menu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}>
<PrivateMenuItemLink root={rootPath} icon={<FolderOutlined />} key={'depthToDay'} path={'depthToDay'} title={'Глубина-день'} />
<PrivateMenuItemLink root={rootPath} icon={<FolderOutlined />} key={'depthToInterval'} path={'depthToInterval'} title={'Глубина-интервал'} />
<PrivateMenuItemLink root={rootPath} icon={<FolderOutlined />} key={'operationsSummary'} path={'operationsSummary'} title={'Все операции'} />
<PrivateMenuItemLink root={rootPath} icon={<FolderOutlined />} key={'operationsToInterval'} path={'operationsToInterval'} title={'Операции-интервал'} />
</Menu>
<Layout>
<Content className={'site-layout-background'}>
<Switch>
<PrivateRoute root={rootPath} path={'depthToDay'}>
<TelemetryAnalysisDepthToDay idWell={idWell}/>
</PrivateRoute>
<PrivateRoute root={rootPath} path={'depthToInterval'}>
<TelemetryAnalysisDepthToInterval idWell={idWell}/>
</PrivateRoute>
<PrivateRoute root={rootPath} path={'operationsSummary'}>
<TelemetryAnalysisOperationsSummary idWell={idWell}/>
</PrivateRoute>
<PrivateRoute root={rootPath} path={'operationsToInterval'}>
<TelemetryAnalysisOperationsToInterval idWell={idWell}/>
</PrivateRoute>
<PrivateDefaultRoute urls={[
`${rootPath}/depthToDay`,
`${rootPath}/depthToInterval`,
`${rootPath}/operationsSummary`,
`${rootPath}/operationsToInterval`,
]}/>
</Switch>
</Content>
</Layout>
</>
)
})
export default TelemetryAnalysis

View File

@ -1,4 +1,4 @@
import { Layout, Menu } from 'antd'
import { memo } from 'react'
import {
FolderOutlined,
FundViewOutlined,
@ -8,9 +8,10 @@ import {
ExperimentOutlined,
FundProjectionScreenOutlined,
} from '@ant-design/icons'
import { Link, Redirect, Route, Switch, useParams } from 'react-router-dom'
import { Layout, Menu } from 'antd'
import { Switch, useParams } from 'react-router-dom'
import { PrivateMenuItem } from '@components/Private'
import { PrivateRoute, PrivateDefaultRoute, PrivateMenuItem } from '@components/Private'
import Report from './Report'
import Archive from './Archive'
@ -18,102 +19,76 @@ import Measure from './Measure'
import Messages from './Messages'
import Documents from './Documents'
import TelemetryView from './TelemetryView'
import { makeMenuItems } from './Documents'
import WellOperations from './WellOperations'
import DrillingProgram from './DrillingProgram'
import TelemetryAnalysis from './TelemetryAnalysis'
const { Content } = Layout
const { SubMenu } = Menu
export const Well = () => {
export const Well = memo(() => {
const { idWell, tab } = useParams()
const rootPath = `/well/${idWell}`
return (
<Layout>
<Menu
mode={'horizontal'}
selectable={true}
selectedKeys={[tab]}
className={'well_menu'}
>
<PrivateMenuItem key={'telemetry'} icon={<FundViewOutlined />}>
<Link to={`${rootPath}/telemetry`}>Мониторинг</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'message'} icon={<AlertOutlined/>}>
<Link to={`${rootPath}/message`}>Сообщения</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'report'} icon={<FilePdfOutlined />}>
<Link to={`${rootPath}/report`}>Рапорт</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'operations'} icon={<FolderOutlined />}>
<Link to={`${rootPath}/operations`}>Операции по скважине</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'archive'} icon={<DatabaseOutlined />}>
<Link to={`${rootPath}/archive`}>Архив</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'telemetryAnalysis'} icon={<FundProjectionScreenOutlined />} roles={'admin'}>
<Link to={`${rootPath}/telemetryAnalysis/depthToDay`}>Операции по телеметрии</Link>
</PrivateMenuItem>
<SubMenu
key={'document'}
title={
<Link to={`${rootPath}/document/fluidService`} className={'linkDocuments'}>
Документы
</Link>
}
icon={<FolderOutlined />}
selectable={true}
>
{makeMenuItems(`${rootPath}/document`)}
</SubMenu>
<PrivateMenuItem key={'measure'} icon={<ExperimentOutlined />}>
<Link to={`${rootPath}/measure`}>Измерения</Link>
</PrivateMenuItem>
<PrivateMenuItem key={'drillingProgram'} icon={<FolderOutlined />}>
<Link to={`${rootPath}/drillingProgram`}>Программа бурения</Link>
</PrivateMenuItem>
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
<PrivateMenuItem.Link root={rootPath} key={'telemetry'} path={'telemetry'} icon={<FundViewOutlined />} title={'Мониторинг'}/>
<PrivateMenuItem.Link root={rootPath} key={'message'} path={'message'} icon={<AlertOutlined/>} title={'Сообщения'} />
<PrivateMenuItem.Link root={rootPath} key={'report'} path={'report'} icon={<FilePdfOutlined />} title={'Рапорт'} />
<PrivateMenuItem.Link root={rootPath} key={'operations'} path={'operations'} icon={<FolderOutlined />} title={'Операции по скважине'} />
<PrivateMenuItem.Link root={rootPath} key={'archive'} path={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
<PrivateMenuItem.Link root={rootPath} key={'telemetryAnalysis'} path={'telemetryAnalysis'} icon={<FundProjectionScreenOutlined />} title={'Операции по телеметрии'} />
<PrivateMenuItem.Link root={rootPath} key={'document'} path={'document'} icon={<FolderOutlined />} title={'Документы'} />
<PrivateMenuItem.Link root={rootPath} key={'measure'} path={'measure'} icon={<ExperimentOutlined />} title={'Измерения'} />
<PrivateMenuItem.Link root={rootPath} key={'drillingProgram'} path={'drillingProgram'} icon={<FolderOutlined />} title={'Программа бурения'} />
</Menu>
<Layout>
<Content className={'site-layout-background'}>
<Switch>
<Route path={'/well/:idWell/telemetry'}>
<PrivateRoute path={`${rootPath}/telemetry`}>
<TelemetryView idWell={idWell} />
</Route>
<Route path={'/well/:idWell/message'}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/message`}>
<Messages idWell={idWell} />
</Route>
<Route path={'/well/:idWell/report'}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/report`}>
<Report idWell={idWell} />
</Route>
<Route path={'/well/:idWell/operations/:tab?'}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/operations/:tab?`}>
<WellOperations idWell={idWell} />
</Route>
<Route path={'/well/:idWell/archive'}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/archive`}>
<Archive idWell={idWell} />
</Route>
<Route path={'/well/:idWell/telemetryAnalysis/:tab'}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/telemetryAnalysis/:tab`}>
<TelemetryAnalysis idWell={idWell} />
</Route>
<Route path={'/well/:idWell/document/:category'}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/document/:category`}>
<Documents idWell={idWell} />
</Route>
<Route path={'/well/:id/measure'}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/measure`}>
<Measure idWell={idWell}/>
</Route>
<Route path={'/well/:id/drillingProgram'}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/drillingProgram`}>
<DrillingProgram idWell={idWell}/>
</Route>
<Route path={'/'}>
<Redirect to={`${rootPath}/telemetry`} />
</Route>
</PrivateRoute>
<PrivateDefaultRoute urls={[
`${rootPath}/telemetry`,
`${rootPath}/message`,
`${rootPath}/report`,
`${rootPath}/operations`,
`${rootPath}/archive`,
`${rootPath}/telemetryAnalysis`,
`${rootPath}/document`,
`${rootPath}/measure`,
`${rootPath}/drillingProgram`,
]}/>
</Switch>
</Content>
</Layout>
</Layout>
)
}
})
export default Well

View File

@ -1,13 +1,15 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, memo } from 'react'
import {
EditableTable,
makeNumericMinMax,
makeNumericStartEnd,
} from '../../components/Table'
import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { DrillFlowChartService } from '../../services/api'
import { arrayOrDefault } from '../../utils'
} from '@components/Table'
import { DrillFlowChartService } from '@api'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils'
const columns = [
makeNumericStartEnd('Глубина, м', 'depth'),
@ -18,7 +20,7 @@ const columns = [
makeNumericMinMax('Расход, л/с', 'flow')
]
export const DrillProcessFlow = ({ idWell }) => {
export const DrillProcessFlow = memo(({ idWell }) => {
const [flows, setFlows] = useState([])
const [showLoader, setShowLoader] = useState(false)
@ -59,11 +61,13 @@ export const DrillProcessFlow = ({ idWell }) => {
bordered
columns={columns}
dataSource={flows}
onRowAdd={onAdd}
onRowEdit={onEdit}
onRowDelete={onDelete}
onRowAdd={hasPermission('DrillFlowChart.edit') && onAdd}
onRowEdit={hasPermission('DrillFlowChart.edit') && onEdit}
onRowDelete={hasPermission('DrillFlowChart.delete') && onDelete}
pagination={false}
/>
</LoaderPortal>
)
}
})
export default DrillProcessFlow

View File

@ -1,12 +1,14 @@
import { memo, useState } from 'react'
import { Button, Tooltip, Modal } from 'antd'
import {useState} from 'react'
import { FileOutlined, ImportOutlined, ExportOutlined } from '@ant-design/icons'
import { download } from '../../components/factory'
import { download } from '@components/factory'
import { ImportOperations } from './ImportOperations'
const style = {margin:4}
const style = { margin: 4 }
export const ImportExportBar = ({idWell, onImported, disabled}) =>{
export const ImportExportBar = memo(({ idWell, onImported, disabled }) => {
const [isImportModalVisible, setIsImportModalVisible] = useState(false)
const downloadTemplate = async () => download(`/api/well/${idWell}/wellOperations/template`)
@ -14,44 +16,41 @@ export const ImportExportBar = ({idWell, onImported, disabled}) =>{
const onDone = () => {
setIsImportModalVisible(false)
if(onImported)
onImported()
onImported?.()
}
return <>
<Tooltip title = 'Импорт - загрузить файл с операциями на сервер'>
<Button
disabled={disabled}
icon={<ImportOutlined/>}
style={style}
onClick={_=>setIsImportModalVisible(true)}/>
</Tooltip>
return (
<>
<Tooltip title={'Импорт - загрузить файл с операциями на сервер'}>
<Button
disabled={disabled}
icon={<ImportOutlined/>}
style={style}
onClick={() => setIsImportModalVisible(true)}/>
</Tooltip>
<Tooltip title = 'Экспорт - скачать файл с операциями по скважине'>
<Button
disabled={disabled}
icon={<ExportOutlined/>}
style={style}
onClick={downloadExport}/>
</Tooltip>
<Tooltip title={'Экспорт - скачать файл с операциями по скважине'}>
<Button
disabled={disabled}
icon={<ExportOutlined/>}
style={style}
onClick={downloadExport}/>
</Tooltip>
<Tooltip title = 'Скачать шаблон для импорта операций'>
<Button
disabled={disabled}
icon={<FileOutlined/>}
style={style}
onClick={downloadTemplate}/>
</Tooltip>
<Tooltip title={'Скачать шаблон для импорта операций'}>
<Button disabled={disabled} icon={<FileOutlined />} style={style} onClick={downloadTemplate} />
</Tooltip>
<Modal
title='Импорт операций'
visible={isImportModalVisible}
onCancel={_=>setIsImportModalVisible(false)}
<Modal
title={'Импорт операций'}
visible={isImportModalVisible}
onCancel={() => setIsImportModalVisible(false)}
footer={null}
>
<ImportOperations idWell={idWell} onDone={onDone} />
</Modal>
</>
)
})
footer={null}>
<ImportOperations
idWell={idWell}
onDone={onDone}/>
</Modal >
</>
}
export default ImportExportBar

View File

@ -1,3 +1,4 @@
import { Switch } from 'antd'
import { memo, useState, useRef, useEffect } from 'react'
import {
@ -13,12 +14,42 @@ import 'chartjs-adapter-moment'
import zoomPlugin from 'chartjs-plugin-zoom'
import ChartDataLabels from 'chartjs-plugin-datalabels'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { getOperations } from '@pages/Cluster/functions'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { getOperations } from '@pages/Cluster/functions'
Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin)
const scaleTypes = {
day: {
min: 0,
type: 'linear',
display: true,
title: { display: false, text: '' },
ticks: { stepSize: 1 }
},
date: {
display: true,
title: { display: true },
type: 'time',
time: {
unit: 'hour',
displayFormats: { 'hour': 'MM.DD' }
},
grid: { drawTicks: true },
ticks: {
stepSize: 3,
major: { enabled: true },
z: 1,
display: true,
textStrokeColor: '#fff',
textStrokeWidth: 2,
color: '#000',
}
}
}
const defaultOptions = {
responsive: true,
aspectRatio: 2.6,
@ -27,20 +58,8 @@ const defaultOptions = {
mode: 'index',
},
scales: {
x: {
min: 0,
type: 'linear',
display: true,
title: {
display: false,
text: '',
},
ticks: {
stepSize: 1,
}
},
y:{
x: scaleTypes.day,
y: {
type: 'linear',
position: 'top',
reverse: true,
@ -90,7 +109,8 @@ const makeDataset = (data, label, color, borderWidth = 1.5, borderDash) => ({
export const Tvd = memo(({ idWell, title }) => {
const [operations, setOperations] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [isLoading, setIsLoading] = useState(false)
const [xLabel, setXLabel] = useState('day')
const [chart, setChart] = useState()
const chartRef = useRef(null)
@ -101,7 +121,7 @@ export const Tvd = memo(({ idWell, title }) => {
const operations = await getOperations(idWell)
setOperations(operations)
},
setShowLoader,
setIsLoading,
`Не удалось загрузить операции по скважине "${idWell}"`,
)
}, [idWell])
@ -118,6 +138,8 @@ export const Tvd = memo(({ idWell, title }) => {
if (chartRef.current && !chart) {
const thisOptions = {}
Object.assign(thisOptions, defaultOptions)
thisOptions.scales.x = scaleTypes[xLabel]
thisOptions.parsing.xAxisKey = xLabel
const newChart = new Chart(chartRef.current, {
type: 'line',
@ -130,9 +152,11 @@ export const Tvd = memo(({ idWell, title }) => {
return () => chart?.destroy()
} else {
chart.data = data
chart.options.scales.x = scaleTypes[xLabel]
chart.options.parsing.xAxisKey = xLabel
chart.update()
}
}, [chart, operations])
}, [chart, operations, xLabel])
return (
<div className={'container'}>
@ -140,10 +164,20 @@ export const Tvd = memo(({ idWell, title }) => {
{title || (
<h2 className={'mt-20px'}>График Глубина-день</h2>
)}
<LoaderPortal show={showLoader}>
<LoaderPortal show={isLoading}>
<canvas ref={chartRef} />
<div style={{ display: 'flex', justifyContent: 'center', width: '100%' }}>
<Switch
checkedChildren={'Дата'}
unCheckedChildren={'Дни со старта'}
loading={isLoading}
onChange={(checked) => setXLabel(checked ? 'date' : 'day')}
/>
</div>
</LoaderPortal>
</div>
</div>
)
})
export default Tvd

View File

@ -1,20 +1,20 @@
import { Link } from 'react-router-dom'
import { useState, useEffect, memo } from 'react'
import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons'
import { Table, Tag, Button, Badge, Divider, Modal, Row, Col, Popconfirm } from 'antd'
import { Link } from 'react-router-dom'
import { useState, useEffect } from 'react'
import { makeTextColumn, makeNumericColumnPlanFact } from '../../../components/Table'
import { DrillParamsService, WellCompositeService } from '../../../services/api'
import LoaderPortal from '../../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../../components/factory'
import { CompanyView } from '../../../components/views'
import { CompanyView } from '@components/views'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table'
import { DrillParamsService, WellCompositeService } from '@api'
import {
calcAndUpdateStatsBySections,
makeFilterMinMaxFunction,
getOperations
} from '../../Cluster/functions'
import WellOperationsTable from '../../Cluster/WellOperationsTable'
} from '@pages/Cluster/functions'
import WellOperationsTable from '@pages/Cluster/WellOperationsTable'
import { Tvd } from '../Tvd'
import { getColumns } from '../WellDrillParams'
@ -27,7 +27,7 @@ const filtersMinMax = [
const filtersSectionsType = []
const DAY_IN_MS = 1000 * 60 * 60 * 24
export const WellCompositeSections = ({ idWell, statsWells, selectedSections }) => {
export const WellCompositeSections = memo(({ idWell, statsWells, selectedSections }) => {
const [rows, setRows] = useState([])
const [params, setParams] = useState([])
const [paramsColumns, setParamsColumns] = useState([])
@ -288,4 +288,6 @@ export const WellCompositeSections = ({ idWell, statsWells, selectedSections })
</Modal>
</>
)
}
})
export default WellCompositeSections

View File

@ -1,22 +1,23 @@
import { useState, useEffect } from 'react'
import { Redirect, Route, Switch, Link, useParams } from 'react-router-dom'
import { useState, useEffect, memo } from 'react'
import { Switch, useParams } from 'react-router-dom'
import { Col, Layout, Menu, Row, Tag, TreeSelect } from 'antd'
import LoaderPortal from '../../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../../components/factory'
import {
DepositService,
OperationStatService,
WellCompositeService,
} from '../../../services/api'
import { arrayOrDefault } from '../../../utils'
} from '@api'
import { arrayOrDefault } from '@utils'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { PrivateDefaultRoute, PrivateMenuItemLink, PrivateRoute } from '@components/Private'
import ClusterWells from '../../Cluster/ClusterWells'
import ClusterWells from '@pages/Cluster/ClusterWells'
import { WellCompositeSections } from './WellCompositeSections'
const { Content } = Layout
export const WellCompositeEditor = ({ idWell }) => {
export const WellCompositeEditor = memo(({ idWell }) => {
const rootPath = `/well/${idWell}/operations/composite`
const { tab } = useParams()
@ -94,7 +95,7 @@ export const WellCompositeEditor = ({ idWell }) => {
treeLine={{ showLeafIcon: false }}
onChange={(value) => setSelectedIdWells(value)}
size={'middle'}
style={{width: '100%'}}
style={{ width: '100%' }}
value={selectedIdWells}
placeholder={'Выберите скважины'}
tagRender={(props) => (
@ -103,18 +104,9 @@ export const WellCompositeEditor = ({ idWell }) => {
/>
</Col>
<Col span={6}>
<Menu
mode={'horizontal'}
selectable={true}
className={'well_menu'}
selectedKeys={[tab]}
>
<Menu.Item key={'wells'}>
<Link to={`${rootPath}/wells`}>Статистика по скважинам</Link>
</Menu.Item>
<Menu.Item key={'sections'}>
<Link to={`${rootPath}/sections`}>Статистика по секциям</Link>
</Menu.Item>
<Menu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}>
<PrivateMenuItemLink root={rootPath} key={'wells'} path={'wells'} title={'Статистика по скважинам'} />
<PrivateMenuItemLink root={rootPath} key={'sections'} path={'sections'} title={'Статистика по секциям'} />
</Menu>
</Col>
</Row>
@ -122,23 +114,23 @@ export const WellCompositeEditor = ({ idWell }) => {
<Content className={'site-layout-background'}>
<LoaderPortal show={showTabLoader}>
<Switch>
<Route path={`${rootPath}/wells`}>
<PrivateRoute path={`${rootPath}/wells`}>
<ClusterWells statsWells={statsWells}/>
</Route>
<Route path={`${rootPath}/sections`}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/sections`}>
<WellCompositeSections
idWell={idWell}
statsWells={statsWells}
selectedSections={selectedSections}
/>
</Route>
<Route path={rootPath}>
<Redirect to={`${rootPath}/wells`}/>
</Route>
</PrivateRoute>
<PrivateDefaultRoute urls={[`${rootPath}/wells`, `${rootPath}/sections`]}/>
</Switch>
</LoaderPortal>
</Content>
</Layout>
</LoaderPortal>
)
}
})
export default WellCompositeEditor

View File

@ -1,15 +1,16 @@
import { useState, useEffect, useCallback } from 'react'
import { useState, useEffect, useCallback, memo } from 'react'
import { DrillParamsService, WellOperationService } from '../../services/api'
import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import {
EditableTable,
makeSelectColumn,
makeActionHandler,
makeNumericAvgRange,
} from '../../components/Table'
import { arrayOrDefault } from '../../utils'
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillParamsService, WellOperationService } from '@api'
import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils'
export const getColumns = async (idWell) => {
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
@ -31,7 +32,7 @@ export const getColumns = async (idWell) => {
]
}
export const WellDrillParams = ({ idWell }) => {
export const WellDrillParams = memo(({ idWell }) => {
const [params, setParams] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([])
@ -69,11 +70,13 @@ export const WellDrillParams = ({ idWell }) => {
bordered
columns={columns}
dataSource={params}
onRowAdd={makeActionHandler('insert', handlerProps, recordParser)}
onRowEdit={makeActionHandler('update', handlerProps, recordParser)}
onRowDelete={makeActionHandler('delete', handlerProps, recordParser)}
onRowAdd={hasPermission('DrillParams.edit') && makeActionHandler('insert', handlerProps, recordParser)}
onRowEdit={hasPermission('DrillParams.edit') && makeActionHandler('update', handlerProps, recordParser)}
onRowDelete={hasPermission('DrillParams.delete') && makeActionHandler('delete', handlerProps, recordParser)}
pagination={false}
/>
</LoaderPortal>
)
}
})
export default WellDrillParams

View File

@ -1,6 +1,6 @@
import moment from 'moment'
import { Input } from 'antd'
import { useState, useEffect } from 'react'
import { useState, useEffect, memo } from 'react'
import {
EditableTable,
@ -10,11 +10,12 @@ import {
makeNumericColumnOptions,
makeSelectColumn,
makeActionHandler,
} from '../../components/Table'
import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { WellOperationService} from '../../services/api'
import { formatDate } from '../../utils'
} from '@components/Table'
import { WellOperationService} from '@api'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { hasPermission } from '@utils/permissions'
import { formatDate } from '@utils'
const { TextArea } = Input
@ -52,7 +53,7 @@ const defaultColumns = [
makeColumn('Комментарий', 'comment', { editable: true, input: <TextArea/> }),
]
export const WellOperationsEditor = ({idWell, idType}) => {
export const WellOperationsEditor = memo(({ idWell, idType, ...other }) => {
const [pageNumAndPageSize, setPageNumAndPageSize] = useState({current:1, pageSize:basePageSize})
const [paginationTotal, setPaginationTotal] = useState(0)
const [operations, setOperations] = useState([])
@ -113,13 +114,14 @@ export const WellOperationsEditor = ({idWell, idType}) => {
return (
<LoaderPortal show={showLoader}>
<EditableTable
size={'small'}
{...other}
bordered
size={'small'}
columns={columns}
dataSource={operations}
onRowAdd={makeActionHandler('insertRange', handlerProps, recordParser)}
onRowEdit={makeActionHandler('update', handlerProps, recordParser)}
onRowDelete={makeActionHandler('delete', handlerProps)}
onRowAdd={hasPermission('WellOperation.edit') && makeActionHandler('insertRange', handlerProps, recordParser)}
onRowEdit={hasPermission('WellOperation.edit') && makeActionHandler('update', handlerProps, recordParser)}
onRowDelete={hasPermission('WellOperation.delete') && makeActionHandler('delete', handlerProps)}
pagination={{
current: pageNumAndPageSize.current,
pageSize: pageNumAndPageSize.pageSize,
@ -130,4 +132,6 @@ export const WellOperationsEditor = ({idWell, idType}) => {
/>
</LoaderPortal>
)
}
})
export default WellOperationsEditor

View File

@ -1,5 +1,6 @@
import {Layout, Menu} from "antd";
import {Switch, Link, Route, Redirect, useParams, useHistory} from "react-router-dom";
import { memo } from 'react'
import { Layout, Menu } from 'antd'
import { Switch, useParams, useHistory } from 'react-router-dom'
import {
BarChartOutlined,
BuildOutlined,
@ -7,84 +8,79 @@ import {
DeploymentUnitOutlined,
LineChartOutlined,
TableOutlined,
} from "@ant-design/icons";
} from '@ant-design/icons'
import { PrivateDefaultRoute, PrivateRoute, PrivateMenuItemLink } from '@components/Private'
import { Tvd } from './Tvd'
import { ImportExportBar } from './ImportExportBar'
import { WellDrillParams } from './WellDrillParams'
import { DrillProcessFlow } from './DrillProcessFlow'
import { WellSectionsStat } from './WellSectionsStat'
import { WellCompositeEditor } from './WellCompositeEditor'
import { WellOperationsEditor } from './WellOperationsEditor'
import { Tvd } from './Tvd'
import { ImportExportBar } from "./ImportExportBar";
import { DrillProcessFlow } from "./DrillProcessFlow";
const { Content } = Layout
export default function WellOperations({idWell}) {
let {tab} = useParams()
let history = useHistory()
export const WellOperations = memo(({ idWell }) => {
const { tab } = useParams()
const history = useHistory()
const rootPath = `/well/${idWell}/operations`
const onImported = () => history.push(`${rootPath}`)
const isIEBarDisabled = !["plan", "fact"].includes(tab)
const isIEBarDisabled = !['plan', 'fact'].includes(tab)
return(<>
<Menu
mode="horizontal"
selectable={true}
className="well_menu"
selectedKeys={[tab]}>
<Menu.Item key="tvd" icon={<LineChartOutlined />}>
<Link to={`${rootPath}/tvd`}>TVD</Link>
</Menu.Item>
<Menu.Item key="sections" icon={<BuildOutlined />}>
<Link to={`${rootPath}/sections`}>Секции</Link>
</Menu.Item>
<Menu.Item key="plan" icon={<TableOutlined />}>
<Link to={`${rootPath}/plan`}>План</Link>
</Menu.Item>
<Menu.Item key="fact" icon={<TableOutlined />}>
<Link to={`${rootPath}/fact`}>Факт</Link>
</Menu.Item>
<Menu.Item key="drillProcessFlow" icon={<BarChartOutlined />}>
<Link to={`${rootPath}/drillProcessFlow`}>РТК</Link>
</Menu.Item>
<Menu.Item key="params" icon={<ControlOutlined />}>
<Link to={`${rootPath}/params`}>Режимы</Link>
</Menu.Item>
<Menu.Item key="composite" icon={<DeploymentUnitOutlined />}>
<Link to={`${rootPath}/composite`}>Аналитика</Link>
</Menu.Item>
return(
<>
<Menu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}>
<PrivateMenuItemLink root={rootPath} icon={<LineChartOutlined />} key={'tvd'} path={'tvd'} title={'TVD'} />
<PrivateMenuItemLink root={rootPath} icon={<BuildOutlined />} key={'sections'} path={'sections'} title={'Секции'} />
<PrivateMenuItemLink root={rootPath} icon={<TableOutlined />} key={'plan'} path={'plan'} title={'План'} />
<PrivateMenuItemLink root={rootPath} icon={<TableOutlined />} key={'fact'} path={'fact'} title={'Факт'} />
<PrivateMenuItemLink root={rootPath} icon={<BarChartOutlined />} key={'drillProcessFlow'} path={'drillProcessFlow'} title={'РТК'} />
<PrivateMenuItemLink root={rootPath} icon={<ControlOutlined />} key={'params'} path={'params'} title={'Режимы'} />
<PrivateMenuItemLink root={rootPath} icon={<DeploymentUnitOutlined />} key={'composite'} path={'composite'} title={'Аналитика'} />
<ImportExportBar idWell={idWell} disabled={isIEBarDisabled} onImported={onImported}/>
</Menu>
<Layout>
<Content className="site-layout-background">
<Switch>
<Route path={`${rootPath}/tvd`}>
</Menu>
<Layout>
<Content className={'site-layout-background'}>
<Switch>
<PrivateRoute path={`${rootPath}/tvd`}>
<Tvd idWell={idWell}/>
</Route>
<Route path={`${rootPath}/sections`}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/sections`}>
<WellSectionsStat idWell={idWell}/>
</Route>
<Route path={`${rootPath}/plan`}>
<WellOperationsEditor idWell={idWell} idType={0}/>
</Route>
<Route path={`${rootPath}/fact`}>
<WellOperationsEditor idWell={idWell} idType={1}/>
</Route>
<Route path={`${rootPath}/drillProcessFlow`}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/plan`}>
<WellOperationsEditor idWell={idWell} idType={0} tableName={'well_operations_plan'} showSettingsChanger />
</PrivateRoute>
<PrivateRoute path={`${rootPath}/fact`}>
<WellOperationsEditor idWell={idWell} idType={1} tableName={'well_operations_fact'}/>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/drillProcessFlow`}>
<DrillProcessFlow idWell={idWell} />
</Route>
<Route path={`${rootPath}/params`}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/params`}>
<WellDrillParams idWell={idWell}/>
</Route>
<Route path={`${rootPath}/composite/:tab?`}>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/composite/:tab?`}>
<WellCompositeEditor idWell={idWell}/>
</Route>
<Route path={rootPath}>
<Redirect to={`${rootPath}/plan`}/>
</Route>
</PrivateRoute>
<PrivateDefaultRoute urls={[
`${rootPath}/plan`,
`${rootPath}/fact`,
`${rootPath}/tvd`,
`${rootPath}/sections`,
`${rootPath}/drillProcessFlow`,
`${rootPath}/params`,
`${rootPath}/composite`
]}/>
</Switch>
</Content>
</Layout>
</>)
}
</>
)
})
export default WellOperations

View File

@ -3,10 +3,13 @@ import { getUserPermissions, getUserRoles } from './storage'
export type Role = string
export type Permission = string
export const hasPermission = (permission?: Permission): boolean => {
if (typeof permission !== 'string') return true
return getUserPermissions().includes(permission)
export const hasPermission = (permission?: Permission | Permission[], userPermissions?: Permission[]): boolean => {
if (!Array.isArray(permission) && typeof permission !== 'string')
return true
const userPerms = userPermissions ?? getUserPermissions()
if (typeof permission === 'string')
permission = [permission]
return permission.every((perm) => userPerms.includes(perm))
}
export const isInRole = (roles?: Role[] | Role): boolean => {
@ -15,43 +18,104 @@ export const isInRole = (roles?: Role[] | Role): boolean => {
if (!roles?.length) return true
const user_roles = getUserRoles()
return roles.some((role) => role in user_roles)
return roles.some((role) => user_roles.includes(role))
}
/*
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)
*/
const sectionAvailable = (section: PermissionRecord, userPermission: Permission[] = getUserPermissions()) => {
for (const child of Object.values(section)) {
if (!child) continue
if (Array.isArray(child)) {
if (hasPermission(child, userPermission)) return true
} else {
if (sectionAvailable(child, userPermission)) return true
}
}
return false
}
export const isURLAvailable = (path: string, userPermissions?: Permission[]) => {
const pathParts = path.replaceAll('/', ' ').trim().split(' ')
if (pathParts.length <= 0) return false
let perms: PermissionRecord | string[] | null = requirements
for (let i = 0; i < pathParts.length; i++) {
if (!perms) return false
if (Array.isArray(perms)) break
if (pathParts[i] in perms) {
if (!perms[pathParts[i]]) return false
perms = perms[pathParts[i]]
} else if ('*' in perms) {
perms = perms['*']
} else {
if(i + 1 < pathParts.length || pathParts[i][0] !== ':') return false
// Если последним аргументом указан параметр, считаем, что началась секция
break
}
}
if (!perms) return false
const userPerms: string[] = userPermissions ?? getUserPermissions()
if (Array.isArray(perms)) {
return perms.every((perm) => userPerms.includes(perm))
}
return sectionAvailable(perms, userPerms)
}
interface PermissionRecord extends Record<string, string[] | PermissionRecord | null> {}
export const requirements: PermissionRecord = {
'': [],
login: [], // ['Auth.edit']
register: [], // ['Auth.edit']
admin: {
user: ['AdminCompany.get', 'AdminUserRole.get', 'AdminUser.get'],
well: ['AdminCompany.get', 'AdminCluster.get', 'AdminTelemetry.get', 'AdminWell.get'],
cluster: ['AdminCluster.get', 'AdminDeposit.get'],
company: ['AdminCompany.get', 'AdminCompanyType.get'],
company_type: ['AdminCompanyType.get'],
deposit: ['AdminDeposit.get'],
permission: ['AdminPermission.get'],
role: ['AdminUserRole.get', 'AdminPermission.get'],
visit_log: ['RequerstTracker.get'],
telemetry: ['AdminTelemetry.get'],
},
deposit: ['Deposit.get', 'Cluster.get'],
cluster: {
'*': ['Deposit.get', 'Cluster.get', 'OperationStat.get'],
},
well: {
'*': {
document: {
'*': ['Deposit.get', 'File.get'],
},
archive: ['Deposit.get', 'TelemetryDataSaub.get'],
telemetry: ['Deposit.get', 'DrillFlowChart.get', 'TelemetryDataSaub.get', 'TelemetryDataSpin.get'],
message: ['Deposit.get', 'TelemetryDataSaub.get'],
report: ['Deposit.get', 'Report.get'],
operations: {
tvd: ['Deposit.get', 'OperationStat.get'],
sections: ['Deposit.get', 'OperationStat.get'],
plan: ['Deposit.get', 'WellOperation.get'],
fact: ['Deposit.get', 'WellOperation.get'],
drillProcessFlow: ['Deposit.get', 'DrillFlowChart.get'],
params: ['Deposit.get', 'WellOperation.get', 'DrillParams.get'],
composite: {
wells: ['Deposit.get', 'OperationStat.get', 'WellComposite.get'],
sections: ['Deposit.get', 'OperationStat.get', 'WellComposite.get', 'DrillParams.get'],
}
},
telemetryAnalysis: {
depthToDay: ['Deposit.get', 'TelemetryAnalytics.get'],
depthToInterval: ['Deposit.get', 'TelemetryAnalytics.get'],
operationsSummary: ['Deposit.get', 'TelemetryAnalytics.get'],
operationsToInterval: ['Deposit.get'], // Не имеет собственных требований
},
measure: ['Deposit.get', 'Measure.get'],
drillingProgram: ['Deposit.get', 'Well.get', 'DrillingProgram.get'],
}
}
}
// Страницы Login и Register используют Auth.edit, однако не учитываются, так как считается, что их использует аноним
// Для всех страниц, на которых в PageHeader используется WellTreeSelector небходим доступ к Deposit.get