forked from ddrilling/asb_cloud_front
Добавлена страница Оценки по ЦБ
This commit is contained in:
parent
6a09ab9af3
commit
afed418b56
@ -1,6 +1,6 @@
|
||||
import { memo, useCallback, useEffect, useState, ReactNode } from 'react'
|
||||
import { Table as RawTable, TableProps } from 'antd'
|
||||
import { memo, useCallback, useEffect, useState } from 'react'
|
||||
import { ColumnGroupType, ColumnType } from 'antd/lib/table'
|
||||
import { Table as RawTable, TableProps } from 'antd'
|
||||
|
||||
import { OmitExtends } from '@utils'
|
||||
import { getTableSettings, setTableSettings } from '@utils/storage'
|
||||
@ -9,50 +9,50 @@ import { applySettings, ColumnSettings, TableSettings } from '@utils/table_setti
|
||||
import TableSettingsChanger from './TableSettingsChanger'
|
||||
import { tryAddKeys } from './EditableTable'
|
||||
|
||||
import '@styles/index.css'
|
||||
|
||||
export type BaseTableColumn<T = any> = ColumnGroupType<T> | ColumnType<T>
|
||||
export type TableColumns<T = any> = OmitExtends<BaseTableColumn<T>, ColumnSettings>[]
|
||||
|
||||
export type TableContainer = TableProps<any> & {
|
||||
columns: TableColumns
|
||||
dataSource: any[]
|
||||
tableName?: string
|
||||
showSettingsChanger?: boolean
|
||||
columns: TableColumns
|
||||
dataSource: any[]
|
||||
tableName?: string
|
||||
showSettingsChanger?: boolean
|
||||
}
|
||||
|
||||
export const Table = memo<TableContainer>(({ columns, dataSource, tableName, showSettingsChanger, ...other }) => {
|
||||
const [newColumns, setNewColumns] = useState<TableColumns>([])
|
||||
const [settings, setSettings] = useState<TableSettings>({})
|
||||
const [newColumns, setNewColumns] = useState<TableColumns>([])
|
||||
const [settings, setSettings] = useState<TableSettings>({})
|
||||
|
||||
const onSettingsChanged = useCallback((settings?: TableSettings | null) => {
|
||||
if (tableName)
|
||||
setTableSettings(tableName, settings)
|
||||
setSettings(settings ?? {})
|
||||
}, [tableName])
|
||||
const onSettingsChanged = useCallback((settings?: TableSettings | null) => {
|
||||
if (tableName)
|
||||
setTableSettings(tableName, settings)
|
||||
setSettings(settings ?? {})
|
||||
}, [tableName])
|
||||
|
||||
useEffect(() => setSettings(tableName ? getTableSettings(tableName) : {}), [tableName])
|
||||
useEffect(() => setNewColumns(() => {
|
||||
const newColumns = applySettings(columns, settings)
|
||||
if (tableName && showSettingsChanger) {
|
||||
const oldTitle = newColumns[0].title
|
||||
newColumns[0].title = (props) => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'stretch', justifyContent: 'space-between', position: 'relative', padding: '16px 0' }}>
|
||||
<TableSettingsChanger columns={columns} settings={settings} onChange={onSettingsChanged}/>
|
||||
<div>
|
||||
{typeof oldTitle === 'function' ? oldTitle(props) : oldTitle}
|
||||
useEffect(() => setSettings(tableName ? getTableSettings(tableName) : {}), [tableName])
|
||||
useEffect(() => setNewColumns(() => {
|
||||
const newColumns = applySettings(columns, settings)
|
||||
if (tableName && showSettingsChanger) {
|
||||
const oldTitle = newColumns[0].title
|
||||
newColumns[0].title = (props) => (
|
||||
<div className={'first-column-title'}>
|
||||
<TableSettingsChanger columns={columns} settings={settings} onChange={onSettingsChanged}/>
|
||||
<div>{typeof oldTitle === 'function' ? oldTitle(props) : oldTitle}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return newColumns
|
||||
}), [settings, columns, onSettingsChanged, showSettingsChanger, tableName])
|
||||
)
|
||||
}
|
||||
return newColumns
|
||||
}), [settings, columns, onSettingsChanged, showSettingsChanger, tableName])
|
||||
|
||||
return (
|
||||
<RawTable
|
||||
columns={newColumns}
|
||||
dataSource={tryAddKeys(dataSource)}
|
||||
{...other}
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<RawTable
|
||||
columns={newColumns}
|
||||
dataSource={tryAddKeys(dataSource)}
|
||||
{...other}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export default Table
|
||||
|
77
src/components/WellSelector.jsx
Normal file
77
src/components/WellSelector.jsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { Tag, TreeSelect } from 'antd'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { hasPermission } from '@utils/permissions'
|
||||
import { DepositService } from '@api'
|
||||
|
||||
export const getTreeData = async () => {
|
||||
const deposits = await DepositService.getDeposits()
|
||||
const wellsTree = deposits.map((deposit, dIdx) => ({
|
||||
title: deposit.caption,
|
||||
key: `0-${dIdx}`,
|
||||
value: `0-${dIdx}`,
|
||||
children: deposit.clusters.map((cluster, cIdx) => ({
|
||||
title: cluster.caption,
|
||||
key: `0-${dIdx}-${cIdx}`,
|
||||
value: `0-${dIdx}-${cIdx}`,
|
||||
children: cluster.wells.map(well => ({
|
||||
title: well.caption,
|
||||
key: well.id,
|
||||
value: well.id,
|
||||
})),
|
||||
})),
|
||||
}))
|
||||
|
||||
return wellsTree
|
||||
}
|
||||
|
||||
export const getTreeLabels = (treeData) => {
|
||||
const labels = {}
|
||||
treeData.forEach((deposit) =>
|
||||
deposit?.children?.forEach((cluster) =>
|
||||
cluster?.children?.forEach((well) => {
|
||||
labels[well.value] = `${deposit.title}.${cluster.title}.${well.title}`
|
||||
})))
|
||||
return labels
|
||||
}
|
||||
|
||||
export const WellSelector = memo(({ idWell, value, onChange, treeData, treeLabels, ...other }) => {
|
||||
const [wellsTree, setWellsTree] = useState([])
|
||||
const [wellLabels, setWellLabels] = useState([])
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const wellsTree = treeData ?? await getTreeData()
|
||||
const labels = treeLabels ?? getTreeLabels(wellsTree)
|
||||
setWellsTree(wellsTree)
|
||||
setWellLabels(labels)
|
||||
},
|
||||
null,
|
||||
'Не удалось загрузить список скважин',
|
||||
'Получение списка скважин'
|
||||
), [idWell, treeData])
|
||||
|
||||
return (
|
||||
<TreeSelect
|
||||
multiple
|
||||
treeCheckable
|
||||
showCheckedStrategy={TreeSelect.SHOW_CHILD}
|
||||
treeDefaultExpandAll
|
||||
treeData={wellsTree}
|
||||
treeLine={{ showLeafIcon: false }}
|
||||
size={'middle'}
|
||||
style={{ width: '100%' }}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={'Выберите скважины'}
|
||||
tagRender={(props) => (
|
||||
<Tag {...props}>{wellLabels[props.value] ?? props.label}</Tag>
|
||||
)}
|
||||
disabled={wellsTree.length <= 0 || !hasPermission('WellOperation.edit')}
|
||||
{...other}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export default WellSelector
|
218
src/pages/Analytics/Statistics.jsx
Normal file
218
src/pages/Analytics/Statistics.jsx
Normal file
@ -0,0 +1,218 @@
|
||||
import { Table as RawTable, Typography } from 'antd'
|
||||
import { Fragment, memo, useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import LoaderPortal from '@components/LoaderPortal'
|
||||
import { getTreeData, getTreeLabels, WellSelector } from '@components/WellSelector'
|
||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||
import { makeGroupColumn, makeNumericColumn, makeNumericRender, makeTextColumn, Table } from '@components/Table'
|
||||
import { OperationStatService, WellOperationService } from '@api'
|
||||
import { arrayOrDefault } from '@utils'
|
||||
|
||||
import '@styles/index.css'
|
||||
import '@styles/statistics.less'
|
||||
|
||||
const { Text } = Typography
|
||||
const { Summary } = RawTable
|
||||
const { Cell, Row } = Summary
|
||||
|
||||
const numericRender = makeNumericRender()
|
||||
const speedNumericRender = (section) => numericRender(section?.speed)
|
||||
|
||||
export const makeSectionColumn = (title, key, { speedRender } = {}) => makeGroupColumn(title, [
|
||||
makeNumericColumn('Проходка', key, null, null, (section => numericRender(section?.depth)), 100),
|
||||
makeNumericColumn('Время', key, null, null, (section => numericRender(section?.time)), 100),
|
||||
makeNumericColumn((<>V<sub>рейсовая</sub></>), key, null, null, speedRender ?? speedNumericRender, 100),
|
||||
])
|
||||
|
||||
export const defaultColumns = [
|
||||
//makeTextColumn('Куст', 'cluster', null, null, null, { fixed: 'left', width: 100 }),
|
||||
makeTextColumn('Скважина', 'caption', null, null, null, { fixed: 'left', width: 100 }),
|
||||
]
|
||||
|
||||
const scrollSettings = { scrollToFirstRowOnChange: true, x: 100, y: 200 }
|
||||
const summaryColSpan = 1 /// TODO: Когда добавится куст изменить на 2
|
||||
|
||||
const getWellData = async (wellsList) => {
|
||||
const stats = arrayOrDefault(await OperationStatService.getWellsStat(wellsList))
|
||||
const wellData = stats.map((well) => {
|
||||
const stat = {
|
||||
// cluster: null,
|
||||
caption: well.caption,
|
||||
}
|
||||
|
||||
well.sections?.forEach(({ id, fact }) => {
|
||||
if (!fact) return
|
||||
stat[`section_${id}`] = {
|
||||
time: (+new Date(fact.end) - +new Date(fact.start)) / 3600_000,
|
||||
depth: fact.wellDepthEnd - fact.wellDepthStart,
|
||||
speed: fact.routeSpeed,
|
||||
}
|
||||
})
|
||||
|
||||
return stat
|
||||
})
|
||||
|
||||
return wellData
|
||||
}
|
||||
|
||||
export const Statistics = memo(({ idWell }) => {
|
||||
const [sectionTypes, setSectionTypes] = useState([])
|
||||
const [avgColumns, setAvgColumns] = useState(defaultColumns)
|
||||
const [cmpColumns, setCmpColumns] = useState(defaultColumns)
|
||||
const [isAvgTableLoading, setIsAvgTableLoading] = useState(false)
|
||||
const [isCmpTableLoading, setIsCmpTableLoading] = useState(false)
|
||||
const [isPageLoading, setIsPageLoading] = useState(false)
|
||||
const [avgWells, setAvgWells] = useState([])
|
||||
const [cmpWells, setCmpWells] = useState([])
|
||||
const [filteredCmpWells, setFilteredCmpWells] = useState([])
|
||||
const [avgData, setAvgData] = useState([])
|
||||
const [cmpData, setCmpData] = useState([])
|
||||
const [avgRow, setAvgRow] = useState({})
|
||||
|
||||
const cmpSpeedRender = useCallback((key) => (section) => {
|
||||
let spanClass = ''
|
||||
// Дополнительная проверка на "null" необходима, чтобы значение "0" не стало исключением
|
||||
if ((avgRow[key]?.speed ?? null) !== null && (section?.speed ?? null) !== null) {
|
||||
const avgSpeed = avgRow[key].speed - section.speed
|
||||
if (avgSpeed < 0)
|
||||
spanClass = 'high-efficienty'
|
||||
else if (avgSpeed > 0)
|
||||
spanClass = 'low-efficienty'
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={spanClass}>
|
||||
{numericRender(section?.speed)}
|
||||
</span>
|
||||
)
|
||||
}, [avgRow])
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const types = await WellOperationService.getSectionTypes(idWell)
|
||||
setSectionTypes(Object.entries(types))
|
||||
setAvgColumns([
|
||||
...defaultColumns,
|
||||
...Object.entries(types).map(([id, name]) => makeSectionColumn(name, `section_${id}`)),
|
||||
])
|
||||
setCmpColumns([
|
||||
...defaultColumns,
|
||||
...Object.entries(types).map(([id, name]) => makeSectionColumn(name, `section_${id}`, {
|
||||
speedRender: cmpSpeedRender(`section_${id}`)
|
||||
}))
|
||||
])
|
||||
},
|
||||
setIsPageLoading,
|
||||
`Не удалось получить типы секции`,
|
||||
`Получение списка возможных секций`,
|
||||
), [idWell, cmpSpeedRender])
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const avgData = await getWellData(avgWells)
|
||||
setAvgData(avgData)
|
||||
|
||||
const avgRow = {}
|
||||
|
||||
avgData.forEach((row) => row && Object.keys(row).forEach((key) => {
|
||||
if (!key.startsWith('section_')) return
|
||||
if (!avgRow[key]) avgRow[key] = { depth: 0, time: 0, speed: 0, count: 0 }
|
||||
avgRow[key].depth += row[key].depth ?? 0
|
||||
avgRow[key].time += row[key].time ?? 0
|
||||
avgRow[key].speed += row[key].speed ?? 0
|
||||
avgRow[key].count++
|
||||
}))
|
||||
|
||||
Object.values(avgRow).forEach((section) => section.speed /= section.count)
|
||||
|
||||
setAvgRow(avgRow)
|
||||
},
|
||||
setIsAvgTableLoading,
|
||||
'Не удалось загрузить данные для расчёта средних значений',
|
||||
), [avgWells])
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const cmpData = await getWellData(cmpWells)
|
||||
setCmpData(cmpData)
|
||||
},
|
||||
setIsCmpTableLoading,
|
||||
'Не удалось получить скважины для сравнения',
|
||||
), [cmpWells])
|
||||
|
||||
const getStatisticsAvgSummary = useCallback((data) => (
|
||||
<Summary fixed={'bottom'}>
|
||||
<Row>
|
||||
<Cell index={0} colSpan={summaryColSpan}>
|
||||
<Text>Итого:</Text>
|
||||
</Cell>
|
||||
{sectionTypes.map(([id, _], i) => (
|
||||
<Fragment key={id ?? i}>
|
||||
<Cell index={3 * i + summaryColSpan}>
|
||||
<Text>{numericRender(avgRow[`section_${id}`]?.depth)}</Text>
|
||||
</Cell>
|
||||
<Cell index={3 * i + summaryColSpan + 1}>
|
||||
<Text>{numericRender(avgRow[`section_${id}`]?.time)}</Text>
|
||||
</Cell>
|
||||
<Cell index={3 * i + summaryColSpan + 2}>
|
||||
<Text>{numericRender(avgRow[`section_${id}`]?.speed)}</Text>
|
||||
</Cell>
|
||||
</Fragment>
|
||||
))}
|
||||
</Row>
|
||||
</Summary>
|
||||
), [avgRow, sectionTypes])
|
||||
|
||||
return (
|
||||
<div className={'statistics-page'}>
|
||||
<LoaderPortal show={isPageLoading}>
|
||||
<h2 style={{ padding: '10px' }}>Расчёт средних значений без Цифровой буровой</h2>
|
||||
<div className={'average-table'}>
|
||||
<div className={'well-selector'}>
|
||||
<span className={'well-selector-label'}>Выберите скважины для расчёта средних значений:</span>
|
||||
<WellSelector
|
||||
idWell={idWell}
|
||||
value={avgWells}
|
||||
style={{ flex: 100 }}
|
||||
onChange={setAvgWells}
|
||||
placeholder={'Ничего не выбрано'}
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
bordered
|
||||
size={'small'}
|
||||
pagination={false}
|
||||
columns={avgColumns}
|
||||
dataSource={avgData}
|
||||
scroll={scrollSettings}
|
||||
loading={isAvgTableLoading}
|
||||
summary={getStatisticsAvgSummary}
|
||||
/>
|
||||
</div>
|
||||
<div className={'compare-table'}>
|
||||
<div className={'well-selector'}>
|
||||
<span className={'well-selector-label'}>Выберите скважины сравнения:</span>
|
||||
<WellSelector
|
||||
idWell={idWell}
|
||||
value={cmpWells}
|
||||
style={{ flex: 100 }}
|
||||
onChange={setCmpWells}
|
||||
placeholder={'Ничего не выбрано'}
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
bordered
|
||||
size={'small'}
|
||||
pagination={false}
|
||||
columns={cmpColumns}
|
||||
dataSource={cmpData}
|
||||
scroll={scrollSettings}
|
||||
loading={isCmpTableLoading}
|
||||
/>
|
||||
</div>
|
||||
</LoaderPortal>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default Statistics
|
@ -86,7 +86,7 @@ export const WellCompositeEditor = memo(({ idWell, rootPath }) => {
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<Row align={'middle'} justify={'space-between'} wrap={false}>
|
||||
<Row align={'middle'} justify={'space-between'} wrap={false} style={{ backgroundColor: 'white' }}>
|
||||
<Col span={18}>
|
||||
<TreeSelect
|
||||
multiple
|
37
src/pages/Analytics/index.jsx
Normal file
37
src/pages/Analytics/index.jsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { memo } from 'react'
|
||||
import { Layout, Menu } from 'antd'
|
||||
import { Switch, useParams } from 'react-router-dom'
|
||||
|
||||
import { PrivateDefaultRoute, PrivateMenuItemLink, PrivateRoute } from '@components/Private'
|
||||
|
||||
import WellCompositeEditor from './WellCompositeEditor'
|
||||
import Statistics from './Statistics'
|
||||
|
||||
export const Analytics = memo(({ idWell }) => {
|
||||
const { tab } = useParams()
|
||||
const rootPath = `/well/${idWell}/analytics`
|
||||
|
||||
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>
|
||||
<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>
|
||||
</Layout>
|
||||
</Layout>
|
||||
)
|
||||
})
|
||||
|
||||
export default Analytics
|
@ -17,12 +17,12 @@ import Report from './Report'
|
||||
import Archive from './Archive'
|
||||
import Measure from './Measure'
|
||||
import Messages from './Messages'
|
||||
import Analytics from './Analytics'
|
||||
import Documents from './Documents'
|
||||
import TelemetryView from './TelemetryView'
|
||||
import WellOperations from './WellOperations'
|
||||
import DrillingProgram from './DrillingProgram'
|
||||
import TelemetryAnalysis from './TelemetryAnalysis'
|
||||
import WellCompositeEditor from './WellCompositeEditor'
|
||||
|
||||
import '@styles/index.css'
|
||||
|
||||
@ -38,7 +38,7 @@ export const Well = memo(() => {
|
||||
<PrivateMenuItem.Link root={rootPath} key={'telemetry'} path={'telemetry'} icon={<FundViewOutlined />} title={'Мониторинг'}/>
|
||||
<PrivateMenuItem.Link root={rootPath} key={'message'} path={'message'} icon={<AlertOutlined/>} title={'Сообщения'} />
|
||||
<PrivateMenuItem.Link root={rootPath} key={'report'} path={'report'} icon={<FilePdfOutlined />} title={'Рапорт'} />
|
||||
<PrivateMenuItem.Link root={rootPath} key={'composite'} path={'composite'} icon={<DeploymentUnitOutlined />} 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={'archive'} path={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
|
||||
{/* <PrivateMenuItem.Link root={rootPath} key={'telemetryAnalysis'} path={'telemetryAnalysis'} icon={<FundProjectionScreenOutlined />} title={'Операции по телеметрии'} /> */}
|
||||
@ -59,8 +59,8 @@ export const Well = memo(() => {
|
||||
<PrivateRoute path={`${rootPath}/report`}>
|
||||
<Report idWell={idWell} />
|
||||
</PrivateRoute>
|
||||
<PrivateRoute path={`${rootPath}/composite/:tab?`}>
|
||||
<WellCompositeEditor idWell={idWell} rootPath={`${rootPath}/composite`}/>
|
||||
<PrivateRoute path={`${rootPath}/analytics/:tab?`}>
|
||||
<Analytics idWell={idWell} rootPath={`${rootPath}/analytics`}/>
|
||||
</PrivateRoute>
|
||||
<PrivateRoute path={`${rootPath}/operations/:tab?`}>
|
||||
<WellOperations idWell={idWell} />
|
||||
@ -84,7 +84,7 @@ export const Well = memo(() => {
|
||||
`${rootPath}/telemetry`,
|
||||
`${rootPath}/message`,
|
||||
`${rootPath}/report`,
|
||||
`${rootPath}/composite`,
|
||||
`${rootPath}/analytics`,
|
||||
`${rootPath}/operations`,
|
||||
`${rootPath}/archive`,
|
||||
`${rootPath}/telemetryAnalysis`,
|
||||
|
@ -75,6 +75,10 @@ body {
|
||||
height: calc(100vh - 64px);
|
||||
}
|
||||
|
||||
.p-10 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.vertical-align-center {
|
||||
vertical-align: center;
|
||||
}
|
||||
@ -115,3 +119,11 @@ code {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.first-column-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
25
src/styles/statistics.less
Normal file
25
src/styles/statistics.less
Normal file
@ -0,0 +1,25 @@
|
||||
.statistics-page {
|
||||
background-color: white;
|
||||
|
||||
.well-selector {
|
||||
display: flex;
|
||||
padding: 10px 0 10px 10px;
|
||||
align-items: center;
|
||||
|
||||
> .well-selector-label {
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.compare-table {
|
||||
.high-efficienty {
|
||||
color: limegreen;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.low-efficienty {
|
||||
color: red;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
@ -93,9 +93,12 @@ export const requirements: PermissionRecord = {
|
||||
telemetry: ['Deposit.get', 'DrillFlowChart.get', 'TelemetryDataSaub.get', 'TelemetryDataSpin.get'],
|
||||
message: ['Deposit.get', 'TelemetryDataSaub.get'],
|
||||
report: ['Deposit.get', 'Report.get'],
|
||||
composite: {
|
||||
wells: ['Deposit.get', 'OperationStat.get', 'WellComposite.get'],
|
||||
sections: ['Deposit.get', 'OperationStat.get', 'WellComposite.get', 'DrillParams.get'],
|
||||
analytics: {
|
||||
composite: {
|
||||
wells: ['Deposit.get', 'OperationStat.get', 'WellComposite.get'],
|
||||
sections: ['Deposit.get', 'OperationStat.get', 'WellComposite.get', 'DrillParams.get'],
|
||||
},
|
||||
statistics: ['Deposit.get', 'WellOperation.get'],
|
||||
},
|
||||
operations: {
|
||||
tvd: ['Deposit.get', 'OperationStat.get'],
|
||||
|
Loading…
Reference in New Issue
Block a user