Merge branch 'dev' into feature/daily-report

This commit is contained in:
goodmice 2022-04-19 15:44:03 +05:00
commit b2d8b7729c
46 changed files with 605 additions and 475 deletions

View File

@ -1,8 +1,10 @@
import {Row, Col} from 'antd' import {Row, Col} from 'antd'
import Documents from '../Documents/DocumentsTemplate'
import '../../styles/equipment_details.css'
export default function EquipmentDetails({id, equipmentTimers, equipmentSensors}) { import Documents from '../Documents/DocumentsTemplate'
import '@styles/equipment_details.css'
export default function EquipmentDetails({ id, equipmentTimers, equipmentSensors }) {
let stateOfEquipmentDetails = equipmentTimers.map(timer => { let stateOfEquipmentDetails = equipmentTimers.map(timer => {
return( return(
<p key={timer.label}>{timer.label}: <span className="right-text"><b>{timer.value} {timer.unit}</b></span></p> <p key={timer.label}>{timer.label}: <span className="right-text"><b>{timer.value} {timer.unit}</b></span></p>

View File

@ -0,0 +1,49 @@
import { join } from 'path'
import { Menu, MenuItemProps, MenuProps } from 'antd'
import { Children, cloneElement, memo, ReactElement, useContext, useMemo } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { isURLAvailable } from '@utils/permissions'
import { RootPathContext } from '@pages/Main'
export type PrivateMenuProps = MenuProps & { root?: string }
export type PrivateMenuLinkProps = MenuItemProps & {
tabName?: string
path?: string
title: string
visible?: boolean
}
export const PrivateMenuLink = memo<PrivateMenuLinkProps>(({ tabName = '', path = '', title, ...other }) => {
const location = useLocation()
return (
<Menu.Item key={tabName} {...other}>
<Link to={{ pathname: path, state: { from: location.pathname }}}>{title}</Link>
</Menu.Item>
)
})
const PrivateMenuMain = memo<PrivateMenuProps>(({ root, children, ...other }) => {
const rootContext = useContext(RootPathContext)
const rootPath = useMemo(() => root ?? rootContext ?? '', [root, rootContext])
const items = useMemo(() => Children.toArray(children).map((child) => {
const element = child as ReactElement
let key = element.key?.toString()
const visible: boolean | undefined = element.props.visible
if (key && visible !== false) {
key = key.slice(key.lastIndexOf('$') + 1) // Ключ автоматический преобразуется в "(.+)\$ключ"
const path = join(rootPath, key)
if (visible || isURLAvailable(path))
return cloneElement(element, { key, path, tabName: key })
}
return null
}), [children, rootPath])
return <Menu children={items} {...other} />
})
export const PrivateMenu = Object.assign(PrivateMenuMain, { Link: PrivateMenuLink })
export default PrivateMenu

View File

@ -1,22 +1,22 @@
import { join } from 'path' import { join } from 'path'
import { Menu, MenuItemProps } from 'antd' import { Menu, MenuItemProps } from 'antd'
import { memo, NamedExoticComponent } from 'react' import { memo, NamedExoticComponent } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { isURLAvailable } from '@utils/permissions' import { isURLAvailable } from '@utils/permissions'
import { Link, useLocation } from 'react-router-dom'
export type PrivateMenuItemProps = MenuItemProps & { export type PrivateMenuItemProps = MenuItemProps & {
root: string root: string
path: string path: string
} }
export type PrivateMenuLinkProps = MenuItemProps & { export type PrivateMenuItemLinkProps = MenuItemProps & {
root?: string root?: string
path: string path: string
title: string title: string
} }
export const PrivateMenuItemLink = memo<PrivateMenuLinkProps>(({ root = '', path, title, ...other }) => { export const PrivateMenuItemLink = memo<PrivateMenuItemLinkProps>(({ root = '', path, title, ...other }) => {
const location = useLocation() const location = useLocation()
return ( return (
<PrivateMenuItem key={path} root={root} path={path} {...other}> <PrivateMenuItem key={path} root={root} path={path} {...other}>
@ -26,9 +26,9 @@ export const PrivateMenuItemLink = memo<PrivateMenuLinkProps>(({ root = '', path
}) })
export const PrivateMenuItem: NamedExoticComponent<PrivateMenuItemProps> & { export const PrivateMenuItem: NamedExoticComponent<PrivateMenuItemProps> & {
Link: NamedExoticComponent<PrivateMenuLinkProps> Link: NamedExoticComponent<PrivateMenuItemLinkProps>
} = Object.assign(memo<PrivateMenuItemProps>(({ root, path, ...other }) => } = Object.assign(memo<PrivateMenuItemProps>(({ root, path, ...other }) =>
isURLAvailable(join(root, path)) ? <Menu.Item key={path} {...other}/> : null <Menu.Item key={path} hidden={!isURLAvailable(join(root, path))} {...other} />
), { ), {
Link: PrivateMenuItemLink Link: PrivateMenuItemLink
}) })

View File

@ -0,0 +1,75 @@
import { join } from 'path'
import { Location } from 'history'
import { Children, cloneElement, memo, ReactElement, ReactNode, useCallback, useContext, useMemo } from 'react'
import { Redirect, Route, Switch, SwitchProps, useLocation } from 'react-router-dom'
import { isURLAvailable } from '@utils/permissions'
import { getUserId } from '@utils/storage'
import { RootPathContext } from '@pages/Main'
export type PrivateSwitchProps = SwitchProps & {
root?: string
redirect?: (location?: Location<unknown>) => ReactNode
elseRedirect?: string | string[]
}
const getDefaultRedirectPath = () => getUserId() ? '/access_denied' : '/login'
export const defaultRedirect = (location?: Location<unknown>) => (
<Redirect to={{ pathname: getDefaultRedirectPath(), state: { from: location?.pathname } }} />
)
export const PrivateSwitch = memo<PrivateSwitchProps>(({ root, elseRedirect, redirect = defaultRedirect, children }) => {
const rootContext = useContext(RootPathContext)
const rootPath = useMemo(() => root ?? rootContext ?? '', [root, rootContext])
const location = useLocation()
const toAbsolute = useCallback((path: string) => path.startsWith('/') ? path : join(rootPath, path), [rootPath])
const items = useMemo(() => Children.toArray(children).map((child) => {
const element = child as ReactElement
let key = element.key?.toString()
if (!key) return null
key = key.slice(key.lastIndexOf('$') + 1).replaceAll('=2', ':')
// Ключ автоматический преобразуется в "(.+)\$ключ"
// Все ":" в ключе заменяются на "=2"
// TODO: улучшить метод нормализации ключа
const path = toAbsolute(key)
return (
<Route
key={key}
path={path}
render={({ location }) => isURLAvailable(path) ? cloneElement(element) : redirect(location)}
/>
)
}), [children, redirect, toAbsolute])
const defaultRoute = useMemo(() => {
if (!elseRedirect) {
const path = items.map((elm) => elm?.props.path).find((path) => path && isURLAvailable(path))
if (path) return path
} else if (Array.isArray(elseRedirect)) {
const path = elseRedirect.find((path) => {
if (!path) return false
return isURLAvailable(toAbsolute(path))
})
if (path) return toAbsolute(path)
} else if(elseRedirect && isURLAvailable(toAbsolute(elseRedirect))) {
return toAbsolute(elseRedirect)
}
return getDefaultRedirectPath()
}, [items, elseRedirect, toAbsolute])
return (
<Switch>
{items}
<Route path={'/'}>
<Redirect to={{ pathname: defaultRoute, state: { from: location.pathname } }} />
</Route>
</Switch>
)
})
export default PrivateSwitch

View File

@ -1,9 +1,13 @@
export { PrivateRoute, defaultRedirect } from './PrivateRoute' export { PrivateRoute, defaultRedirect } from './PrivateRoute'
export { PrivateContent } from './PrivateContent' export { PrivateContent } from './PrivateContent' // TODO: Remove
export { PrivateMenuItem, PrivateMenuItemLink } from './PrivateMenuItem' export { PrivateMenuItem, PrivateMenuItemLink } from './PrivateMenuItem' // TODO: Remove
export { PrivateDefaultRoute } from './PrivateDefaultRoute' export { PrivateDefaultRoute } from './PrivateDefaultRoute'
export { PrivateMenu, PrivateMenuLink } from './PrivateMenu'
export { PrivateSwitch } from './PrivateSwitch'
export type { PrivateRouteProps } from './PrivateRoute' export type { PrivateRouteProps } from './PrivateRoute'
export type { PrivateContentProps } from './PrivateContent' export type { PrivateContentProps } from './PrivateContent' // TODO: Remove
export type { PrivateMenuItemProps, PrivateMenuLinkProps } from './PrivateMenuItem' export type { PrivateMenuItemProps, PrivateMenuItemLinkProps } from './PrivateMenuItem' // TODO: Remove
export type { PrivateDefaultRouteProps } from './PrivateDefaultRoute' export type { PrivateDefaultRouteProps } from './PrivateDefaultRoute'
export type { PrivateMenuProps, PrivateMenuLinkProps } from './PrivateMenu'
export type { PrivateSwitchProps } from './PrivateSwitch'

View File

@ -1,41 +1,41 @@
import { Layout, Menu } from 'antd' import { Layout } from 'antd'
import { lazy, memo, Suspense } from 'react' import { lazy, memo, Suspense, useContext, useMemo } from 'react'
import { Switch, useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { PrivateMenuItem, PrivateRoute, PrivateDefaultRoute } from '@components/Private' import { PrivateMenu, PrivateSwitch } from '@components/Private'
import { SuspenseFallback } from '@pages/SuspenseFallback' import { SuspenseFallback } from '@pages/SuspenseFallback'
import { RootPathContext } from '@pages/Main'
const TelemetryViewer = lazy(() => import('./TelemetryViewer')) const TelemetryViewer = lazy(() => import('./TelemetryViewer'))
const TelemetryMerger = lazy(() => import('./TelemetryMerger')) const TelemetryMerger = lazy(() => import('./TelemetryMerger'))
const rootPath = '/admin/telemetry'
export const Telemetry = memo(() => { export const Telemetry = memo(() => {
const { tab } = useParams() const { tab } = useParams()
return ( const root = useContext(RootPathContext)
<Layout> const rootPath = useMemo(() => `${root}/telemetry`, [root])
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]}>
<PrivateMenuItem.Link root={rootPath} key={'viewer'} path={'viewer'} title={'Просмотр'} />
<PrivateMenuItem.Link root={rootPath} key={'merger'} path={'merger'} title={'Объединение'} />
</Menu>
return (
<RootPathContext.Provider value={rootPath}>
<Layout> <Layout>
<Layout.Content className={'site-layout-background'}> <PrivateMenu mode={'horizontal'} selectable={true} selectedKeys={[tab]}>
<Suspense fallback={<SuspenseFallback />}> <PrivateMenu.Link key={'viewer'} title={'Просмотр'} />
<Switch> <PrivateMenu.Link key={'merger'} title={'Объединение'} />
<PrivateRoute path={`${rootPath}/viewer`} component={TelemetryViewer} /> </PrivateMenu>
<PrivateRoute path={`${rootPath}/merger`} component={TelemetryMerger} />
<PrivateDefaultRoute urls={[ <Layout>
`${rootPath}/viewer`, <Layout.Content className={'site-layout-background'}>
`${rootPath}/merger`, <Suspense fallback={<SuspenseFallback />}>
]}/> <PrivateSwitch elseRedirect={['viewer', 'merger']}>
</Switch> <TelemetryViewer key={'viewer'} />
</Suspense> <TelemetryMerger key={'merger'} />
</Layout.Content> </PrivateSwitch>
</Suspense>
</Layout.Content>
</Layout>
</Layout> </Layout>
</Layout> </RootPathContext.Provider>
) )
}) })

View File

@ -188,7 +188,7 @@ export const UserController = memo(() => {
allowClear allowClear
placeholder={'Введите текст для поиска (по всем полям за исключением ролей)...'} placeholder={'Введите текст для поиска (по всем полям за исключением ролей)...'}
onChange={onSearchTextChange} onChange={onSearchTextChange}
style={{ marginBottom: '15px' }} style={{ margin: '15px 0' }}
loading={isSearching} loading={isSearching}
/> />
<EditableTable <EditableTable

View File

@ -1,9 +1,10 @@
import { Layout, Menu } from 'antd' import { Layout } from 'antd'
import { lazy, memo, Suspense } from 'react' import { lazy, memo, Suspense, useContext, useMemo } from 'react'
import { Switch, useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { PrivateMenuItem, PrivateRoute, PrivateDefaultRoute } from '@components/Private' import { PrivateMenu, PrivateSwitch } from '@components/Private'
import { RootPathContext } from '@pages/Main'
import { SuspenseFallback } from '@pages/SuspenseFallback' import { SuspenseFallback } from '@pages/SuspenseFallback'
const ClusterController = lazy(() => import( './ClusterController')) const ClusterController = lazy(() => import( './ClusterController'))
@ -17,57 +18,48 @@ const PermissionController = lazy(() => import( './PermissionController'))
const TelemetrySection = lazy(() => import( './Telemetry')) const TelemetrySection = lazy(() => import( './Telemetry'))
const VisitLog = lazy(() => import( './VisitLog')) const VisitLog = lazy(() => import( './VisitLog'))
const rootPath = '/admin'
export const AdminPanel = memo(() => { export const AdminPanel = memo(() => {
const { tab } = useParams() const { tab } = useParams()
return ( const root = useContext(RootPathContext)
<Layout> const rootPath = useMemo(() => `${root}/admin`, [root])
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]}>
<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>
return (
<RootPathContext.Provider value={rootPath}>
<Layout> <Layout>
<Layout.Content className={'site-layout-background'}> <PrivateMenu mode={'horizontal'} selectable={true} selectedKeys={[tab]}>
<Suspense fallback={<SuspenseFallback />}> <PrivateMenu.Link key={'deposit' } title={'Месторождения' } />
<Switch> <PrivateMenu.Link key={'cluster' } title={'Кусты' } />
<PrivateRoute path={`${rootPath}/deposit` } component={ DepositController} /> <PrivateMenu.Link key={'well' } title={'Скважины' } />
<PrivateRoute path={`${rootPath}/cluster` } component={ ClusterController} /> <PrivateMenu.Link key={'user' } title={'Пользователи' } />
<PrivateRoute path={`${rootPath}/well` } component={ WellController} /> <PrivateMenu.Link key={'company' } title={'Компании' } />
<PrivateRoute path={`${rootPath}/user` } component={ UserController} /> <PrivateMenu.Link key={'company_type'} title={'Типы компаний' } />
<PrivateRoute path={`${rootPath}/company` } component={ CompanyController} /> <PrivateMenu.Link key={'role' } title={'Роли' } />
<PrivateRoute path={`${rootPath}/company_type` } component={CompanyTypeController} /> <PrivateMenu.Link key={'permission' } title={'Разрешения' } />
<PrivateRoute path={`${rootPath}/role` } component={ RoleController} /> <PrivateMenu.Link key={'telemetry' } title={'Телеметрия' } />
<PrivateRoute path={`${rootPath}/permission` } component={ PermissionController} /> <PrivateMenu.Link key={'visit_log' } title={'Журнал посещений'} />
<PrivateRoute path={`${rootPath}/telemetry/:tab?`} component={TelemetrySection} /> </PrivateMenu>
<PrivateRoute path={`${rootPath}/visit_log` } component={VisitLog} />
<PrivateDefaultRoute urls={[ <Layout>
`${rootPath}/deposit`, <Layout.Content className={'site-layout-background'}>
`${rootPath}/cluster`, <Suspense fallback={<SuspenseFallback />}>
`${rootPath}/well`, <PrivateSwitch elseRedirect={['deposit', 'cluster', 'well', 'user', 'company', 'company_type', 'role', 'permission', 'telemetry', 'visit_log']}>
`${rootPath}/user`, <DepositController key={'deposit'} />
`${rootPath}/company`, <ClusterController key={'cluster'} />
`${rootPath}/company_type`, <WellController key={'well'} />
`${rootPath}/role`, <UserController key={'user'} />
`${rootPath}/permission`, <CompanyController key={'company'} />
`${rootPath}/telemetry`, <CompanyTypeController key={'company_type'} />
`${rootPath}/visit_log`, <RoleController key={'role'} />
]}/> <PermissionController key={'permission'} />
</Switch> <TelemetrySection key={'telemetry/:tab?'} />
</Suspense> <VisitLog key={'visit_log'} />
</Layout.Content> </PrivateSwitch>
</Suspense>
</Layout.Content>
</Layout>
</Layout> </Layout>
</Layout> </RootPathContext.Provider>
) )
}) })

View File

@ -1,5 +1,5 @@
import { Table as RawTable, Typography } from 'antd' import { Table as RawTable, Typography } from 'antd'
import { Fragment, memo, useCallback, useEffect, useState } from 'react' import { Fragment, memo, useCallback, useContext, useEffect, useState } from 'react'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
@ -8,6 +8,8 @@ import { makeGroupColumn, makeNumericColumn, makeNumericRender, makeTextColumn,
import { OperationStatService, WellOperationService } from '@api' import { OperationStatService, WellOperationService } from '@api'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import { IdWellContext } from '../Well'
import '@styles/index.css' import '@styles/index.css'
import '@styles/statistics.less' import '@styles/statistics.less'
@ -62,7 +64,7 @@ const getWellData = async (wellsList) => {
return wellData return wellData
} }
export const Statistics = memo(({ idWell }) => { export const Statistics = memo(() => {
const [sectionTypes, setSectionTypes] = useState([]) const [sectionTypes, setSectionTypes] = useState([])
const [avgColumns, setAvgColumns] = useState(defaultColumns) const [avgColumns, setAvgColumns] = useState(defaultColumns)
const [cmpColumns, setCmpColumns] = useState(defaultColumns) const [cmpColumns, setCmpColumns] = useState(defaultColumns)
@ -75,6 +77,8 @@ export const Statistics = memo(({ idWell }) => {
const [cmpData, setCmpData] = useState([]) const [cmpData, setCmpData] = useState([])
const [avgRow, setAvgRow] = useState({}) const [avgRow, setAvgRow] = useState({})
const idWell = useContext(IdWellContext)
const cmpSpeedRender = useCallback((key) => (section) => { const cmpSpeedRender = useCallback((key) => (section) => {
let spanClass = '' let spanClass = ''
// Дополнительная проверка на "null" необходима, чтобы значение "0" не стало исключением // Дополнительная проверка на "null" необходима, чтобы значение "0" не стало исключением

View File

@ -1,4 +1,4 @@
import { memo, useCallback, useEffect, useState } from 'react' import { memo, useCallback, useContext, useEffect, useState } from 'react'
import { Button, Modal, Popconfirm } from 'antd' import { Button, Modal, Popconfirm } from 'antd'
import { Table } from '@components/Table' import { Table } from '@components/Table'
@ -6,14 +6,17 @@ import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillParamsService } from '@api' import { DrillParamsService } from '@api'
import { IdWellContext } from '@pages/Well'
import { getColumns } from '@pages/WellOperations/WellDrillParams' import { getColumns } from '@pages/WellOperations/WellDrillParams'
export const NewParamsTable = memo(({ idWell, selectedWellsKeys }) => { export const NewParamsTable = memo(({ selectedWellsKeys }) => {
const [params, setParams] = useState([]) const [params, setParams] = useState([])
const [paramsColumns, setParamsColumns] = useState([]) const [paramsColumns, setParamsColumns] = useState([])
const [showParamsLoader, setShowParamsLoader] = useState(false) const [showParamsLoader, setShowParamsLoader] = useState(false)
const [isParamsModalVisible, setIsParamsModalVisible] = useState(false) const [isParamsModalVisible, setIsParamsModalVisible] = useState(false)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => setParamsColumns(await getColumns(idWell)) async () => setParamsColumns(await getColumns(idWell))
), [idWell]) ), [idWell])

View File

@ -1,5 +1,5 @@
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import { useState, useEffect, memo, useMemo } from 'react' import { useState, useEffect, memo, useMemo, useContext } from 'react'
import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons' import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons'
import { Table, Tag, Button, Badge, Divider, Modal, Row, Col } from 'antd' import { Table, Tag, Button, Badge, Divider, Modal, Row, Col } from 'antd'
@ -15,6 +15,7 @@ import {
getOperations getOperations
} from '@utils/functions' } from '@utils/functions'
import { IdWellContext } from '@pages/Well'
import { Tvd } from '@pages/WellOperations/Tvd' import { Tvd } from '@pages/WellOperations/Tvd'
import WellOperationsTable from '@pages/Cluster/WellOperationsTable' import WellOperationsTable from '@pages/Cluster/WellOperationsTable'
import NewParamsTable from './NewParamsTable' import NewParamsTable from './NewParamsTable'
@ -29,7 +30,7 @@ const sortBySectionId = (a, b) => a.sectionId - b.sectionId
const filtersSectionsType = [] const filtersSectionsType = []
const DAY_IN_MS = 1000 * 60 * 60 * 24 const DAY_IN_MS = 1000 * 60 * 60 * 24
export const WellCompositeSections = memo(({ idWell, statsWells, selectedSections }) => { export const WellCompositeSections = memo(({ statsWells, selectedSections }) => {
const [selectedWells, setSelectedWells] = useState([]) const [selectedWells, setSelectedWells] = useState([])
const [wellOperations, setWellOperations] = useState([]) const [wellOperations, setWellOperations] = useState([])
const [selectedWellsKeys, setSelectedWellsKeys] = useState([]) const [selectedWellsKeys, setSelectedWellsKeys] = useState([])
@ -38,6 +39,8 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
const [isTVDModalVisible, setIsTVDModalVisible] = useState(false) const [isTVDModalVisible, setIsTVDModalVisible] = useState(false)
const [isOpsModalVisible, setIsOpsModalVisible] = useState(false) const [isOpsModalVisible, setIsOpsModalVisible] = useState(false)
const idWell = useContext(IdWellContext)
const location = useLocation() const location = useLocation()
const rows = useMemo(() => { const rows = useMemo(() => {
@ -208,9 +211,7 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
pagination={false} pagination={false}
/> />
<Row justify={'end'} style={{ margin: '1rem 0' }}> <Row justify={'end'} style={{ margin: '1rem 0' }}>
<Col> <Col><NewParamsTable selectedWellsKeys={selectedWellsKeys} /></Col>
<NewParamsTable idWell={idWell} selectedWellsKeys={selectedWellsKeys} />
</Col>
</Row> </Row>
<Modal <Modal

View File

@ -1,24 +1,23 @@
import { useState, useEffect, memo } from 'react' import { useState, useEffect, memo, useContext } from 'react'
import { Switch, useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { Col, Layout, Menu, Row } from 'antd' import { Col, Layout, Row } from 'antd'
import {
OperationStatService,
WellCompositeService,
} from '@api'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import WellSelector from '@components/selectors/WellSelector' import WellSelector from '@components/selectors/WellSelector'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { PrivateDefaultRoute, PrivateMenuItemLink, PrivateRoute } from '@components/Private' import { PrivateMenu, PrivateSwitch } from '@components/Private'
import { OperationStatService, WellCompositeService } from '@api'
import { IdWellContext } from '@pages/Well'
import ClusterWells from '@pages/Cluster/ClusterWells' import ClusterWells from '@pages/Cluster/ClusterWells'
import { WellCompositeSections } from './WellCompositeSections' import { WellCompositeSections } from './WellCompositeSections'
const { Content } = Layout const { Content } = Layout
export const WellCompositeEditor = memo(({ idWell, rootPath }) => { export const WellCompositeEditor = memo(({ rootPath }) => {
const { tab } = useParams() const { tab } = useParams()
const idWell = useContext(IdWellContext)
const [statsWells, setStatsWells] = useState([]) const [statsWells, setStatsWells] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -67,28 +66,19 @@ export const WellCompositeEditor = memo(({ idWell, rootPath }) => {
/> />
</Col> </Col>
<Col span={6}> <Col span={6}>
<Menu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}> <PrivateMenu root={rootPath} mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}>
<PrivateMenuItemLink root={rootPath} key={'wells'} path={'wells'} title={'Статистика по скважинам'} /> <PrivateMenu.Link key={'wells'} title={'Статистика по скважинам'} />
<PrivateMenuItemLink root={rootPath} key={'sections'} path={'sections'} title={'Статистика по секциям'} /> <PrivateMenu.Link key={'sections'} title={'Статистика по секциям'} />
</Menu> </PrivateMenu>
</Col> </Col>
</Row> </Row>
<Layout> <Layout>
<Content className={'site-layout-background'}> <Content className={'site-layout-background'}>
<LoaderPortal show={showTabLoader}> <LoaderPortal show={showTabLoader}>
<Switch> <PrivateSwitch root={rootPath} elseRedirect={['wells', 'sections']}>
<PrivateRoute path={`${rootPath}/wells`}> <ClusterWells key={'wells'} statsWells={statsWells} />
<ClusterWells statsWells={statsWells}/> <WellCompositeSections key={'sections'} statsWells={statsWells} selectedSections={selectedSections} />
</PrivateRoute> </PrivateSwitch>
<PrivateRoute path={`${rootPath}/sections`}>
<WellCompositeSections
idWell={idWell}
statsWells={statsWells}
selectedSections={selectedSections}
/>
</PrivateRoute>
<PrivateDefaultRoute urls={[`${rootPath}/wells`, `${rootPath}/sections`]}/>
</Switch>
</LoaderPortal> </LoaderPortal>
</Content> </Content>
</Layout> </Layout>

View File

@ -1,36 +1,35 @@
import { memo, useMemo } from 'react' import { memo, useContext, useMemo } from 'react'
import { Layout, Menu } from 'antd' import { useParams } from 'react-router-dom'
import { Switch, useParams } from 'react-router-dom' import { Layout } from 'antd'
import { PrivateDefaultRoute, PrivateMenuItemLink, PrivateRoute } from '@components/Private' import { PrivateMenu, PrivateSwitch } from '@components/Private'
import WellCompositeEditor from './WellCompositeEditor'
import Statistics from './Statistics' import Statistics from './Statistics'
import WellCompositeEditor from './WellCompositeEditor'
import { RootPathContext } from '@pages/Main'
export const Analytics = memo(({ idWell }) => { export const Analytics = memo(() => {
const { tab } = useParams() const { tab } = useParams()
const rootPath = useMemo(() => `/well/${idWell}/analytics`, [idWell]) const root = useContext(RootPathContext)
const rootPath = useMemo(() => `${root}/analytics`, [root])
return ( return (
<Layout> <RootPathContext.Provider value={rootPath}>
<Menu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}>
<PrivateMenuItemLink root={rootPath} key={'composite'} path={'composite'} title={'Композитная скважина'} />
<PrivateMenuItemLink root={rootPath} key={'statistics'} path={'statistics'} title={'Оценка по ЦБ'} />
</Menu>
<Layout> <Layout>
<Layout.Content> <PrivateMenu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}>
<Switch> <PrivateMenu.Link key={'composite'} title={'Композитная скважина'} />
<PrivateRoute path={`${rootPath}/composite/:tab?`}> <PrivateMenu.Link key={'statistics'} title={'Оценка по ЦБ'} />
<WellCompositeEditor idWell={idWell} rootPath={`${rootPath}/composite`} /> </PrivateMenu>
</PrivateRoute> <Layout>
<PrivateRoute path={`${rootPath}/statistics`}> <Layout.Content>
<Statistics idWell={idWell} /> <PrivateSwitch elseRedirect={'composite'}>
</PrivateRoute> <WellCompositeEditor key={'composite/:tab?'} rootPath={`${rootPath}/composite`} />
<PrivateDefaultRoute urls={[`${rootPath}/composite`]}/> <Statistics key={'statistics'} />
</Switch> </PrivateSwitch>
</Layout.Content> </Layout.Content>
</Layout>
</Layout> </Layout>
</Layout> </RootPathContext.Provider>
) )
}) })

View File

@ -1,4 +1,4 @@
import { useState, useEffect, useMemo, useCallback } from 'react' import { useState, useEffect, useMemo, useCallback, useContext } from 'react'
import { DatePicker, Button, Input } from 'antd' import { DatePicker, Button, Input } from 'antd'
import { FileService } from '@api' import { FileService } from '@api'
@ -9,6 +9,8 @@ import { EditableTable, makeColumn, makeDateColumn, makeNumericColumn, makePagin
import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory' import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory'
import { hasPermission } from '@utils/permissions' import { hasPermission } from '@utils/permissions'
import { IdWellContext } from '@pages/Well'
const pageSize = 12 const pageSize = 12
const { RangePicker } = DatePicker const { RangePicker } = DatePicker
const { Search } = Input const { Search } = Input
@ -30,7 +32,7 @@ const columns = [
makeColumn('Компания', 'company', { render: (_, record) => <CompanyView company={record?.author?.company}/> }) makeColumn('Компания', 'company', { render: (_, record) => <CompanyView company={record?.author?.company}/> })
] ]
export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, customColumns, beforeTable, onChange, tableName }) => { export const DocumentsTemplate = ({ idCategory, idWell: wellId, accept, headerChild, customColumns, beforeTable, onChange, tableName }) => {
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
const [filterDataRange, setFilterDataRange] = useState([]) const [filterDataRange, setFilterDataRange] = useState([])
const [filterCompanyName, setFilterCompanyName] = useState([]) const [filterCompanyName, setFilterCompanyName] = useState([])
@ -39,6 +41,9 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
const [files, setFiles] = useState([]) const [files, setFiles] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const idwellContext = useContext(IdWellContext)
const idWell = useMemo(() => wellId ?? idwellContext, [wellId])
const uploadUrl = useMemo(() => `/api/well/${idWell}/files/?idCategory=${idCategory}`, [idWell, idCategory]) const uploadUrl = useMemo(() => `/api/well/${idWell}/files/?idCategory=${idCategory}`, [idWell, idCategory])
const mergedColumns = useMemo(() => [...columns, ...(customColumns ?? [])], [customColumns]) const mergedColumns = useMemo(() => [...columns, ...(customColumns ?? [])], [customColumns])

View File

@ -1,12 +1,12 @@
import { join } from 'path' import { useParams } from 'react-router-dom'
import { memo, useMemo } from 'react' import { memo, useContext, useMemo } from 'react'
import { Layout, Menu } from 'antd'
import { FolderOutlined } from '@ant-design/icons' import { FolderOutlined } from '@ant-design/icons'
import { Switch, useParams } from 'react-router-dom' import { Layout } from 'antd'
import { PrivateDefaultRoute, PrivateMenuItemLink, PrivateRoute } from '@components/Private' import { PrivateMenu, PrivateSwitch } from '@components/Private'
import DocumentsTemplate from './DocumentsTemplate' import DocumentsTemplate from './DocumentsTemplate'
import { RootPathContext } from '@pages/Main'
const { Content } = Layout const { Content } = Layout
@ -23,37 +23,37 @@ export const documentCategories = [
{ id: 9, key: 'closingService', title: 'Сервис по заканчиванию скважины' }, { id: 9, key: 'closingService', title: 'Сервис по заканчиванию скважины' },
] ]
export const MenuDocuments = memo(({ idWell }) => { export const MenuDocuments = memo(() => {
const { category } = useParams() const { category } = useParams()
const root = useMemo(() => `/well/${idWell}/document`, [idWell]) const root = useContext(RootPathContext)
const rootPath = useMemo(() => `${root}/document`, [root])
return ( return (
<> <RootPathContext.Provider value={rootPath}>
<Menu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[category]}> <PrivateMenu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[category]}>
{documentCategories.map(category => ( {documentCategories.map(category => (
<PrivateMenuItemLink <PrivateMenu.Link
root={root}
key={`${category.key}`} key={`${category.key}`}
path={`${category.key}`}
className={'ant-menu-item'} className={'ant-menu-item'}
icon={<FolderOutlined/>} icon={<FolderOutlined/>}
title={category.title} title={category.title}
/> />
))} ))}
</Menu> </PrivateMenu>
<Layout> <Layout>
<Content className={'site-layout-background'}> <Content className={'site-layout-background'}>
<Switch> <PrivateSwitch elseRedirect={documentCategories.map((cat) => cat.key)}>
{documentCategories.map(category => ( {documentCategories.map(category => (
<PrivateRoute path={join(root, category.key)} key={`${category.key}`}> <DocumentsTemplate
<DocumentsTemplate idCategory={category.id} idWell={idWell} tableName={`documents_${category.key}`} /> key={category.key}
</PrivateRoute> idCategory={category.id}
tableName={`documents_${category.key}`}
/>
))} ))}
<PrivateDefaultRoute urls={documentCategories.map((cat) => join(root, cat.key))}/> </PrivateSwitch>
</Switch>
</Content> </Content>
</Layout> </Layout>
</> </RootPathContext.Provider>
) )
}) })

View File

@ -1,11 +1,13 @@
import { Form, Select } from 'antd' import { Form, Select } from 'antd'
import { FileAddOutlined } from '@ant-design/icons' import { FileAddOutlined } from '@ant-design/icons'
import { memo, useCallback, useEffect, useState } from 'react' import { memo, useCallback, useContext, useEffect, useState } from 'react'
import Poprompt from '@components/selectors/Poprompt' import Poprompt from '@components/selectors/Poprompt'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillingProgramService } from '@api' import { DrillingProgramService } from '@api'
import { IdWellContext } from '@pages/Well'
import '@styles/drilling_program.less' import '@styles/drilling_program.less'
const catSelectorRules = [{ const catSelectorRules = [{
@ -13,12 +15,14 @@ const catSelectorRules = [{
message: 'Пожалуйста, выберите категории' message: 'Пожалуйста, выберите категории'
}] }]
export const CategoryAdder = memo(({ categories, idWell, onUpdate, className, ...other }) => { export const CategoryAdder = memo(({ categories, onUpdate, className, ...other }) => {
const [options, setOptions] = useState([]) const [options, setOptions] = useState([])
const [value, setValue] = useState([]) const [value, setValue] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [showCatLoader, setShowCatLoader] = useState(false) const [showCatLoader, setShowCatLoader] = useState(false)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
setOptions(categories.map((category) => ({ setOptions(categories.map((category) => ({

View File

@ -1,5 +1,5 @@
import { Input, Modal, Radio } from 'antd' import { Input, Modal, Radio } from 'antd'
import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { BehaviorSubject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs' import { BehaviorSubject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
import { UserView } from '@components/views' import { UserView } from '@components/views'
@ -9,6 +9,8 @@ import { makeColumn, makeNumericSorter, Table } from '@components/Table'
import { DrillingProgramService } from '@api' import { DrillingProgramService } from '@api'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import { IdWellContext } from '@pages/Well'
const userRules = [ const userRules = [
{ label: 'Нет', value: 0 }, { label: 'Нет', value: 0 },
{ label: 'Публикатор', value: 1 }, { label: 'Публикатор', value: 1 },
@ -17,7 +19,7 @@ const userRules = [
const SEARCH_TIMEOUT = 400 const SEARCH_TIMEOUT = 400
export const CategoryEditor = memo(({ idWell, visible, category, onClosed }) => { export const CategoryEditor = memo(({ visible, category, onClosed }) => {
const [title, setTitle] = useState() const [title, setTitle] = useState()
const [users, setUsers] = useState([]) const [users, setUsers] = useState([])
const [allUsers, setAllUsers] = useState([]) const [allUsers, setAllUsers] = useState([])
@ -27,6 +29,8 @@ export const CategoryEditor = memo(({ idWell, visible, category, onClosed }) =>
const [searchValue, setSearchValue] = useState('') const [searchValue, setSearchValue] = useState('')
const [subject, setSubject] = useState(null) const [subject, setSubject] = useState(null)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const filteredUsers = users.filter(({ user }) => user && [ const filteredUsers = users.filter(({ user }) => user && [

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react' import { useCallback, useContext, useEffect, useState } from 'react'
import { Button, DatePicker, Input, Modal } from 'antd' import { Button, DatePicker, Input, Modal } from 'antd'
import { CompanyView } from '@components/views' import { CompanyView } from '@components/views'
@ -10,6 +10,7 @@ import { arrayOrDefault, formatDate } from '@utils'
import { FileService } from '@api' import { FileService } from '@api'
import MarksCard from './MarksCard' import MarksCard from './MarksCard'
import { IdWellContext } from '@pages/Well'
import '@styles/drilling_program.less' import '@styles/drilling_program.less'
@ -54,7 +55,7 @@ export const historyColumns = [
}) })
] ]
export const CategoryHistory = ({ idWell, idCategory, visible, onClose }) => { export const CategoryHistory = ({ idCategory, visible, onClose }) => {
const [data, setData] = useState([]) const [data, setData] = useState([])
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
const [total, setTotal] = useState(0) const [total, setTotal] = useState(0)
@ -64,6 +65,8 @@ export const CategoryHistory = ({ idWell, idCategory, visible, onClose }) => {
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [companyName, setCompanyName] = useState('') const [companyName, setCompanyName] = useState('')
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
if (!visible) return if (!visible) return

View File

@ -1,4 +1,4 @@
import { memo, useCallback, useMemo, useState } from 'react' import { memo, useCallback, useContext, useMemo, useState } from 'react'
import { Button, Input, Popconfirm, Form } from 'antd' import { Button, Input, Popconfirm, Form } from 'antd'
import { import {
DeleteOutlined, DeleteOutlined,
@ -16,6 +16,7 @@ import { DrillingProgramService } from '@api'
import { formatDate } from '@utils' import { formatDate } from '@utils'
import MarksCard from './MarksCard' import MarksCard from './MarksCard'
import { IdWellContext } from '@pages/Well'
import '@styles/drilling_program.less' import '@styles/drilling_program.less'
@ -34,7 +35,7 @@ const CommentPrompt = memo(({ isRequired = true, ...props }) => (
</Poprompt> </Poprompt>
)) ))
export const CategoryRender = memo(({ idWell, partData, onUpdate, onEdit, onHistory, setIsLoading, ...other }) => { export const CategoryRender = memo(({ partData, onUpdate, onEdit, onHistory, setIsLoading, ...other }) => {
const { const {
idFileCategory, idFileCategory,
name: title, // Название категории name: title, // Название категории
@ -44,6 +45,8 @@ export const CategoryRender = memo(({ idWell, partData, onUpdate, onEdit, onHist
file // Информация о файле file // Информация о файле
} = partData ?? {} } = partData ?? {}
const idWell = useContext(IdWellContext)
const uploadUrl = useMemo(() => `/api/well/${idWell}/drillingProgram/part/${idFileCategory}`, [idWell, idFileCategory]) const uploadUrl = useMemo(() => `/api/well/${idWell}/drillingProgram/part/${idFileCategory}`, [idWell, idFileCategory])
const approvedMarks = useMemo(() => file?.fileMarks?.filter((mark) => mark.idMarkType === 1), [file]) const approvedMarks = useMemo(() => file?.fileMarks?.filter((mark) => mark.idMarkType === 1), [file])
const rejectMarks = useMemo(() => file?.fileMarks?.filter((mark) => mark.idMarkType === 0), [file]) const rejectMarks = useMemo(() => file?.fileMarks?.filter((mark) => mark.idMarkType === 0), [file])

View File

@ -8,7 +8,7 @@ import {
ReloadOutlined, ReloadOutlined,
WarningOutlined, WarningOutlined,
} from '@ant-design/icons' } from '@ant-design/icons'
import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { downloadFile, formatBytes, invokeWebApiWrapperAsync } from '@components/factory' import { downloadFile, formatBytes, invokeWebApiWrapperAsync } from '@components/factory'
@ -19,6 +19,7 @@ import CategoryAdder from './CategoryAdder'
import CategoryRender from './CategoryRender' import CategoryRender from './CategoryRender'
import CategoryEditor from './CategoryEditor' import CategoryEditor from './CategoryEditor'
import CategoryHistory from './CategoryHistory' import CategoryHistory from './CategoryHistory'
import { IdWellContext } from '@pages/Well'
import '@styles/drilling_program.less' import '@styles/drilling_program.less'
@ -38,7 +39,7 @@ const stateString = {
[idStateUnknown]: { icon: WarningOutlined, text: 'Неизвестно' }, [idStateUnknown]: { icon: WarningOutlined, text: 'Неизвестно' },
} }
export const DrillingProgram = memo(({ idWell }) => { export const DrillingProgram = memo(() => {
const [selectedCategory, setSelectedCategory] = useState() const [selectedCategory, setSelectedCategory] = useState()
const [historyVisible, setHistoryVisible] = useState(false) const [historyVisible, setHistoryVisible] = useState(false)
const [editorVisible, setEditorVisible] = useState(false) const [editorVisible, setEditorVisible] = useState(false)
@ -46,6 +47,8 @@ export const DrillingProgram = memo(({ idWell }) => {
const [categories, setCategories] = useState([]) const [categories, setCategories] = useState([])
const [data, setData] = useState({}) const [data, setData] = useState({})
const idWell = useContext(IdWellContext)
const { const {
idState, idState,
permissionToEdit, permissionToEdit,
@ -99,11 +102,7 @@ export const DrillingProgram = memo(({ idWell }) => {
<h3>Программа бурения</h3> <h3>Программа бурения</h3>
{permissionToEdit && ( {permissionToEdit && (
<div> <div>
<CategoryAdder <CategoryAdder categories={categories} onUpdate={updateData} />
idWell={idWell}
categories={categories}
onUpdate={updateData}
/>
</div> </div>
)} )}
</div> </div>
@ -143,7 +142,6 @@ export const DrillingProgram = memo(({ idWell }) => {
{parts?.map?.((part, idx) => part && ( {parts?.map?.((part, idx) => part && (
<CategoryRender <CategoryRender
key={`${idx}`} key={`${idx}`}
idWell={idWell}
partData={part} partData={part}
onEdit={permissionToEdit && onCategoryEdit} onEdit={permissionToEdit && onCategoryEdit}
onUpdate={updateData} onUpdate={updateData}
@ -154,7 +152,6 @@ export const DrillingProgram = memo(({ idWell }) => {
{permissionToEdit && ( {permissionToEdit && (
<> <>
<CategoryEditor <CategoryEditor
idWell={idWell}
visible={editorVisible} visible={editorVisible}
onClosed={onEditorClosed} onClosed={onEditorClosed}
category={parts?.find((part) => part.idFileCategory === selectedCategory) ?? {}} category={parts?.find((part) => part.idFileCategory === selectedCategory) ?? {}}
@ -163,7 +160,6 @@ export const DrillingProgram = memo(({ idWell }) => {
)} )}
<CategoryHistory <CategoryHistory
idWell={idWell}
idCategory={selectedCategory} idCategory={selectedCategory}
onClose={() => setHistoryVisible(false)} onClose={() => setHistoryVisible(false)}
visible={historyVisible} visible={historyVisible}

View File

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

View File

@ -1,4 +1,4 @@
import { useState, useEffect, memo, useMemo, useCallback } from 'react' import { useState, useEffect, memo, useMemo, useCallback, useContext } from 'react'
import { Button, Form, Input, Popconfirm, Timeline } from 'antd' import { Button, Form, Input, Popconfirm, Timeline } from 'antd'
import { import {
CheckSquareOutlined, CheckSquareOutlined,
@ -16,6 +16,7 @@ import { formatDate } from '@utils'
import { MeasureService } from '@api' import { MeasureService } from '@api'
import { View } from './View' import { View } from './View'
import { IdWellContext } from '@pages/Well'
import '@styles/index.css' import '@styles/index.css'
import '@styles/measure.css' import '@styles/measure.css'
@ -25,13 +26,15 @@ const createEditingColumns = (cols, renderDelegate) =>
const disabled = !hasPermission('Measure.edit') const disabled = !hasPermission('Measure.edit')
export const MeasureTable = memo(({ idWell, group, updateMeasuresFunc, additionalButtons }) => { export const MeasureTable = memo(({ group, updateMeasuresFunc, additionalButtons }) => {
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [displayedValues, setDisplayedValues] = useState({}) const [displayedValues, setDisplayedValues] = useState({})
const [editingColumns, setEditingColumns] = useState(group.columns) const [editingColumns, setEditingColumns] = useState(group.columns)
const [isTableEditing, setIsTableEditing] = useState(false) const [isTableEditing, setIsTableEditing] = useState(false)
const [editingActionName, setEditingActionName] = useState('') const [editingActionName, setEditingActionName] = useState('')
const idWell = useContext(IdWellContext)
const [measuresForm] = Form.useForm() const [measuresForm] = Form.useForm()
const data = useMemo(() => group?.values?.length > 0 ? group.values : [group?.defaultValue], [group?.defaultValue, group?.values]) const data = useMemo(() => group?.values?.length > 0 ? group.values : [group?.defaultValue], [group?.defaultValue, group?.values])

View File

@ -1,5 +1,5 @@
import { Button } from 'antd' import { Button } from 'antd'
import { useState, useEffect, memo } from 'react' import { useState, useEffect, memo, useContext } from 'react'
import { TableOutlined } from '@ant-design/icons' import { TableOutlined } from '@ant-design/icons'
import { MeasureService } from '@api' import { MeasureService } from '@api'
@ -11,6 +11,7 @@ import { InclinometryTable } from './InclinometryTable'
import { columnsNnb, nnbDefaultData } from './nnbData' import { columnsNnb, nnbDefaultData } from './nnbData'
import { columnsMudDiagram, mudDiagramDefaultData } from './mudDiagramData' import { columnsMudDiagram, mudDiagramDefaultData } from './mudDiagramData'
import { columnsDrillingFluid, drillingFluidDefaultData } from './drillingFluidData' import { columnsDrillingFluid, drillingFluidDefaultData } from './drillingFluidData'
import { IdWellContext } from '@pages/Well'
const defaultData = [ const defaultData = [
{ {
@ -41,12 +42,14 @@ const defaultData = [
} }
] ]
export const Measure = memo(({ idWell }) => { export const Measure = memo(() => {
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [isMeasuresUpdating, setIsMeasuresUpdating] = useState(true) const [isMeasuresUpdating, setIsMeasuresUpdating] = useState(true)
const [data, setData] = useState(defaultData) const [data, setData] = useState(defaultData)
const [tableIdx, setTableIdx] = useState(-1) const [tableIdx, setTableIdx] = useState(-1)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
if (!isMeasuresUpdating) return if (!isMeasuresUpdating) return
@ -73,7 +76,6 @@ export const Measure = memo(({ idWell }) => {
{data.map((group, idx) => ( {data.map((group, idx) => (
<MeasureTable <MeasureTable
key={idx} key={idx}
idWell={idWell}
group={group} group={group}
updateMeasuresFunc={() => setIsMeasuresUpdating(true)} updateMeasuresFunc={() => setIsMeasuresUpdating(true)}
additionalButtons={group.additionalButtons?.(group, idx, setTableIdx)} additionalButtons={group.additionalButtons?.(group, idx, setTableIdx)}

View File

@ -1,5 +1,5 @@
import { Button, Tooltip } from 'antd' import { Button, Tooltip } from 'antd'
import { useState, useEffect, memo } from 'react' import { useState, useEffect, memo, useContext } from 'react'
import { FilePdfOutlined, FileTextOutlined } from '@ant-design/icons' import { FilePdfOutlined, FileTextOutlined } from '@ant-design/icons'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
@ -8,6 +8,8 @@ import { invokeWebApiWrapperAsync, downloadFile } from '@components/factory'
import { formatDate, periodToString } from '@utils' import { formatDate, periodToString } from '@utils'
import { ReportService } from '@api' import { ReportService } from '@api'
import { IdWellContext } from '@pages/Well'
const imgPaths = { const imgPaths = {
'.pdf': <FilePdfOutlined/>, '.pdf': <FilePdfOutlined/>,
'.las': <FileTextOutlined/>, '.las': <FileTextOutlined/>,
@ -55,10 +57,12 @@ const columns = [
}, },
] ]
export const Reports = memo(({ idWell }) => { export const Reports = memo(() => {
const [reports, setReports] = useState([]) const [reports, setReports] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const reportsResponse = await ReportService.getAllReportsNamesByWell(idWell) const reportsResponse = await ReportService.getAllReportsNamesByWell(idWell)

View File

@ -1,6 +1,6 @@
import 'moment/locale/ru' import 'moment/locale/ru'
import moment from 'moment' import moment from 'moment'
import { useState, useEffect, memo, useCallback } from 'react' import { useState, useEffect, memo, useCallback, useContext } from 'react'
import { Radio, Button, Select, notification } from 'antd' import { Radio, Button, Select, notification } from 'antd'
import { LoaderPortal } from '@components/LoaderPortal' import { LoaderPortal } from '@components/LoaderPortal'
@ -11,6 +11,7 @@ import { ReportService } from '@api'
import { Reports } from './Reports' import { Reports } from './Reports'
import { ReportCreationNotify } from './ReportCreationNotify' import { ReportCreationNotify } from './ReportCreationNotify'
import { IdWellContext } from '@pages/Well'
const timePeriodNames = [ const timePeriodNames = [
{ label: '1 секунда', value: 1 }, { label: '1 секунда', value: 1 },
@ -32,7 +33,7 @@ const reportFormats = [
{ value: 1, label: 'LAS' }, { value: 1, label: 'LAS' },
] ]
export const DiagramReport = memo(({ idWell }) => { export const DiagramReport = memo(() => {
const [aviableDateRange, setAviableDateRange] = useState([moment(), moment()]) const [aviableDateRange, setAviableDateRange] = useState([moment(), moment()])
const [filterDateRange, setFilterDateRange] = useState([ const [filterDateRange, setFilterDateRange] = useState([
moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').startOf('day'),
@ -43,6 +44,8 @@ export const DiagramReport = memo(({ idWell }) => {
const [pagesCount, setPagesCount] = useState(0) const [pagesCount, setPagesCount] = useState(0)
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const idWell = useContext(IdWellContext)
const handleReportCreation = useCallback(async () => await invokeWebApiWrapperAsync( const handleReportCreation = useCallback(async () => await invokeWebApiWrapperAsync(
async () => { async () => {
const taskId = await ReportService.createReport( const taskId = await ReportService.createReport(
@ -166,7 +169,7 @@ export const DiagramReport = memo(({ idWell }) => {
</div> </div>
</div> </div>
</LoaderPortal> </LoaderPortal>
<Reports idWell={idWell} /> <Reports />
</div> </div>
) )
}) })

View File

@ -1,5 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { useState, useEffect, memo, useCallback } from 'react' import { useState, useEffect, memo, useCallback, useContext } from 'react'
import { Flex } from '@components/Grid' import { Flex } from '@components/Grid'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
@ -8,6 +8,7 @@ import { DatePickerWrapper, makeDateSorter } from '@components/Table'
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker' import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
import { TelemetryDataSaubService } from '@api' import { TelemetryDataSaubService } from '@api'
import { IdWellContext } from '@pages/Well'
import { normalizeData } from '../TelemetryView' import { normalizeData } from '../TelemetryView'
import { ArchiveDisplay, cutData } from './ArchiveDisplay' import { ArchiveDisplay, cutData } from './ArchiveDisplay'
@ -61,7 +62,7 @@ const range = (start, end) => {
return result return result
} }
export const Archive = memo(({ idWell }) => { export const Archive = memo(() => {
const [dataSaub, setDataSaub] = useState([]) const [dataSaub, setDataSaub] = useState([])
const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() }) const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() })
const [chartInterval, setChartInterval] = useState(parseInt(defaultPeriod) * 1000) const [chartInterval, setChartInterval] = useState(parseInt(defaultPeriod) * 1000)
@ -69,6 +70,8 @@ export const Archive = memo(({ idWell }) => {
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [loaded, setLoaded] = useState(null) const [loaded, setLoaded] = useState(null)
const idWell = useContext(IdWellContext)
const onGraphWheel = useCallback((e) => { const onGraphWheel = useCallback((e) => {
if (loaded && dateLimit.from && dateLimit.to) { if (loaded && dateLimit.from && dateLimit.to) {
setStartDate((prevStartDate) => { setStartDate((prevStartDate) => {

View File

@ -1,4 +1,4 @@
import { memo, useCallback, useEffect, useMemo, useReducer, useState } from 'react' import { memo, useCallback, useContext, useEffect, useMemo, useReducer, useState } from 'react'
import { useHistory, useParams } from 'react-router-dom' import { useHistory, useParams } from 'react-router-dom'
import { CloseOutlined } from '@ant-design/icons' import { CloseOutlined } from '@ant-design/icons'
import { Button, Menu, Popconfirm } from 'antd' import { Button, Menu, Popconfirm } from 'antd'
@ -19,10 +19,12 @@ import {
WitsRecord61Service, WitsRecord61Service,
} from '@api' } from '@api'
import { IdWellContext } from '@pages/Well'
import { RootPathContext } from '@pages/Main'
import AddGroupWindow from './AddGroupWindow'
import AddWidgetWindow, { makeWidgetFromWits } from './AddWidgetWindow' import AddWidgetWindow, { makeWidgetFromWits } from './AddWidgetWindow'
import '@styles/dashboard_nnb.less' import '@styles/dashboard_nnb.less'
import AddGroupWindow from './AddGroupWindow'
const getWitsInfo = async () => { const getWitsInfo = async () => {
// TODO: Добавить expire с принудительным обновлением // TODO: Добавить expire с принудительным обновлением
@ -105,19 +107,21 @@ const groupsReducer = (groups, action) => {
return newGroups return newGroups
} }
export const DashboardNNB = memo(({ idWell }) => { export const DashboardNNB = memo(() => {
const [groups, dispatchGroups] = useReducer(groupsReducer, []) const [groups, dispatchGroups] = useReducer(groupsReducer, [])
const [witsInfo, setWitsInfo] = useState([]) const [witsInfo, setWitsInfo] = useState([])
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [selectedSettings, setSelectedSettings] = useState(null) const [selectedSettings, setSelectedSettings] = useState(null)
const [values, setValues] = useState({}) const [values, setValues] = useState({})
const root = useMemo(() => `/well/${idWell}/telemetry/dashboard_nnb`, [idWell]) const idWell = useContext(IdWellContext)
const root = useContext(RootPathContext)
const rootPath = useMemo(() => `${root}/dashboard_nnb`, [root])
const history = useHistory() const history = useHistory()
const { tab: selectedGroup } = useParams() const { tab: selectedGroup } = useParams()
if (!selectedGroup && groups?.length > 0) if (!selectedGroup && groups?.length > 0)
history.push(`${root}/${groups[0].id}`) history.push(`${rootPath}/${groups[0].id}`)
const group = useMemo(() => ({ const group = useMemo(() => ({
@ -172,8 +176,8 @@ export const DashboardNNB = memo(({ idWell }) => {
const removeGroup = useCallback((id) => { const removeGroup = useCallback((id) => {
dispatchGroups({ type: 'remove_group', groupId: `${id}` }) dispatchGroups({ type: 'remove_group', groupId: `${id}` })
if (id === selectedGroup) history.push(`${root}`) if (id === selectedGroup) history.push(`${rootPath}`)
}, [root, history, selectedGroup]) }, [rootPath, history, selectedGroup])
const addWidget = useCallback((settings) => dispatchGroups({ const addWidget = useCallback((settings) => dispatchGroups({
type: 'add_widget', type: 'add_widget',
@ -226,7 +230,7 @@ export const DashboardNNB = memo(({ idWell }) => {
<Button type={'link'} icon={<CloseOutlined />} /> <Button type={'link'} icon={<CloseOutlined />} />
</Popconfirm> </Popconfirm>
)} )}
<Button type={'text'} style={{ paddingLeft: 0 }} onClick={() => history.push(`${root}/${id}`)}>{name}</Button> <Button type={'text'} style={{ paddingLeft: 0 }} onClick={() => history.push(`${rootPath}/${id}`)}>{name}</Button>
</Menu.Item> </Menu.Item>
))} ))}
</Menu> </Menu>

View File

@ -1,10 +1,12 @@
import moment from 'moment' import { useState, useEffect, memo, useCallback, useContext } from 'react'
import { useState, useEffect, memo, useCallback } from 'react'
import { Table, Select, DatePicker, Input } from 'antd' import { Table, Select, DatePicker, Input } from 'antd'
import { MessageService } from '@api'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { makeColumn, makeDateColumn, makeNumericSorter } from '@components/Table'
import { MessageService } from '@api'
import { IdWellContext } from '../Well'
import '@styles/message.css' import '@styles/message.css'
@ -22,41 +24,18 @@ const categoryDictionary = {
// Конфигурация таблицы // Конфигурация таблицы
export const columns = [ export const columns = [
{ makeDateColumn('Дата', 'date', undefined, undefined, { width: '10rem' }),
makeColumn('Глубина', 'wellDepth', { width: '10rem', render: depth => <span>{depth.toFixed(2)} м.</span> }),
makeColumn('Категория', 'categoryId', {
width: '10rem', width: '10rem',
title: 'Дата',
key: 'date',
dataIndex: 'date',
render: item => moment(item).format('DD MMM YYYY, HH:mm:ss'),
sorter: (a, b) => new Date(b.date) - new Date(a.date),
sortDirections: ['descend', 'ascend'],
}, {
width: '10rem',
title: 'Глубина',
key: 'wellDepth',
dataIndex: 'wellDepth',
render: depth => <span>{depth.toFixed(2)} м.</span>,
}, {
width: '10rem',
title: 'Категория',
key: 'categoryId',
dataIndex: 'categoryId',
render: (_, item) => categoryDictionary[item.categoryId].title, render: (_, item) => categoryDictionary[item.categoryId].title,
style: (_, item) => categoryDictionary[item.categoryId]?.style, style: (_, item) => categoryDictionary[item.categoryId]?.style,
sorter: (a, b) => a.categoryId - b.categoryId, sorter: makeNumericSorter('categoryId'),
sortDirections: ['descend', 'ascend'], sortDirections: ['descend', 'ascend'],
ellipsis: true, ellipsis: true,
}, { }),
title: 'Сообщение', makeColumn('Сообщение', 'message', { onFilter: (value, record) => record.name.indexOf(value) === 0 }),
key: 'message', makeColumn('Пользователь', 'user', { width: '10rem' }),
dataIndex: 'message',
onFilter: (value, record) => record.name.indexOf(value) === 0,
}, {
width: '10rem',
title: 'Пользователь',
key: 'user',
dataIndex: 'user',
},
] ]
const filterOptions = [ const filterOptions = [
@ -68,7 +47,7 @@ const filterOptions = [
const children = filterOptions.map((line) => <Option key={line.value}>{line.label}</Option>) const children = filterOptions.map((line) => <Option key={line.value}>{line.label}</Option>)
// Данные для таблицы // Данные для таблицы
export const Messages = memo(({ idWell }) => { export const Messages = memo(() => {
const [messages, setMessages] = useState([]) const [messages, setMessages] = useState([])
const [pagination, setPagination] = useState(null) const [pagination, setPagination] = useState(null)
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
@ -77,6 +56,8 @@ export const Messages = memo(({ idWell }) => {
const [searchString, setSearchString] = useState('') const [searchString, setSearchString] = useState('')
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const idWell = useContext(IdWellContext)
const onChangeSearchString = useCallback((message) => setSearchString(message.length > 2 ? message : ''), []) const onChangeSearchString = useCallback((message) => setSearchString(message.length > 2 ? message : ''), [])
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(

View File

@ -1,5 +1,5 @@
import { Table } from 'antd' import { Table } from 'antd'
import { useState, useEffect, useCallback, memo } from 'react' import { useState, useEffect, useCallback, memo, useContext } from 'react'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
@ -7,13 +7,16 @@ import { Subscribe } from '@services/signalr'
import { MessageService } from '@api' import { MessageService } from '@api'
import { columns } from '../Messages' import { columns } from '../Messages'
import { IdWellContext } from '@pages/Well'
import '@styles/message.css' import '@styles/message.css'
export const ActiveMessagesOnline = memo(({ idWell }) => { export const ActiveMessagesOnline = memo(() => {
const [messages, setMessages] = useState([]) const [messages, setMessages] = useState([])
const [loader, setLoader] = useState(false) const [loader, setLoader] = useState(false)
const idWell = useContext(IdWellContext)
const handleReceiveMessages = useCallback((messages) => { const handleReceiveMessages = useCallback((messages) => {
if (messages) if (messages)
setMessages(messages.items.splice(0, 4)) setMessages(messages.items.splice(0, 4))

View File

@ -1,4 +1,4 @@
import { memo, useCallback, useMemo, useState } from 'react' import { memo, useCallback, useContext, useMemo, useState } from 'react'
import { Select, Modal, Input, InputNumber } from 'antd' import { Select, Modal, Input, InputNumber } from 'antd'
import { SetpointsService } from '@api' import { SetpointsService } from '@api'
@ -8,12 +8,16 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
import { makeNumericRender, EditableTable } from '@components/Table' import { makeNumericRender, EditableTable } from '@components/Table'
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker' import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
export const SetpointSender = memo(({ idWell, onClose, visible, setpointNames }) => { import { IdWellContext } from '@pages/Well'
export const SetpointSender = memo(({ onClose, visible, setpointNames }) => {
const [expirePeriod, setExpirePeriod] = useState(defaultPeriod) const [expirePeriod, setExpirePeriod] = useState(defaultPeriod)
const [comment, setComment] = useState('') const [comment, setComment] = useState('')
const [setpoints, setSetpoints] = useState([]) const [setpoints, setSetpoints] = useState([])
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const idWell = useContext(IdWellContext)
const addingColumns = useMemo(() => [ const addingColumns = useMemo(() => [
{ {
title: 'Наименование уставки', title: 'Наименование уставки',

View File

@ -1,5 +1,5 @@
import { Button, Modal } from 'antd' import { Button, Modal } from 'antd'
import { useState, useEffect, memo, useCallback, useMemo } from 'react' import { useState, useEffect, memo, useCallback, useMemo, useContext } from 'react'
import { Table } from '@components/Table' import { Table } from '@components/Table'
import { UserView } from '@components/views' import { UserView } from '@components/views'
@ -10,10 +10,11 @@ import { makeStringCutter } from '@utils/string'
import { formatDate } from '@utils' import { formatDate } from '@utils'
import { SetpointsService } from '@api' import { SetpointsService } from '@api'
import { IdWellContext } from '@pages/Well'
import SetpointSender from './SetpointSender' import SetpointSender from './SetpointSender'
import { SetpointViewer, getSetpointStatus } from './SetpointViewer' import { SetpointViewer, getSetpointStatus } from './SetpointViewer'
export const Setpoints = memo(({ idWell, ...other }) => { export const Setpoints = memo(({ ...other }) => {
const [isModalVisible, setIsModalVisible] = useState(false) const [isModalVisible, setIsModalVisible] = useState(false)
const [isSenderVisible, setIsSenderVisible] = useState(false) const [isSenderVisible, setIsSenderVisible] = useState(false)
const [isViewerVisible, setIsViewerVisible] = useState(false) const [isViewerVisible, setIsViewerVisible] = useState(false)
@ -22,6 +23,8 @@ export const Setpoints = memo(({ idWell, ...other }) => {
const [selected, setSelected] = useState(null) const [selected, setSelected] = useState(null)
const [setpointNames, setSetpointNames] = useState([]) const [setpointNames, setSetpointNames] = useState([])
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const names = await SetpointsService.getSetpointsNamesByIdWell(idWell) const names = await SetpointsService.getSetpointsNamesByIdWell(idWell)
@ -102,7 +105,6 @@ export const Setpoints = memo(({ idWell, ...other }) => {
</LoaderPortal> </LoaderPortal>
</Modal> </Modal>
<SetpointSender <SetpointSender
idWell={idWell}
setpointNames={setpointNames} setpointNames={setpointNames}
visible={isSenderVisible} visible={isSenderVisible}
onClose={onSenderClose} onClose={onSenderClose}

View File

@ -1,13 +1,17 @@
import { memo, useCallback, useEffect, useState } from 'react' import { memo, useCallback, useContext, useEffect, useState } from 'react'
import { TelemetryWirelineRunOutService } from '@api' import { TelemetryWirelineRunOutService } from '@api'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { WirelineView } from '@components/views' import { WirelineView } from '@components/views'
export const WirelineRunOut = memo(({ idWell }) => { import { IdWellContext } from '@pages/Well'
export const WirelineRunOut = memo(() => {
const [twro, setTwro] = useState({}) const [twro, setTwro] = useState({})
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const idWell = useContext(IdWellContext)
const update = useCallback(() => invokeWebApiWrapperAsync( const update = useCallback(() => invokeWebApiWrapperAsync(
async () => { async () => {
const twro = await TelemetryWirelineRunOutService.getData(idWell) const twro = await TelemetryWirelineRunOutService.getData(idWell)

View File

@ -1,5 +1,5 @@
import { Select } from 'antd' import { Select } from 'antd'
import { useState, useEffect, useCallback } from 'react' import { useState, useEffect, useCallback, useContext } from 'react'
import { import {
DrillFlowChartService, DrillFlowChartService,
@ -22,6 +22,8 @@ import ActiveMessagesOnline from './ActiveMessagesOnline'
import { ModeDisplay } from './ModeDisplay' import { ModeDisplay } from './ModeDisplay'
import { UserOfWell } from './UserOfWells' import { UserOfWell } from './UserOfWells'
import { Setpoints } from './Setpoints' import { Setpoints } from './Setpoints'
import WirelineRunOut from './WirelineRunOut'
import { IdWellContext } from '@pages/Well'
import MomentStabPicEnabled from '@images/DempherOn.png' import MomentStabPicEnabled from '@images/DempherOn.png'
import MomentStabPicDisabled from '@images/DempherOff.png' import MomentStabPicDisabled from '@images/DempherOff.png'
@ -29,7 +31,6 @@ import SpinPicEnabled from '@images/SpinEnabled.png'
import SpinPicDisabled from '@images/SpinDisabled.png' import SpinPicDisabled from '@images/SpinDisabled.png'
import '@styles/message.css' import '@styles/message.css'
import WirelineRunOut from './WirelineRunOut'
const { Option } = Select const { Option } = Select
@ -303,7 +304,7 @@ export const normalizeData = (data) => data?.map(item => ({
blockSpeed: Math.abs(item.blockSpeed) blockSpeed: Math.abs(item.blockSpeed)
})) ?? [] })) ?? []
export default function TelemetryView({ idWell }) { export default function TelemetryView() {
const [dataSaub, setDataSaub] = useState([]) const [dataSaub, setDataSaub] = useState([])
const [dataSpin, setDataSpin] = useState([]) const [dataSpin, setDataSpin] = useState([])
const [chartInterval, setChartInterval] = useState(defaultPeriod) const [chartInterval, setChartInterval] = useState(defaultPeriod)
@ -312,6 +313,8 @@ export default function TelemetryView({ idWell }) {
const [flowChartData, setFlowChartData] = useState([]) const [flowChartData, setFlowChartData] = useState([])
const [rop, setRop] = useState(null) const [rop, setRop] = useState(null)
const idWell = useContext(IdWellContext)
const handleDataSaub = useCallback((data) => { const handleDataSaub = useCallback((data) => {
if (data) { if (data) {
const dataSaub = normalizeData(data) const dataSaub = normalizeData(data)
@ -392,9 +395,9 @@ export default function TelemetryView({ idWell }) {
<Option value={2}>Завершено</Option> <Option value={2}>Завершено</Option>
</Select> </Select>
</div> </div>
<Setpoints idWell={idWell} style={{ marginLeft: '1rem' }} /> <Setpoints style={{ marginLeft: '1rem' }} />
<span style={{ flexGrow: 20 }}>&nbsp;</span> <span style={{ flexGrow: 20 }}>&nbsp;</span>
<WirelineRunOut idWell={idWell} /> <WirelineRunOut />
<img src={isTorqueStabEnabled(dataSpin) ? MomentStabPicEnabled : MomentStabPicDisabled} style={{ marginRight: '15px' }} alt={'TorqueMaster'} /> <img src={isTorqueStabEnabled(dataSpin) ? MomentStabPicEnabled : MomentStabPicDisabled} style={{ marginRight: '15px' }} alt={'TorqueMaster'} />
<img src={isSpinEnabled(dataSpin) ? SpinPicEnabled : SpinPicDisabled} style={{ marginRight: '15px' }} alt={'SpinMaster'} /> <img src={isSpinEnabled(dataSpin) ? SpinPicEnabled : SpinPicDisabled} style={{ marginRight: '15px' }} alt={'SpinMaster'} />
<h2 style={{ marginBottom: 0, marginRight: '15px', fontWeight: 'bold', color: isMseEnabled(dataSaub) ? 'green' : 'lightgrey' }}>MSE</h2> <h2 style={{ marginBottom: 0, marginRight: '15px', fontWeight: 'bold', color: isMseEnabled(dataSaub) ? 'green' : 'lightgrey' }}>MSE</h2>

View File

@ -1,57 +1,47 @@
import { Switch, useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { memo, useMemo } from 'react' import { memo, useContext, useMemo } from 'react'
import { Layout, Menu } from 'antd' import { Layout } from 'antd'
import { AlertOutlined, FundViewOutlined, DatabaseOutlined } from '@ant-design/icons' import { AlertOutlined, FundViewOutlined, DatabaseOutlined } from '@ant-design/icons'
import { PrivateRoute, PrivateDefaultRoute, PrivateMenuItem } from '@components/Private' import { PrivateSwitch, PrivateMenu } from '@components/Private'
import Archive from './Archive' import Archive from './Archive'
import Messages from './Messages' import Messages from './Messages'
import DashboardNNB from './DashboardNNB' import DashboardNNB from './DashboardNNB'
import TelemetryView from './TelemetryView' import TelemetryView from './TelemetryView'
import { RootPathContext } from '@pages/Main'
import '@styles/index.css' import '@styles/index.css'
const { Content } = Layout const { Content } = Layout
export const Telemetry = memo(({ idWell }) => { export const Telemetry = memo(() => {
const { tab } = useParams() const { tab } = useParams()
const rootPath = useMemo(() => `/well/${idWell}/telemetry`, [idWell]) const root = useContext(RootPathContext)
const rootPath = useMemo(() => `${root}/telemetry`, [root])
return ( return (
<Layout> <RootPathContext.Provider value={rootPath}>
<Menu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
<PrivateMenuItem.Link root={rootPath} key={'monitoring'} path={'monitoring'} icon={<FundViewOutlined />} title={'Мониторинг'}/>
<PrivateMenuItem.Link root={rootPath} key={'messages'} path={'messages'} icon={<AlertOutlined/>} title={'Сообщения'} />
<PrivateMenuItem.Link root={rootPath} key={'archive'} path={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
<PrivateMenuItem.Link root={rootPath} key={'dashboard_nnb'} path={'dashboard_nnb'} title={'ННБ'} />
</Menu>
<Layout> <Layout>
<Content className={'site-layout-background'}> <PrivateMenu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
<Switch> <PrivateMenu.Link key={'monitoring'} icon={<FundViewOutlined />} title={'Мониторинг'}/>
<PrivateRoute path={`${rootPath}/monitoring`}> <PrivateMenu.Link key={'messages'} icon={<AlertOutlined/>} title={'Сообщения'} />
<TelemetryView idWell={idWell} /> <PrivateMenu.Link key={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
</PrivateRoute> <PrivateMenu.Link key={'dashboard_nnb'} title={'ННБ'} />
<PrivateRoute path={`${rootPath}/messages`}> </PrivateMenu>
<Messages idWell={idWell} />
</PrivateRoute> <Layout>
<PrivateRoute path={`${rootPath}/archive`}> <Content className={'site-layout-background'}>
<Archive idWell={idWell} /> <PrivateSwitch elseRedirect={['monitoring', 'messages', 'archive', 'dashboard_nnb']}>
</PrivateRoute> <TelemetryView key={'monitoring'} />
<PrivateRoute path={`${rootPath}/dashboard_nnb/:tab?`}> <Messages key={'messages'} />
<DashboardNNB idWell={idWell} /> <Archive key={'archive'} />
</PrivateRoute> <DashboardNNB key={'dashboard_nnb/:tab?'} />
<PrivateDefaultRoute urls={[ </PrivateSwitch>
`${rootPath}/monitoring`, </Content>
`${rootPath}/messages`, </Layout>
`${rootPath}/archive`,
`${rootPath}/dashboard_nnb`,
]}/>
</Switch>
</Content>
</Layout> </Layout>
</Layout> </RootPathContext.Provider>
) )
}) })

View File

@ -1,15 +1,19 @@
import { useState, useEffect, memo } from 'react' import { useState, useEffect, memo, useContext } from 'react'
import { TelemetryAnalyticsService } from '@api' import { TelemetryAnalyticsService } from '@api'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { ChartTelemetryDepthToDay } from '@components/charts/ChartTelemetryDepthToDay' import { ChartTelemetryDepthToDay } from '@components/charts/ChartTelemetryDepthToDay'
export const TelemetryAnalysisDepthToDay = memo(({ idWell }) => { import { IdWellContext } from '@pages/Well'
export const TelemetryAnalysisDepthToDay = memo(() => {
const [depthData, setDepthData] = useState([]) const [depthData, setDepthData] = useState([])
const [bitPositionData, setBitPositionData] = useState([]) const [bitPositionData, setBitPositionData] = useState([])
const [loader, setLoader] = useState(false) const [loader, setLoader] = useState(false)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const depthToDayData = await TelemetryAnalyticsService.getWellDepthToDay(idWell) const depthToDayData = await TelemetryAnalyticsService.getWellDepthToDay(idWell)

View File

@ -1,11 +1,13 @@
import { Select } from 'antd' import { Select } from 'antd'
import { useState, useEffect, memo } from 'react' import { useState, useEffect, memo, useContext } from 'react'
import { arrayOrDefault } from '@utils'
import { TelemetryAnalyticsService } from '@api'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { ChartTelemetryDepthToInterval } from '@components/charts/ChartTelemetryDepthToInterval' import { ChartTelemetryDepthToInterval } from '@components/charts/ChartTelemetryDepthToInterval'
import { TelemetryAnalyticsService } from '@api'
import { arrayOrDefault } from '@utils'
import { IdWellContext } from '@pages/Well'
const timePeriodCollection = [ const timePeriodCollection = [
{ value: '3600', label: '1 час' }, { value: '3600', label: '1 час' },
@ -14,11 +16,13 @@ const timePeriodCollection = [
{ value: '86400', label: '24 часа' } { value: '86400', label: '24 часа' }
] ]
export const TelemetryAnalysisDepthToInterval = memo(({ idWell }) => { export const TelemetryAnalysisDepthToInterval = memo(() => {
const [depthToIntervalData, setDepthToIntervalData] = useState([]) const [depthToIntervalData, setDepthToIntervalData] = useState([])
const [chartInterval, setChartInterval] = useState(86400) const [chartInterval, setChartInterval] = useState(86400)
const [loader, setLoader] = useState(false) const [loader, setLoader] = useState(false)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const depthToIntervalData = await TelemetryAnalyticsService.getWellDepthToInterval(idWell, chartInterval) const depthToIntervalData = await TelemetryAnalyticsService.getWellDepthToInterval(idWell, chartInterval)

View File

@ -1,20 +1,24 @@
import moment from 'moment' import moment from 'moment'
import { Form, DatePicker } from 'antd' import { Form, DatePicker } from 'antd'
import { useState, useEffect, memo } from 'react' import { useState, useEffect, memo, useContext } from 'react'
import { TelemetryAnalyticsService } from '@api' import { TelemetryAnalyticsService } from '@api'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { ChartTelemetryOperationsSummary } from '@components/charts/ChartTelemetryOperationsSummary' import { ChartTelemetryOperationsSummary } from '@components/charts/ChartTelemetryOperationsSummary'
import { IdWellContext } from '@pages/Well'
const { RangePicker } = DatePicker const { RangePicker } = DatePicker
export const TelemetryAnalysisOperationsSummary = memo(({ idWell }) => { export const TelemetryAnalysisOperationsSummary = memo(() => {
const [avilableDatesRange, setAviableDatesRange] = useState([moment(),moment()]) const [avilableDatesRange, setAviableDatesRange] = useState([moment(),moment()])
const [filterDateRange, setFilterDateRange] = useState([moment().subtract(1, 'days'),moment()]) const [filterDateRange, setFilterDateRange] = useState([moment().subtract(1, 'days'),moment()])
const [operationsData, setOperationsData] = useState([]) const [operationsData, setOperationsData] = useState([])
const [loader, setLoader] = useState(false) const [loader, setLoader] = useState(false)
const idWell = useContext(IdWellContext)
const disabledDate = (current) => current < avilableDatesRange[0] || current > avilableDatesRange[1] const disabledDate = (current) => current < avilableDatesRange[0] || current > avilableDatesRange[1]
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(

View File

@ -1,5 +1,5 @@
import { memo } from 'react' import { memo } from 'react'
export const TelemetryAnalysisOperationsToInterval = memo(({ idWell }) => (<>{idWell} 123</>)) export const TelemetryAnalysisOperationsToInterval = memo(() => (<>123</>))
export default TelemetryAnalysisOperationsToInterval export default TelemetryAnalysisOperationsToInterval

View File

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

View File

@ -1,4 +1,4 @@
import { memo, useMemo } from 'react' import { createContext, memo, useContext, useMemo } from 'react'
import { import {
FolderOutlined, FolderOutlined,
FundViewOutlined, FundViewOutlined,
@ -6,10 +6,10 @@ import {
ExperimentOutlined, ExperimentOutlined,
DeploymentUnitOutlined, DeploymentUnitOutlined,
} from '@ant-design/icons' } from '@ant-design/icons'
import { Layout, Menu } from 'antd' import { Layout } from 'antd'
import { Switch, useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { PrivateRoute, PrivateDefaultRoute, PrivateMenuItem } from '@components/Private' import { PrivateMenu, PrivateSwitch } from '@components/Private'
import Measure from './Measure' import Measure from './Measure'
import Reports from './Reports' import Reports from './Reports'
@ -19,69 +19,51 @@ import Telemetry from './Telemetry'
import WellOperations from './WellOperations' import WellOperations from './WellOperations'
import DrillingProgram from './DrillingProgram' import DrillingProgram from './DrillingProgram'
import TelemetryAnalysis from './TelemetryAnalysis' import TelemetryAnalysis from './TelemetryAnalysis'
import { RootPathContext } from './Main'
import '@styles/index.css' import '@styles/index.css'
const { Content } = Layout const { Content } = Layout
export const IdWellContext = createContext(null)
export const Well = memo(() => { export const Well = memo(() => {
const { idWell, tab } = useParams() const { idWell, tab } = useParams()
const rootPath = useMemo(() => `/well/${idWell}`, [idWell]) const root = useContext(RootPathContext)
const rootPath = useMemo(() => `${root}/well/${idWell}`, [root, idWell])
return ( return (
<Layout> <RootPathContext.Provider value={rootPath}>
<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={'reports'} path={'reports'} icon={<FilePdfOutlined />} title={'Рапорта'} />
<PrivateMenuItem.Link root={rootPath} key={'analytics'} path={'analytics'} icon={<DeploymentUnitOutlined />} title={'Аналитика'} />
<PrivateMenuItem.Link root={rootPath} key={'operations'} path={'operations'} icon={<FolderOutlined />} 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> <Layout>
<Content className={'site-layout-background'}> <PrivateMenu mode={'horizontal'} selectable={true} selectedKeys={[tab]} className={'well_menu'}>
<Switch> <PrivateMenu.Link key={'telemetry'} icon={<FundViewOutlined />} title={'Телеметрия'}/>
<PrivateRoute path={`${rootPath}/telemetry/:tab?`}> <PrivateMenu.Link key={'reports'} icon={<FilePdfOutlined />} title={'Рапорта'} />
<Telemetry idWell={idWell} /> <PrivateMenu.Link key={'analytics'} icon={<DeploymentUnitOutlined />} title={'Аналитика'} />
</PrivateRoute> <PrivateMenu.Link key={'operations'} icon={<FolderOutlined />} title={'Операции по скважине'} />
<PrivateRoute path={`${rootPath}/reports/:tab?`}> {/* <PrivateMenu.Link key={'telemetryAnalysis'} icon={<FundProjectionScreenOutlined />} title={'Операции по телеметрии'} /> */}
<Reports idWell={idWell} /> <PrivateMenu.Link key={'document'} icon={<FolderOutlined />} title={'Документы'} />
</PrivateRoute> <PrivateMenu.Link key={'measure'} icon={<ExperimentOutlined />} title={'Измерения'} />
<PrivateRoute path={`${rootPath}/analytics/:tab?`}> <PrivateMenu.Link key={'drillingProgram'} icon={<FolderOutlined />} title={'Программа бурения'} />
<Analytics idWell={idWell} rootPath={`${rootPath}/analytics`}/> </PrivateMenu>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/operations/:tab?`}> <IdWellContext.Provider value={idWell}>
<WellOperations idWell={idWell} /> <Layout>
</PrivateRoute> <Content className={'site-layout-background'}>
<PrivateRoute path={`${rootPath}/telemetryAnalysis/:tab?`}> <PrivateSwitch elseRedirect={['telemetry', 'reports', 'analytics', 'operations', 'telemetryAnalysis', 'document', 'measure', 'drillingProgram']}>
<TelemetryAnalysis idWell={idWell} /> <Telemetry key={'telemetry/:tab?'} />
</PrivateRoute> <Reports key={'reports/:tab?'} />
<PrivateRoute path={`${rootPath}/document/:category?`}> <Analytics key={'analytics/:tab?'} />
<Documents idWell={idWell} /> <WellOperations key={'operations/:tab?'} />
</PrivateRoute> <TelemetryAnalysis key={'telemetryAnalysis/:tab?'} />
<PrivateRoute path={`${rootPath}/measure`}> <Documents key={'document/:category?'} />
<Measure idWell={idWell}/> <Measure key={'measure'} />
</PrivateRoute> <DrillingProgram key={'drillingProgram'} />
<PrivateRoute path={`${rootPath}/drillingProgram`}> </PrivateSwitch>
<DrillingProgram idWell={idWell}/> </Content>
</PrivateRoute> </Layout>
<PrivateDefaultRoute urls={[ </IdWellContext.Provider>
`${rootPath}/telemetry`,
`${rootPath}/report`,
`${rootPath}/analytics`,
`${rootPath}/operations`,
`${rootPath}/telemetryAnalysis`,
`${rootPath}/document`,
`${rootPath}/measure`,
`${rootPath}/drillingProgram`,
]}/>
</Switch>
</Content>
</Layout> </Layout>
</Layout> </RootPathContext.Provider>
) )
}) })

View File

@ -1,4 +1,4 @@
import { useState, useEffect, memo } from 'react' import { useState, useEffect, memo, useContext } from 'react'
import { import {
EditableTable, EditableTable,
@ -11,6 +11,8 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
import { hasPermission } from '@utils/permissions' import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import { IdWellContext } from '../Well'
const columns = [ const columns = [
makeNumericStartEnd('Глубина, м', 'depth'), makeNumericStartEnd('Глубина, м', 'depth'),
makeNumericMinMax('Нагрузка, т', 'axialLoad'), makeNumericMinMax('Нагрузка, т', 'axialLoad'),
@ -20,10 +22,12 @@ const columns = [
makeNumericMinMax('Расход, л/с', 'flow') makeNumericMinMax('Расход, л/с', 'flow')
] ]
export const DrillProcessFlow = memo(({ idWell }) => { export const DrillProcessFlow = memo(() => {
const [flows, setFlows] = useState([]) const [flows, setFlows] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const idWell = useContext(IdWellContext)
const updateFlows = () => invokeWebApiWrapperAsync( const updateFlows = () => invokeWebApiWrapperAsync(
async () => { async () => {
const flows = await DrillFlowChartService.get(idWell) const flows = await DrillFlowChartService.get(idWell)

View File

@ -1,5 +1,5 @@
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import { memo, useState, useRef, useEffect, useCallback } from 'react' import { memo, useState, useRef, useEffect, useCallback, useContext, useMemo } from 'react'
import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons' import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons'
import { Switch, Button } from 'antd' import { Switch, Button } from 'antd'
@ -24,6 +24,7 @@ import { getOperations } from '@utils/functions'
import NptTable from './NptTable' import NptTable from './NptTable'
import NetGraphExport from './NetGraphExport' import NetGraphExport from './NetGraphExport'
import AdditionalTables from './AdditionalTables' import AdditionalTables from './AdditionalTables'
import { IdWellContext } from '@pages/Well'
import '@styles/index.css' import '@styles/index.css'
import '@styles/tvd.less' import '@styles/tvd.less'
@ -114,13 +115,16 @@ const makeDataset = (data, label, color, borderWidth = 1.5, borderDash) => ({
borderDash, borderDash,
}) })
export const Tvd = memo(({ idWell, title, ...other }) => { export const Tvd = memo(({ idWell: wellId, title, ...other }) => {
const [chart, setChart] = useState() const [chart, setChart] = useState()
const [xLabel, setXLabel] = useState('day') const [xLabel, setXLabel] = useState('day')
const [operations, setOperations] = useState({}) const [operations, setOperations] = useState({})
const [tableVisible, setTableVisible] = useState(false) const [tableVisible, setTableVisible] = useState(false)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const idWellContext = useContext(IdWellContext)
const idWell = useMemo(() => wellId ?? idWellContext, [wellId, idWellContext])
const chartRef = useRef(null) const chartRef = useRef(null)
const history = useHistory() const history = useHistory()

View File

@ -1,4 +1,4 @@
import { useState, useEffect, useCallback, memo, useMemo } from 'react' import { useState, useEffect, useCallback, memo, useMemo, useContext } from 'react'
import { import {
EditableTable, EditableTable,
@ -13,6 +13,8 @@ import { DrillParamsService, WellOperationService } from '@api'
import { hasPermission } from '@utils/permissions' import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import { IdWellContext } from '../Well'
export const getColumns = async (idWell) => { export const getColumns = async (idWell) => {
let sectionTypes = await WellOperationService.getSectionTypes(idWell) let sectionTypes = await WellOperationService.getSectionTypes(idWell)
sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({ sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({
@ -34,11 +36,13 @@ export const getColumns = async (idWell) => {
] ]
} }
export const WellDrillParams = memo(({ idWell }) => { export const WellDrillParams = memo(() => {
const [params, setParams] = useState([]) const [params, setParams] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([]) const [columns, setColumns] = useState([])
const idWell = useContext(IdWellContext)
const updateParams = useCallback(async () => await invokeWebApiWrapperAsync( const updateParams = useCallback(async () => await invokeWebApiWrapperAsync(
async () => { async () => {
const params = arrayOrDefault(await DrillParamsService.getAll(idWell)) const params = arrayOrDefault(await DrillParamsService.getAll(idWell))

View File

@ -1,7 +1,7 @@
import moment from 'moment' import moment from 'moment'
import { Input } from 'antd' import { Input } from 'antd'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import { useState, useEffect, memo, useMemo, useCallback } from 'react' import { useState, useEffect, memo, useMemo, useCallback, useContext } from 'react'
import { import {
EditableTable, EditableTable,
@ -21,6 +21,8 @@ import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
import { WellOperationService } from '@api' import { WellOperationService } from '@api'
import { IdWellContext } from '../Well'
const { TextArea } = Input const { TextArea } = Input
const basePageSize = 160 const basePageSize = 160
@ -52,12 +54,14 @@ const generateColumns = (showNpt = false, categories = [], sectionTypes = []) =>
makeTextColumn('Комментарий', 'comment', null, null, null, { editable: true, input: <TextArea/> }), makeTextColumn('Комментарий', 'comment', null, null, null, { editable: true, input: <TextArea/> }),
].filter(Boolean) ].filter(Boolean)
export const WellOperationsEditor = memo(({ idWell, idType, showNpt, ...other }) => { export const WellOperationsEditor = memo(({ idType, showNpt, ...other }) => {
const [pageNumAndPageSize, setPageNumAndPageSize] = useState({ current: 1, pageSize: basePageSize }) const [pageNumAndPageSize, setPageNumAndPageSize] = useState({ current: 1, pageSize: basePageSize })
const [paginationTotal, setPaginationTotal] = useState(0) const [paginationTotal, setPaginationTotal] = useState(0)
const [operations, setOperations] = useState([]) const [operations, setOperations] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const idWell = useContext(IdWellContext)
const [categories, setCategories] = useState([]) const [categories, setCategories] = useState([])
const [sectionTypes, setSectionTypes] = useState([]) const [sectionTypes, setSectionTypes] = useState([])

View File

@ -1,10 +1,12 @@
import { useState, useEffect, memo } from 'react' import { useState, useEffect, memo, useContext } from 'react'
import LoaderPortal from '@components/LoaderPortal' import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
import { Table, makeColumn, makeColumnsPlanFact, makeNumericRender } from '@components/Table' import { Table, makeColumn, makeColumnsPlanFact, makeNumericRender } from '@components/Table'
import { OperationStatService } from '@api'
import { calcDuration } from '@utils/datetime' import { calcDuration } from '@utils/datetime'
import { OperationStatService } from '@api'
import { IdWellContext } from '../Well'
const numericRender = makeNumericRender(2) const numericRender = makeNumericRender(2)
@ -19,10 +21,12 @@ const columns = [
makeColumnsPlanFact('Спуск ОК, м/ч' ,'casingDownSpeed', { render: numericRender }), makeColumnsPlanFact('Спуск ОК, м/ч' ,'casingDownSpeed', { render: numericRender }),
] ]
export const WellSectionsStat = memo(({ idWell }) => { export const WellSectionsStat = memo(() => {
const [sections, setSections] = useState([]) const [sections, setSections] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const sectionsResponse = await OperationStatService.getStatWell(idWell) const sectionsResponse = await OperationStatService.getStatWell(idWell)

View File

@ -1,6 +1,6 @@
import { memo, useCallback } from 'react' import { useParams, useHistory, useLocation } from 'react-router-dom'
import { Layout, Menu } from 'antd' import { memo, useCallback, useContext, useMemo } from 'react'
import { Switch, useParams, useHistory, useLocation } from 'react-router-dom' import { Layout } from 'antd'
import { import {
BarChartOutlined, BarChartOutlined,
BuildOutlined, BuildOutlined,
@ -9,7 +9,7 @@ import {
TableOutlined, TableOutlined,
} from '@ant-design/icons' } from '@ant-design/icons'
import { PrivateDefaultRoute, PrivateRoute, PrivateMenuItemLink } from '@components/Private' import { PrivateMenu, PrivateSwitch } from '@components/Private'
import { Tvd } from './Tvd' import { Tvd } from './Tvd'
import { ImportExportBar } from './ImportExportBar' import { ImportExportBar } from './ImportExportBar'
@ -17,68 +17,53 @@ import { WellDrillParams } from './WellDrillParams'
import { DrillProcessFlow } from './DrillProcessFlow' import { DrillProcessFlow } from './DrillProcessFlow'
import { WellSectionsStat } from './WellSectionsStat' import { WellSectionsStat } from './WellSectionsStat'
import { WellOperationsEditor } from './WellOperationsEditor' import { WellOperationsEditor } from './WellOperationsEditor'
import { Flex } from '@asb/components/Grid' import { Flex } from '@components/Grid'
import { RootPathContext } from '@pages/Main'
import { IdWellContext } from '@pages/Well'
const { Content } = Layout const { Content } = Layout
export const WellOperations = memo(({ idWell }) => { export const WellOperations = memo(() => {
const { tab } = useParams() const { tab } = useParams()
const history = useHistory() const history = useHistory()
const location = useLocation() const location = useLocation()
const rootPath = `/well/${idWell}/operations` const root = useContext(RootPathContext)
const idWell = useContext(IdWellContext)
const rootPath = useMemo(() => `${root}/operations`, [root])
const onImported = useCallback(() => const onImported = useCallback(() =>
history.push({ pathname: `${rootPath}`, state: { from: location.pathname }}) history.push({ pathname: `${rootPath}`, state: { from: location.pathname }})
, [history, location, rootPath]) , [history, location, rootPath])
const isIEBarDisabled = !['plan', 'fact'].includes(tab) const isIEBarDisabled = useMemo(() => !['plan', 'fact'].includes(tab), [tab])
return( return(
<> <RootPathContext.Provider value={rootPath}>
<Flex style={{ width: '100%' }}> <Flex style={{ width: '100%' }}>
<Menu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]} style={{ flex: 1 }}> <PrivateMenu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]} style={{ flex: 1 }}>
<PrivateMenuItemLink root={rootPath} icon={<LineChartOutlined />} key={'tvd'} path={'tvd'} title={'TVD'} /> <PrivateMenu.Link icon={<LineChartOutlined />} key={'tvd'} title={'TVD'} />
<PrivateMenuItemLink root={rootPath} icon={<BuildOutlined />} key={'sections'} path={'sections'} title={'Секции'} /> <PrivateMenu.Link icon={<BuildOutlined />} key={'sections'} title={'Секции'} />
<PrivateMenuItemLink root={rootPath} icon={<TableOutlined />} key={'plan'} path={'plan'} title={'План'} /> <PrivateMenu.Link icon={<TableOutlined />} key={'plan'} title={'План'} />
<PrivateMenuItemLink root={rootPath} icon={<TableOutlined />} key={'fact'} path={'fact'} title={'Факт'} /> <PrivateMenu.Link icon={<TableOutlined />} key={'fact'} title={'Факт'} />
<PrivateMenuItemLink root={rootPath} icon={<BarChartOutlined />} key={'drillProcessFlow'} path={'drillProcessFlow'} title={'РТК'} /> <PrivateMenu.Link icon={<BarChartOutlined />} key={'drillProcessFlow'} title={'РТК'} />
<PrivateMenuItemLink root={rootPath} icon={<ControlOutlined />} key={'params'} path={'params'} title={'Режимы'} /> <PrivateMenu.Link icon={<ControlOutlined />} key={'params'} title={'Режимы'} />
</Menu> </PrivateMenu>
<ImportExportBar idWell={idWell} disabled={isIEBarDisabled} onImported={onImported}/> <ImportExportBar idWell={idWell} disabled={isIEBarDisabled} onImported={onImported}/>
</Flex> </Flex>
<Layout> <Layout>
<Content className={'site-layout-background'}> <Content className={'site-layout-background'}>
<Switch> <PrivateSwitch elseRedirect={['plan', 'fact', 'tvd', 'sections', 'drillProcessFlow', 'params']}>
<PrivateRoute path={`${rootPath}/tvd`}> <Tvd key={'tvd'} />
<Tvd idWell={idWell}/> <WellSectionsStat key={'sections'} />
</PrivateRoute> <WellOperationsEditor key={'plan'} idType={0} tableName={'well_operations_plan'}/>
<PrivateRoute path={`${rootPath}/sections`}> <WellOperationsEditor key={'fact'} idType={1} showNpt tableName={'well_operations_fact'}/>
<WellSectionsStat idWell={idWell}/> <DrillProcessFlow key={'drillProcessFlow'} />
</PrivateRoute> <WellDrillParams key={'params'} />
<PrivateRoute path={`${rootPath}/plan`}> </PrivateSwitch>
<WellOperationsEditor idWell={idWell} idType={0} tableName={'well_operations_plan'}/>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/fact`}>
<WellOperationsEditor idWell={idWell} idType={1} showNpt tableName={'well_operations_fact'}/>
</PrivateRoute>
<PrivateRoute path={`${rootPath}/drillProcessFlow`}>
<DrillProcessFlow idWell={idWell} />
</PrivateRoute>
<PrivateRoute path={`${rootPath}/params`}>
<WellDrillParams idWell={idWell}/>
</PrivateRoute>
<PrivateDefaultRoute urls={[
`${rootPath}/plan`,
`${rootPath}/fact`,
`${rootPath}/tvd`,
`${rootPath}/sections`,
`${rootPath}/drillProcessFlow`,
`${rootPath}/params`
]}/>
</Switch>
</Content> </Content>
</Layout> </Layout>
</> </RootPathContext.Provider>
) )
}) })