(({
- editing,
- dataIndex,
- input,
- isRequired,
- formItemClass,
- formItemRules,
- children,
- initialValue,
- ...other
-}) => (
-
- {!editing ? children : (
-
- {input ?? }
-
- )}
- |
-))
+ editing,
+ dataIndex,
+ input,
+ isRequired,
+ formItemClass,
+ formItemRules,
+ children,
+ initialValue,
+ ...other
+}) => {
+ const rules = useMemo(() => formItemRules || [{
+ required: isRequired,
+ message: `Это обязательное поле!`,
+ }], [formItemRules, isRequired])
+
+ const name = useMemo(() => dataIndex ? String(dataIndex).split('.') : undefined, [dataIndex])
+ const tdStyle = useMemo(() => editing ? { padding: 0 } : undefined, [editing])
+
+ const edititngItem = useMemo(() => (
+
+ {input ?? }
+
+ ), [name, rules, formItemClass, initialValue, input])
+
+ return (
+
+ {editing ? edititngItem : children}
+ |
+ )
+})
diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx
index 3b25a0d..c9ecbbe 100755
--- a/src/components/Table/Table.tsx
+++ b/src/components/Table/Table.tsx
@@ -1,26 +1,65 @@
-import { memo, useCallback, useEffect, useState } from 'react'
+import { Key, memo, useCallback, useEffect, useState } from 'react'
import { ColumnGroupType, ColumnType } from 'antd/lib/table'
import { Table as RawTable, TableProps } from 'antd'
+import { RenderMethod } from './Columns'
+import { tryAddKeys } from './EditableTable'
+import TableSettingsChanger from './TableSettingsChanger'
import type { OmitExtends } from '@utils/types'
import { applyTableSettings, getTableSettings, setTableSettings, TableColumnSettings, TableSettings } from '@utils'
-import TableSettingsChanger from './TableSettingsChanger'
-import { tryAddKeys } from './EditableTable'
-
import '@styles/index.css'
export type BaseTableColumn = ColumnGroupType | ColumnType
-export type TableColumns = OmitExtends, TableColumnSettings>[]
+export type TableColumn = OmitExtends, TableColumnSettings>
+export type TableColumns = TableColumn[]
export type TableContainer = TableProps & {
- columns: TableColumns
+ columns: TableColumn[]
tableName?: string
showSettingsChanger?: boolean
}
-const _Table = ({ columns, dataSource, tableName, showSettingsChanger, ...other }: TableContainer) => {
- const [newColumns, setNewColumns] = useState>([])
+export interface DataSet {
+ [k: Key]: DataSet | T | D
+}
+
+export const getObjectByDeepKey = (key: Key | undefined, data: DataSet): T | undefined => {
+ if (!key) return undefined
+ const parts = String(key).split('.')
+ let out = data
+ for (let i = 0; i < parts.length && out; i++) {
+ const key = parts[i]
+ if (!(key in out)) return undefined // Если ключ не найдем, считаем значение null
+ out = out[key] as DataSet // Углубляемся внутрь объекта
+ }
+ return out as T
+}
+
+export const makeColumnRenderWrapper = >(key: Key | undefined, render: RenderMethod | undefined): RenderMethod =>
+ (_: any, dataset: T, index: number) => {
+ const renderFunc: RenderMethod = typeof render === 'function' ? render : (record) => String(record)
+ return renderFunc(getObjectByDeepKey(key, dataset), dataset, index)
+ }
+
+
+const applyColumnWrappers = >(columns: BaseTableColumn[]): BaseTableColumn[] => {
+ return columns.map((column) => {
+ if ('children' in column) {
+ return {
+ ...column,
+ children: applyColumnWrappers(column.children),
+ }
+ }
+ return {
+ ...column,
+ render: makeColumnRenderWrapper(column.key, column.render),
+ }
+ })
+}
+
+function _Table>({ columns, dataSource, tableName, showSettingsChanger, ...other }: TableContainer) {
+ const [newColumns, setNewColumns] = useState[]>([])
const [settings, setSettings] = useState({})
const onSettingsChanged = useCallback((settings?: TableSettings | null) => {
@@ -31,7 +70,7 @@ const _Table = ({ columns, dataSource, tableName, showSettings
useEffect(() => setSettings(tableName ? getTableSettings(tableName) : {}), [tableName])
useEffect(() => setNewColumns(() => {
- const newColumns = applyTableSettings(columns, settings)
+ const newColumns = applyTableSettings(applyColumnWrappers(columns), settings)
if (tableName && showSettingsChanger) {
const oldTitle = newColumns[0].title
newColumns[0].title = (props) => (
diff --git a/src/components/Table/TableSettingsChanger.tsx b/src/components/Table/TableSettingsChanger.tsx
index 4ca622f..2bbb805 100755
--- a/src/components/Table/TableSettingsChanger.tsx
+++ b/src/components/Table/TableSettingsChanger.tsx
@@ -1,11 +1,11 @@
import { memo, useCallback, useEffect, useState } from 'react'
import { ColumnsType } from 'antd/lib/table'
-import { Button, Modal, Switch, Table } from 'antd'
+import { Button, Modal, Switch } from 'antd'
import { SettingOutlined } from '@ant-design/icons'
import { TableColumnSettings, makeTableSettings, mergeTableSettings, TableSettings } from '@utils'
-import { TableColumns } from './Table'
-import { makeColumn } from '.'
+import { Table, TableColumns } from './Table'
+import { makeColumn, makeTextColumn } from '.'
const parseSettings = (columns?: TableColumns, settings?: TableSettings | null): TableColumnSettings[] => {
const newSettings = mergeTableSettings(makeTableSettings(columns ?? []), settings ?? {})
@@ -46,8 +46,8 @@ const _TableSettingsChanger = ({ title, columns, settings, onC
useEffect(() => {
setTableColumns([
- makeColumn('Название', 'title'),
- makeColumn(null, 'visible', {
+ makeTextColumn('Название', 'title'),
+ makeColumn(null, 'visible', {
title: () => (
<>
Показать
@@ -56,7 +56,7 @@ const _TableSettingsChanger = ({ title, columns, settings, onC
>
),
- render: (visible: boolean, _?: TableColumnSettings, index: number = NaN) => (
+ render: (visible, _, index = NaN) => (
(key: keyof DataType): CompareFn> =>
- (a: DataType, b: DataType) => Number(a[key]) - Number(b[key])
-
-export const makeNumericObjSorter = (key: [string, string]) =>
- (a: DataType, b: DataType) => Number(a[key[0]][key[1]]) - Number(b[key[0]][key[1]])
-
-export const makeStringSorter = (key: keyof DataType) => (a?: DataType | null, b?: DataType | null) => {
- if (!a && !b) return 0
- if (!a) return 1
- if (!b) return -1
-
- return String(a[key]).localeCompare(String(b[key]))
-}
-
-export const makeDateSorter = (key: keyof DataType) => (a: DataType, b: DataType) => {
- const adate = a[key]
- const bdate = b[key]
- if (!isRawDate(adate) || !isRawDate(bdate))
- throw new Error('Date column contains not date formatted string(s)')
-
- const date = new Date(adate)
-
- return date.getTime() - new Date(bdate).getTime()
-}
-
-export const makeTimeSorter = (key: keyof DataType) => (a: DataType, b: DataType) => {
- const elma = a[key]
- const elmb = b[key]
-
- if (!elma && !elmb) return 0
- if (!elma) return 1
- if (!elmb) return -1
-
- return timeToMoment(elma).diff(timeToMoment(elmb))
-}
diff --git a/src/pages/AdminPanel/index.jsx b/src/pages/AdminPanel/index.jsx
index ad338e2..e4c11b8 100755
--- a/src/pages/AdminPanel/index.jsx
+++ b/src/pages/AdminPanel/index.jsx
@@ -1,9 +1,9 @@
-import { Navigate, Route, Routes } from 'react-router-dom'
-import { lazy, memo, useMemo } from 'react'
+import { Navigate, Route, Routes, useLocation } from 'react-router-dom'
+import { lazy, memo, useEffect, useMemo } from 'react'
import { RootPathContext, useLayoutProps, useRootPath } from '@asb/context'
import { FastRunMenu } from '@components/FastRunMenu'
-import { MenuBreadcrumbItems } from '@components/MenuBreadcrumb'
+import { makeMenuBreadcrumbItems } from '@components/MenuBreadcrumb'
import { NoAccessComponent, withPermissions } from '@utils'
import { AdminNavigationMenu, menuItems } from './AdminNavigationMenu'
@@ -21,18 +21,21 @@ const TelemetryViewer = lazy(() => import('./Telemetry/TelemetryViewer'))
const TelemetryMerger = lazy(() => import('./Telemetry/TelemetryMerger'))
const VisitLog = lazy(() => import('./VisitLog'))
-const layoutProps = {
- sider: ,
- title: 'Администраторская панель',
- isAdmin: true,
- breadcrumb: ,
-}
-
const AdminPanel = memo(() => {
+ const location = useLocation()
const root = useRootPath()
const rootPath = useMemo(() => `${root}/admin`, [root])
- useLayoutProps(layoutProps)
+ const setLayoutProps = useLayoutProps()
+
+ useEffect(() => {
+ setLayoutProps({
+ sider: ,
+ title: 'Администраторская панель',
+ isAdmin: true,
+ breadcrumb: makeMenuBreadcrumbItems(menuItems, location.pathname, /^\/admin\//),
+ })
+ }, [location.pathname])
return (
diff --git a/src/pages/Cluster/WellOperationsTable.jsx b/src/pages/Cluster/WellOperationsTable.jsx
index 1d2b094..1e8ee65 100755
--- a/src/pages/Cluster/WellOperationsTable.jsx
+++ b/src/pages/Cluster/WellOperationsTable.jsx
@@ -1,7 +1,6 @@
import { memo, useMemo } from 'react'
-import { Table } from 'antd'
-import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table'
+import { Table, makeTextColumn, makeNumericColumnPlanFact } from '@components/Table'
import { getPrecision } from '@utils/functions'
const columns = [
diff --git a/src/pages/FileDownload.jsx b/src/pages/FileDownload.jsx
index 217edfb..4da1a72 100644
--- a/src/pages/FileDownload.jsx
+++ b/src/pages/FileDownload.jsx
@@ -1,40 +1,30 @@
-import { Link, useNavigate, useParams } from 'react-router-dom'
-import { memo, useCallback, useEffect, useState } from 'react'
+import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
+import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { InfoCircleFilled, CloseCircleOutlined } from '@ant-design/icons'
import { Button, Result, Typography } from 'antd'
import { downloadFile, invokeWebApiWrapperAsync } from '@components/factory'
import { withPermissions } from '@utils'
-import { FileService, WellService } from '@api'
+import { FileService } from '@api'
const { Paragraph, Text } = Typography
-export const getLinkToFile = (fileInfo) => `/file_download/${fileInfo.idWell}/${fileInfo.id}`
+export const getLinkToFile = (fileInfo) => `/file_download/${fileInfo.id}`
const FileDownload = memo(function FileDownload() {
- const { idWell, idFile } = useParams()
- const [well, setWell] = useState({})
+ const { idFile } = useParams()
const [file, setFile] = useState({})
const [isError, setIsError] = useState(false)
const navigate = useNavigate()
-
- useEffect(() => {
- invokeWebApiWrapperAsync(
- async () => setWell(await WellService.get(idWell)),
- null,
- 'Не удалось получить информацию о скважине',
- { actionName: 'Получение данных о скважине' }
- )
- }, [idWell])
+ const location = useLocation()
+ const isFirstOpenApp = useMemo(() => location.key === 'default', [location])
useEffect(() => {
invokeWebApiWrapperAsync(
async () => {
- const files = await FileService.getFilesInfo(idWell)
- // TODO Получается только одна категория файлов.
- // Поменять при появлении метода получения инфы о конкретном файле
- setFile(files.items.find((file) => file.id === idFile) ?? { id: idFile, idWell, name: `File_${idWell}_${idFile}` })
+ const file = await FileService.getFileInfo(idFile)
+ setFile(file)
},
null,
() => {
@@ -43,10 +33,10 @@ const FileDownload = memo(function FileDownload() {
},
{ actionName: 'Получение информации о файле' }
)
- }, [idWell, idFile])
+ }, [idFile])
const download = useCallback(async () => {
- if (!await downloadFile(file))
+ if (!file || !await downloadFile(file))
setIsError(true)
}, [file])
@@ -58,13 +48,16 @@ const FileDownload = memo(function FileDownload() {
<>
Вы перешли к странице загрузки файла!
- Файл "{file.name ?? ('№' + idFile)}", скважина "{well.caption ?? ('№' + idWell)}".
+ Файл "{file?.name ?? ('№' + idFile)}".
>
)}
// subTitle={}
extra={(
<>
-
+ {isFirstOpenApp
+ ?
+ :
+ }
>
)}
diff --git a/src/pages/Well/Analytics/WellCompositeEditor/WellCompositeSections.jsx b/src/pages/Well/Analytics/WellCompositeEditor/WellCompositeSections.jsx
index 426ed21..52931a4 100644
--- a/src/pages/Well/Analytics/WellCompositeEditor/WellCompositeSections.jsx
+++ b/src/pages/Well/Analytics/WellCompositeEditor/WellCompositeSections.jsx
@@ -1,12 +1,12 @@
import { Link, useLocation } from 'react-router-dom'
import { useState, useEffect, memo, useMemo, lazy, Suspense } from 'react'
import { LineChartOutlined, ProfileOutlined, TeamOutlined } from '@ant-design/icons'
-import { Table, Button, Badge, Divider, Modal, Row, Col } from 'antd'
+import { Button, Badge, Divider, Modal, Row, Col } from 'antd'
import { useWell } from '@asb/context'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
-import { makeTextColumn, makeNumericColumnPlanFact, makeNumericColumn } from '@components/Table'
+import { Table, makeTextColumn, makeNumericColumnPlanFact, makeNumericColumn } from '@components/Table'
import { WellCompositeService } from '@api'
import {
hasPermission,
diff --git a/src/pages/Well/Reports/DailyReport/ReportEditor.jsx b/src/pages/Well/Reports/DailyReport/ReportEditor.jsx
index e77fc61..05c171a 100644
--- a/src/pages/Well/Reports/DailyReport/ReportEditor.jsx
+++ b/src/pages/Well/Reports/DailyReport/ReportEditor.jsx
@@ -1,15 +1,15 @@
-import { DatePicker, Descriptions, Form, Input, InputNumber, Modal, Table, Tabs } from 'antd'
+import { DatePicker, Descriptions, Form, Input, InputNumber, Modal, Table as RawTable, Tabs } from 'antd'
import { memo, useCallback, useEffect, useState } from 'react'
import moment from 'moment'
import { useWell } from '@asb/context'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
-import { makeColumn, makeGroupColumn } from '@components/Table'
+import { Table, makeColumn, makeGroupColumn } from '@components/Table'
import { DailyReportService } from '@api'
const { Item: RawItem } = Form
-const { Summary } = Table
+const { Summary } = RawTable
const { TabPane } = Tabs
const Item = memo(({ style, ...other }) => )
diff --git a/src/pages/Well/Reports/DailyReport/index.jsx b/src/pages/Well/Reports/DailyReport/index.jsx
index aa19d13..0c303b1 100644
--- a/src/pages/Well/Reports/DailyReport/index.jsx
+++ b/src/pages/Well/Reports/DailyReport/index.jsx
@@ -72,8 +72,7 @@ const DailyReport = memo(() => {
const filteredData = useMemo(() => {
if (!searchDate) return data
- const endDate = moment(searchDate[1]).add(1, 'd')
- return data.filter((row) => moment(row.reportDate).isBetween(searchDate[0], endDate, 'ms', '[)'))
+ return data.filter((row) => moment(row.reportDate).isBetween(searchDate[0], searchDate[1], 'day', '[]'))
}, [data, searchDate])
return (
diff --git a/src/pages/Well/Telemetry/TelemetryView/ActiveMessagesOnline.jsx b/src/pages/Well/Telemetry/TelemetryView/ActiveMessagesOnline.jsx
index 910aac7..dbe5af1 100644
--- a/src/pages/Well/Telemetry/TelemetryView/ActiveMessagesOnline.jsx
+++ b/src/pages/Well/Telemetry/TelemetryView/ActiveMessagesOnline.jsx
@@ -1,7 +1,7 @@
-import { Table } from 'antd'
import { useState, useEffect, useCallback, memo, useMemo } from 'react'
import { useWell } from '@asb/context'
+import { Table } from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { Subscribe } from '@services/signalr'
@@ -22,7 +22,7 @@ export const ActiveMessagesOnline = memo(({ well: givenWell }) => {
if (messages)
setMessages(messages.items.splice(0, 4))
}, [])
-
+
const columns = useMemo(() => makeMessageColumns(well.id), [well.id])
useEffect(() => {
diff --git a/src/pages/Well/WellCase/index.jsx b/src/pages/Well/WellCase/index.jsx
index 3912da6..1eb9709 100644
--- a/src/pages/Well/WellCase/index.jsx
+++ b/src/pages/Well/WellCase/index.jsx
@@ -1,5 +1,5 @@
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
-import { Alert, Button, Typography } from 'antd'
+import { Alert, Button } from 'antd'
import { useWell } from '@asb/context'
import { UserView } from '@components/views'
diff --git a/src/pages/Well/WellOperations/WellDrillParams.jsx b/src/pages/Well/WellOperations/WellDrillParams.jsx
index 486b422..0ae6097 100644
--- a/src/pages/Well/WellOperations/WellDrillParams.jsx
+++ b/src/pages/Well/WellOperations/WellDrillParams.jsx
@@ -1,88 +1,17 @@
import { useState, useEffect, useCallback, memo, useMemo } from 'react'
-import { InputNumber } from 'antd'
import { useWell } from '@asb/context'
import {
EditableTable,
makeSelectColumn,
- makeGroupColumn,
- makeNumericRender,
makeNumericSorter,
- RegExpIsFloat,
+ makeNumericAvgRange,
} from '@components/Table'
import LoaderPortal from '@components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DrillParamsService, WellOperationService } from '@api'
import { arrayOrDefault } from '@utils'
-import { makeNumericObjSorter } from '@components/Table/sorters'
-
-const makeNumericObjRender = (fixed, columnKey) => (value, obj) => {
- let val = '-'
- const isSelected = obj && columnKey && obj[columnKey[0]] ? obj[columnKey[0]][columnKey[1]] : false
-
- if ((value ?? null) !== null && Number.isFinite(+value)) {
- val = (fixed ?? null) !== null
- ? (+value).toFixed(fixed)
- : (+value).toPrecision(5)
- }
-
- return (
-
- {val}
-
- )
-}
-
-const makeNumericColumnOptionsWithColor = (fixed, sorterKey, columnKey) => ({
- editable: true,
- initialValue: 0,
- width: 100,
- sorter: sorterKey ? makeNumericObjSorter(sorterKey) : undefined,
- formItemRules: [{
- required: true,
- message: 'Введите число',
- pattern: RegExpIsFloat,
- }],
- render: makeNumericObjRender(fixed, columnKey),
-})
-
-const makeNumericObjColumn = (
- title,
- dataIndex,
- filters,
- filterDelegate,
- renderDelegate,
- width,
- other
-) => ({
- title: title,
- dataIndex: dataIndex,
- key: dataIndex,
- filters: filters,
- onFilter: filterDelegate ? filterDelegate(dataIndex) : null,
- sorter: makeNumericObjSorter(dataIndex),
- width: width,
- input: ,
- render: renderDelegate ?? makeNumericRender(),
- align: 'right',
- ...other
-})
-
-const makeNumericAvgRange = (
- title,
- dataIndex,
- fixed,
- filters,
- filterDelegate,
- renderDelegate,
- width
-) => makeGroupColumn(title, [
- makeNumericObjColumn('мин', [dataIndex, 'min'], filters, filterDelegate, renderDelegate, width, makeNumericColumnOptionsWithColor(fixed, [dataIndex, 'min'], [dataIndex, 'isMin'])),
- makeNumericObjColumn('сред', [dataIndex, 'avg'], filters, filterDelegate, renderDelegate, width, makeNumericColumnOptionsWithColor(fixed, [dataIndex, 'avg'])),
- makeNumericObjColumn('макс', [dataIndex, 'max'], filters, filterDelegate, renderDelegate, width, makeNumericColumnOptionsWithColor(fixed, [dataIndex, 'max'], [dataIndex, 'isMax']))
-])
-
export const getColumns = async (idWell) => {
let sectionTypes = await WellOperationService.getSectionTypes(idWell)
sectionTypes = Object.entries(sectionTypes).map(([id, value]) => ({
diff --git a/src/pages/Well/index.jsx b/src/pages/Well/index.jsx
index 534b313..398a0b8 100644
--- a/src/pages/Well/index.jsx
+++ b/src/pages/Well/index.jsx
@@ -4,7 +4,7 @@ import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-do
import { WellContext, RootPathContext, useRootPath, useLayoutProps, TopRightBlockContext } from '@asb/context'
import { FastRunMenu } from '@components/FastRunMenu'
import { invokeWebApiWrapperAsync } from '@components/factory'
-import { MenuBreadcrumbItems } from '@components/MenuBreadcrumb'
+import { makeMenuBreadcrumbItems } from '@components/MenuBreadcrumb'
import { NoAccessComponent, withPermissions } from '@utils'
import { WellService } from '@api'
@@ -78,9 +78,9 @@ const Well = memo(() => {
useEffect(() => setLayoutProps({
sider: ,
- breadcrumb: ,
+ breadcrumb: makeMenuBreadcrumbItems(menuItems, location.pathname, /^\/well\/[0-9]+\//),
topRightBlock: topRightBlock,
- }), [well, setLayoutProps, topRightBlock])
+ }), [well, location.pathname, setLayoutProps, topRightBlock])
useEffect(() => setTopRightBlock(undefined), [location])
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
deleted file mode 100755
index 6431bc5..0000000
--- a/src/react-app-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/src/reportWebVitals.ts b/src/reportWebVitals.ts
deleted file mode 100644
index 30e6a9d..0000000
--- a/src/reportWebVitals.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-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