asb_cloud_front/src/pages/Analytics/Statistics.jsx
goodmice d5e827532d * блок utils подразделён на functions, hooks, types и filters
* добавлен хук useFunctionalValue
* добавлен хук useCachedFetch
* удалён RCA
* добавлен конфиг babel
* добавлен конфиг webpack
* обновлены все пакеты
* добавлены базовые моки
* добавлены конфиги для тестов
* добавлена кнопка копирования url
* роутер переписан
* в Messages добавлен переход в Архив при клике на сообщение
2022-06-09 17:51:41 +05:00

249 lines
10 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Table as RawTable, Typography } from 'antd'
import { Fragment, memo, useCallback, useEffect, useState } from 'react'
import { useIdWell } from '@asb/context'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { WellSelector } from '@components/selectors/WellSelector'
import { makeGroupColumn, makeNumericColumn, makeNumericRender, makeTextColumn, Table } from '@components/Table'
import { OperationStatService, WellOperationService } from '@api'
import { arrayOrDefault, wrapPrivateComponent } 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)
const makeSectionSorter = (key, name) => (a, b) => (a?.[key]?.[name] ?? 0) - (b?.[key]?.[name] ?? 0)
export const makeSectionColumn = (title, key, { speedRender } = {}) => makeGroupColumn(title, [
makeNumericColumn('Проходка', key, null, null, (section => numericRender(section?.depth)), 100, {
sorter: makeSectionSorter(key, 'depth'),
}),
makeNumericColumn('Время', key, null, null, (section => numericRender(section?.time)), 100, {
sorter: makeSectionSorter(key, 'time'),
}),
makeNumericColumn((<>V<sub>рейсовая</sub></>), key, null, null, speedRender ?? speedNumericRender, 100, {
sorter: makeSectionSorter(key, 'speed'),
}),
])
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
}
const Statistics = memo(() => {
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 [avgData, setAvgData] = useState([])
const [cmpData, setCmpData] = useState([])
const [avgRow, setAvgRow] = useState({})
const idWell = useIdWell()
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))
},
setIsPageLoading,
`Не удалось получить типы секции`,
`Получение списка возможных секций`,
)
}, [idWell])
useEffect(() => {
invokeWebApiWrapperAsync(
async () => {
const filteredSections = avgData?.length > 0 ? sectionTypes.filter(([id, _]) => avgData.some((row) => `section_${id}` in row)) : sectionTypes
setAvgColumns([
...defaultColumns,
...filteredSections.map(([id, name]) => makeSectionColumn(name, `section_${id}`)),
])
setCmpColumns([
...defaultColumns,
...filteredSections.map(([id, name]) => makeSectionColumn(name, `section_${id}`, {
speedRender: cmpSpeedRender(`section_${id}`)
}))
])
},
setIsPageLoading,
'Не удалось установить необходимые столбцы'
)
}, [sectionTypes, avgData, 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 wrapPrivateComponent(Statistics, {
requirements: [],
title: 'Оценка по ЦБ',
route: 'statistics',
})