forked from ddrilling/asb_cloud_front
* Добавлен компонент для получения Route по умолчанию
* Доблена страница "Доступ запрещён" * Актуализирована работа с правами * Добавлен переключатель оси X TVD
This commit is contained in:
parent
439df9401a
commit
85f98044c6
@ -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
|
||||
|
17
src/components/Private/PrivateDefaultRoute.tsx
Normal file
17
src/components/Private/PrivateDefaultRoute.tsx
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
<PrivateMenuItemLink path={'/'} title={'Вернуться на сайт'}/>
|
||||
) : (
|
||||
<Link to={'/admin'}>Панель администратора</Link>
|
||||
<PrivateMenuItemLink path={'/admin'} title={'Панель администратора'}/>
|
||||
)}
|
||||
</PrivateMenuItem>
|
||||
<Menu.Item>
|
||||
<Link to={'/'} onClick={onChangePasswordClick}>Сменить пароль</Link>
|
||||
</Menu.Item>
|
||||
@ -58,4 +55,4 @@ export const UserMenu: React.FC<UserMenuProps> = ({ isAdmin }) => {
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
15
src/pages/AccessDenied.jsx
Normal file
15
src/pages/AccessDenied.jsx
Normal 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
|
@ -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>
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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}
|
||||
|
@ -3,15 +3,10 @@ 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 (
|
||||
export const TelemetrySelect = memo(({ telemetry, value, onChange }) => (
|
||||
<Select
|
||||
value={value?.id}
|
||||
onChange={onSelectChange}
|
||||
onChange={(id) => onChange?.(telemetry.find((row) => row.id === id))}
|
||||
dropdownClassName={'telemetry_select'}
|
||||
>
|
||||
{telemetry.map((row, i) => (
|
||||
@ -22,7 +17,6 @@ export const TelemetrySelect = memo(({ telemetry, value, onChange }) => {
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
)
|
||||
})
|
||||
))
|
||||
|
||||
export default TelemetrySelect
|
||||
|
@ -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}
|
||||
/>
|
||||
|
@ -1,8 +1,10 @@
|
||||
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'
|
||||
|
||||
import { PrivateMenuItem, PrivateRoute, PrivateDefaultRoute } from '@components/Private'
|
||||
|
||||
import { SuspenseFallback } from '@pages/SuspenseFallback'
|
||||
|
||||
const ClusterController = lazy(() => import( './ClusterController'))
|
||||
const CompanyController = lazy(() => import( './CompanyController'))
|
||||
@ -15,62 +17,52 @@ 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}/role` } component={ RoleController} />
|
||||
<PrivateRoute 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>
|
||||
<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>
|
||||
|
@ -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,6 +146,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
|
||||
</datalist>
|
||||
</div>
|
||||
|
||||
{hasPermission(`File.edit${idCategory}`) && (
|
||||
<div>
|
||||
<span>Загрузка</span>
|
||||
<UploadForm
|
||||
@ -154,6 +156,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
|
||||
onUploadComplete={handleUploadComplete}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
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>
|
||||
<Route path={`${rootPath}/depthToDay`}>
|
||||
<PrivateRoute root={rootPath} path={'depthToDay'}>
|
||||
<TelemetryAnalysisDepthToDay idWell={idWell}/>
|
||||
</Route>
|
||||
<Route path={`${rootPath}/depthToInterval`}>
|
||||
</PrivateRoute>
|
||||
<PrivateRoute root={rootPath} path={'depthToInterval'}>
|
||||
<TelemetryAnalysisDepthToInterval idWell={idWell}/>
|
||||
</Route>
|
||||
<Route path={`${rootPath}/operationsSummary`}>
|
||||
</PrivateRoute>
|
||||
<PrivateRoute root={rootPath} path={'operationsSummary'}>
|
||||
<TelemetryAnalysisOperationsSummary idWell={idWell}/>
|
||||
</Route>
|
||||
<Route path={`${rootPath}/operationsToInterval`}>
|
||||
</PrivateRoute>
|
||||
<PrivateRoute root={rootPath} path={'operationsToInterval'}>
|
||||
<TelemetryAnalysisOperationsToInterval idWell={idWell}/>
|
||||
</Route>
|
||||
<Route path={`/`}>
|
||||
<Redirect to={`${rootPath}/depthToDay`} />
|
||||
</Route>
|
||||
</PrivateRoute>
|
||||
<PrivateDefaultRoute urls={[
|
||||
`${rootPath}/depthToDay`,
|
||||
`${rootPath}/depthToInterval`,
|
||||
`${rootPath}/operationsSummary`,
|
||||
`${rootPath}/operationsToInterval`,
|
||||
]}/>
|
||||
</Switch>
|
||||
</Content>
|
||||
</Layout>
|
||||
</>)
|
||||
}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default TelemetryAnalysis
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
||||
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,20 +16,20 @@ export const ImportExportBar = ({idWell, onImported, disabled}) =>{
|
||||
|
||||
const onDone = () => {
|
||||
setIsImportModalVisible(false)
|
||||
if(onImported)
|
||||
onImported()
|
||||
onImported?.()
|
||||
}
|
||||
|
||||
return <>
|
||||
<Tooltip title = 'Импорт - загрузить файл с операциями на сервер'>
|
||||
return (
|
||||
<>
|
||||
<Tooltip title={'Импорт - загрузить файл с операциями на сервер'}>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
icon={<ImportOutlined/>}
|
||||
style={style}
|
||||
onClick={_=>setIsImportModalVisible(true)}/>
|
||||
onClick={() => setIsImportModalVisible(true)}/>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title = 'Экспорт - скачать файл с операциями по скважине'>
|
||||
<Tooltip title={'Экспорт - скачать файл с операциями по скважине'}>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
icon={<ExportOutlined/>}
|
||||
@ -35,23 +37,20 @@ export const ImportExportBar = ({idWell, onImported, disabled}) =>{
|
||||
onClick={downloadExport}/>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title = 'Скачать шаблон для импорта операций'>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
icon={<FileOutlined/>}
|
||||
style={style}
|
||||
onClick={downloadTemplate}/>
|
||||
<Tooltip title={'Скачать шаблон для импорта операций'}>
|
||||
<Button disabled={disabled} icon={<FileOutlined />} style={style} onClick={downloadTemplate} />
|
||||
</Tooltip>
|
||||
|
||||
<Modal
|
||||
title='Импорт операций'
|
||||
title={'Импорт операций'}
|
||||
visible={isImportModalVisible}
|
||||
onCancel={_=>setIsImportModalVisible(false)}
|
||||
|
||||
footer={null}>
|
||||
<ImportOperations
|
||||
idWell={idWell}
|
||||
onDone={onDone}/>
|
||||
onCancel={() => setIsImportModalVisible(false)}
|
||||
footer={null}
|
||||
>
|
||||
<ImportOperations idWell={idWell} onDone={onDone} />
|
||||
</Modal>
|
||||
</>
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
export default ImportExportBar
|
||||
|
@ -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,19 +58,7 @@ const defaultOptions = {
|
||||
mode: 'index',
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
min: 0,
|
||||
type: 'linear',
|
||||
display: true,
|
||||
title: {
|
||||
display: false,
|
||||
text: '',
|
||||
},
|
||||
ticks: {
|
||||
stepSize: 1,
|
||||
}
|
||||
},
|
||||
|
||||
x: scaleTypes.day,
|
||||
y: {
|
||||
type: 'linear',
|
||||
position: 'top',
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
<Content className={'site-layout-background'}>
|
||||
<Switch>
|
||||
<Route path={`${rootPath}/tvd`}>
|
||||
<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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user