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 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 => {
return(
<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 { Menu, MenuItemProps } from 'antd'
import { memo, NamedExoticComponent } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { isURLAvailable } from '@utils/permissions'
import { Link, useLocation } from 'react-router-dom'
export type PrivateMenuItemProps = MenuItemProps & {
root: string
path: string
}
export type PrivateMenuLinkProps = MenuItemProps & {
export type PrivateMenuItemLinkProps = MenuItemProps & {
root?: string
path: string
title: string
}
export const PrivateMenuItemLink = memo<PrivateMenuLinkProps>(({ root = '', path, title, ...other }) => {
export const PrivateMenuItemLink = memo<PrivateMenuItemLinkProps>(({ root = '', path, title, ...other }) => {
const location = useLocation()
return (
<PrivateMenuItem key={path} root={root} path={path} {...other}>
@ -26,9 +26,9 @@ export const PrivateMenuItemLink = memo<PrivateMenuLinkProps>(({ root = '', path
})
export const PrivateMenuItem: NamedExoticComponent<PrivateMenuItemProps> & {
Link: NamedExoticComponent<PrivateMenuLinkProps>
Link: NamedExoticComponent<PrivateMenuItemLinkProps>
} = 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
})

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 { PrivateContent } from './PrivateContent'
export { PrivateMenuItem, PrivateMenuItemLink } from './PrivateMenuItem'
export { PrivateContent } from './PrivateContent' // TODO: Remove
export { PrivateMenuItem, PrivateMenuItemLink } from './PrivateMenuItem' // TODO: Remove
export { PrivateDefaultRoute } from './PrivateDefaultRoute'
export { PrivateMenu, PrivateMenuLink } from './PrivateMenu'
export { PrivateSwitch } from './PrivateSwitch'
export type { PrivateRouteProps } from './PrivateRoute'
export type { PrivateContentProps } from './PrivateContent'
export type { PrivateMenuItemProps, PrivateMenuLinkProps } from './PrivateMenuItem'
export type { PrivateContentProps } from './PrivateContent' // TODO: Remove
export type { PrivateMenuItemProps, PrivateMenuItemLinkProps } from './PrivateMenuItem' // TODO: Remove
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 { lazy, memo, Suspense } from 'react'
import { Switch, useParams } from 'react-router-dom'
import { Layout } from 'antd'
import { lazy, memo, Suspense, useContext, useMemo } from 'react'
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 { RootPathContext } from '@pages/Main'
const TelemetryViewer = lazy(() => import('./TelemetryViewer'))
const TelemetryMerger = lazy(() => import('./TelemetryMerger'))
const rootPath = '/admin/telemetry'
export const Telemetry = memo(() => {
const { tab } = useParams()
return (
<Layout>
<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>
const root = useContext(RootPathContext)
const rootPath = useMemo(() => `${root}/telemetry`, [root])
return (
<RootPathContext.Provider value={rootPath}>
<Layout>
<Layout.Content className={'site-layout-background'}>
<Suspense fallback={<SuspenseFallback />}>
<Switch>
<PrivateRoute path={`${rootPath}/viewer`} component={TelemetryViewer} />
<PrivateRoute path={`${rootPath}/merger`} component={TelemetryMerger} />
<PrivateDefaultRoute urls={[
`${rootPath}/viewer`,
`${rootPath}/merger`,
]}/>
</Switch>
</Suspense>
</Layout.Content>
<PrivateMenu mode={'horizontal'} selectable={true} selectedKeys={[tab]}>
<PrivateMenu.Link key={'viewer'} title={'Просмотр'} />
<PrivateMenu.Link key={'merger'} title={'Объединение'} />
</PrivateMenu>
<Layout>
<Layout.Content className={'site-layout-background'}>
<Suspense fallback={<SuspenseFallback />}>
<PrivateSwitch elseRedirect={['viewer', 'merger']}>
<TelemetryViewer key={'viewer'} />
<TelemetryMerger key={'merger'} />
</PrivateSwitch>
</Suspense>
</Layout.Content>
</Layout>
</Layout>
</Layout>
</RootPathContext.Provider>
)
})

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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 { invokeWebApiWrapperAsync } from '@components/factory'
@ -8,6 +8,8 @@ import { makeGroupColumn, makeNumericColumn, makeNumericRender, makeTextColumn,
import { OperationStatService, WellOperationService } from '@api'
import { arrayOrDefault } from '@utils'
import { IdWellContext } from '../Well'
import '@styles/index.css'
import '@styles/statistics.less'
@ -62,7 +64,7 @@ const getWellData = async (wellsList) => {
return wellData
}
export const Statistics = memo(({ idWell }) => {
export const Statistics = memo(() => {
const [sectionTypes, setSectionTypes] = useState([])
const [avgColumns, setAvgColumns] = useState(defaultColumns)
const [cmpColumns, setCmpColumns] = useState(defaultColumns)
@ -75,6 +77,8 @@ export const Statistics = memo(({ idWell }) => {
const [cmpData, setCmpData] = useState([])
const [avgRow, setAvgRow] = useState({})
const idWell = useContext(IdWellContext)
const cmpSpeedRender = useCallback((key) => (section) => {
let spanClass = ''
// Дополнительная проверка на "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 { Table } from '@components/Table'
@ -6,14 +6,17 @@ import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillParamsService } from '@api'
import { IdWellContext } from '@pages/Well'
import { getColumns } from '@pages/WellOperations/WellDrillParams'
export const NewParamsTable = memo(({ idWell, selectedWellsKeys }) => {
export const NewParamsTable = memo(({ selectedWellsKeys }) => {
const [params, setParams] = useState([])
const [paramsColumns, setParamsColumns] = useState([])
const [showParamsLoader, setShowParamsLoader] = useState(false)
const [isParamsModalVisible, setIsParamsModalVisible] = useState(false)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync(
async () => setParamsColumns(await getColumns(idWell))
), [idWell])

View File

@ -1,5 +1,5 @@
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 { Table, Tag, Button, Badge, Divider, Modal, Row, Col } from 'antd'
@ -15,6 +15,7 @@ import {
getOperations
} from '@utils/functions'
import { IdWellContext } from '@pages/Well'
import { Tvd } from '@pages/WellOperations/Tvd'
import WellOperationsTable from '@pages/Cluster/WellOperationsTable'
import NewParamsTable from './NewParamsTable'
@ -29,7 +30,7 @@ const sortBySectionId = (a, b) => a.sectionId - b.sectionId
const filtersSectionsType = []
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 [wellOperations, setWellOperations] = useState([])
const [selectedWellsKeys, setSelectedWellsKeys] = useState([])
@ -38,6 +39,8 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
const [isTVDModalVisible, setIsTVDModalVisible] = useState(false)
const [isOpsModalVisible, setIsOpsModalVisible] = useState(false)
const idWell = useContext(IdWellContext)
const location = useLocation()
const rows = useMemo(() => {
@ -208,9 +211,7 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
pagination={false}
/>
<Row justify={'end'} style={{ margin: '1rem 0' }}>
<Col>
<NewParamsTable idWell={idWell} selectedWellsKeys={selectedWellsKeys} />
</Col>
<Col><NewParamsTable selectedWellsKeys={selectedWellsKeys} /></Col>
</Row>
<Modal

View File

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

View File

@ -1,36 +1,35 @@
import { memo, useMemo } from 'react'
import { Layout, Menu } from 'antd'
import { Switch, useParams } from 'react-router-dom'
import { memo, useContext, useMemo } from 'react'
import { 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 WellCompositeEditor from './WellCompositeEditor'
import { RootPathContext } from '@pages/Main'
export const Analytics = memo(({ idWell }) => {
export const Analytics = memo(() => {
const { tab } = useParams()
const rootPath = useMemo(() => `/well/${idWell}/analytics`, [idWell])
const root = useContext(RootPathContext)
const rootPath = useMemo(() => `${root}/analytics`, [root])
return (
<Layout>
<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>
<RootPathContext.Provider value={rootPath}>
<Layout>
<Layout.Content>
<Switch>
<PrivateRoute path={`${rootPath}/composite/:tab?`}>
<WellCompositeEditor idWell={idWell} rootPath={`${rootPath}/composite`} />
</PrivateRoute>
<PrivateRoute path={`${rootPath}/statistics`}>
<Statistics idWell={idWell} />
</PrivateRoute>
<PrivateDefaultRoute urls={[`${rootPath}/composite`]}/>
</Switch>
</Layout.Content>
<PrivateMenu mode={'horizontal'} selectable={true} className={'well_menu'} selectedKeys={[tab]}>
<PrivateMenu.Link key={'composite'} title={'Композитная скважина'} />
<PrivateMenu.Link key={'statistics'} title={'Оценка по ЦБ'} />
</PrivateMenu>
<Layout>
<Layout.Content>
<PrivateSwitch elseRedirect={'composite'}>
<WellCompositeEditor key={'composite/:tab?'} rootPath={`${rootPath}/composite`} />
<Statistics key={'statistics'} />
</PrivateSwitch>
</Layout.Content>
</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 { FileService } from '@api'
@ -9,6 +9,8 @@ import { EditableTable, makeColumn, makeDateColumn, makeNumericColumn, makePagin
import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory'
import { hasPermission } from '@utils/permissions'
import { IdWellContext } from '@pages/Well'
const pageSize = 12
const { RangePicker } = DatePicker
const { Search } = Input
@ -30,7 +32,7 @@ const columns = [
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 [filterDataRange, setFilterDataRange] = useState([])
const [filterCompanyName, setFilterCompanyName] = useState([])
@ -39,6 +41,9 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
const [files, setFiles] = useState([])
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 mergedColumns = useMemo(() => [...columns, ...(customColumns ?? [])], [customColumns])

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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 { UserView } from '@components/views'
@ -9,6 +9,8 @@ import { makeColumn, makeNumericSorter, Table } from '@components/Table'
import { DrillingProgramService } from '@api'
import { arrayOrDefault } from '@utils'
import { IdWellContext } from '@pages/Well'
const userRules = [
{ label: 'Нет', value: 0 },
{ label: 'Публикатор', value: 1 },
@ -17,7 +19,7 @@ const userRules = [
const SEARCH_TIMEOUT = 400
export const CategoryEditor = memo(({ idWell, visible, category, onClosed }) => {
export const CategoryEditor = memo(({ visible, category, onClosed }) => {
const [title, setTitle] = useState()
const [users, setUsers] = useState([])
const [allUsers, setAllUsers] = useState([])
@ -27,6 +29,8 @@ export const CategoryEditor = memo(({ idWell, visible, category, onClosed }) =>
const [searchValue, setSearchValue] = useState('')
const [subject, setSubject] = useState(null)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync(
async () => {
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 { CompanyView } from '@components/views'
@ -10,6 +10,7 @@ import { arrayOrDefault, formatDate } from '@utils'
import { FileService } from '@api'
import MarksCard from './MarksCard'
import { IdWellContext } from '@pages/Well'
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 [page, setPage] = useState(1)
const [total, setTotal] = useState(0)
@ -64,6 +65,8 @@ export const CategoryHistory = ({ idWell, idCategory, visible, onClose }) => {
const [isLoading, setIsLoading] = useState(false)
const [companyName, setCompanyName] = useState('')
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync(
async () => {
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 {
DeleteOutlined,
@ -16,6 +16,7 @@ import { DrillingProgramService } from '@api'
import { formatDate } from '@utils'
import MarksCard from './MarksCard'
import { IdWellContext } from '@pages/Well'
import '@styles/drilling_program.less'
@ -34,7 +35,7 @@ const CommentPrompt = memo(({ isRequired = true, ...props }) => (
</Poprompt>
))
export const CategoryRender = memo(({ idWell, partData, onUpdate, onEdit, onHistory, setIsLoading, ...other }) => {
export const CategoryRender = memo(({ partData, onUpdate, onEdit, onHistory, setIsLoading, ...other }) => {
const {
idFileCategory,
name: title, // Название категории
@ -44,6 +45,8 @@ export const CategoryRender = memo(({ idWell, partData, onUpdate, onEdit, onHist
file // Информация о файле
} = partData ?? {}
const idWell = useContext(IdWellContext)
const uploadUrl = useMemo(() => `/api/well/${idWell}/drillingProgram/part/${idFileCategory}`, [idWell, idFileCategory])
const approvedMarks = useMemo(() => file?.fileMarks?.filter((mark) => mark.idMarkType === 1), [file])
const rejectMarks = useMemo(() => file?.fileMarks?.filter((mark) => mark.idMarkType === 0), [file])

View File

@ -8,7 +8,7 @@ import {
ReloadOutlined,
WarningOutlined,
} 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 { downloadFile, formatBytes, invokeWebApiWrapperAsync } from '@components/factory'
@ -19,6 +19,7 @@ import CategoryAdder from './CategoryAdder'
import CategoryRender from './CategoryRender'
import CategoryEditor from './CategoryEditor'
import CategoryHistory from './CategoryHistory'
import { IdWellContext } from '@pages/Well'
import '@styles/drilling_program.less'
@ -38,7 +39,7 @@ const stateString = {
[idStateUnknown]: { icon: WarningOutlined, text: 'Неизвестно' },
}
export const DrillingProgram = memo(({ idWell }) => {
export const DrillingProgram = memo(() => {
const [selectedCategory, setSelectedCategory] = useState()
const [historyVisible, setHistoryVisible] = useState(false)
const [editorVisible, setEditorVisible] = useState(false)
@ -46,6 +47,8 @@ export const DrillingProgram = memo(({ idWell }) => {
const [categories, setCategories] = useState([])
const [data, setData] = useState({})
const idWell = useContext(IdWellContext)
const {
idState,
permissionToEdit,
@ -99,11 +102,7 @@ export const DrillingProgram = memo(({ idWell }) => {
<h3>Программа бурения</h3>
{permissionToEdit && (
<div>
<CategoryAdder
idWell={idWell}
categories={categories}
onUpdate={updateData}
/>
<CategoryAdder categories={categories} onUpdate={updateData} />
</div>
)}
</div>
@ -143,7 +142,6 @@ export const DrillingProgram = memo(({ idWell }) => {
{parts?.map?.((part, idx) => part && (
<CategoryRender
key={`${idx}`}
idWell={idWell}
partData={part}
onEdit={permissionToEdit && onCategoryEdit}
onUpdate={updateData}
@ -154,7 +152,6 @@ export const DrillingProgram = memo(({ idWell }) => {
{permissionToEdit && (
<>
<CategoryEditor
idWell={idWell}
visible={editorVisible}
onClosed={onEditorClosed}
category={parts?.find((part) => part.idFileCategory === selectedCategory) ?? {}}
@ -163,7 +160,6 @@ export const DrillingProgram = memo(({ idWell }) => {
)}
<CategoryHistory
idWell={idWell}
idCategory={selectedCategory}
onClose={() => setHistoryVisible(false)}
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 { AdminLayoutPortal, LayoutPortal } from '@components/Layout'
@ -10,33 +10,37 @@ import Deposit from './Deposit'
import AdminPanel from './AdminPanel'
import AccessDenied from './AccessDenied'
export const RootPathContext = createContext('')
export const Main = memo(() => (
<Switch>
<PrivateRoute path={'/admin/:tab?'}>
<AdminLayoutPortal title={'Администраторская панель'}>
<AdminPanel />
</AdminLayoutPortal>
</PrivateRoute>
<PrivateRoute path={'/deposit'}>
<LayoutPortal noSheet title='Месторождение'>
<Deposit />
</LayoutPortal>
</PrivateRoute>
<PrivateRoute path={'/cluster/:idCluster'}>
<LayoutPortal title={'Анализ скважин куста'}>
<Cluster />
</LayoutPortal>
</PrivateRoute>
<PrivateRoute path={'/well/:idWell/:tab?'}>
<LayoutPortal>
<Well />
</LayoutPortal>
</PrivateRoute>
<Route path={'/access_denied'}>
<AccessDenied />
</Route>
<PrivateDefaultRoute urls={['/deposit']} />
</Switch>
<RootPathContext.Provider value={''}>
<Switch>
<PrivateRoute path={'/admin/:tab?'}>
<AdminLayoutPortal title={'Администраторская панель'}>
<AdminPanel />
</AdminLayoutPortal>
</PrivateRoute>
<PrivateRoute path={'/deposit'}>
<LayoutPortal noSheet title='Месторождение'>
<Deposit />
</LayoutPortal>
</PrivateRoute>
<PrivateRoute path={'/cluster/:idCluster'}>
<LayoutPortal title={'Анализ скважин куста'}>
<Cluster />
</LayoutPortal>
</PrivateRoute>
<PrivateRoute path={'/well/:idWell/:tab?'}>
<LayoutPortal>
<Well />
</LayoutPortal>
</PrivateRoute>
<Route path={'/access_denied'}>
<AccessDenied />
</Route>
<PrivateDefaultRoute urls={['/deposit']} />
</Switch>
</RootPathContext.Provider>
))
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 {
CheckSquareOutlined,
@ -16,6 +16,7 @@ import { formatDate } from '@utils'
import { MeasureService } from '@api'
import { View } from './View'
import { IdWellContext } from '@pages/Well'
import '@styles/index.css'
import '@styles/measure.css'
@ -25,13 +26,15 @@ const createEditingColumns = (cols, renderDelegate) =>
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 [displayedValues, setDisplayedValues] = useState({})
const [editingColumns, setEditingColumns] = useState(group.columns)
const [isTableEditing, setIsTableEditing] = useState(false)
const [editingActionName, setEditingActionName] = useState('')
const idWell = useContext(IdWellContext)
const [measuresForm] = Form.useForm()
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 { useState, useEffect, memo } from 'react'
import { useState, useEffect, memo, useContext } from 'react'
import { TableOutlined } from '@ant-design/icons'
import { MeasureService } from '@api'
@ -11,6 +11,7 @@ import { InclinometryTable } from './InclinometryTable'
import { columnsNnb, nnbDefaultData } from './nnbData'
import { columnsMudDiagram, mudDiagramDefaultData } from './mudDiagramData'
import { columnsDrillingFluid, drillingFluidDefaultData } from './drillingFluidData'
import { IdWellContext } from '@pages/Well'
const defaultData = [
{
@ -41,12 +42,14 @@ const defaultData = [
}
]
export const Measure = memo(({ idWell }) => {
export const Measure = memo(() => {
const [showLoader, setShowLoader] = useState(false)
const [isMeasuresUpdating, setIsMeasuresUpdating] = useState(true)
const [data, setData] = useState(defaultData)
const [tableIdx, setTableIdx] = useState(-1)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync(
async () => {
if (!isMeasuresUpdating) return
@ -73,7 +76,6 @@ export const Measure = memo(({ idWell }) => {
{data.map((group, idx) => (
<MeasureTable
key={idx}
idWell={idWell}
group={group}
updateMeasuresFunc={() => setIsMeasuresUpdating(true)}
additionalButtons={group.additionalButtons?.(group, idx, setTableIdx)}

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/* 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 LoaderPortal from '@components/LoaderPortal'
@ -8,6 +8,7 @@ import { DatePickerWrapper, makeDateSorter } from '@components/Table'
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
import { TelemetryDataSaubService } from '@api'
import { IdWellContext } from '@pages/Well'
import { normalizeData } from '../TelemetryView'
import { ArchiveDisplay, cutData } from './ArchiveDisplay'
@ -61,7 +62,7 @@ const range = (start, end) => {
return result
}
export const Archive = memo(({ idWell }) => {
export const Archive = memo(() => {
const [dataSaub, setDataSaub] = useState([])
const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() })
const [chartInterval, setChartInterval] = useState(parseInt(defaultPeriod) * 1000)
@ -69,6 +70,8 @@ export const Archive = memo(({ idWell }) => {
const [showLoader, setShowLoader] = useState(false)
const [loaded, setLoaded] = useState(null)
const idWell = useContext(IdWellContext)
const onGraphWheel = useCallback((e) => {
if (loaded && dateLimit.from && dateLimit.to) {
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 { CloseOutlined } from '@ant-design/icons'
import { Button, Menu, Popconfirm } from 'antd'
@ -19,10 +19,12 @@ import {
WitsRecord61Service,
} from '@api'
import { IdWellContext } from '@pages/Well'
import { RootPathContext } from '@pages/Main'
import AddGroupWindow from './AddGroupWindow'
import AddWidgetWindow, { makeWidgetFromWits } from './AddWidgetWindow'
import '@styles/dashboard_nnb.less'
import AddGroupWindow from './AddGroupWindow'
const getWitsInfo = async () => {
// TODO: Добавить expire с принудительным обновлением
@ -105,19 +107,21 @@ const groupsReducer = (groups, action) => {
return newGroups
}
export const DashboardNNB = memo(({ idWell }) => {
export const DashboardNNB = memo(() => {
const [groups, dispatchGroups] = useReducer(groupsReducer, [])
const [witsInfo, setWitsInfo] = useState([])
const [isLoading, setIsLoading] = useState(false)
const [selectedSettings, setSelectedSettings] = useState(null)
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 { tab: selectedGroup } = useParams()
if (!selectedGroup && groups?.length > 0)
history.push(`${root}/${groups[0].id}`)
history.push(`${rootPath}/${groups[0].id}`)
const group = useMemo(() => ({
@ -172,8 +176,8 @@ export const DashboardNNB = memo(({ idWell }) => {
const removeGroup = useCallback((id) => {
dispatchGroups({ type: 'remove_group', groupId: `${id}` })
if (id === selectedGroup) history.push(`${root}`)
}, [root, history, selectedGroup])
if (id === selectedGroup) history.push(`${rootPath}`)
}, [rootPath, history, selectedGroup])
const addWidget = useCallback((settings) => dispatchGroups({
type: 'add_widget',
@ -226,7 +230,7 @@ export const DashboardNNB = memo(({ idWell }) => {
<Button type={'link'} icon={<CloseOutlined />} />
</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>

View File

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

View File

@ -1,5 +1,5 @@
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 { invokeWebApiWrapperAsync } from '@components/factory'
@ -7,13 +7,16 @@ import { Subscribe } from '@services/signalr'
import { MessageService } from '@api'
import { columns } from '../Messages'
import { IdWellContext } from '@pages/Well'
import '@styles/message.css'
export const ActiveMessagesOnline = memo(({ idWell }) => {
export const ActiveMessagesOnline = memo(() => {
const [messages, setMessages] = useState([])
const [loader, setLoader] = useState(false)
const idWell = useContext(IdWellContext)
const handleReceiveMessages = useCallback((messages) => {
if (messages)
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 { SetpointsService } from '@api'
@ -8,12 +8,16 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
import { makeNumericRender, EditableTable } from '@components/Table'
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 [comment, setComment] = useState('')
const [setpoints, setSetpoints] = useState([])
const [isLoading, setIsLoading] = useState(false)
const idWell = useContext(IdWellContext)
const addingColumns = useMemo(() => [
{
title: 'Наименование уставки',

View File

@ -1,5 +1,5 @@
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 { UserView } from '@components/views'
@ -10,10 +10,11 @@ import { makeStringCutter } from '@utils/string'
import { formatDate } from '@utils'
import { SetpointsService } from '@api'
import { IdWellContext } from '@pages/Well'
import SetpointSender from './SetpointSender'
import { SetpointViewer, getSetpointStatus } from './SetpointViewer'
export const Setpoints = memo(({ idWell, ...other }) => {
export const Setpoints = memo(({ ...other }) => {
const [isModalVisible, setIsModalVisible] = useState(false)
const [isSenderVisible, setIsSenderVisible] = useState(false)
const [isViewerVisible, setIsViewerVisible] = useState(false)
@ -22,6 +23,8 @@ export const Setpoints = memo(({ idWell, ...other }) => {
const [selected, setSelected] = useState(null)
const [setpointNames, setSetpointNames] = useState([])
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const names = await SetpointsService.getSetpointsNamesByIdWell(idWell)
@ -102,7 +105,6 @@ export const Setpoints = memo(({ idWell, ...other }) => {
</LoaderPortal>
</Modal>
<SetpointSender
idWell={idWell}
setpointNames={setpointNames}
visible={isSenderVisible}
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 { invokeWebApiWrapperAsync } from '@components/factory'
import { WirelineView } from '@components/views'
export const WirelineRunOut = memo(({ idWell }) => {
import { IdWellContext } from '@pages/Well'
export const WirelineRunOut = memo(() => {
const [twro, setTwro] = useState({})
const [isLoading, setIsLoading] = useState(false)
const idWell = useContext(IdWellContext)
const update = useCallback(() => invokeWebApiWrapperAsync(
async () => {
const twro = await TelemetryWirelineRunOutService.getData(idWell)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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 { Switch, Button } from 'antd'
@ -24,6 +24,7 @@ import { getOperations } from '@utils/functions'
import NptTable from './NptTable'
import NetGraphExport from './NetGraphExport'
import AdditionalTables from './AdditionalTables'
import { IdWellContext } from '@pages/Well'
import '@styles/index.css'
import '@styles/tvd.less'
@ -114,13 +115,16 @@ const makeDataset = (data, label, color, borderWidth = 1.5, borderDash) => ({
borderDash,
})
export const Tvd = memo(({ idWell, title, ...other }) => {
export const Tvd = memo(({ idWell: wellId, title, ...other }) => {
const [chart, setChart] = useState()
const [xLabel, setXLabel] = useState('day')
const [operations, setOperations] = useState({})
const [tableVisible, setTableVisible] = useState(false)
const [isLoading, setIsLoading] = useState(false)
const idWellContext = useContext(IdWellContext)
const idWell = useMemo(() => wellId ?? idWellContext, [wellId, idWellContext])
const chartRef = useRef(null)
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 {
EditableTable,
@ -13,6 +13,8 @@ import { DrillParamsService, WellOperationService } from '@api'
import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils'
import { IdWellContext } from '../Well'
export const getColumns = async (idWell) => {
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
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 [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([])
const idWell = useContext(IdWellContext)
const updateParams = useCallback(async () => await invokeWebApiWrapperAsync(
async () => {
const params = arrayOrDefault(await DrillParamsService.getAll(idWell))

View File

@ -1,7 +1,7 @@
import moment from 'moment'
import { Input } from 'antd'
import { useLocation } from 'react-router-dom'
import { useState, useEffect, memo, useMemo, useCallback } from 'react'
import { useState, useEffect, memo, useMemo, useCallback, useContext } from 'react'
import {
EditableTable,
@ -21,6 +21,8 @@ import { hasPermission } from '@utils/permissions'
import { arrayOrDefault } from '@utils'
import { WellOperationService } from '@api'
import { IdWellContext } from '../Well'
const { TextArea } = Input
const basePageSize = 160
@ -52,12 +54,14 @@ const generateColumns = (showNpt = false, categories = [], sectionTypes = []) =>
makeTextColumn('Комментарий', 'comment', null, null, null, { editable: true, input: <TextArea/> }),
].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 [paginationTotal, setPaginationTotal] = useState(0)
const [operations, setOperations] = useState([])
const [showLoader, setShowLoader] = useState(false)
const idWell = useContext(IdWellContext)
const [categories, setCategories] = 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 { invokeWebApiWrapperAsync } from '@components/factory'
import { Table, makeColumn, makeColumnsPlanFact, makeNumericRender } from '@components/Table'
import { OperationStatService } from '@api'
import { calcDuration } from '@utils/datetime'
import { OperationStatService } from '@api'
import { IdWellContext } from '../Well'
const numericRender = makeNumericRender(2)
@ -19,10 +21,12 @@ const columns = [
makeColumnsPlanFact('Спуск ОК, м/ч' ,'casingDownSpeed', { render: numericRender }),
]
export const WellSectionsStat = memo(({ idWell }) => {
export const WellSectionsStat = memo(() => {
const [sections, setSections] = useState([])
const [showLoader, setShowLoader] = useState(false)
const idWell = useContext(IdWellContext)
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const sectionsResponse = await OperationStatService.getStatWell(idWell)

View File

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