forked from ddrilling/asb_cloud_front
Merge branch 'dev' into feature/daily-report
This commit is contained in:
commit
3d80d16134
@ -1,163 +0,0 @@
|
|||||||
import { Table, Select, DatePicker } from "antd";
|
|
||||||
import { TelemetryAnalyticsService } from "../src/services/api";
|
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import notify from "../src/components/notify";
|
|
||||||
import LoaderPortal from "../src/components/LoaderPortal";
|
|
||||||
import moment from "moment";
|
|
||||||
import "../styles/message.css";
|
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
const pageSize = 26;
|
|
||||||
const { RangePicker } = DatePicker;
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: "Название операции",
|
|
||||||
key: "name",
|
|
||||||
dataIndex: "name",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Дата начала операции",
|
|
||||||
key: "beginDate",
|
|
||||||
dataIndex: "beginDate",
|
|
||||||
render: (item) => moment.utc(item).local().format("DD MMM YYYY, HH:mm:ss"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Дата окончания операции",
|
|
||||||
key: "endDate",
|
|
||||||
dataIndex: "endDate",
|
|
||||||
render: (item) => moment.utc(item).local().format("DD MMM YYYY, HH:mm:ss"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Глубина скважины в начале операции",
|
|
||||||
key: "beginWellDepth",
|
|
||||||
dataIndex: "startWellDepth",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Глубина скважины в конце операции",
|
|
||||||
key: "endWellDepth",
|
|
||||||
dataIndex: "endWellDepth",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const filterOptions = [
|
|
||||||
{ label: "Невозможно определить операцию", value: 1 },
|
|
||||||
{ label: "Роторное бурение", value: 2 },
|
|
||||||
{ label: "Слайдирование", value: 3 },
|
|
||||||
{ label: "Подъем с проработкой", value: 4 },
|
|
||||||
{ label: "Спуск с проработкой", value: 5 },
|
|
||||||
{ label: "Подъем с промывкой", value: 6 },
|
|
||||||
{ label: "Спуск с промывкой", value: 7 },
|
|
||||||
{ label: "Спуск в скважину", value: 8 },
|
|
||||||
{ label: "Спуск с вращением", value: 9 },
|
|
||||||
{ label: "Подъем из скважины", value: 10 },
|
|
||||||
{ label: "Подъем с вращением", value: 11 },
|
|
||||||
{ label: "Промывка в покое", value: 12 },
|
|
||||||
{ label: "Промывка с вращением", value: 13 },
|
|
||||||
{ label: "Удержание в клиньях", value: 14 },
|
|
||||||
{ label: "Неподвижное состояние", value: 15 },
|
|
||||||
{ label: "Вращение без циркуляции", value: 16 },
|
|
||||||
{ label: "На поверхности", value: 17 },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function WellTelemetryAnalysis() {
|
|
||||||
let { id } = useParams();
|
|
||||||
|
|
||||||
const [page, setPage] = useState(1);
|
|
||||||
const [range, setRange] = useState([]);
|
|
||||||
const [categories, setCategories] = useState([]);
|
|
||||||
const [pagination, setPagination] = useState(null);
|
|
||||||
const [operations, setOperations] = useState([]);
|
|
||||||
|
|
||||||
const [loader, setLoader] = useState(false);
|
|
||||||
|
|
||||||
const children = filterOptions.map((line) => (
|
|
||||||
<Option key={line.value}>{line.label}</Option>
|
|
||||||
));
|
|
||||||
|
|
||||||
const onChangeRange = (range) => {
|
|
||||||
setRange(range);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const GetOperations = async () => {
|
|
||||||
setLoader(true);
|
|
||||||
try {
|
|
||||||
let begin = null;
|
|
||||||
let end = null;
|
|
||||||
if (range?.length > 1) {
|
|
||||||
begin = range[0].toISOString();
|
|
||||||
end = range[1].toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
await TelemetryAnalyticsService.getOperationsByWell(
|
|
||||||
`${id}`,
|
|
||||||
(page - 1) * pageSize,
|
|
||||||
pageSize,
|
|
||||||
categories,
|
|
||||||
begin,
|
|
||||||
end
|
|
||||||
).then((paginatedOperations) => {
|
|
||||||
setOperations(
|
|
||||||
paginatedOperations?.items.map((o) => {
|
|
||||||
return {
|
|
||||||
key: o.id,
|
|
||||||
begin: o.date,
|
|
||||||
...o,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
setPagination({
|
|
||||||
total: paginatedOperations?.count,
|
|
||||||
current: Math.floor(paginatedOperations?.skip / pageSize),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (ex) {
|
|
||||||
notify(`Не удалось загрузить операции по скважине "${id}"`, "error");
|
|
||||||
console.log(ex);
|
|
||||||
}
|
|
||||||
setLoader(false);
|
|
||||||
};
|
|
||||||
GetOperations();
|
|
||||||
}, [id, categories, range, page]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="filter-group">
|
|
||||||
<h3 className="filter-group-heading">Фильтр операций</h3>
|
|
||||||
<Select
|
|
||||||
mode="multiple"
|
|
||||||
allowClear
|
|
||||||
placeholder="Фильтр операций"
|
|
||||||
className="filter-selector"
|
|
||||||
value={categories}
|
|
||||||
onChange={setCategories}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</Select>
|
|
||||||
<RangePicker
|
|
||||||
showTime
|
|
||||||
placeholder={["Дата начала операции", "Дата окончания операции"]}
|
|
||||||
onChange={onChangeRange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<LoaderPortal show={loader}>
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
dataSource={operations}
|
|
||||||
size={"small"}
|
|
||||||
pagination={{
|
|
||||||
pageSize: pageSize,
|
|
||||||
showSizeChanger: false,
|
|
||||||
total: pagination?.total,
|
|
||||||
current: page,
|
|
||||||
onChange: (page) => setPage(page),
|
|
||||||
}}
|
|
||||||
rowKey={(record) => record.id}
|
|
||||||
/>
|
|
||||||
</LoaderPortal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
# Вопрос Олегу: Какой бит и в каком регистре (или значение регистра) контроллера спинмастера сигнализирует что система спинмастер активана/работает?
|
|
||||||
Ответ: Такого бита нет, но можно понять если etap %MW1600: INT; не равен 6 и не равен 0 тогда система в работе
|
|
||||||
|
|
||||||
# Вопрос Олегу: Какой бит и в каком регистре (или значение регистра) контроллера спинмастера сигнализирует что система стабилизации крутящего момента активна/работает?
|
|
||||||
Ответ: Такого бита по сути тоже нет но можно понять что
|
|
||||||
когда stik_sleep %MX2802.01: BOOL; =true
|
|
||||||
и
|
|
||||||
etap %MW1600: INT; =7
|
|
||||||
тогда торк мастер работает
|
|
1475
package-lock.json
generated
1475
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,16 +4,18 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@craco/craco": "^6.1.2",
|
"@craco/craco": "^6.1.2",
|
||||||
"@microsoft/signalr": "^5.0.5",
|
"@microsoft/signalr": "^6.0.4",
|
||||||
"@testing-library/jest-dom": "^5.11.10",
|
"@testing-library/jest-dom": "^5.11.10",
|
||||||
"@testing-library/react": "^11.2.6",
|
"@testing-library/react": "^11.2.6",
|
||||||
"@testing-library/user-event": "^12.8.3",
|
"@testing-library/user-event": "^12.8.3",
|
||||||
|
"@types/react-dom": "^18.0.3",
|
||||||
"antd": "^4.15.0",
|
"antd": "^4.15.0",
|
||||||
"chart.js": "^3.6.0",
|
"chart.js": "^3.6.0",
|
||||||
"chartjs-adapter-moment": "^1.0.0",
|
"chartjs-adapter-moment": "^1.0.0",
|
||||||
"chartjs-plugin-datalabels": "^2.0.0-rc.1",
|
"chartjs-plugin-datalabels": "^2.0.0-rc.1",
|
||||||
"chartjs-plugin-zoom": "^1.1.1",
|
"chartjs-plugin-zoom": "^1.1.1",
|
||||||
"craco-less": "^1.17.1",
|
"craco-less": "^1.17.1",
|
||||||
|
"d3": "^7.4.4",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"pigeon-maps": "^0.19.7",
|
"pigeon-maps": "^0.19.7",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
@ -55,6 +57,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/d3": "^7.1.0",
|
||||||
"@types/react": "^17.0.3",
|
"@types/react": "^17.0.3",
|
||||||
"@types/react-router-dom": "^5.3.2",
|
"@types/react-router-dom": "^5.3.2",
|
||||||
"craco-alias": "^3.0.1",
|
"craco-alias": "^3.0.1",
|
||||||
|
8
src/App.js → src/App.tsx
Executable file → Normal file
8
src/App.js → src/App.tsx
Executable file → Normal file
@ -3,22 +3,22 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Route
|
Route
|
||||||
} from 'react-router-dom'
|
} from 'react-router-dom'
|
||||||
|
import { memo } from 'react'
|
||||||
import { ConfigProvider } from 'antd'
|
import { ConfigProvider } from 'antd'
|
||||||
import locale from 'antd/lib/locale/ru_RU'
|
import locale from 'antd/lib/locale/ru_RU'
|
||||||
|
|
||||||
import { OpenAPI } from '@api'
|
|
||||||
import { getUserToken } from '@utils/storage'
|
|
||||||
import { PrivateRoute } from '@components/Private'
|
import { PrivateRoute } from '@components/Private'
|
||||||
|
import { getUserToken } from '@utils/storage'
|
||||||
|
import { OpenAPI } from '@api'
|
||||||
|
|
||||||
import Main from '@pages/Main'
|
import Main from '@pages/Main'
|
||||||
import Login from '@pages/Login'
|
import Login from '@pages/Login'
|
||||||
import Register from '@pages/Register'
|
import Register from '@pages/Register'
|
||||||
|
|
||||||
import '@styles/App.less'
|
import '@styles/App.less'
|
||||||
import { memo } from 'react'
|
|
||||||
|
|
||||||
//OpenAPI.BASE = 'http://localhost:3000'
|
//OpenAPI.BASE = 'http://localhost:3000'
|
||||||
OpenAPI.TOKEN = async () => getUserToken()
|
OpenAPI.TOKEN = async () => getUserToken() ?? ''
|
||||||
OpenAPI.HEADERS = {'Content-Type': 'application/json'}
|
OpenAPI.HEADERS = {'Content-Type': 'application/json'}
|
||||||
|
|
||||||
export const App = memo(() => (
|
export const App = memo(() => (
|
@ -1,41 +0,0 @@
|
|||||||
import moment from 'moment'
|
|
||||||
import { DatePicker } from 'antd'
|
|
||||||
import { useState, useEffect } from 'react'
|
|
||||||
import { useParams } from 'react-router-dom'
|
|
||||||
|
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
|
||||||
import { TelemetryAnalyticsService } from '@api'
|
|
||||||
import { ChartOperationTime } from './charts/ChartOperationTime'
|
|
||||||
|
|
||||||
const { RangePicker } = DatePicker
|
|
||||||
|
|
||||||
const lines = [{ labelAccessorName: 'processName', pieceAccessorName: 'duration' }]
|
|
||||||
|
|
||||||
export const AnalysisOperationTime = () => {
|
|
||||||
const { id } = useParams()
|
|
||||||
const [operationTimeData, setOperationTimeData] = useState([])
|
|
||||||
const [loader, setLoader] = useState(false)
|
|
||||||
const [range, setRange] = useState([moment().subtract(1,'days'), moment()])
|
|
||||||
|
|
||||||
useEffect(() => invokeWebApiWrapperAsync(
|
|
||||||
async () => {
|
|
||||||
const begin = range?.length > 1 ? range[0].toISOString() : null
|
|
||||||
const end = range?.length > 1 ? range[1].toISOString() : null
|
|
||||||
const summary = await TelemetryAnalyticsService.getOperationsSummary(id, begin, end)
|
|
||||||
setOperationTimeData(summary)
|
|
||||||
},
|
|
||||||
setLoader,
|
|
||||||
`Не удалось получить данные для анализа Операция-Время по скважине '${id}' за период с ${begin} по ${end}`,
|
|
||||||
'Получение данных для анализа Операция-Время по скважине'
|
|
||||||
), [id, range])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LoaderPortal show={loader}>
|
|
||||||
<RangePicker showTime onChange={(range) => setRange(range)} />
|
|
||||||
<ChartOperationTime data={operationTimeData} lines={lines} />
|
|
||||||
</LoaderPortal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AnalysisOperationTime
|
|
40
src/components/Display.jsx → src/components/Display.tsx
Executable file → Normal file
40
src/components/Display.jsx → src/components/Display.tsx
Executable file → Normal file
@ -1,19 +1,33 @@
|
|||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { useState, useEffect, memo } from 'react'
|
import { useState, useEffect, memo, ReactNode } from 'react'
|
||||||
import {CaretUpOutlined, CaretDownOutlined, CaretRightOutlined} from '@ant-design/icons'
|
import {CaretUpOutlined, CaretDownOutlined, CaretRightOutlined} from '@ant-design/icons'
|
||||||
|
|
||||||
import '@styles/display.css'
|
import '@styles/display.css'
|
||||||
|
|
||||||
export const formatNumber = (value, format) =>
|
export const formatNumber = (value?: unknown, format?: number) =>
|
||||||
Number.isInteger(format) && Number.isFinite(value)
|
Number.isInteger(format) && Number.isFinite(value)
|
||||||
? (+value).toFixed(format)
|
? Number(value).toFixed(format)
|
||||||
: (+value).toPrecision(4)
|
: Number(value).toPrecision(4)
|
||||||
|
|
||||||
const iconStyle = { color:'#0008' }
|
const iconStyle = { color:'#0008' }
|
||||||
const displayValueStyle = { display: 'flex', flexGrow: 1 }
|
const displayValueStyle = { display: 'flex', flexGrow: 1 }
|
||||||
|
|
||||||
export const ValueDisplay = ({ prefix, value, suffix, isArrowVisible, format, enumeration }) => {
|
export type ValueDisplayProps = {
|
||||||
const [val, setVal] = useState('---')
|
prefix?: ReactNode
|
||||||
|
suffix?: ReactNode
|
||||||
|
format?: number | string
|
||||||
|
isArrowVisible?: boolean
|
||||||
|
enumeration?: Record<string, string>
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DisplayProps = ValueDisplayProps & {
|
||||||
|
className?: string
|
||||||
|
label?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ValueDisplay = memo<ValueDisplayProps>(({ prefix, value, suffix, isArrowVisible, format, enumeration }) => {
|
||||||
|
const [val, setVal] = useState<string>('---')
|
||||||
const [arrowState, setArrowState] = useState({
|
const [arrowState, setArrowState] = useState({
|
||||||
preVal: NaN,
|
preVal: NaN,
|
||||||
preTimestamp: Date.now(),
|
preTimestamp: Date.now(),
|
||||||
@ -28,25 +42,25 @@ export const ValueDisplay = ({ prefix, value, suffix, isArrowVisible, format, en
|
|||||||
if (Number.isFinite(+value)) {
|
if (Number.isFinite(+value)) {
|
||||||
if (isArrowVisible && (arrowState.preTimestamp + 1000 < Date.now())) {
|
if (isArrowVisible && (arrowState.preTimestamp + 1000 < Date.now())) {
|
||||||
let direction = 0
|
let direction = 0
|
||||||
if (value > arrowState.preVal)
|
if (+value > arrowState.preVal)
|
||||||
direction = 1
|
direction = 1
|
||||||
if (value < arrowState.preVal)
|
if (+value < arrowState.preVal)
|
||||||
direction = -1
|
direction = -1
|
||||||
|
|
||||||
setArrowState({
|
setArrowState({
|
||||||
preVal: value,
|
preVal: +value,
|
||||||
preTimestamp: Date.now(),
|
preTimestamp: Date.now(),
|
||||||
direction: direction,
|
direction: direction,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return formatNumber(value, format)
|
return formatNumber(value, Number(format))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.length > 4) {
|
if (value.length > 4) {
|
||||||
const valueDate = moment(value)
|
const valueDate = moment(value)
|
||||||
if (valueDate.isValid())
|
if (valueDate.isValid())
|
||||||
return valueDate.format(format)
|
return valueDate.format(String(format))
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
@ -74,9 +88,9 @@ export const ValueDisplay = ({ prefix, value, suffix, isArrowVisible, format, en
|
|||||||
{prefix} {val} {suffix}{arrow}
|
{prefix} {val} {suffix}{arrow}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
export const Display = memo(({ className, label, ...other })=> (
|
export const Display = memo<DisplayProps>(({ className, label, ...other })=> (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div className={'display_label'}>{label}</div>
|
<div className={'display_label'}>{label}</div>
|
||||||
<div style={displayValueStyle}>
|
<div style={displayValueStyle}>
|
4
src/components/ErrorFetch.js → src/components/ErrorFetch.ts
Executable file → Normal file
4
src/components/ErrorFetch.js → src/components/ErrorFetch.ts
Executable file → Normal file
@ -1,5 +1,7 @@
|
|||||||
export class ErrorFetch extends Error {
|
export class ErrorFetch extends Error {
|
||||||
constructor(status, message) {
|
public status: number
|
||||||
|
|
||||||
|
constructor(status: number, message?: string) {
|
||||||
super(message)
|
super(message)
|
||||||
this.name = 'ErrorFetch'
|
this.name = 'ErrorFetch'
|
||||||
this.status = status
|
this.status = status
|
@ -3,8 +3,8 @@ import { Menu, MenuItemProps, MenuProps } from 'antd'
|
|||||||
import { Children, cloneElement, memo, ReactElement, useContext, useMemo } from 'react'
|
import { Children, cloneElement, memo, ReactElement, useContext, useMemo } from 'react'
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { isURLAvailable } from '@utils/permissions'
|
import { isURLAvailable } from '@utils/permissions'
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
|
|
||||||
export type PrivateMenuProps = MenuProps & { root?: string }
|
export type PrivateMenuProps = MenuProps & { root?: string }
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { getUserId } from '@utils/storage'
|
|||||||
import { isURLAvailable } from '@utils/permissions'
|
import { isURLAvailable } from '@utils/permissions'
|
||||||
|
|
||||||
export type PrivateRouteProps = RouteProps & {
|
export type PrivateRouteProps = RouteProps & {
|
||||||
root: string
|
root?: string
|
||||||
path: string
|
path: string
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
redirect?: (location?: Location<unknown>) => ReactNode
|
redirect?: (location?: Location<unknown>) => ReactNode
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { Location } from 'history'
|
import { Location } from 'history'
|
||||||
import { Children, cloneElement, memo, ReactElement, ReactNode, useCallback, useContext, useMemo } from 'react'
|
import { Children, cloneElement, memo, ReactElement, ReactNode, useCallback, useContext, useMemo } from 'react'
|
||||||
|
|
||||||
import { Redirect, Route, Switch, SwitchProps, useLocation } from 'react-router-dom'
|
import { Redirect, Route, Switch, SwitchProps, useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { isURLAvailable } from '@utils/permissions'
|
import { isURLAvailable } from '@utils/permissions'
|
||||||
import { getUserId } from '@utils/storage'
|
import { getUserId } from '@utils/storage'
|
||||||
|
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
|
|
||||||
export type PrivateSwitchProps = SwitchProps & {
|
export type PrivateSwitchProps = SwitchProps & {
|
||||||
root?: string
|
root?: string
|
||||||
|
21
src/components/UploadForm.jsx → src/components/UploadForm.tsx
Executable file → Normal file
21
src/components/UploadForm.jsx → src/components/UploadForm.tsx
Executable file → Normal file
@ -1,22 +1,35 @@
|
|||||||
import { memo, useCallback, useState } from 'react'
|
import { memo, useCallback, useState } from 'react'
|
||||||
import { Upload, Button } from 'antd'
|
import { Upload, Button } from 'antd'
|
||||||
import { UploadOutlined } from '@ant-design/icons'
|
import { UploadOutlined } from '@ant-design/icons'
|
||||||
|
import { UploadFile } from 'antd/lib/upload/interface'
|
||||||
|
|
||||||
import { upload } from './factory'
|
import { upload } from './factory'
|
||||||
import { ErrorFetch } from './ErrorFetch'
|
import { ErrorFetch } from './ErrorFetch'
|
||||||
|
|
||||||
export const UploadForm = memo(({ url, disabled, accept, style, formData, onUploadStart, onUploadSuccess, onUploadComplete, onUploadError }) => {
|
export type UploadFormProps = {
|
||||||
const [fileList, setfileList] = useState([])
|
url: string
|
||||||
|
disabled?: boolean
|
||||||
|
accept?: string
|
||||||
|
style?: CSSStyleSheet
|
||||||
|
formData: FormData
|
||||||
|
onUploadStart?: () => void
|
||||||
|
onUploadSuccess?: () => void
|
||||||
|
onUploadComplete?: () => void
|
||||||
|
onUploadError?: (error: unknown) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UploadForm = memo<UploadFormProps>(({ url, disabled, accept, style, formData, onUploadStart, onUploadSuccess, onUploadComplete, onUploadError }) => {
|
||||||
|
const [fileList, setfileList] = useState<UploadFile<any>[]>([])
|
||||||
|
|
||||||
const handleFileSend = useCallback(async () => {
|
const handleFileSend = useCallback(async () => {
|
||||||
onUploadStart?.()
|
onUploadStart?.()
|
||||||
try {
|
try {
|
||||||
const formDataLocal = new FormData()
|
const formDataLocal = new FormData()
|
||||||
fileList.forEach((val) => formDataLocal.append('files', val.originFileObj))
|
fileList.forEach((val) => formDataLocal.append('files', String(val.originFileObj)))
|
||||||
|
|
||||||
if(formData)
|
if(formData)
|
||||||
for(const propName in formData)
|
for(const propName in formData)
|
||||||
formDataLocal.append(propName, formData[propName])
|
formDataLocal.append(propName, String(formData.get(propName)))
|
||||||
|
|
||||||
const response = await upload(url, formDataLocal)
|
const response = await upload(url, formDataLocal)
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
@ -1,68 +0,0 @@
|
|||||||
import moment from 'moment'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
import { ChartOpertationTimeBase } from './ChartOperationTimeBase'
|
|
||||||
|
|
||||||
export const CreateLabels = () => {
|
|
||||||
let labels = []
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CreateData = (lineConfig) => {
|
|
||||||
let datasets = {
|
|
||||||
label: lineConfig.label,
|
|
||||||
data: [],
|
|
||||||
backgroundColor: [
|
|
||||||
'#f00', '#ff0', '#f0f', '#0ff', '#00f', '#0f0'
|
|
||||||
],
|
|
||||||
}
|
|
||||||
return datasets
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ChartOperationTime = ({ lines, data, rangeDate }) => {
|
|
||||||
const [opertationTimeDataParams, setOpertationTimeDataParams] = useState({ data: { labels: [], datasets: [] } })
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if ((!lines)
|
|
||||||
|| (!data))
|
|
||||||
return
|
|
||||||
|
|
||||||
let newLabels = lines.map(lineCfg => {
|
|
||||||
let labels = CreateLabels(lineCfg)
|
|
||||||
if (data.length !== 0)
|
|
||||||
labels = data.map(dataItem => {
|
|
||||||
return dataItem[lineCfg.labelAccessorName]
|
|
||||||
})
|
|
||||||
return labels
|
|
||||||
})
|
|
||||||
|
|
||||||
let newDatasets = lines.map(lineCfg => {
|
|
||||||
let datasets = CreateData(lineCfg)
|
|
||||||
if (data.length !== 0)
|
|
||||||
datasets.data = data.map(dataItem => {
|
|
||||||
return dataItem[lineCfg.pieceAccessorName]
|
|
||||||
})
|
|
||||||
return datasets
|
|
||||||
})
|
|
||||||
|
|
||||||
let interval = rangeDate ? (rangeDate[1] - rangeDate[0]) / 1000 : null
|
|
||||||
let startDate = rangeDate ? rangeDate[0] : moment()
|
|
||||||
let newParams = {
|
|
||||||
xInterval: interval,
|
|
||||||
xStart: startDate,
|
|
||||||
data: {
|
|
||||||
labels: newLabels,
|
|
||||||
datasets: newDatasets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setOpertationTimeDataParams(newParams)
|
|
||||||
|
|
||||||
console.log(newParams)
|
|
||||||
|
|
||||||
}, [data, lines, rangeDate])
|
|
||||||
|
|
||||||
return (<>
|
|
||||||
<ChartOpertationTimeBase dataParams={opertationTimeDataParams} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import {
|
|
||||||
Chart,
|
|
||||||
ArcElement,
|
|
||||||
TimeScale,
|
|
||||||
Legend,
|
|
||||||
PointElement,
|
|
||||||
ChartData,
|
|
||||||
ChartTypeRegistry,
|
|
||||||
ChartOptions,
|
|
||||||
DoughnutController,
|
|
||||||
} from 'chart.js'
|
|
||||||
import 'chartjs-adapter-moment'
|
|
||||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
|
||||||
|
|
||||||
Chart.register(TimeScale, DoughnutController, PointElement, ArcElement, Legend, ChartDataLabels)
|
|
||||||
|
|
||||||
const defaultOptions = {
|
|
||||||
responsive: true,
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
position: 'top',
|
|
||||||
text: 'Doughnut Chart',
|
|
||||||
fontSize: 18,
|
|
||||||
fontColor: '#111'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: true,
|
|
||||||
position: 'bottom',
|
|
||||||
labels: {
|
|
||||||
fontColor: '#333',
|
|
||||||
fontSize: 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ChartTimeData = ChartData<keyof ChartTypeRegistry, {
|
|
||||||
x: String
|
|
||||||
label: number
|
|
||||||
y: number
|
|
||||||
}[], unknown>
|
|
||||||
|
|
||||||
export type ChartTimeDataParams = {
|
|
||||||
data: ChartTimeData,
|
|
||||||
xStart?: Date,
|
|
||||||
xInterval?: number,
|
|
||||||
displayLabels?: Boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ChartTimeBaseProps = {
|
|
||||||
dataParams: ChartTimeDataParams,
|
|
||||||
// TODO: Create good type for options
|
|
||||||
options?: ChartOptions<keyof ChartTypeRegistry> | any,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TimeParams = {
|
|
||||||
unit: String
|
|
||||||
stepSize: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const linesPerInterval = 32
|
|
||||||
|
|
||||||
export const timeUnitByInterval = (intervalSec: number): String => {
|
|
||||||
if (intervalSec <= 60)
|
|
||||||
return 'millisecond'
|
|
||||||
|
|
||||||
if (intervalSec <= 32 * 60)
|
|
||||||
return 'second'
|
|
||||||
|
|
||||||
if (intervalSec <= 32 * 60 * 60)
|
|
||||||
return 'minute'
|
|
||||||
|
|
||||||
if (intervalSec <= 32 * 12 * 60 * 60)
|
|
||||||
return 'hour'
|
|
||||||
|
|
||||||
if (intervalSec <= 32 * 24 * 60 * 60)
|
|
||||||
return 'day'
|
|
||||||
|
|
||||||
if (intervalSec <= 32 * 7 * 24 * 60 * 60)
|
|
||||||
return 'week'
|
|
||||||
|
|
||||||
if (intervalSec <= 32 * 30.4375 * 24 * 60 * 60)
|
|
||||||
return 'month'
|
|
||||||
|
|
||||||
if (intervalSec <= 32 * 121.75 * 24 * 60 * 60)
|
|
||||||
return 'quarter'
|
|
||||||
else
|
|
||||||
return 'year'
|
|
||||||
}
|
|
||||||
|
|
||||||
export const timeParamsByInterval = (intervalSec: number): TimeParams => {
|
|
||||||
let stepSize = intervalSec
|
|
||||||
let unit = timeUnitByInterval(intervalSec)
|
|
||||||
|
|
||||||
switch (unit) {
|
|
||||||
case 'millisecond':
|
|
||||||
stepSize *= 1000
|
|
||||||
break
|
|
||||||
case 'second':
|
|
||||||
//stepSize *= 1
|
|
||||||
break
|
|
||||||
case 'minute':
|
|
||||||
stepSize /= 60
|
|
||||||
break
|
|
||||||
case 'hour':
|
|
||||||
stepSize /= 60 * 60
|
|
||||||
break
|
|
||||||
case 'day':
|
|
||||||
stepSize /= 24 * 60 * 60
|
|
||||||
break
|
|
||||||
case 'week':
|
|
||||||
stepSize /= 7 * 24 * 60 * 60
|
|
||||||
break
|
|
||||||
case 'month':
|
|
||||||
stepSize /= 30 * 24 * 60 * 60
|
|
||||||
break
|
|
||||||
case 'quarter':
|
|
||||||
stepSize /= 91 * 24 * 60 * 60
|
|
||||||
break
|
|
||||||
case 'year':
|
|
||||||
stepSize /= 365.25 * 24 * 60 * 60
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
stepSize = Math.round(stepSize / linesPerInterval)
|
|
||||||
stepSize = stepSize > 0 ? stepSize : 1
|
|
||||||
return { unit, stepSize }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ChartOpertationTimeBase: React.FC<ChartTimeBaseProps> = ({ options, dataParams }) => {
|
|
||||||
const chartRef = useRef<HTMLCanvasElement>(null)
|
|
||||||
const [chart, setChart] = useState<any>()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if ((chartRef.current) && (!chart)) {
|
|
||||||
let thisOptions = {}
|
|
||||||
Object.assign(thisOptions, defaultOptions, options)
|
|
||||||
|
|
||||||
let newChart = new Chart(chartRef.current, {
|
|
||||||
type: 'doughnut',
|
|
||||||
plugins: [ChartDataLabels],
|
|
||||||
options: thisOptions,
|
|
||||||
data: dataParams.data
|
|
||||||
})
|
|
||||||
setChart(newChart)
|
|
||||||
|
|
||||||
return () => chart?.destroy()
|
|
||||||
}
|
|
||||||
}, [chart, options, dataParams])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!chart)
|
|
||||||
return
|
|
||||||
|
|
||||||
chart.data = dataParams.data
|
|
||||||
chart.options.aspectRatio = options?.aspectRatio
|
|
||||||
if (dataParams.xStart) {
|
|
||||||
let interval = Number(dataParams.xInterval ?? 600)
|
|
||||||
let start = new Date(dataParams.xStart)
|
|
||||||
let end = new Date(dataParams.xStart)
|
|
||||||
end.setSeconds(end.getSeconds() + interval)
|
|
||||||
let { unit, stepSize } = timeParamsByInterval(interval)
|
|
||||||
|
|
||||||
if (chart.options.scales?.x) {
|
|
||||||
chart.options.scales.x.max = end.getTime()
|
|
||||||
chart.options.scales.x.min = start.getTime()
|
|
||||||
chart.options.scales.x.ticks.display = dataParams.displayLabels ?? true
|
|
||||||
chart.options.scales.x.time.unit = unit
|
|
||||||
chart.options.scales.x.time.stepSize = stepSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chart.update()
|
|
||||||
}, [chart, dataParams, options])
|
|
||||||
|
|
||||||
return (<canvas ref={chartRef} />)
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import {
|
|
||||||
Chart,
|
|
||||||
TimeScale,
|
|
||||||
LinearScale,
|
|
||||||
Legend,
|
|
||||||
LineController,
|
|
||||||
PointElement,
|
|
||||||
LineElement
|
|
||||||
} from 'chart.js'
|
|
||||||
import 'chartjs-adapter-moment'
|
|
||||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
|
||||||
import zoomPlugin from 'chartjs-plugin-zoom'
|
|
||||||
|
|
||||||
Chart.register(TimeScale, LinearScale, LineController, LineElement, PointElement, Legend, ChartDataLabels, zoomPlugin)
|
|
||||||
|
|
||||||
const defaultOptions = {
|
|
||||||
responsive: true,
|
|
||||||
aspectRatio: 2.45,
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
type: 'time',
|
|
||||||
time: {
|
|
||||||
unit: 'hour',
|
|
||||||
displayFormats: {
|
|
||||||
hour: 'MM.DD'
|
|
||||||
},
|
|
||||||
tooltipFormat: 'DD T'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
type: 'linear',
|
|
||||||
position: 'top',
|
|
||||||
reverse: true,
|
|
||||||
// ticks: {
|
|
||||||
// // forces step size to be 50 units
|
|
||||||
// stepSize: 50
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
parsing: {
|
|
||||||
xAxisKey: 'date',
|
|
||||||
yAxisKey: 'depth'
|
|
||||||
},
|
|
||||||
elements: {
|
|
||||||
point: {
|
|
||||||
radius: 1.7,
|
|
||||||
hoverRadius: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
datalabels: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeDataset = (data, label, color, width = 1.5, dash) => ({
|
|
||||||
label: label,
|
|
||||||
data: data,
|
|
||||||
backgroundColor: color,
|
|
||||||
borderColor: color,
|
|
||||||
borderWidth: width,
|
|
||||||
borderDash: dash,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const ChartTelemetryDepthToDay = ({ depthData, bitPositionData }) => {
|
|
||||||
const chartRef = useRef(null)
|
|
||||||
const [chart, setChart] = useState()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const data = {
|
|
||||||
datasets: [
|
|
||||||
makeDataset(depthData, 'Глубина', '#0A0'),
|
|
||||||
makeDataset(bitPositionData, 'Положение долота', 'blue'),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
if(chartRef.current && !chart) {
|
|
||||||
const thisOptions = {}
|
|
||||||
Object.assign(thisOptions, defaultOptions)
|
|
||||||
|
|
||||||
const newChart = new Chart(chartRef.current, {
|
|
||||||
type: 'line',
|
|
||||||
plugins: [ChartDataLabels],
|
|
||||||
options: thisOptions,
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
setChart(newChart)
|
|
||||||
|
|
||||||
return () => chart?.destroy()
|
|
||||||
} else {
|
|
||||||
chart.data = data
|
|
||||||
chart.update()
|
|
||||||
}
|
|
||||||
}, [chart, depthData, bitPositionData])
|
|
||||||
|
|
||||||
return(<canvas ref={chartRef} />)
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import { Chart, registerables } from 'chart.js'
|
|
||||||
|
|
||||||
Chart.register(...registerables)
|
|
||||||
|
|
||||||
const defaultOptions = {
|
|
||||||
responsive: true,
|
|
||||||
aspectRatio: 2.6,
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Дата начала интервала'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: 'Коэффициент скорости'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
datalabels: {
|
|
||||||
display: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ChartTelemetryDepthToInterval = ({ depthToIntervalData }) => {
|
|
||||||
const chartRef = useRef(null)
|
|
||||||
const [chart, setChart] = useState()
|
|
||||||
|
|
||||||
const calculateBarWidth = (dataLength) => {
|
|
||||||
if (dataLength < 3) return 150
|
|
||||||
if (dataLength < 16) return 70
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const xData = depthToIntervalData.map(el => new Date(el.intervalStartDate).toLocaleString())
|
|
||||||
const yData = depthToIntervalData.map(el => el.intervalDepthProgress.toFixed(3))
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
labels: xData,
|
|
||||||
datasets: [{
|
|
||||||
label: 'Скорость проходки за интервал',
|
|
||||||
data: yData,
|
|
||||||
borderColor: '#00b300',
|
|
||||||
borderWidth: 2,
|
|
||||||
backgroundColor: '#0A0',
|
|
||||||
barThickness: calculateBarWidth(xData.length)
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
const thisOptions = {}
|
|
||||||
Object.assign(thisOptions, defaultOptions)
|
|
||||||
|
|
||||||
if (chartRef.current && !chart) {
|
|
||||||
const newChart = new Chart(chartRef.current, {
|
|
||||||
type: 'bar',
|
|
||||||
options: thisOptions,
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
setChart(newChart)
|
|
||||||
} else {
|
|
||||||
chart.data = data
|
|
||||||
chart.options = thisOptions
|
|
||||||
chart.update()
|
|
||||||
}
|
|
||||||
}, [chart, depthToIntervalData])
|
|
||||||
|
|
||||||
return (<canvas ref={chartRef} />)
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import { Chart, registerables } from 'chart.js'
|
|
||||||
|
|
||||||
Chart.register(...registerables)
|
|
||||||
|
|
||||||
const transformSecondsToHoursString = (seconds) => {
|
|
||||||
const hours = Math.floor(seconds / 3600)
|
|
||||||
const minutes = Math.floor((seconds % 3600) / 60)
|
|
||||||
const s = seconds - (hours * 3600) - (minutes * 60)
|
|
||||||
|
|
||||||
return `${hours} ч.${minutes} мин.${s} сек.`
|
|
||||||
}
|
|
||||||
|
|
||||||
const transformSecondsToTimeString = (seconds) => {
|
|
||||||
if (seconds === 1) // 1 is default state if null returned (1 is to show chart anyway with 0 sec)
|
|
||||||
return '0 сек.'
|
|
||||||
else if(seconds < 60)
|
|
||||||
return seconds + ' сек.'
|
|
||||||
else if (seconds < 3600)
|
|
||||||
return Math.floor(seconds / 60) + ' мин. ' + (0.6 * (seconds % 60)).toFixed() + ' сек.'
|
|
||||||
else
|
|
||||||
return transformSecondsToHoursString(seconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOptions = {
|
|
||||||
responsive: true,
|
|
||||||
aspectRatio: 2.8,
|
|
||||||
plugins: {
|
|
||||||
datalabels: {
|
|
||||||
color: '#ffffff',
|
|
||||||
formatter: transformSecondsToTimeString,
|
|
||||||
font: {
|
|
||||||
weight: 'bold'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
label: (tooltipItem) => transformSecondsToTimeString(tooltipItem.parsed),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const chartPartsColors = [
|
|
||||||
'#54a60c', '#0ca68a', '#0c8aa6', '#0c57a6', '#0c33a6',
|
|
||||||
'#6f10d5', '#d510a1', '#f1bc41', '#c5f141', '#41f196',
|
|
||||||
'#41cbf1', '#4196f1', '#bf41f1', '#41f1c5', '#cbf141',
|
|
||||||
'#f1ce41', '#f17f41', '#f14141', '#34b40e', '#420eb4'
|
|
||||||
]
|
|
||||||
|
|
||||||
export const ChartTelemetryOperationsSummary = ({ operationsData }) => {
|
|
||||||
const chartRef = useRef(null)
|
|
||||||
const [chart, setChart] = useState()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const namesData = operationsData?.map(el => el.operationName) ?? ['Нет операций']
|
|
||||||
const durationsData = operationsData?.map(el => el.duration) ?? [1]
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
labels: namesData,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Скорость проходки за интервал',
|
|
||||||
data: durationsData,
|
|
||||||
borderColor: chartPartsColors,
|
|
||||||
backgroundColor: chartPartsColors,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const thisOptions = {}
|
|
||||||
Object.assign(thisOptions, defaultOptions)
|
|
||||||
|
|
||||||
if (chartRef.current && !chart) {
|
|
||||||
const newChart = new Chart(chartRef.current, {
|
|
||||||
type: 'doughnut',
|
|
||||||
options: thisOptions,
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
setChart(newChart)
|
|
||||||
} else {
|
|
||||||
chart.data = data
|
|
||||||
chart.options = thisOptions
|
|
||||||
chart.update()
|
|
||||||
}
|
|
||||||
}, [chart, operationsData])
|
|
||||||
|
|
||||||
return (<canvas ref={chartRef} />)
|
|
||||||
}
|
|
@ -1,11 +1,10 @@
|
|||||||
import { TreeSelect } from 'antd'
|
import { TreeSelect } from 'antd'
|
||||||
|
import { LabelInValueType } from 'rc-select/lib/Select'
|
||||||
|
import { RawValueType } from 'rc-tree-select/lib/TreeSelect'
|
||||||
import { DefaultValueType } from 'rc-tree-select/lib/interface'
|
import { DefaultValueType } from 'rc-tree-select/lib/interface'
|
||||||
import { useState, useEffect, ReactNode, useCallback, memo } from 'react'
|
import { useState, useEffect, ReactNode, useCallback, memo } from 'react'
|
||||||
import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'
|
import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'
|
||||||
|
|
||||||
import { RawValueType } from 'rc-tree-select/lib/TreeSelect'
|
|
||||||
import { LabelInValueType } from 'rc-select/lib/Select'
|
|
||||||
|
|
||||||
import { isRawDate } from '@utils'
|
import { isRawDate } from '@utils'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { WellIcon, WellIconState } from '@components/icons'
|
import { WellIcon, WellIconState } from '@components/icons'
|
||||||
@ -54,7 +53,7 @@ const getLabel = (wellsTree: TreeNodeData[], value?: string): string | undefined
|
|||||||
break
|
break
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
return value
|
return 'Ошибка! Скважина не найдена!'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WellTreeSelector = memo(({ ...other }) => {
|
export const WellTreeSelector = memo(({ ...other }) => {
|
||||||
@ -100,14 +99,9 @@ export const WellTreeSelector = memo(({ ...other }) => {
|
|||||||
)
|
)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => setValue(getLabel(wellsTree, routeMatch?.url)), [wellsTree, routeMatch])
|
||||||
setValue(getLabel(wellsTree, routeMatch?.url))
|
|
||||||
}, [wellsTree, routeMatch])
|
|
||||||
|
|
||||||
const onChange = useCallback((value: string): void => {
|
const onChange = useCallback((value?: string): void => setValue(getLabel(wellsTree, value)), [wellsTree])
|
||||||
if (wellsTree)
|
|
||||||
setValue(getLabel(wellsTree, value))
|
|
||||||
}, [wellsTree])
|
|
||||||
|
|
||||||
const onSelect = useCallback((value: RawValueType | LabelInValueType): void => {
|
const onSelect = useCallback((value: RawValueType | LabelInValueType): void => {
|
||||||
if (['number', 'string'].includes(typeof value))
|
if (['number', 'string'].includes(typeof value))
|
||||||
|
4
src/context.ts
Normal file
4
src/context.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { createContext } from 'react'
|
||||||
|
|
||||||
|
export const IdWellContext = createContext<number | null>(null)
|
||||||
|
export const RootPathContext = createContext<string>('')
|
@ -1,5 +0,0 @@
|
|||||||
import {createContext} from 'react'
|
|
||||||
|
|
||||||
const Context = createContext()
|
|
||||||
|
|
||||||
export default Context
|
|
0
src/index.js → src/index.tsx
Executable file → Normal file
0
src/index.js → src/index.tsx
Executable file → Normal file
@ -2,10 +2,10 @@ import { Layout } from 'antd'
|
|||||||
import { lazy, memo, Suspense, useContext, useMemo } from 'react'
|
import { lazy, memo, Suspense, useContext, useMemo } from 'react'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||||
|
|
||||||
import { SuspenseFallback } from '@pages/SuspenseFallback'
|
import { SuspenseFallback } from '@pages/SuspenseFallback'
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
|
|
||||||
const TelemetryViewer = lazy(() => import('./TelemetryViewer'))
|
const TelemetryViewer = lazy(() => import('./TelemetryViewer'))
|
||||||
const TelemetryMerger = lazy(() => import('./TelemetryMerger'))
|
const TelemetryMerger = lazy(() => import('./TelemetryMerger'))
|
||||||
|
@ -2,9 +2,9 @@ import { Layout } from 'antd'
|
|||||||
import { lazy, memo, Suspense, useContext, useMemo } from 'react'
|
import { lazy, memo, Suspense, useContext, useMemo } from 'react'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||||
|
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
import { SuspenseFallback } from '@pages/SuspenseFallback'
|
import { SuspenseFallback } from '@pages/SuspenseFallback'
|
||||||
|
|
||||||
const ClusterController = lazy(() => import( './ClusterController'))
|
const ClusterController = lazy(() => import( './ClusterController'))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Table as RawTable, Typography } from 'antd'
|
import { Table as RawTable, Typography } from 'antd'
|
||||||
import { Fragment, memo, useCallback, useContext, useEffect, useState } from 'react'
|
import { Fragment, memo, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { WellSelector } from '@components/selectors/WellSelector'
|
import { WellSelector } from '@components/selectors/WellSelector'
|
||||||
@ -8,7 +9,6 @@ import { makeGroupColumn, makeNumericColumn, makeNumericRender, makeTextColumn,
|
|||||||
import { OperationStatService, WellOperationService } from '@api'
|
import { OperationStatService, WellOperationService } from '@api'
|
||||||
import { arrayOrDefault } from '@utils'
|
import { arrayOrDefault } from '@utils'
|
||||||
|
|
||||||
import { IdWellContext } from '../Well'
|
|
||||||
|
|
||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
import '@styles/statistics.less'
|
import '@styles/statistics.less'
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
import { Button, Modal, Popconfirm } from 'antd'
|
import { Button, Modal, Popconfirm } from 'antd'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { Table } from '@components/Table'
|
import { Table } from '@components/Table'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { DrillParamsService } from '@api'
|
import { DrillParamsService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
import { getColumns } from '@pages/WellOperations/WellDrillParams'
|
import { getColumns } from '@pages/WellOperations/WellDrillParams'
|
||||||
|
|
||||||
export const NewParamsTable = memo(({ selectedWellsKeys }) => {
|
export const NewParamsTable = memo(({ selectedWellsKeys }) => {
|
||||||
|
@ -3,6 +3,7 @@ import { useState, useEffect, memo, useMemo, useContext } from 'react'
|
|||||||
import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons'
|
import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons'
|
||||||
import { Table, Tag, Button, Badge, Divider, Modal, Row, Col } from 'antd'
|
import { Table, Tag, Button, Badge, Divider, Modal, Row, Col } from 'antd'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { CompanyView } from '@components/views'
|
import { CompanyView } from '@components/views'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
@ -15,7 +16,6 @@ import {
|
|||||||
getOperations
|
getOperations
|
||||||
} from '@utils/functions'
|
} from '@utils/functions'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
import { Tvd } from '@pages/WellOperations/Tvd'
|
import { Tvd } from '@pages/WellOperations/Tvd'
|
||||||
import WellOperationsTable from '@pages/Cluster/WellOperationsTable'
|
import WellOperationsTable from '@pages/Cluster/WellOperationsTable'
|
||||||
import NewParamsTable from './NewParamsTable'
|
import NewParamsTable from './NewParamsTable'
|
||||||
|
@ -2,14 +2,14 @@ import { useState, useEffect, memo, useContext } from 'react'
|
|||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import { Col, Layout, Row } from 'antd'
|
import { Col, Layout, Row } from 'antd'
|
||||||
|
|
||||||
import { arrayOrDefault } from '@utils'
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import WellSelector from '@components/selectors/WellSelector'
|
import WellSelector from '@components/selectors/WellSelector'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||||
|
import { arrayOrDefault } from '@utils'
|
||||||
import { OperationStatService, WellCompositeService } from '@api'
|
import { OperationStatService, WellCompositeService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
import ClusterWells from '@pages/Cluster/ClusterWells'
|
import ClusterWells from '@pages/Cluster/ClusterWells'
|
||||||
import { WellCompositeSections } from './WellCompositeSections'
|
import { WellCompositeSections } from './WellCompositeSections'
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@ import { memo, useContext, useMemo } from 'react'
|
|||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
import { Layout } from 'antd'
|
import { Layout } from 'antd'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||||
|
|
||||||
import Statistics from './Statistics'
|
import Statistics from './Statistics'
|
||||||
import WellCompositeEditor from './WellCompositeEditor'
|
import WellCompositeEditor from './WellCompositeEditor'
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
|
|
||||||
export const Analytics = memo(() => {
|
export const Analytics = memo(() => {
|
||||||
const { tab } = useParams()
|
const { tab } = useParams()
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
makeNumericColumnPlanFact,
|
makeNumericColumnPlanFact,
|
||||||
Table,
|
Table,
|
||||||
makeNumericRender,
|
makeNumericRender,
|
||||||
|
makeNumericColumn,
|
||||||
} from '@components/Table'
|
} from '@components/Table'
|
||||||
import { CompanyView } from '@components/views'
|
import { CompanyView } from '@components/views'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
@ -36,6 +37,7 @@ const DAY_IN_MS = 86_400_000
|
|||||||
const ONLINE_DEADTIME = 600_000
|
const ONLINE_DEADTIME = 600_000
|
||||||
|
|
||||||
const getDate = (str) => isRawDate(str) ? new Date(str).toLocaleString() : '-'
|
const getDate = (str) => isRawDate(str) ? new Date(str).toLocaleString() : '-'
|
||||||
|
const numericRender = makeNumericRender(1)
|
||||||
|
|
||||||
export const ClusterWells = memo(({ statsWells }) => {
|
export const ClusterWells = memo(({ statsWells }) => {
|
||||||
const [selectedWellId, setSelectedWellId] = useState(0)
|
const [selectedWellId, setSelectedWellId] = useState(0)
|
||||||
@ -127,38 +129,23 @@ export const ClusterWells = memo(({ statsWells }) => {
|
|||||||
makeColumn('начало', 'factStart', { sorter: makeDateSorter('factStart'), render: getDate }),
|
makeColumn('начало', 'factStart', { sorter: makeDateSorter('factStart'), render: getDate }),
|
||||||
makeColumn('окончание', 'factEnd', { sorter: makeDateSorter('factEnd'), render: getDate }),
|
makeColumn('окончание', 'factEnd', { sorter: makeDateSorter('factEnd'), render: getDate }),
|
||||||
]),
|
]),
|
||||||
makeNumericColumnPlanFact('Продолжительность, сут', 'period', filtersMinMax, makeFilterMinMaxFunction, makeNumericRender(1)),
|
makeNumericColumnPlanFact('Продолжительность, сут', 'period', filtersMinMax, makeFilterMinMaxFunction, numericRender),
|
||||||
makeNumericColumnPlanFact('МСП, м/ч', 'rateOfPenetration', filtersMinMax, makeFilterMinMaxFunction, makeNumericRender(1)),
|
makeNumericColumnPlanFact('МСП, м/ч', 'rateOfPenetration', filtersMinMax, makeFilterMinMaxFunction, numericRender),
|
||||||
makeNumericColumnPlanFact('Рейсовая скорость, м/ч', 'routeSpeed', filtersMinMax, makeFilterMinMaxFunction, makeNumericRender(1)),
|
makeNumericColumnPlanFact('Рейсовая скорость, м/ч', 'routeSpeed', filtersMinMax, makeFilterMinMaxFunction, numericRender),
|
||||||
makeNumericColumnPlanFact('НПВ, сут', 'notProductiveTime', filtersMinMax, makeFilterMinMaxFunction, makeNumericRender(1)),
|
makeNumericColumn('НПВ, ч', 'notProductiveTimeFact', filtersMinMax, makeFilterMinMaxFunction, numericRender),
|
||||||
{
|
makeColumn('TVD', 'tvd', { align: 'center', render: (_, value) => (
|
||||||
title: 'TVD',
|
<Button onClick={() => { setSelectedWellId(value?.id); setIsTVDModalVisible(true) }} children={<LineChartOutlined />} />
|
||||||
key: 'tvd',
|
) }),
|
||||||
render: (value) => <Button onClick={()=> {
|
makeColumn('Операции', 'operations', { align: 'center', render: (_, value) => (
|
||||||
setSelectedWellId(value.id)
|
<Button onClick={() => { setSelectedWellId(value?.id); setIsOpsModalVisible(true) }} children={<ProfileOutlined />} />
|
||||||
setIsTVDModalVisible(true)
|
) }),
|
||||||
}}><LineChartOutlined /></Button>,
|
makeColumn('Участники', 'companies', {
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Операции',
|
|
||||||
key: 'operations',
|
|
||||||
render: (value) => <Button onClick={()=> {
|
|
||||||
setSelectedWellId(value.id)
|
|
||||||
setIsOpsModalVisible(true)
|
|
||||||
}}><ProfileOutlined /></Button>,
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Участники',
|
|
||||||
key: 'companies',
|
|
||||||
dataIndex: 'companies',
|
|
||||||
render: (item) => item?.map((company) => (
|
render: (item) => item?.map((company) => (
|
||||||
<Tag key={company.caption} color='blue'>
|
<Tag key={company.caption} color={'blue'}>
|
||||||
<CompanyView company={company} />
|
<CompanyView company={company} />
|
||||||
</Tag>
|
</Tag>
|
||||||
)) ?? '-',
|
)) ?? '-',
|
||||||
},
|
}),
|
||||||
], [location.pathname])
|
], [location.pathname])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -181,7 +168,7 @@ export const ClusterWells = memo(({ statsWells }) => {
|
|||||||
width={1500}
|
width={1500}
|
||||||
footer={null}
|
footer={null}
|
||||||
>
|
>
|
||||||
<Tvd idWell={selectedWellId} />
|
<Tvd style={{ minHeight: '600px' }} idWell={selectedWellId} />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import { useState, useEffect, useMemo, useCallback, useContext } from 'react'
|
import { useState, useEffect, useMemo, useCallback, useContext } from 'react'
|
||||||
import { DatePicker, Button, Input } from 'antd'
|
import { DatePicker, Button, Input } from 'antd'
|
||||||
|
|
||||||
import { FileService } from '@api'
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { UploadForm } from '@components/UploadForm'
|
import { UploadForm } from '@components/UploadForm'
|
||||||
import { CompanyView, UserView } from '@components/views'
|
import { CompanyView, UserView } from '@components/views'
|
||||||
import { EditableTable, makeColumn, makeDateColumn, makeNumericColumn, makePaginationObject } from '@components/Table'
|
|
||||||
import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory'
|
import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory'
|
||||||
|
import { EditableTable, makeColumn, makeDateColumn, makeNumericColumn, makePaginationObject } from '@components/Table'
|
||||||
import { hasPermission } from '@utils/permissions'
|
import { hasPermission } from '@utils/permissions'
|
||||||
|
import { FileService } from '@api'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
const pageSize = 12
|
const pageSize = 12
|
||||||
const { RangePicker } = DatePicker
|
const { RangePicker } = DatePicker
|
||||||
@ -42,7 +41,7 @@ export const DocumentsTemplate = ({ idCategory, idWell: wellId, accept, headerCh
|
|||||||
const [showLoader, setShowLoader] = useState(false)
|
const [showLoader, setShowLoader] = useState(false)
|
||||||
|
|
||||||
const idwellContext = useContext(IdWellContext)
|
const idwellContext = useContext(IdWellContext)
|
||||||
const idWell = useMemo(() => wellId ?? idwellContext, [wellId])
|
const idWell = useMemo(() => wellId ?? idwellContext, [wellId, idwellContext])
|
||||||
|
|
||||||
const uploadUrl = useMemo(() => `/api/well/${idWell}/files/?idCategory=${idCategory}`, [idWell, idCategory])
|
const uploadUrl = useMemo(() => `/api/well/${idWell}/files/?idCategory=${idCategory}`, [idWell, idCategory])
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@ import { memo, useContext, useMemo } from 'react'
|
|||||||
import { FolderOutlined } from '@ant-design/icons'
|
import { FolderOutlined } from '@ant-design/icons'
|
||||||
import { Layout } from 'antd'
|
import { Layout } from 'antd'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||||
|
|
||||||
import DocumentsTemplate from './DocumentsTemplate'
|
import DocumentsTemplate from './DocumentsTemplate'
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
|
|
||||||
const { Content } = Layout
|
const { Content } = Layout
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@ import { Form, Select } from 'antd'
|
|||||||
import { FileAddOutlined } from '@ant-design/icons'
|
import { FileAddOutlined } from '@ant-design/icons'
|
||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import Poprompt from '@components/selectors/Poprompt'
|
import Poprompt from '@components/selectors/Poprompt'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { DrillingProgramService } from '@api'
|
import { DrillingProgramService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
import '@styles/drilling_program.less'
|
import '@styles/drilling_program.less'
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { Input, Modal, Radio } from 'antd'
|
|||||||
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
import { BehaviorSubject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
|
import { BehaviorSubject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { UserView } from '@components/views'
|
import { UserView } from '@components/views'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
@ -9,7 +10,6 @@ import { makeColumn, makeNumericSorter, Table } from '@components/Table'
|
|||||||
import { DrillingProgramService } from '@api'
|
import { DrillingProgramService } from '@api'
|
||||||
import { arrayOrDefault } from '@utils'
|
import { arrayOrDefault } from '@utils'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
const userRules = [
|
const userRules = [
|
||||||
{ label: 'Нет', value: 0 },
|
{ label: 'Нет', value: 0 },
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useCallback, useContext, useEffect, useState } from 'react'
|
import { useCallback, useContext, useEffect, useState } from 'react'
|
||||||
import { Button, DatePicker, Input, Modal } from 'antd'
|
import { Button, DatePicker, Input, Modal } from 'antd'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { CompanyView } from '@components/views'
|
import { CompanyView } from '@components/views'
|
||||||
import DownloadLink from '@components/DownloadLink'
|
import DownloadLink from '@components/DownloadLink'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
@ -10,7 +11,6 @@ import { arrayOrDefault, formatDate } from '@utils'
|
|||||||
import { FileService } from '@api'
|
import { FileService } from '@api'
|
||||||
|
|
||||||
import MarksCard from './MarksCard'
|
import MarksCard from './MarksCard'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
import '@styles/drilling_program.less'
|
import '@styles/drilling_program.less'
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
TableOutlined,
|
TableOutlined,
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { UserView } from '@components/views'
|
import { UserView } from '@components/views'
|
||||||
import UploadForm from '@components/UploadForm'
|
import UploadForm from '@components/UploadForm'
|
||||||
import DownloadLink from '@components/DownloadLink'
|
import DownloadLink from '@components/DownloadLink'
|
||||||
@ -16,7 +17,6 @@ import { DrillingProgramService } from '@api'
|
|||||||
import { formatDate } from '@utils'
|
import { formatDate } from '@utils'
|
||||||
|
|
||||||
import MarksCard from './MarksCard'
|
import MarksCard from './MarksCard'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
import '@styles/drilling_program.less'
|
import '@styles/drilling_program.less'
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { downloadFile, formatBytes, invokeWebApiWrapperAsync } from '@components/factory'
|
import { downloadFile, formatBytes, invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { arrayOrDefault, formatDate } from '@utils'
|
import { arrayOrDefault, formatDate } from '@utils'
|
||||||
@ -19,7 +20,6 @@ import CategoryAdder from './CategoryAdder'
|
|||||||
import CategoryRender from './CategoryRender'
|
import CategoryRender from './CategoryRender'
|
||||||
import CategoryEditor from './CategoryEditor'
|
import CategoryEditor from './CategoryEditor'
|
||||||
import CategoryHistory from './CategoryHistory'
|
import CategoryHistory from './CategoryHistory'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
import '@styles/drilling_program.less'
|
import '@styles/drilling_program.less'
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { createContext, memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { Route, Switch } from 'react-router-dom'
|
import { Route, Switch } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { AdminLayoutPortal, LayoutPortal } from '@components/Layout'
|
import { AdminLayoutPortal, LayoutPortal } from '@components/Layout'
|
||||||
import { PrivateDefaultRoute, PrivateRoute } from '@components/Private'
|
import { PrivateDefaultRoute, PrivateRoute } from '@components/Private'
|
||||||
|
|
||||||
@ -10,8 +11,6 @@ import Deposit from './Deposit'
|
|||||||
import AdminPanel from './AdminPanel'
|
import AdminPanel from './AdminPanel'
|
||||||
import AccessDenied from './AccessDenied'
|
import AccessDenied from './AccessDenied'
|
||||||
|
|
||||||
export const RootPathContext = createContext('')
|
|
||||||
|
|
||||||
export const Main = memo(() => (
|
export const Main = memo(() => (
|
||||||
<RootPathContext.Provider value={''}>
|
<RootPathContext.Provider value={''}>
|
||||||
<Switch>
|
<Switch>
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
DeleteOutlined
|
DeleteOutlined
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { hasPermission } from '@utils/permissions'
|
import { hasPermission } from '@utils/permissions'
|
||||||
@ -16,7 +17,6 @@ import { formatDate } from '@utils'
|
|||||||
import { MeasureService } from '@api'
|
import { MeasureService } from '@api'
|
||||||
|
|
||||||
import { View } from './View'
|
import { View } from './View'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
import '@styles/measure.css'
|
import '@styles/measure.css'
|
||||||
|
@ -2,16 +2,16 @@ import { Button } from 'antd'
|
|||||||
import { useState, useEffect, memo, useContext } from 'react'
|
import { useState, useEffect, memo, useContext } from 'react'
|
||||||
import { TableOutlined } from '@ant-design/icons'
|
import { TableOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
import { MeasureService } from '@api'
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
import { MeasureService } from '@api'
|
||||||
|
|
||||||
import { MeasureTable } from './MeasureTable'
|
import { MeasureTable } from './MeasureTable'
|
||||||
import { InclinometryTable } from './InclinometryTable'
|
import { InclinometryTable } from './InclinometryTable'
|
||||||
import { columnsNnb, nnbDefaultData } from './nnbData'
|
import { columnsNnb, nnbDefaultData } from './nnbData'
|
||||||
import { columnsMudDiagram, mudDiagramDefaultData } from './mudDiagramData'
|
import { columnsMudDiagram, mudDiagramDefaultData } from './mudDiagramData'
|
||||||
import { columnsDrillingFluid, drillingFluidDefaultData } from './drillingFluidData'
|
import { columnsDrillingFluid, drillingFluidDefaultData } from './drillingFluidData'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
const defaultData = [
|
const defaultData = [
|
||||||
{
|
{
|
||||||
|
@ -2,12 +2,12 @@ import moment from 'moment'
|
|||||||
import { DatePicker, Descriptions, Divider, Form, Input, InputNumber, Modal, Select, Space, Table } from 'antd'
|
import { DatePicker, Descriptions, Divider, Form, Input, InputNumber, Modal, Select, Space, Table } from 'antd'
|
||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { makeColumn, makeGroupColumn } from '@components/Table'
|
import { makeColumn, makeGroupColumn } from '@components/Table'
|
||||||
import { DailyReportService } from '@api'
|
import { DailyReportService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
const { Item: RawItem } = Form
|
const { Item: RawItem } = Form
|
||||||
const { Summary } = Table
|
const { Summary } = Table
|
||||||
@ -165,7 +165,7 @@ export const ReportEditor = memo(({ visible, data, onDone, onCancel }) => {
|
|||||||
setIsLoading,
|
setIsLoading,
|
||||||
'Не удалось загрузить автозаполняемые данные для нового рапорта',
|
'Не удалось загрузить автозаполняемые данные для нового рапорта',
|
||||||
'Получение автозаполняемых данных суточного рапорта',
|
'Получение автозаполняемых данных суточного рапорта',
|
||||||
))
|
), [idWell, data, setFields])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -3,6 +3,7 @@ import { Button, DatePicker } from 'antd'
|
|||||||
import { FileExcelOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'
|
import { FileExcelOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons'
|
||||||
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
import { memo, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { Table, makeDateColumn, makeColumn } from '@components/Table'
|
import { Table, makeDateColumn, makeColumn } from '@components/Table'
|
||||||
import { download, invokeWebApiWrapperAsync } from '@components/factory'
|
import { download, invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
@ -10,7 +11,6 @@ import { DailyReportService } from '@api'
|
|||||||
import { arrayOrDefault } from '@utils'
|
import { arrayOrDefault } from '@utils'
|
||||||
|
|
||||||
import ReportEditor from './ReportEditor'
|
import ReportEditor from './ReportEditor'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
export const DailyReport = memo(() => {
|
export const DailyReport = memo(() => {
|
||||||
const [data, setData] = useState([])
|
const [data, setData] = useState([])
|
||||||
|
@ -2,14 +2,13 @@ import { Button, Tooltip } from 'antd'
|
|||||||
import { useState, useEffect, memo, useContext } from 'react'
|
import { useState, useEffect, memo, useContext } from 'react'
|
||||||
import { FilePdfOutlined, FileTextOutlined } from '@ant-design/icons'
|
import { FilePdfOutlined, FileTextOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { Table, makeDateSorter, makeNumericSorter } from '@components/Table'
|
import { Table, makeDateSorter, makeNumericSorter } from '@components/Table'
|
||||||
import { invokeWebApiWrapperAsync, downloadFile } from '@components/factory'
|
import { invokeWebApiWrapperAsync, downloadFile } from '@components/factory'
|
||||||
import { formatDate, periodToString } from '@utils'
|
import { formatDate, periodToString } from '@utils'
|
||||||
import { ReportService } from '@api'
|
import { ReportService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
const imgPaths = {
|
const imgPaths = {
|
||||||
'.pdf': <FilePdfOutlined/>,
|
'.pdf': <FilePdfOutlined/>,
|
||||||
'.las': <FileTextOutlined/>,
|
'.las': <FileTextOutlined/>,
|
||||||
|
@ -3,15 +3,15 @@ import moment from 'moment'
|
|||||||
import { useState, useEffect, memo, useCallback, useContext } from 'react'
|
import { useState, useEffect, memo, useCallback, useContext } from 'react'
|
||||||
import { Radio, Button, Select, notification } from 'antd'
|
import { Radio, Button, Select, notification } from 'antd'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { LoaderPortal } from '@components/LoaderPortal'
|
import { LoaderPortal } from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { DateRangeWrapper } from '@components/Table/DateRangeWrapper'
|
import { DateRangeWrapper } from 'components/Table/DateRangeWrapper'
|
||||||
import { Subscribe } from '@services/signalr'
|
import { Subscribe } from '@services/signalr'
|
||||||
import { ReportService } from '@api'
|
import { ReportService } from '@api'
|
||||||
|
|
||||||
import { Reports } from './Reports'
|
import { Reports } from './Reports'
|
||||||
import { ReportCreationNotify } from './ReportCreationNotify'
|
import { ReportCreationNotify } from './ReportCreationNotify'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
const timePeriodNames = [
|
const timePeriodNames = [
|
||||||
{ label: '1 секунда', value: 1 },
|
{ label: '1 секунда', value: 1 },
|
||||||
|
@ -3,11 +3,11 @@ import { memo, useContext, useMemo } from 'react'
|
|||||||
import { FilePdfOutlined } from '@ant-design/icons'
|
import { FilePdfOutlined } from '@ant-design/icons'
|
||||||
import { Layout } from 'antd'
|
import { Layout } from 'antd'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||||
|
|
||||||
import DailyReport from './DailyReport'
|
import DailyReport from './DailyReport'
|
||||||
import DiagramReport from './DiagramReport'
|
import DiagramReport from './DiagramReport'
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
|
|
||||||
const { Content } = Layout
|
const { Content } = Layout
|
||||||
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { useState, useEffect, memo, useCallback, useContext } from 'react'
|
import { useState, useEffect, memo, useCallback, useContext } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { Flex } from '@components/Grid'
|
import { Flex } from '@components/Grid'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { DatePickerWrapper, makeDateSorter } from '@components/Table'
|
import { DatePickerWrapper, makeDateSorter } from '@components/Table'
|
||||||
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
||||||
import { TelemetryDataSaubService } from '@api'
|
import { TelemetryDataSaubService } from '@api'
|
||||||
|
import { range } from '@utils'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
import { normalizeData } from '../TelemetryView'
|
import { normalizeData } from '../TelemetryView'
|
||||||
import { ArchiveDisplay, cutData } from './ArchiveDisplay'
|
import { ArchiveDisplay, cutData } from './ArchiveDisplay'
|
||||||
|
|
||||||
@ -55,13 +56,6 @@ const getLoadingInterval = (loaded, startDate, interval) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const range = (start, end) => {
|
|
||||||
const result = []
|
|
||||||
for (let i = start; i < end; i++)
|
|
||||||
result.push(i)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Archive = memo(() => {
|
export const Archive = memo(() => {
|
||||||
const [dataSaub, setDataSaub] = useState([])
|
const [dataSaub, setDataSaub] = useState([])
|
||||||
const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() })
|
const [dateLimit, setDateLimit] = useState({ from: 0, to: new Date() })
|
||||||
@ -91,17 +85,17 @@ export const Archive = memo(() => {
|
|||||||
}, [dateLimit])
|
}, [dateLimit])
|
||||||
|
|
||||||
const isDateTimeDisabled = useCallback((date) => ({
|
const isDateTimeDisabled = useCallback((date) => ({
|
||||||
disabledHours: () => range(0, 24).filter(h => {
|
disabledHours: () => range(24).filter(h => {
|
||||||
if (!date) return false
|
if (!date) return false
|
||||||
const dt = +new Date(date).setHours(h)
|
const dt = +new Date(date).setHours(h)
|
||||||
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||||
}),
|
}),
|
||||||
disabledMinutes: () => range(0, 60).filter(m => {
|
disabledMinutes: () => range(60).filter(m => {
|
||||||
if (!date) return false
|
if (!date) return false
|
||||||
const dt = +new Date(date).setMinutes(m)
|
const dt = +new Date(date).setMinutes(m)
|
||||||
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||||
}),
|
}),
|
||||||
disabledSeconds: () => range(0, 60).filter(s => {
|
disabledSeconds: () => range(60).filter(s => {
|
||||||
if (!date) return false
|
if (!date) return false
|
||||||
const dt = +new Date(date).setSeconds(s)
|
const dt = +new Date(date).setSeconds(s)
|
||||||
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
|
||||||
|
@ -3,6 +3,7 @@ import { useHistory, useParams } from 'react-router-dom'
|
|||||||
import { CloseOutlined } from '@ant-design/icons'
|
import { CloseOutlined } from '@ant-design/icons'
|
||||||
import { Button, Menu, Popconfirm } from 'antd'
|
import { Button, Menu, Popconfirm } from 'antd'
|
||||||
|
|
||||||
|
import { IdWellContext, RootPathContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { BaseWidget, WidgetSettingsWindow } from '@components/widgets'
|
import { BaseWidget, WidgetSettingsWindow } from '@components/widgets'
|
||||||
@ -19,8 +20,6 @@ import {
|
|||||||
WitsRecord61Service,
|
WitsRecord61Service,
|
||||||
} from '@api'
|
} from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
import AddGroupWindow from './AddGroupWindow'
|
import AddGroupWindow from './AddGroupWindow'
|
||||||
import AddWidgetWindow, { makeWidgetFromWits } from './AddWidgetWindow'
|
import AddWidgetWindow, { makeWidgetFromWits } from './AddWidgetWindow'
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { useState, useEffect, memo, useCallback, useContext } from 'react'
|
import { useState, useEffect, memo, useCallback, useContext } from 'react'
|
||||||
import { Table, Select, DatePicker, Input } from 'antd'
|
import { Table, Select, DatePicker, Input } from 'antd'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { makeColumn, makeDateColumn, makeNumericSorter } from '@components/Table'
|
import { makeColumn, makeDateColumn, makeNumericSorter } from '@components/Table'
|
||||||
import { MessageService } from '@api'
|
import { MessageService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '../Well'
|
|
||||||
|
|
||||||
import '@styles/message.css'
|
import '@styles/message.css'
|
||||||
|
|
||||||
|
50
src/pages/Telemetry/Operations/OperationsChart.jsx
Normal file
50
src/pages/Telemetry/Operations/OperationsChart.jsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { memo, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import * as d3 from 'd3'
|
||||||
|
|
||||||
|
import '@styles/detected_operations.less'
|
||||||
|
|
||||||
|
export const OperationsChart = memo(({ data, yDomain, width, height, bottom = 30, left = 50, top = 10, right = 10, color = '#00F' }) => {
|
||||||
|
const [ref, setRef] = useState(null)
|
||||||
|
const axisX = useRef(null)
|
||||||
|
const axisY = useRef(null)
|
||||||
|
const w = useMemo(() => Number.isFinite(+width) ? +width : ref?.offsetWidth, [width, ref])
|
||||||
|
const h = useMemo(() => Number.isFinite(+height) ? +height : ref?.offsetHeight, [height, ref])
|
||||||
|
|
||||||
|
const d = useMemo(() => data.map((row) => ({
|
||||||
|
date: new Date(row.dateStart),
|
||||||
|
value: row.durationMinutes,
|
||||||
|
})), [data]) // Нормализуем данные для графика
|
||||||
|
|
||||||
|
const x = useMemo(() => d3
|
||||||
|
.scaleTime()
|
||||||
|
.range([0, w - left - right])
|
||||||
|
.domain([d3.min(d, d => d.date), d3.max(d, d => d.date)])
|
||||||
|
, [w, d, left, right]) // Создаём ось X
|
||||||
|
|
||||||
|
const y = useMemo(() => d3
|
||||||
|
.scaleLinear()
|
||||||
|
.range([h - bottom - top, 0])
|
||||||
|
.domain([0, yDomain ?? d3.max(d, d => d.value)])
|
||||||
|
, [h, d, top, bottom, yDomain]) // Создаём ось Y
|
||||||
|
|
||||||
|
const lines = useMemo(() => d.map(d => ({ x: x(d.date), y: y(d.value) })), [d, x, y]) // Получаем массив координат линий
|
||||||
|
|
||||||
|
useEffect(() => d3.select(axisX.current).call(d3.axisBottom(x)), [axisX, x]) // Рисуем ось X
|
||||||
|
useEffect(() => d3.select(axisY.current).call(d3.axisLeft(y)), [axisY, y]) // Рисуем ось Y
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'page-left'} ref={setRef}>
|
||||||
|
<svg width={width ?? '100%'} height={height ?? '100%'}>
|
||||||
|
<g ref={axisX} className={'axis x'} transform={`translate(${left}, ${h - bottom})`} />
|
||||||
|
<g ref={axisY} className={'axis y'} transform={`translate(${left}, ${top})`} />
|
||||||
|
<g transform={`translate(${left}, ${top})`} stroke={color}>
|
||||||
|
{lines.map(({ x, y }, i) => (
|
||||||
|
<line key={i} x1={x} y1={h - bottom - top} x2={x} y2={y} />
|
||||||
|
))}
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default OperationsChart
|
30
src/pages/Telemetry/Operations/OperationsTable.jsx
Normal file
30
src/pages/Telemetry/Operations/OperationsTable.jsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
|
||||||
|
import { Table, makeTextColumn, makeNumericColumn, makeDateColumn, makeNumericRender } from '@components/Table'
|
||||||
|
|
||||||
|
import '@styles/detected_operations.less'
|
||||||
|
|
||||||
|
export const columns = [
|
||||||
|
makeTextColumn('Пользователь', 'telemetryUserName', null, null, null, { width: 200 }),
|
||||||
|
makeDateColumn('Дата начала', 'dateStart', null, undefined, { width: 150 }),
|
||||||
|
makeNumericColumn('Продолжительность (мин)', 'durationMinutes', null, null, makeNumericRender(2), 150),
|
||||||
|
makeNumericColumn('Глубина (м)', 'depthStart', null, null, makeNumericRender(1), 100),
|
||||||
|
]
|
||||||
|
|
||||||
|
export const OperationsTable = memo(({ data, height, ...other }) => (
|
||||||
|
<div className={'page-right'}>
|
||||||
|
<Table
|
||||||
|
bordered
|
||||||
|
size={'small'}
|
||||||
|
pagination={false}
|
||||||
|
{...other}
|
||||||
|
sticky={true}
|
||||||
|
columns={columns}
|
||||||
|
dataSource={data}
|
||||||
|
tableName={'well_telemetry_detected_operations'}
|
||||||
|
scroll={{ y: height ?? '70vh', scrollToFirstRowOnChange: true }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
|
||||||
|
export default OperationsTable
|
91
src/pages/Telemetry/Operations/index.jsx
Normal file
91
src/pages/Telemetry/Operations/index.jsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
|
import { InputNumber } from 'antd'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
import DateRangeWrapper from '@components/Table/DateRangeWrapper'
|
||||||
|
import { DetectedOperationService, TelemetryDataSaubService } from '@api'
|
||||||
|
import { range } from '@utils'
|
||||||
|
|
||||||
|
import OperationsChart from './OperationsChart'
|
||||||
|
import OperationsTable from './OperationsTable'
|
||||||
|
|
||||||
|
import '@styles/detected_operations.less'
|
||||||
|
|
||||||
|
export const Operations = memo(() => {
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [dateRange, setDateRange] = useState([])
|
||||||
|
const [yDomain, setYDomain] = useState(20)
|
||||||
|
const [dates, setDates] = useState()
|
||||||
|
const [data, setData] = useState([])
|
||||||
|
|
||||||
|
const idWell = useContext(IdWellContext)
|
||||||
|
|
||||||
|
const disabledDates = useCallback((current) => current && !moment(current).isBetween(...dateRange, 'day', '[]'), [dateRange])
|
||||||
|
|
||||||
|
const disabledTimes = useCallback((date) => ({
|
||||||
|
disabledHours: () => range(24).filter(h => date && !moment(date).hours(h).isBetween(...dateRange, 'hour', '[]')),
|
||||||
|
disabledMinutes: () => range(60).filter(m => date && !moment(date).minutes(m).isBetween(...dateRange, 'minute', '[]')),
|
||||||
|
disabledSeconds: () => range(60).filter(s => date && !moment(date).seconds(s).isBetween(...dateRange, 'second', '[]'))
|
||||||
|
}), [dateRange])
|
||||||
|
|
||||||
|
useEffect(() => invokeWebApiWrapperAsync(
|
||||||
|
async () => {
|
||||||
|
const dates = await TelemetryDataSaubService.getDataDatesRange(idWell)
|
||||||
|
if (dates) {
|
||||||
|
const dt = [moment(dates.from), moment(dates.to)]
|
||||||
|
setDateRange(dt)
|
||||||
|
setDates(dt)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setIsLoading,
|
||||||
|
'Не удалось загрузить диапазон доступных дат',
|
||||||
|
'Получение дапазона доступних дат',
|
||||||
|
), [idWell])
|
||||||
|
|
||||||
|
useEffect(() => invokeWebApiWrapperAsync(
|
||||||
|
async () => {
|
||||||
|
if (!dates) return
|
||||||
|
const data = await DetectedOperationService.get(idWell, undefined, dates[0].toISOString(), dates[1].toISOString())
|
||||||
|
setData(data)
|
||||||
|
},
|
||||||
|
setIsLoading,
|
||||||
|
'Не удалось загрузить список определённых операций',
|
||||||
|
'Получение списка определённых операций',
|
||||||
|
), [idWell, dates])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'container detected-operations-page'}>
|
||||||
|
<div className={'page-top'}>
|
||||||
|
<DateRangeWrapper
|
||||||
|
value={dates}
|
||||||
|
onChange={setDates}
|
||||||
|
disabled={isLoading}
|
||||||
|
disabledDate={disabledDates}
|
||||||
|
disabledTime={disabledTimes}
|
||||||
|
style={{ marginRight: '10px' }}
|
||||||
|
showTime={{ hideDisabledOptions: true }}
|
||||||
|
/>
|
||||||
|
<InputNumber
|
||||||
|
min={1}
|
||||||
|
max={2*24*60}
|
||||||
|
value={yDomain}
|
||||||
|
onChange={setYDomain}
|
||||||
|
addonAfter={'мин'}
|
||||||
|
addonBefore={'Верхняя граница'}
|
||||||
|
style={{ marginRight: '10px' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<LoaderPortal show={isLoading}>
|
||||||
|
<div className={'page-main'}>
|
||||||
|
<OperationsChart data={data} height={'50vh'} yDomain={yDomain} />
|
||||||
|
<OperationsTable data={data} height={'20vh'} />
|
||||||
|
</div>
|
||||||
|
</LoaderPortal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default Operations
|
@ -1,13 +1,13 @@
|
|||||||
import { Table } from 'antd'
|
import { Table } from 'antd'
|
||||||
import { useState, useEffect, useCallback, memo, useContext } from 'react'
|
import { useState, useEffect, useCallback, memo, useContext } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { Subscribe } from '@services/signalr'
|
import { Subscribe } from '@services/signalr'
|
||||||
import { MessageService } from '@api'
|
import { MessageService } from '@api'
|
||||||
|
|
||||||
import { columns } from '../Messages'
|
import { columns } from '../Messages'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
import '@styles/message.css'
|
import '@styles/message.css'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { memo, useCallback, useEffect, useState } from 'react'
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { Grid, GridItem } from '@components/Grid'
|
import { Grid, GridItem } from '@components/Grid'
|
||||||
import { makeDateSorter } from '@components/Table'
|
import { makeDateSorter } from '@components/Table'
|
||||||
@ -31,17 +31,33 @@ const RemoveSimilar = (input, accessor) => {
|
|||||||
|
|
||||||
const addPointData = (point) => ({ depth: point.wellDepth })
|
const addPointData = (point) => ({ depth: point.wellDepth })
|
||||||
|
|
||||||
|
const ChartValues = memo(({ pv }) => pv?.map((v, idx) => {
|
||||||
|
const text = `${v.value?.toFixed(2) ?? '--'} ${v.unit}`
|
||||||
|
return (
|
||||||
|
<GridItem
|
||||||
|
key={idx}
|
||||||
|
row={idx + 1}
|
||||||
|
col={1}
|
||||||
|
className={'monitoring_value'}
|
||||||
|
style={{ color: v.color, padding: '0 4px' }}
|
||||||
|
data-before={text}
|
||||||
|
children={text}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
|
||||||
export const MonitoringColumn = memo(({ lineGroup, data, flowChartData, interval, showBorder, style, headerHeight, pointCount = 2048, additionalLabels }) => {
|
export const MonitoringColumn = memo(({ lineGroup, data, flowChartData, interval, showBorder, style, headerHeight, pointCount = 2048, additionalLabels }) => {
|
||||||
const [dataStore, setDataStore] = useState([])
|
const [dataStore, setDataStore] = useState([])
|
||||||
const [lineGroupWithoutShapes, setLineGroupWithoutShapes] = useState([])
|
const dataLast = useMemo(() => data?.[data.length - 1], [data])
|
||||||
const dataLast = data?.[data.length - 1]
|
const yStart = useMemo(() => new Date((dataLast?.date ? +new Date(dataLast.date) : Date.now()) - interval * 0.97), [dataLast, interval])
|
||||||
const yStart = new Date((dataLast?.date ? +new Date(dataLast.date) : Date.now()) - interval * 0.97)
|
const pv = useMemo(() => lineGroup.filter(line => line.showLabels).map(line => ({
|
||||||
let pv = lineGroup.filter(line => line.showLabels).map(line => ({
|
|
||||||
color: line.color,
|
color: line.color,
|
||||||
label: line.label,
|
label: line.label,
|
||||||
unit: line.units,
|
unit: line.units,
|
||||||
value: dataLast?.[line.xAccessorName]
|
value: dataLast?.[line.xAccessorName]
|
||||||
}))
|
})), [lineGroup, dataLast])
|
||||||
|
|
||||||
|
const lineGroupWithoutShapes = useMemo(() => lineGroup.filter(cfg => !cfg.isShape), [lineGroup])
|
||||||
|
|
||||||
const postParsing = useCallback((data) => {
|
const postParsing = useCallback((data) => {
|
||||||
lineGroupWithoutShapes.forEach(lineCfg => {
|
lineGroupWithoutShapes.forEach(lineCfg => {
|
||||||
@ -67,10 +83,6 @@ export const MonitoringColumn = memo(({ lineGroup, data, flowChartData, interval
|
|||||||
})
|
})
|
||||||
}, [data, pointCount])
|
}, [data, pointCount])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLineGroupWithoutShapes(lineGroup.filter(cfg => !cfg.isShape))
|
|
||||||
}, [lineGroup])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={style}>
|
<div style={style}>
|
||||||
<Grid style={{ height: headerHeight, boxShadow: showBorder ? 'inset 0px 0px 0px 3px black' : '', margin: 0 }}>
|
<Grid style={{ height: headerHeight, boxShadow: showBorder ? 'inset 0px 0px 0px 3px black' : '', margin: 0 }}>
|
||||||
@ -80,20 +92,7 @@ export const MonitoringColumn = memo(({ lineGroup, data, flowChartData, interval
|
|||||||
</Grid>
|
</Grid>
|
||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
<Grid className={'display_chart_values'}>
|
<Grid className={'display_chart_values'}>
|
||||||
{pv?.map((v, idx) => {
|
<ChartValues pv={pv} />
|
||||||
const text = `${v.value?.toFixed(2) ?? '--'} ${v.unit}`
|
|
||||||
return (
|
|
||||||
<GridItem
|
|
||||||
key={idx}
|
|
||||||
row={idx + 1}
|
|
||||||
col={1}
|
|
||||||
className={'monitoring_value'}
|
|
||||||
style={{ color: v.color, padding: '0 4px' }}
|
|
||||||
data-before={text}
|
|
||||||
children={text}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
{additionalLabels?.map((label, idx) => (
|
{additionalLabels?.map((label, idx) => (
|
||||||
<GridItem
|
<GridItem
|
||||||
key={idx}
|
key={idx}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { memo, useCallback, useContext, useMemo, useState } from 'react'
|
import { memo, useCallback, useContext, useMemo, useState } from 'react'
|
||||||
import { Select, Modal, Input, InputNumber } from 'antd'
|
import { Select, Modal, Input, InputNumber } from 'antd'
|
||||||
|
|
||||||
import { SetpointsService } from '@api'
|
import { IdWellContext } from '@asb/context'
|
||||||
import { Grid, GridItem } from '@components/Grid'
|
import { Grid, GridItem } from '@components/Grid'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { makeNumericRender, EditableTable } from '@components/Table'
|
import { makeNumericRender, EditableTable } from '@components/Table'
|
||||||
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
import { PeriodPicker, defaultPeriod } from '@components/selectors/PeriodPicker'
|
||||||
|
import { SetpointsService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
export const SetpointSender = memo(({ onClose, visible, setpointNames }) => {
|
export const SetpointSender = memo(({ onClose, visible, setpointNames }) => {
|
||||||
const [expirePeriod, setExpirePeriod] = useState(defaultPeriod)
|
const [expirePeriod, setExpirePeriod] = useState(defaultPeriod)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Button, Modal } from 'antd'
|
import { Button, Modal } from 'antd'
|
||||||
import { useState, useEffect, memo, useCallback, useMemo, useContext } from 'react'
|
import { useState, useEffect, memo, useCallback, useMemo, useContext } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { Table } from '@components/Table'
|
import { Table } from '@components/Table'
|
||||||
import { UserView } from '@components/views'
|
import { UserView } from '@components/views'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
@ -10,7 +11,6 @@ import { makeStringCutter } from '@utils/string'
|
|||||||
import { formatDate } from '@utils'
|
import { formatDate } from '@utils'
|
||||||
import { SetpointsService } from '@api'
|
import { SetpointsService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
import SetpointSender from './SetpointSender'
|
import SetpointSender from './SetpointSender'
|
||||||
import { SetpointViewer, getSetpointStatus } from './SetpointViewer'
|
import { SetpointViewer, getSetpointStatus } from './SetpointViewer'
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
import { memo, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { TelemetryWirelineRunOutService } from '@api'
|
import { IdWellContext } from '@asb/context'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
|
||||||
import { WirelineView } from '@components/views'
|
import { WirelineView } from '@components/views'
|
||||||
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
import { TelemetryWirelineRunOutService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
export const WirelineRunOut = memo(() => {
|
export const WirelineRunOut = memo(() => {
|
||||||
const [twro, setTwro] = useState({})
|
const [twro, setTwro] = useState({})
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
TelemetryDataSpinService,
|
TelemetryDataSpinService,
|
||||||
WellService
|
WellService
|
||||||
} from '@api'
|
} from '@api'
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import { makeDateSorter } from '@components/Table'
|
import { makeDateSorter } from '@components/Table'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { Grid, GridItem, Flex } from '@components/Grid'
|
import { Grid, GridItem, Flex } from '@components/Grid'
|
||||||
@ -23,7 +24,6 @@ import { ModeDisplay } from './ModeDisplay'
|
|||||||
import { UserOfWell } from './UserOfWells'
|
import { UserOfWell } from './UserOfWells'
|
||||||
import { Setpoints } from './Setpoints'
|
import { Setpoints } from './Setpoints'
|
||||||
import WirelineRunOut from './WirelineRunOut'
|
import WirelineRunOut from './WirelineRunOut'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
import MomentStabPicEnabled from '@images/DempherOn.png'
|
import MomentStabPicEnabled from '@images/DempherOn.png'
|
||||||
import MomentStabPicDisabled from '@images/DempherOff.png'
|
import MomentStabPicDisabled from '@images/DempherOff.png'
|
||||||
|
@ -3,15 +3,16 @@ import { memo, useContext, useMemo } from 'react'
|
|||||||
import { Layout } from 'antd'
|
import { Layout } from 'antd'
|
||||||
import { AlertOutlined, FundViewOutlined, DatabaseOutlined } from '@ant-design/icons'
|
import { AlertOutlined, FundViewOutlined, DatabaseOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
import { RootPathContext } from '@asb/context'
|
||||||
import { PrivateSwitch, PrivateMenu } from '@components/Private'
|
import { PrivateSwitch, PrivateMenu } from '@components/Private'
|
||||||
|
|
||||||
import Archive from './Archive'
|
import Archive from './Archive'
|
||||||
import Messages from './Messages'
|
import Messages from './Messages'
|
||||||
import DashboardNNB from './DashboardNNB'
|
import DashboardNNB from './DashboardNNB'
|
||||||
import TelemetryView from './TelemetryView'
|
import TelemetryView from './TelemetryView'
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
|
|
||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
|
import Operations from './Operations'
|
||||||
|
|
||||||
const { Content } = Layout
|
const { Content } = Layout
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ export const Telemetry = memo(() => {
|
|||||||
<PrivateMenu.Link key={'messages'} icon={<AlertOutlined/>} title={'Сообщения'} />
|
<PrivateMenu.Link key={'messages'} icon={<AlertOutlined/>} title={'Сообщения'} />
|
||||||
<PrivateMenu.Link key={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
|
<PrivateMenu.Link key={'archive'} icon={<DatabaseOutlined />} title={'Архив'} />
|
||||||
<PrivateMenu.Link key={'dashboard_nnb'} title={'ННБ'} />
|
<PrivateMenu.Link key={'dashboard_nnb'} title={'ННБ'} />
|
||||||
|
<PrivateMenu.Link key={'operations'} title={'Операции'} />
|
||||||
</PrivateMenu>
|
</PrivateMenu>
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
@ -37,6 +39,7 @@ export const Telemetry = memo(() => {
|
|||||||
<Messages key={'messages'} />
|
<Messages key={'messages'} />
|
||||||
<Archive key={'archive'} />
|
<Archive key={'archive'} />
|
||||||
<DashboardNNB key={'dashboard_nnb/:tab?'} />
|
<DashboardNNB key={'dashboard_nnb/:tab?'} />
|
||||||
|
<Operations key={'operations'}/>
|
||||||
</PrivateSwitch>
|
</PrivateSwitch>
|
||||||
</Content>
|
</Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
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'
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
const depths = depthToDayData.map(el => ({depth: el.wellDepth, date: el.date}))
|
|
||||||
.sort((a, b) => new Date(a.date) - new Date(b.date))
|
|
||||||
setDepthData(depths)
|
|
||||||
|
|
||||||
const bitPositions = depthToDayData.map(el => ({depth: el.bitDepth, date: el.date}))
|
|
||||||
.sort((a, b) => new Date(a.date) - new Date(b.date))
|
|
||||||
setBitPositionData(bitPositions)
|
|
||||||
},
|
|
||||||
setLoader,
|
|
||||||
`Не удалось получить данные для анализа Глубина-День по скважине "${idWell}"`,
|
|
||||||
'Получение данных для анализа Глубина-День'
|
|
||||||
), [idWell])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LoaderPortal show={loader}>
|
|
||||||
<div className={'mt-20px'}>
|
|
||||||
<ChartTelemetryDepthToDay
|
|
||||||
depthData={depthData}
|
|
||||||
bitPositionData={bitPositionData}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</LoaderPortal>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TelemetryAnalysisDepthToDay
|
|
@ -1,53 +0,0 @@
|
|||||||
import { Select } from 'antd'
|
|
||||||
import { useState, useEffect, memo, useContext } from 'react'
|
|
||||||
|
|
||||||
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 час' },
|
|
||||||
{ value: '21600', label: '6 часов' },
|
|
||||||
{ value: '43200', label: '12 часов' },
|
|
||||||
{ value: '86400', label: '24 часа' }
|
|
||||||
]
|
|
||||||
|
|
||||||
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)
|
|
||||||
setDepthToIntervalData(arrayOrDefault(depthToIntervalData))
|
|
||||||
},
|
|
||||||
setLoader,
|
|
||||||
`Не удалось получить данные для анализа скорость проходки-интервал "${idWell}"`,
|
|
||||||
'Получение данных для анализа скорость проходки-интервал'
|
|
||||||
), [idWell, chartInterval])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LoaderPortal show={loader}>
|
|
||||||
<div className={'mt-20px'}>
|
|
||||||
<Select
|
|
||||||
options={timePeriodCollection}
|
|
||||||
onChange={setChartInterval}
|
|
||||||
defaultValue={'86400'}
|
|
||||||
className={'ml-30px'}
|
|
||||||
/>
|
|
||||||
<ChartTelemetryDepthToInterval
|
|
||||||
depthToIntervalData={depthToIntervalData}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</LoaderPortal>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TelemetryAnalysisDepthToInterval
|
|
@ -1,72 +0,0 @@
|
|||||||
import moment from 'moment'
|
|
||||||
import { Form, DatePicker } from 'antd'
|
|
||||||
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(() => {
|
|
||||||
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(
|
|
||||||
async() => {
|
|
||||||
const datesRange = await TelemetryAnalyticsService.getOperationsDateRange(idWell)
|
|
||||||
setAviableDatesRange(datesRange)
|
|
||||||
},
|
|
||||||
setLoader,
|
|
||||||
`Не удалось загрузить диапозон дат для скважины "${idWell}"`,
|
|
||||||
'Получение диапозона дат телеметрии скважины'
|
|
||||||
), [idWell])
|
|
||||||
|
|
||||||
useEffect(() => invokeWebApiWrapperAsync(
|
|
||||||
async () => {
|
|
||||||
const operationsSummaryData = await TelemetryAnalyticsService.getOperationsSummary(idWell,
|
|
||||||
filterDateRange[0].toISOString(), filterDateRange[1].toISOString())
|
|
||||||
|
|
||||||
setOperationsData(operationsSummaryData)
|
|
||||||
},
|
|
||||||
setLoader,
|
|
||||||
`Не удалось получить данные для анализа операций по скважине "${idWell}"`,
|
|
||||||
'Получение данных для анализа операций по скважине'
|
|
||||||
), [idWell, filterDateRange])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LoaderPortal show={loader}>
|
|
||||||
<div className={'w-100 mt-20px'}>
|
|
||||||
<Form layout={'vertical'} name={'operationsForm'} initialValues={{ remember: true }}>
|
|
||||||
<div className={'d-flex'}>
|
|
||||||
<Form.Item
|
|
||||||
name={'period'}
|
|
||||||
className={'ml-30px'}
|
|
||||||
initialValue={filterDateRange}
|
|
||||||
label={'Диапазон дат графика:'}
|
|
||||||
>
|
|
||||||
<RangePicker
|
|
||||||
showTime
|
|
||||||
allowClear={false}
|
|
||||||
disabledDate={disabledDate}
|
|
||||||
onCalendarChange={(dates) => setFilterDateRange(dates)}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
<ChartTelemetryOperationsSummary operationsData={operationsData}/>
|
|
||||||
</LoaderPortal>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TelemetryAnalysisOperationsSummary
|
|
@ -1,5 +0,0 @@
|
|||||||
import { memo } from 'react'
|
|
||||||
|
|
||||||
export const TelemetryAnalysisOperationsToInterval = memo(() => (<>123</>))
|
|
||||||
|
|
||||||
export default TelemetryAnalysisOperationsToInterval
|
|
@ -1,44 +0,0 @@
|
|||||||
import { useParams } from 'react-router-dom'
|
|
||||||
import { memo, useContext, useMemo } from 'react'
|
|
||||||
import { FolderOutlined } from '@ant-design/icons'
|
|
||||||
import { Layout } from 'antd'
|
|
||||||
|
|
||||||
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(() => {
|
|
||||||
const { tab } = useParams()
|
|
||||||
const root = useContext(RootPathContext)
|
|
||||||
const rootPath = useMemo(() => `${root}/telemetryAnalysis`, [root])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<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'}>
|
|
||||||
<PrivateSwitch elseRedirect={['depthToDay', 'depthToInterval', 'operationsSummary', 'operationsToInterval']}>
|
|
||||||
<TelemetryAnalysisDepthToDay key={'depthToDay'} />
|
|
||||||
<TelemetryAnalysisDepthToInterval key={'depthToInterval'} />
|
|
||||||
<TelemetryAnalysisOperationsSummary key={'operationsSummary'} />
|
|
||||||
<TelemetryAnalysisOperationsToInterval key={'operationsToInterval'} />
|
|
||||||
</PrivateSwitch>
|
|
||||||
</Content>
|
|
||||||
</Layout>
|
|
||||||
</RootPathContext.Provider>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default TelemetryAnalysis
|
|
@ -1,4 +1,4 @@
|
|||||||
import { createContext, memo, useContext, useMemo } from 'react'
|
import { memo, useContext, useMemo } from 'react'
|
||||||
import {
|
import {
|
||||||
FolderOutlined,
|
FolderOutlined,
|
||||||
FundViewOutlined,
|
FundViewOutlined,
|
||||||
@ -9,6 +9,7 @@ import {
|
|||||||
import { Layout } from 'antd'
|
import { Layout } from 'antd'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { IdWellContext, RootPathContext } from '@asb/context'
|
||||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||||
|
|
||||||
import Measure from './Measure'
|
import Measure from './Measure'
|
||||||
@ -18,15 +19,11 @@ import Documents from './Documents'
|
|||||||
import Telemetry from './Telemetry'
|
import Telemetry from './Telemetry'
|
||||||
import WellOperations from './WellOperations'
|
import WellOperations from './WellOperations'
|
||||||
import DrillingProgram from './DrillingProgram'
|
import DrillingProgram from './DrillingProgram'
|
||||||
import TelemetryAnalysis from './TelemetryAnalysis'
|
|
||||||
import { RootPathContext } from './Main'
|
|
||||||
|
|
||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
|
|
||||||
const { Content } = Layout
|
const { Content } = Layout
|
||||||
|
|
||||||
export const IdWellContext = createContext(null)
|
|
||||||
|
|
||||||
export const Well = memo(() => {
|
export const Well = memo(() => {
|
||||||
const { idWell, tab } = useParams()
|
const { idWell, tab } = useParams()
|
||||||
const root = useContext(RootPathContext)
|
const root = useContext(RootPathContext)
|
||||||
@ -40,7 +37,6 @@ export const Well = memo(() => {
|
|||||||
<PrivateMenu.Link key={'reports'} icon={<FilePdfOutlined />} title={'Рапорта'} />
|
<PrivateMenu.Link key={'reports'} icon={<FilePdfOutlined />} title={'Рапорта'} />
|
||||||
<PrivateMenu.Link key={'analytics'} icon={<DeploymentUnitOutlined />} title={'Аналитика'} />
|
<PrivateMenu.Link key={'analytics'} icon={<DeploymentUnitOutlined />} title={'Аналитика'} />
|
||||||
<PrivateMenu.Link key={'operations'} icon={<FolderOutlined />} 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={'document'} icon={<FolderOutlined />} title={'Документы'} />
|
||||||
<PrivateMenu.Link key={'measure'} icon={<ExperimentOutlined />} title={'Измерения'} />
|
<PrivateMenu.Link key={'measure'} icon={<ExperimentOutlined />} title={'Измерения'} />
|
||||||
<PrivateMenu.Link key={'drillingProgram'} icon={<FolderOutlined />} title={'Программа бурения'} />
|
<PrivateMenu.Link key={'drillingProgram'} icon={<FolderOutlined />} title={'Программа бурения'} />
|
||||||
@ -54,7 +50,6 @@ export const Well = memo(() => {
|
|||||||
<Reports key={'reports/:tab?'} />
|
<Reports key={'reports/:tab?'} />
|
||||||
<Analytics key={'analytics/:tab?'} />
|
<Analytics key={'analytics/:tab?'} />
|
||||||
<WellOperations key={'operations/:tab?'} />
|
<WellOperations key={'operations/:tab?'} />
|
||||||
<TelemetryAnalysis key={'telemetryAnalysis/:tab?'} />
|
|
||||||
<Documents key={'document/:category?'} />
|
<Documents key={'document/:category?'} />
|
||||||
<Measure key={'measure'} />
|
<Measure key={'measure'} />
|
||||||
<DrillingProgram key={'drillingProgram'} />
|
<DrillingProgram key={'drillingProgram'} />
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { useState, useEffect, memo, useContext } from 'react'
|
import { useState, useEffect, memo, useContext } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import {
|
import {
|
||||||
EditableTable,
|
EditableTable,
|
||||||
makeNumericMinMax,
|
makeNumericMinMax,
|
||||||
makeNumericStartEnd,
|
makeNumericStartEnd,
|
||||||
} from '@components/Table'
|
} from '@components/Table'
|
||||||
import { DrillFlowChartService } from '@api'
|
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { hasPermission } from '@utils/permissions'
|
import { hasPermission } from '@utils/permissions'
|
||||||
import { arrayOrDefault } from '@utils'
|
import { arrayOrDefault } from '@utils'
|
||||||
|
import { DrillFlowChartService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '../Well'
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
makeNumericStartEnd('Глубина, м', 'depth'),
|
makeNumericStartEnd('Глубина, м', 'depth'),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Descriptions } from 'antd'
|
import { Descriptions } from 'antd'
|
||||||
import { memo, useEffect, useState } from 'react'
|
import { memo, useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { makeNumericRender } from '@components/Table'
|
import { makeNumericRender } from '@components/Table'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
@ -21,6 +21,11 @@ const printDate = (date) => formatDate(date) ?? '-'
|
|||||||
export const AdditionalTables = memo(({ operations, xLabel, setIsLoading }) => {
|
export const AdditionalTables = memo(({ operations, xLabel, setIsLoading }) => {
|
||||||
const [additionalData, setAdditionalData] = useState({})
|
const [additionalData, setAdditionalData] = useState({})
|
||||||
|
|
||||||
|
const nptSum = useMemo(() => operations.fact
|
||||||
|
?.filter((row) => row.isNPT)
|
||||||
|
.reduce((out, row) => out + (row?.durationHours ?? 0), 0)
|
||||||
|
, [operations.fact])
|
||||||
|
|
||||||
useEffect(() => invokeWebApiWrapperAsync(
|
useEffect(() => invokeWebApiWrapperAsync(
|
||||||
async () => {
|
async () => {
|
||||||
const [factStartDate, factEndDate] = calcEndDate(operations.fact)
|
const [factStartDate, factEndDate] = calcEndDate(operations.fact)
|
||||||
@ -49,6 +54,7 @@ export const AdditionalTables = memo(({ operations, xLabel, setIsLoading }) => {
|
|||||||
<Descriptions bordered column={1} size={'small'} style={{ backgroundColor: 'white' }}>
|
<Descriptions bordered column={1} size={'small'} style={{ backgroundColor: 'white' }}>
|
||||||
<Item label={'Дата завершения'}>{printDate(additionalData.endDate)}</Item>
|
<Item label={'Дата завершения'}>{printDate(additionalData.endDate)}</Item>
|
||||||
<Item label={'Отставание (сут)'}>{numericRender(additionalData.lag)}</Item>
|
<Item label={'Отставание (сут)'}>{numericRender(additionalData.lag)}</Item>
|
||||||
|
<Item label={'Длительность НПВ (ч)'}>{numericRender(nptSum)}</Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</div>
|
</div>
|
||||||
<div className={'tvd-bl-table'} style={{ bottom: xLabel === 'day' ? '35px' : '85px' }}>
|
<div className={'tvd-bl-table'} style={{ bottom: xLabel === 'day' ? '35px' : '85px' }}>
|
||||||
|
@ -53,6 +53,7 @@ export const NptTable = memo(({ operations }) => {
|
|||||||
<LoaderPortal show={isTableLoading}>
|
<LoaderPortal show={isTableLoading}>
|
||||||
<Table
|
<Table
|
||||||
bordered
|
bordered
|
||||||
|
sticky={true}
|
||||||
size={'small'}
|
size={'small'}
|
||||||
dataSource={filteredNPT}
|
dataSource={filteredNPT}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -16,6 +16,7 @@ import 'chartjs-adapter-moment'
|
|||||||
import zoomPlugin from 'chartjs-plugin-zoom'
|
import zoomPlugin from 'chartjs-plugin-zoom'
|
||||||
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
import ChartDataLabels from 'chartjs-plugin-datalabels'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { formatDate, fractionalSum } from '@utils/datetime'
|
import { formatDate, fractionalSum } from '@utils/datetime'
|
||||||
@ -24,7 +25,6 @@ import { getOperations } from '@utils/functions'
|
|||||||
import NptTable from './NptTable'
|
import NptTable from './NptTable'
|
||||||
import NetGraphExport from './NetGraphExport'
|
import NetGraphExport from './NetGraphExport'
|
||||||
import AdditionalTables from './AdditionalTables'
|
import AdditionalTables from './AdditionalTables'
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
import '@styles/tvd.less'
|
import '@styles/tvd.less'
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect, useCallback, memo, useMemo, useContext } from 'react'
|
import { useState, useEffect, useCallback, memo, useMemo, useContext } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import {
|
import {
|
||||||
EditableTable,
|
EditableTable,
|
||||||
makeSelectColumn,
|
makeSelectColumn,
|
||||||
@ -13,7 +14,6 @@ import { DrillParamsService, WellOperationService } from '@api'
|
|||||||
import { hasPermission } from '@utils/permissions'
|
import { hasPermission } from '@utils/permissions'
|
||||||
import { arrayOrDefault } from '@utils'
|
import { arrayOrDefault } from '@utils'
|
||||||
|
|
||||||
import { IdWellContext } from '../Well'
|
|
||||||
|
|
||||||
export const getColumns = async (idWell) => {
|
export const getColumns = async (idWell) => {
|
||||||
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
|
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
|
||||||
|
@ -3,6 +3,7 @@ import { Input } from 'antd'
|
|||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import { useState, useEffect, memo, useMemo, useCallback, useContext } from 'react'
|
import { useState, useEffect, memo, useMemo, useCallback, useContext } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import {
|
import {
|
||||||
EditableTable,
|
EditableTable,
|
||||||
makeColumn,
|
makeColumn,
|
||||||
@ -21,7 +22,6 @@ import { hasPermission } from '@utils/permissions'
|
|||||||
import { arrayOrDefault } from '@utils'
|
import { arrayOrDefault } from '@utils'
|
||||||
import { WellOperationService } from '@api'
|
import { WellOperationService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '../Well'
|
|
||||||
|
|
||||||
const { TextArea } = Input
|
const { TextArea } = Input
|
||||||
|
|
||||||
@ -132,6 +132,7 @@ export const WellOperationsEditor = memo(({ idType, showNpt, ...other }) => {
|
|||||||
bordered
|
bordered
|
||||||
size={'small'}
|
size={'small'}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
sticky={true}
|
||||||
dataSource={operations}
|
dataSource={operations}
|
||||||
onRowAdd={hasPermission('WellOperation.edit') && makeActionHandler('insertRange', handlerProps, recordParser, 'Добавление операции по скважине')}
|
onRowAdd={hasPermission('WellOperation.edit') && makeActionHandler('insertRange', handlerProps, recordParser, 'Добавление операции по скважине')}
|
||||||
onRowEdit={hasPermission('WellOperation.edit') && makeActionHandler('update', handlerProps, recordParser, 'Редактирование операции по скважине')}
|
onRowEdit={hasPermission('WellOperation.edit') && makeActionHandler('update', handlerProps, recordParser, 'Редактирование операции по скважине')}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { useState, useEffect, memo, useContext } from 'react'
|
import { useState, useEffect, memo, useContext } from 'react'
|
||||||
|
|
||||||
|
import { IdWellContext } from '@asb/context'
|
||||||
import LoaderPortal from '@components/LoaderPortal'
|
import LoaderPortal from '@components/LoaderPortal'
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { Table, makeColumn, makeColumnsPlanFact, makeNumericRender } from '@components/Table'
|
import { Table, makeColumn, makeColumnsPlanFact, makeNumericRender } from '@components/Table'
|
||||||
import { calcDuration } from '@utils/datetime'
|
import { calcDuration } from '@utils/datetime'
|
||||||
import { OperationStatService } from '@api'
|
import { OperationStatService } from '@api'
|
||||||
|
|
||||||
import { IdWellContext } from '../Well'
|
|
||||||
|
|
||||||
const numericRender = makeNumericRender(2)
|
const numericRender = makeNumericRender(2)
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
TableOutlined,
|
TableOutlined,
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
|
|
||||||
|
import { IdWellContext, RootPathContext } from '@asb/context'
|
||||||
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
import { PrivateMenu, PrivateSwitch } from '@components/Private'
|
||||||
|
|
||||||
import { Tvd } from './Tvd'
|
import { Tvd } from './Tvd'
|
||||||
@ -19,8 +20,6 @@ import { WellSectionsStat } from './WellSectionsStat'
|
|||||||
import { WellOperationsEditor } from './WellOperationsEditor'
|
import { WellOperationsEditor } from './WellOperationsEditor'
|
||||||
import { Flex } from '@components/Grid'
|
import { Flex } from '@components/Grid'
|
||||||
|
|
||||||
import { RootPathContext } from '@pages/Main'
|
|
||||||
import { IdWellContext } from '@pages/Well'
|
|
||||||
|
|
||||||
const { Content } = Layout
|
const { Content } = Layout
|
||||||
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
export const reportWebVitals = onPerfEntry => {
|
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
|
||||||
getCLS(onPerfEntry)
|
|
||||||
getFID(onPerfEntry)
|
|
||||||
getFCP(onPerfEntry)
|
|
||||||
getLCP(onPerfEntry)
|
|
||||||
getTTFB(onPerfEntry)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default reportWebVitals
|
|
12
src/reportWebVitals.ts
Normal file
12
src/reportWebVitals.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { getCLS, getFID, getFCP, getLCP, getTTFB, ReportHandler } from 'web-vitals'
|
||||||
|
|
||||||
|
export const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||||
|
if (!onPerfEntry) return
|
||||||
|
getCLS(onPerfEntry)
|
||||||
|
getFID(onPerfEntry)
|
||||||
|
getFCP(onPerfEntry)
|
||||||
|
getLCP(onPerfEntry)
|
||||||
|
getTTFB(onPerfEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default reportWebVitals
|
7
src/styles/detected_operations.less
Normal file
7
src/styles/detected_operations.less
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.detected-operations-page {
|
||||||
|
& .page-top {
|
||||||
|
width: 100%;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,6 +15,8 @@ export const deepCopy = <T extends any>(data: T): T => JSON.parse(JSON.stringify
|
|||||||
export const wrapValues = <T, R>(data: Record<string, T>, handler: (data: T, key: string, object: Record<string, T>) => R): Record<string, R> =>
|
export const wrapValues = <T, R>(data: Record<string, T>, handler: (data: T, key: string, object: Record<string, T>) => R): Record<string, R> =>
|
||||||
Object.fromEntries(Object.entries(data).map(([key, value]) => [key, handler(value, key, data)]))
|
Object.fromEntries(Object.entries(data).map(([key, value]) => [key, handler(value, key, data)]))
|
||||||
|
|
||||||
|
export const range = (end: number, start: number = 0) => Array.from({ length: end - start }, (_, i) => start + i)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Объединить типы, исключив совпадающие поля справа
|
* Объединить типы, исключив совпадающие поля справа
|
||||||
* @param T Тип, передаваемый полностью
|
* @param T Тип, передаваемый полностью
|
||||||
|
@ -97,6 +97,7 @@ export const requirements: PermissionRecord = {
|
|||||||
monitoring: ['Deposit.get', 'DrillFlowChart.get', 'TelemetryDataSaub.get', 'TelemetryDataSpin.get'],
|
monitoring: ['Deposit.get', 'DrillFlowChart.get', 'TelemetryDataSaub.get', 'TelemetryDataSpin.get'],
|
||||||
messages: ['Deposit.get', 'TelemetryDataSaub.get'],
|
messages: ['Deposit.get', 'TelemetryDataSaub.get'],
|
||||||
dashboard_nnb: ['Deposit.get'], //'WitsInfo.get', 'WitsRecord1.get', 'WitsRecord7.get', 'WitsRecord8.get', 'WitsRecord50.get', 'WitsRecord60.get', 'WitsRecord61.get'],
|
dashboard_nnb: ['Deposit.get'], //'WitsInfo.get', 'WitsRecord1.get', 'WitsRecord7.get', 'WitsRecord8.get', 'WitsRecord50.get', 'WitsRecord60.get', 'WitsRecord61.get'],
|
||||||
|
operations: ['Deposit.get'], //'TelemetryDataSaubService.get', 'DetectedOperationService.get'],
|
||||||
},
|
},
|
||||||
reports: {
|
reports: {
|
||||||
diagram_report: ['Deposit.get', 'Report.get'],
|
diagram_report: ['Deposit.get', 'Report.get'],
|
||||||
|
Loading…
Reference in New Issue
Block a user