Улучшена мемоизация

This commit is contained in:
Александр Сироткин 2022-03-18 19:40:52 +05:00
parent d98e223468
commit 777fbb63d1
31 changed files with 231 additions and 293 deletions

View File

@ -21,10 +21,16 @@ import { coordsFixed } from './DepositController'
export const ClusterController = memo(() => {
const [deposits, setDeposits] = useState([])
const [clusters, setClusters] = useState([])
const [filteredClusters, setFilteredClusters] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const filteredClusters = useMemo(() => clusters.filter((cluster) => cluster && (!searchValue || [
cluster.caption ?? '',
cluster.latitude?.toString ?? '',
cluster.longitude?.toString() ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))
), [clusters, searchValue])
const clusterColumns = useMemo(() => [
makeSelectColumn('Месторождение', 'idDeposit', deposits, '--', {
width: 200,
@ -65,14 +71,6 @@ export const ClusterController = memo(() => {
useEffect(updateTable, [updateTable])
useEffect(() => {
setFilteredClusters(clusters.filter((cluster) => cluster && (!searchValue || [
cluster.caption ?? '',
cluster.latitude?.toString ?? '',
cluster.longitude?.toString() ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))))
}, [clusters, searchValue])
const handlerProps = useMemo(() => ({
service: AdminClusterService,
setLoader: setShowLoader,
@ -84,7 +82,7 @@ export const ClusterController = memo(() => {
<>
<Input.Search
style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'}
placeholder={'Введите текст для поиска (по полям: Название, Долгота, Широта)...'}
onChange={(e) => setSearchValue(e.target.value)}
value={searchValue}
loading={showLoader}

View File

@ -19,10 +19,13 @@ import { hasPermission } from '@utils/permissions'
export const CompanyController = memo(() => {
const [columns, setColumns] = useState([])
const [companies, setCompanies] = useState([])
const [filteredCompanies, setFilteredCompanies] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const filteredCompanies = useMemo(() => companies.filter((company) => company && (!searchValue ||
company.caption?.toLowerCase()?.includes(searchValue.toLowerCase())
)), [companies, searchValue])
const updateTable = useCallback(async () => {
const companies = await AdminCompanyService.getAll()
setCompanies(arrayOrDefault(companies))
@ -55,12 +58,6 @@ export const CompanyController = memo(() => {
'Получение списка типов команд'
), [updateTable])
useEffect(() => {
setFilteredCompanies(companies.filter((company) => company && (!searchValue ||
company.caption?.toLowerCase()?.includes(searchValue.toLowerCase())
)))
}, [companies, searchValue])
const handlerProps = useMemo(() => ({
service: AdminCompanyService,
setLoader: setShowLoader,
@ -77,7 +74,7 @@ export const CompanyController = memo(() => {
<>
<Input.Search
style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'}
placeholder={'Введите текст для поиска (по полю Название)...'}
onChange={(e) => setSearchValue(e.target.value)}
value={searchValue}
loading={showLoader}

View File

@ -25,10 +25,13 @@ const columns = [
export const CompanyTypeController = memo(() => {
const [companyTypes, setCompanyTypes] = useState([])
const [filteredCompanyTypes, setFilteredCompanyTypes] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const filteredCompanyTypes = useMemo(() => companyTypes.filter((companyType) => companyType && (!searchValue ||
companyType.caption?.toLowerCase()?.includes(searchValue.toLowerCase())
)), [companyTypes, searchValue])
const updateTable = useCallback(() => invokeWebApiWrapperAsync(
async() => {
const companyTypes = await AdminCompanyTypeService.getAll()
@ -41,12 +44,6 @@ export const CompanyTypeController = memo(() => {
useEffect(updateTable, [updateTable])
useEffect(() => {
setFilteredCompanyTypes(companyTypes.filter((companyType) => companyType && (!searchValue ||
companyType.caption?.toLowerCase()?.includes(searchValue.toLowerCase())
)))
}, [companyTypes, searchValue])
const handlerProps = useMemo(() => ({
service: AdminCompanyTypeService,
setLoader: setShowLoader,
@ -58,7 +55,7 @@ export const CompanyTypeController = memo(() => {
<>
<Input.Search
style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'}
placeholder={'Введите текст для поиска (по полю Название)...'}
onChange={(e) => setSearchValue(e.target.value)}
value={searchValue}
loading={showLoader}

View File

@ -19,10 +19,16 @@ const depositColumns = [
export const DepositController = memo(() => {
const [deposits, setDeposits] = useState([])
const [filteredDeposits, setFilteredDeposits] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const filteredDeposits = useMemo(() => deposits.filter((deposit) => deposit && (!searchValue || [
deposit.caption ?? '',
deposit.latitude?.toString() ?? '',
deposit.longitude?.toString() ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))
), [deposits, searchValue])
const updateTable = useCallback(() => invokeWebApiWrapperAsync(
async() => {
const deposits = await AdminDepositService.getAll()
@ -35,14 +41,6 @@ export const DepositController = memo(() => {
useEffect(updateTable, [updateTable])
useEffect(() => {
setFilteredDeposits(deposits.filter((deposit) => deposit && (!searchValue || [
deposit.caption ?? '',
deposit.latitude?.toString() ?? '',
deposit.longitude?.toString() ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))))
}, [deposits, searchValue])
const handlerProps = useMemo(() => ({
service: AdminDepositService,
setLoader: setShowLoader,
@ -54,7 +52,7 @@ export const DepositController = memo(() => {
<>
<Input.Search
style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'}
placeholder={'Введите текст для поиска (по полям: Название, Долгота, Широта)...'}
onChange={(e) => setSearchValue(e.target.value)}
value={searchValue}
loading={showLoader}

View File

@ -28,10 +28,15 @@ const columns = [
export const PermissionController = memo(() => {
const [permissions, setPermissions] = useState([])
const [filteredPermissions, setFilteredPermissions] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const filteredPermissions = useMemo(() => permissions.filter((permission) => permission && (!searchValue || [
permission.name ?? '',
permission.description ?? '',
].join(' ').includes(searchValue.toLowerCase()))
), [permissions, searchValue])
const updateTable = useCallback(async () => invokeWebApiWrapperAsync(
async () => {
const permission = await AdminPermissionService.getAll()
@ -44,13 +49,6 @@ export const PermissionController = memo(() => {
useEffect(() => updateTable(), [updateTable])
useEffect(() => {
setFilteredPermissions(permissions.filter((permission) => permission && (!searchValue || [
permission.name ?? '',
permission.description ?? '',
].join(' ').includes(searchValue.toLowerCase()))))
}, [permissions, searchValue])
const handlerProps = useMemo(() => ({
service: AdminPermissionService,
setLoader: setShowLoader,
@ -62,7 +60,7 @@ export const PermissionController = memo(() => {
<>
<Input.Search
style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'}
placeholder={'Введите текст для поиска (по полям: Название, Описание)...'}
onChange={(e) => setSearchValue(e.target.value)}
value={searchValue}
loading={showLoader}

View File

@ -12,32 +12,32 @@ import { hasPermission } from '@utils/permissions'
export const RoleController = memo(() => {
const [permissions, setPermissions] = useState([])
const [roles, setRoles] = useState([])
const [filteredRoles, setFilteredRoles] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const [columns, setColumns] = useState([])
const filteredRoles = useMemo(() => roles.filter((role) => role && (!searchValue ||
role.caption?.toLowerCase()?.includes(searchValue.toLowerCase())
)), [roles, searchValue])
const columns = useMemo(() => [
makeTextColumn('Название', 'caption', null, null, null, { width: 100, editable: true, formItemRules: min1 }),
makeTagColumn('Включённые роли', 'roles', roles, 'id', 'caption', {
width: 400,
editable: true,
render: (role) => <RoleView role={role} />
}, { allowClear: true }),
makeTagColumn('Разрешения', 'permissions', permissions, 'id', 'name', {
width: 600,
editable: true,
render: (permission) => <PermissionView info={permission} />,
}),
], [roles, permissions])
const loadRoles = useCallback(async () => {
const roles = await AdminUserRoleService.getAll()
setRoles(arrayOrDefault(roles))
}, [])
useEffect(() => {
setColumns([
makeTextColumn('Название', 'caption', null, null, null, { width: 100, editable: true, formItemRules: min1 }),
makeTagColumn('Включённые роли', 'roles', roles, 'id', 'caption', {
width: 400,
editable: true,
render: (role) => <RoleView role={role} />
}, { allowClear: true }),
makeTagColumn('Разрешения', 'permissions', permissions, 'id', 'name', {
width: 600,
editable: true,
render: (permission) => <PermissionView info={permission} />,
}),
])
}, [roles, permissions])
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const permissions = await AdminPermissionService.getAll()
@ -49,12 +49,6 @@ export const RoleController = memo(() => {
'Получение списка ролей'
), [loadRoles])
useEffect(() => {
setFilteredRoles(roles.filter((role) => role && (!searchValue ||
role.caption?.toLowerCase()?.includes(searchValue.toLowerCase())
)))
}, [roles, searchValue])
const handlerProps = useMemo(() => ({
service: AdminUserRoleService,
setLoader: setShowLoader,
@ -63,7 +57,7 @@ export const RoleController = memo(() => {
loadRoles,
setShowLoader,
`Не удалось загрузить список ролей`,
'Получение списка ролей'
'Получение списка ролей',
)
}), [loadRoles])
@ -71,7 +65,7 @@ export const RoleController = memo(() => {
<>
<Input.Search
style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'}
placeholder={'Введите текст для поиска (по полю Название)...'}
onChange={(e) => setSearchValue(e.target.value)}
value={searchValue}
loading={showLoader}

View File

@ -1,4 +1,4 @@
import { memo, useEffect, useState } from 'react'
import { memo, useEffect, useMemo, useState } from 'react'
import { Input } from 'antd'
import {
@ -31,10 +31,24 @@ const columns = [
export const TelemetryController = memo(() => {
const [telemetryData, setTelemetryData] = useState([])
const [filteredTelemetryData, setFilteredTelemetryData] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const filteredTelemetryData = useMemo(() => telemetryData.filter((telemetry) => telemetry && (!searchValue || [
telemetry.id?.toString() ?? '',
telemetry.remoteUid ?? '',
telemetry.realWell ?? '',
telemetry.drillingStartDate ?? '',
telemetry.well ?? '',
telemetry.cluster ?? '',
telemetry.deposit ?? '',
telemetry.customer ?? '',
telemetry.comment ?? '',
telemetry.hmiVersion ?? '',
telemetry.saubPlcVersion ?? '',
telemetry.spinPlcVersion ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))
), [telemetryData, searchValue])
useEffect(() => invokeWebApiWrapperAsync(
async () => {
@ -51,27 +65,11 @@ export const TelemetryController = memo(() => {
'Полученик списка телеметрии скважин'
), [])
useEffect(() => {
setFilteredTelemetryData(telemetryData.filter((telemetry) => telemetry && (!searchValue || [
telemetry.remoteUid ?? '',
telemetry.realWell ?? '',
telemetry.drillingStartDate ?? '',
telemetry.well ?? '',
telemetry.cluster ?? '',
telemetry.deposit ?? '',
telemetry.customer ?? '',
telemetry.comment ?? '',
telemetry.hmiVersion ?? '',
telemetry.saubPlcVersion ?? '',
telemetry.spinPlcVersion ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))))
}, [telemetryData, searchValue])
return (
<>
<Input.Search
style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'}
placeholder={'Введите текст для поиска (по всем полям)...'}
onChange={(e) => setSearchValue(e.target.value)}
value={searchValue}
loading={showLoader}

View File

@ -1,6 +1,6 @@
import { Button, Input, Tag } from 'antd'
import { UserSwitchOutlined } from '@ant-design/icons'
import { memo, useCallback, useEffect, useState } from 'react'
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { BehaviorSubject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
import {
@ -172,12 +172,12 @@ export const UserController = memo(() => {
'Получение списка компаний'
), [])
const handlerProps = {
const handlerProps = useMemo(() => ({
service: AdminUserService,
setLoader: setShowLoader,
errorMsg: `Не удалось выполнить операцию`,
onComplete: updateTable,
}
}), [updateTable])
const onSearchTextChange = useCallback((e) => subject?.next(e?.target?.value), [subject])
@ -186,7 +186,7 @@ export const UserController = memo(() => {
<LoaderPortal show={showLoader}>
<Input.Search
allowClear
placeholder={'Поиск пользователей'}
placeholder={'Введите текст для поиска (по всем полям за исключением ролей)...'}
onChange={onSearchTextChange}
style={{ marginBottom: '15px' }}
loading={isSearching}

View File

@ -1,4 +1,4 @@
import { memo, useEffect, useState } from 'react'
import { memo, useEffect, useMemo, useState } from 'react'
import { Input } from 'antd'
import { invokeWebApiWrapperAsync } from '@components/factory'
@ -12,17 +12,23 @@ const columns = [
makeColumn('Логин', 'login', { sorter: makeStringSorter('login') }),
makeColumn('IP', 'ip', { sorter: makeStringSorter('ip') }),
makeColumn('Дата посещения', 'lastDate', {
render: (date) => formatDate(date, false, 'DD MMM YYYY, HH:mm:ss'),
render: (date) => formatDate(date, false),
sorter: makeDateSorter('lastDate'),
}),
]
export const VisitLog = memo(() => {
const [logData, setLogData] = useState([])
const [filteredLogData, setFilteredLogData] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const filteredLogData = useMemo(() => logData.filter((data) => data && (!searchValue || [
data.login ?? '',
data.ip ?? '',
data.lastDate ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))
), [logData, searchValue])
useEffect(() => invokeWebApiWrapperAsync(
async () => {
const logData = arrayOrDefault(await RequestTrackerService.getUsersStat(logRecordCount))
@ -34,14 +40,6 @@ export const VisitLog = memo(() => {
'Получение списка последних посещений'
), [])
useEffect(() => {
setFilteredLogData(logData.filter((data) => data && (!searchValue || [
data.login ?? '',
data.ip ?? '',
data.lastDate ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))))
}, [logData, searchValue])
return (
<>
<Input.Search

View File

@ -42,10 +42,16 @@ const recordParser = (record) => ({
export const WellController = memo(() => {
const [columns, setColumns] = useState([])
const [wells, setWells] = useState([])
const [filteredWells, setFilteredWells] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('')
const filteredWells = useMemo(() => wells.filter((well) => well && (!searchValue || [
well.caption ?? '',
well.latitude?.toString() ?? '',
well.longitude?.toString() ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))
), [wells, searchValue])
const updateTable = useCallback(async () => invokeWebApiWrapperAsync(
async () => {
@ -116,14 +122,6 @@ export const WellController = memo(() => {
'Получение списка кустов'
), [updateTable])
useEffect(() => {
setFilteredWells(wells.filter((well) => well && (!searchValue || [
well.caption ?? '',
well.latitude?.toString() ?? '',
well.longitude?.toString() ?? '',
].join(' ').toLowerCase().includes(searchValue.toLowerCase()))))
}, [wells, searchValue])
const handlerProps = useMemo(() => ({
service: AdminWellService,
setLoader: setShowLoader,
@ -135,14 +133,14 @@ export const WellController = memo(() => {
<>
<Input.Search
style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'}
placeholder={'Введите текст для поиска (по полям: Название, Долгота, Широта)...'}
onChange={(e) => setSearchValue(e.target.value)}
value={searchValue}
loading={showLoader}
/>
<EditableTable
size={'small'}
bordered
size={'small'}
columns={columns}
loading={showLoader}
dataSource={filteredWells}

View File

@ -1,5 +1,5 @@
import { Link, useLocation } from 'react-router-dom'
import { useState, useEffect, memo, useCallback } from 'react'
import { useState, useEffect, memo, useCallback, useMemo } from 'react'
import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons'
import { Table, Tag, Button, Badge, Divider, Modal, Row, Col, Popconfirm } from 'antd'
@ -29,9 +29,7 @@ const filtersSectionsType = []
const DAY_IN_MS = 1000 * 60 * 60 * 24
export const WellCompositeSections = memo(({ idWell, statsWells, selectedSections }) => {
const [rows, setRows] = useState([])
const [params, setParams] = useState([])
const [paramsColumns, setParamsColumns] = useState([])
const [selectedWells, setSelectedWells] = useState([])
const [wellOperations, setWellOperations] = useState([])
const [selectedWellsKeys, setSelectedWellsKeys] = useState([])
@ -44,21 +42,9 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
const location = useLocation()
useEffect(() => (async() => setParamsColumns(await getColumns(idWell)))(), [idWell])
const paramsColumns = useMemo(async() => await getColumns(idWell), [idWell])
useEffect(() => {
if (isOpsModalVisible || selectedWellId <= 0) return
invokeWebApiWrapperAsync(
async () => {
const { operations } = await getOperations(selectedWellId)
setWellOperations(operations)
},
setShowLoader,
`Не удалось загрузить операции по скважине "${selectedWellId}"`,
)
}, [selectedWellId, isOpsModalVisible])
useEffect(() => {
const rows = useMemo(() => {
const rows = []
statsWells?.forEach((well) => {
well.sections?.forEach((section) => {
@ -113,9 +99,21 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
'nonProductiveTimeFact',
])
setRows(rows)
return rows
}, [statsWells])
useEffect(() => {
if (isOpsModalVisible || selectedWellId <= 0) return
invokeWebApiWrapperAsync(
async () => {
const { operations } = await getOperations(selectedWellId)
setWellOperations(operations)
},
setShowLoader,
`Не удалось загрузить операции по скважине "${selectedWellId}"`,
)
}, [selectedWellId, isOpsModalVisible])
useEffect(() => {
const selected = rows.filter((row) => selectedSections.some(section => (
section.idWellSrc === row.id && section.idWellSectionType === row.sectionId
@ -125,7 +123,7 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
setSelectedWellsKeys(selected.map((row) => row.key))
}, [rows, selectedSections])
const columns = [
const columns = useMemo(() => [
makeTextColumn('скв №', 'caption', null, null,
(text, item) => <Link to={{ pathname: `/well/${item?.id}`, state: { from: location.pathname }}}>{text ?? '-'}</Link>
),
@ -171,9 +169,9 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
</Tag>
)) ?? '-',
},
]
], [location.pathname])
const rowSelection = hasPermission('WellOperation.edit') && {
const rowSelection = useMemo(() => hasPermission('WellOperation.edit') && {
selectedRowKeys: selectedWellsKeys,
onChange: (keys, items) => invokeWebApiWrapperAsync(
async () => {
@ -187,7 +185,7 @@ export const WellCompositeSections = memo(({ idWell, statsWells, selectedSection
`Не удалось сохранить изменения выбранных секций для композитной скважины "${idWell}"`,
'Изменение выбранных секций скважины'
)
}
}, [idWell, selectedWellsKeys])
const onParamButtonClick = useCallback(() => invokeWebApiWrapperAsync(
async () => {

View File

@ -1,4 +1,4 @@
import { memo } from 'react'
import { memo, useMemo } from 'react'
import { Layout, Menu } from 'antd'
import { Switch, useParams } from 'react-router-dom'
@ -9,7 +9,7 @@ import Statistics from './Statistics'
export const Analytics = memo(({ idWell }) => {
const { tab } = useParams()
const rootPath = `/well/${idWell}/analytics`
const rootPath = useMemo(() => `/well/${idWell}/analytics`, [idWell])
return (
<Layout>

View File

@ -1,21 +1,14 @@
import { useEffect, useState } from 'react'
import { memo, useMemo } from 'react'
import { Grid, GridItem } from '@components/Grid'
import { Column } from '@components/charts/Column'
export const ArchiveColumn = ({ lineGroup, data, interval, style, headerHeight, yStart }) => {
const [lineGroupWithoutShapes, setLineGroupWithoutShapes] = useState([])
const [pv, setPV] = useState([])
useEffect(() => {
const lgws = lineGroup.filter(cfg => !cfg.isShape)
setLineGroupWithoutShapes(lgws)
setPV(lgws.filter(line => line.showLabels).map(line => ({
color: line.color,
label: line.label
})))
}, [lineGroup])
export const ArchiveColumn = memo(({ lineGroup, data, interval, style, headerHeight, yStart }) => {
const lgws = useMemo(() => lineGroup.filter(cfg => !cfg.isShape), [lineGroup])
const pv = useMemo(() => lgws.filter(line => line.showLabels).map(line => ({
color: line.color,
label: line.label
})), [lgws])
return (
<div style={style}>
@ -26,13 +19,13 @@ export const ArchiveColumn = ({ lineGroup, data, interval, style, headerHeight,
</Grid>
<Column
data={data}
lineGroup={lineGroupWithoutShapes}
lineGroup={lgws}
interval={interval}
yDisplay={false}
yStart={yStart}
/>
</div>
)
}
})
export default ArchiveColumn

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { memo, useMemo } from 'react'
import { Grid, GridItem } from '@components/Grid'
@ -41,13 +41,9 @@ export const cutData = (data, beginDate, endDate) => {
return data
}
export const ArchiveDisplay = ({data, startDate, interval, onWheel}) => {
const [chartData, setChartData] = useState([])
useEffect(() => {
const endDate = new Date(+startDate + interval)
setChartData(cutData(data, startDate, endDate))
}, [data, startDate, interval])
export const ArchiveDisplay = memo(({data, startDate, interval, onWheel}) => {
const endDate = useMemo(() => new Date(+startDate + interval), [startDate, interval])
const chartData = useMemo(() => cutData(data, startDate, endDate), [data, startDate, endDate])
return (
<Grid onWheel={onWheel}>
@ -65,6 +61,6 @@ export const ArchiveDisplay = ({data, startDate, interval, onWheel}) => {
))}
</Grid>
)
}
})
export default ArchiveDisplay

View File

@ -1,5 +1,5 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useState, useEffect, memo } from 'react'
import { useState, useEffect, memo, useCallback } from 'react'
import { Flex } from '@components/Grid'
import LoaderPortal from '@components/LoaderPortal'
@ -69,7 +69,7 @@ export const Archive = memo(({ idWell }) => {
const [showLoader, setShowLoader] = useState(false)
const [loaded, setLoaded] = useState(null)
const onGraphWheel = (e) => {
const onGraphWheel = useCallback((e) => {
if (loaded && dateLimit.from && dateLimit.to) {
setStartDate((prevStartDate) => {
const offset = e.deltaY * chartInterval * WHEEL_SENSITIVITY
@ -79,14 +79,15 @@ export const Archive = memo(({ idWell }) => {
return new Date(Math.max(firstPossibleDate, Math.min(nextStartDate, lastPossibleDate)))
})
}
}
}, [loaded, dateLimit, chartInterval])
const isDateDisabled = (date) => {
const isDateDisabled = useCallback((date) => {
if (!date) return false
const dt = new Date(date).setHours(0, 0, 0, 0)
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
}
const isDateTimeDisabled = (date) => ({
}, [dateLimit])
const isDateTimeDisabled = useCallback((date) => ({
disabledHours: () => range(0, 24).filter(h => {
if (!date) return false
const dt = +new Date(date).setHours(h)
@ -102,7 +103,7 @@ export const Archive = memo(({ idWell }) => {
const dt = +new Date(date).setSeconds(s)
return dt < dateLimit.from || dt > +dateLimit.to - chartInterval
})
})
}), [dateLimit])
useEffect(() => invokeWebApiWrapperAsync(
async () => {

View File

@ -1,5 +1,5 @@
import { Link, useLocation } from 'react-router-dom'
import { useState, useEffect, memo } from 'react'
import { useState, useEffect, memo, useMemo } from 'react'
import { Tag, Button, Modal } from 'antd'
import { LineChartOutlined, ProfileOutlined } from '@ant-design/icons'
@ -108,7 +108,7 @@ export const ClusterWells = memo(({ statsWells }) => {
setTableData(data)
}, [statsWells])
const columns = [
const columns = useMemo(() => [
makeTextColumn('скв №', 'caption', null, null,
(_, item) => (
<Link to={{ pathname: `/well/${item.id}`, state: { from: location.pathname }}} style={{display: 'flex', alignItems: 'center'}}>
@ -159,7 +159,7 @@ export const ClusterWells = memo(({ statsWells }) => {
</Tag>
)) ?? '-',
},
]
], [location.pathname])
return (
<>

View File

@ -1,18 +1,19 @@
import { memo, useMemo } from 'react'
import { Table } from 'antd'
import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table'
import { getPrecision } from '@utils/functions'
export const WellOperationsTable = ({ wellOperations }) => {
const columns = [
makeTextColumn('Конструкция секции','sectionType'),
makeTextColumn('Операция','operationName'),
makeNumericColumnPlanFact('Глубина забоя', 'depth', null, null, getPrecision),
makeNumericColumnPlanFact('Часы', 'durationHours', null, null, getPrecision),
makeNumericColumnPlanFact('Комментарий', 'comment', null, null, (text) => text ?? '-')
]
const columns = [
makeTextColumn('Конструкция секции', 'sectionType'),
makeTextColumn('Операция', 'operationName'),
makeNumericColumnPlanFact('Глубина забоя', 'depth', null, null, getPrecision),
makeNumericColumnPlanFact('Часы', 'durationHours', null, null, getPrecision),
makeNumericColumnPlanFact('Комментарий', 'comment', null, null, (text) => text ?? '-')
]
const operations = wellOperations?.map(el => ({
export const WellOperationsTable = memo(({ wellOperations }) => {
const operations = useMemo(() => wellOperations?.map(el => ({
key: el.plan?.id ?? el.fact.id,
sectionType: el.plan?.wellSectionTypeName ?? el.fact?.wellSectionTypeName,
operationName: `${el.plan?.categoryName ?? el.fact?.categoryName ?? ''} ${' '}
@ -23,7 +24,7 @@ export const WellOperationsTable = ({ wellOperations }) => {
durationHoursFact: el.fact?.durationHours,
commentPlan: el.plan?.comment ?? '-',
commentFact: el.fact?.comment ?? '-'
}))
})), [wellOperations])
return (
<Table
@ -36,6 +37,6 @@ export const WellOperationsTable = ({ wellOperations }) => {
tableName={'well_operations'}
/>
)
}
})
export default WellOperationsTable

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, memo } from 'react'
import { useParams } from 'react-router-dom'
import { arrayOrDefault } from '@utils'
@ -8,7 +8,7 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
import ClusterWells from './ClusterWells'
export const Cluster = () => {
export const Cluster = memo(() => {
const { idCluster } = useParams()
const [data, setData] = useState([])
const [showLoader, setShowLoader] = useState(false)
@ -28,6 +28,6 @@ export const Cluster = () => {
<ClusterWells statsWells={data} />
</LoaderPortal>
)
}
})
export default Cluster

View File

@ -1,14 +1,13 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, useMemo, useCallback } from 'react'
import { DatePicker, Button, Input } from 'antd'
import { FileService } from '@api'
import { hasPermission } from '@utils/permissions'
import LoaderPortal from '@components/LoaderPortal'
import { UploadForm } from '@components/UploadForm'
import { CompanyView, UserView } from '@components/views'
import { EditableTable, makePaginationObject } from '@components/Table'
import { EditableTable, makeColumn, makeDateColumn, makeNumericColumn, makePaginationObject } from '@components/Table'
import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory'
import { formatDate } from '@utils'
import { hasPermission } from '@utils/permissions'
const pageSize = 12
const { RangePicker } = DatePicker
@ -24,26 +23,11 @@ const columns = [
{name}
</Button>
),
}, {
title: 'Дата загрузки',
key: 'uploadDate',
dataIndex: 'uploadDate',
render: item => formatDate(item, false, 'DD MMM YYYY, HH:mm:ss'),
}, {
title: 'Размер',
key: 'size',
dataIndex: 'size',
render: item => formatBytes(item)
}, {
title: 'Автор',
key: 'author',
dataIndex: 'author',
render: item => <UserView user={item}/>
}, {
title: 'Компания',
key: 'company',
render: (_, record) => <CompanyView company={record?.author?.company}/>
}
},
makeDateColumn('Дата загрузки', 'uploadDate'),
makeNumericColumn('Размер', 'size', null, null, formatBytes),
makeColumn('Автор', 'author', { render: item => <UserView user={item}/> }),
makeColumn('Компания', 'company', { render: (_, record) => <CompanyView company={record?.author?.company}/> })
]
export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, customColumns, beforeTable, onChange, tableName }) => {
@ -55,21 +39,13 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
const [files, setFiles] = useState([])
const [showLoader, setShowLoader] = useState(false)
const uploadUrl = `/api/well/${idWell}/files/?idCategory=${idCategory}`
const uploadUrl = useMemo(() => `/api/well/${idWell}/files/?idCategory=${idCategory}`, [idWell, idCategory])
const handleUploadComplete = () => update()
const mergedColumns = useMemo(() => [...columns, ...(customColumns ?? [])], [customColumns])
const companies = useMemo(() => [...new Set(files.map(file => file.company))].filter(company => company), [files])
const filenames = useMemo(() => [...new Set(files.map(file => file.name))].filter(name => name), [files])
const handleFileDelete = async (file) => {
await FileService.delete(idWell, file.id)
update()
}
const hanleCompanySearch = (value, _) => setFilterCompanyName(value)
const hanleFileNameSearch = (value, _) => setFilterFileName(value)
const mergedColumns = [...columns, ...(customColumns ?? [])]
const update = () => {
const update = useCallback(() => {
let begin = null
let end = null
if (filterDataRange?.length > 1) {
@ -101,13 +77,15 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
`Не удалось загрузить файлы по скважине "${idWell}"`,
'Загрузка файла по скважине'
)
}
}, [filterCompanyName, filterDataRange, filterFileName, idCategory, idWell, page])
useEffect(update, [idWell, idCategory, page, filterDataRange, filterCompanyName, filterFileName])
useEffect(update, [update])
useEffect(() => onChange?.(files), [files, onChange])
const companies = [...new Set(files.map(file => file.company))].filter(company => company)
const filenames = [...new Set(files.map(file => file.name))].filter(name => name)
const handleFileDelete = useMemo(() => hasPermission(`File.edit${idCategory}`) && (async (file) => {
await FileService.delete(idWell, file.id)
update()
}), [idWell, idCategory, update])
return (
<LoaderPortal show={showLoader}>
@ -124,7 +102,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
<Search
list={'listCompanies'}
placeholder={'Фильтр по компании'}
onSearch={hanleCompanySearch}
onSearch={setFilterCompanyName}
/>
<datalist id={'listCompanies'}>
{companies.map((company) => (
@ -138,7 +116,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
<Search
list={'listFileNames'}
placeholder={'Фильтр по имени файла'}
onSearch={hanleFileNameSearch}
onSearch={setFilterFileName}
/>
<datalist id={'listFileNames'}>
{filenames.map((name) => (
@ -154,7 +132,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
url={uploadUrl}
accept={accept}
onUploadStart={() => setShowLoader(true)}
onUploadComplete={handleUploadComplete}
onUploadComplete={update}
/>
</div>
)}
@ -169,9 +147,9 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
pagination={{
...pagination,
showSizeChanger: false,
onChange: (page) => setPage(page),
onChange: setPage,
}}
onRowDelete={hasPermission(`File.edit${idCategory}`) && handleFileDelete}
onRowDelete={handleFileDelete}
rowKey={(record) => record.id}
tableName={tableName ?? `file_${idCategory}`}
/>

View File

@ -1,5 +1,5 @@
import { join } from 'path'
import { memo } from 'react'
import { memo, useMemo } from 'react'
import { Layout, Menu } from 'antd'
import { FolderOutlined } from '@ant-design/icons'
import { Switch, useParams } from 'react-router-dom'
@ -25,7 +25,7 @@ export const documentCategories = [
export const MenuDocuments = memo(({ idWell }) => {
const { category } = useParams()
const root = `/well/${idWell}/document`
const root = useMemo(() => `/well/${idWell}/document`, [idWell])
return (
<>

View File

@ -1,5 +1,5 @@
import { Input, Modal, Radio } from 'antd'
import { memo, useCallback, useEffect, useState } from 'react'
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { BehaviorSubject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
import { UserView } from '@components/views'
@ -113,7 +113,7 @@ export const CategoryEditor = memo(({ idWell, visible, category, onClosed }) =>
`Изменение статуса пользователя`
), [users, idWell, category.idFileCategory])
const userColumns = [
const userColumns = useMemo(() => [
makeColumn('Пользователь', 'user', {
sorter: (a, b) => (a?.user?.surname && b?.user?.surname) ? a.user.surname.localeCompare(b.user.surname) : 0,
render: (user) => <UserView user={user} />,
@ -129,7 +129,7 @@ export const CategoryEditor = memo(({ idWell, visible, category, onClosed }) =>
/>
),
})
]
], [changeUserStatus])
const onSearchTextChange = useCallback((e) => subject?.next(e?.target?.value), [subject])

View File

@ -1,4 +1,4 @@
import { memo, useCallback, useState } from 'react'
import { memo, useCallback, useMemo, useState } from 'react'
import { Button, Input, Popconfirm, Form } from 'antd'
import {
DeleteOutlined,
@ -44,7 +44,7 @@ export const CategoryRender = memo(({ idWell, partData, onUpdate, onEdit, onHist
file // Информация о файле
} = partData ?? {}
const uploadUrl = `/api/well/${idWell}/drillingProgram/part/${idFileCategory}`
const uploadUrl = useMemo(() => `/api/well/${idWell}/drillingProgram/part/${idFileCategory}`, [idWell, idFileCategory])
const [isUploading, setIsUploading] = useState(false)
const [isDeleting, setIsDeleting] = useState(false)

View File

@ -8,7 +8,7 @@ import {
ReloadOutlined,
WarningOutlined,
} from '@ant-design/icons'
import { memo, useCallback, useEffect, useState } from 'react'
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import LoaderPortal from '@components/LoaderPortal'
import { downloadFile, formatBytes, invokeWebApiWrapperAsync } from '@components/factory'
@ -52,11 +52,11 @@ export const DrillingProgram = memo(({ idWell }) => {
parts,
program,
error,
} = data
} = useMemo(() => data, [data])
const stateId = idState ?? idStateUnknown
const state = stateString[stateId]
const StateIcon = state.icon
const stateId = useMemo(() => idState ?? idStateUnknown, [idState])
const state = useMemo(() => stateString[stateId], [stateId])
const StateIcon = useMemo(() => state.icon, [state?.icon])
const updateData = useCallback(async () => await invokeWebApiWrapperAsync(
async () => {
@ -76,15 +76,15 @@ export const DrillingProgram = memo(({ idWell }) => {
useEffect(() => updateData(), [updateData])
const onCategoryEdit = (catId) => {
const onCategoryEdit = useCallback((catId) => {
setSelectedCategory(catId)
setEditorVisible(!!catId)
}
}, [])
const onCategoryHistory = (catId) => {
const onCategoryHistory = useCallback((catId) => {
setSelectedCategory(catId)
setHistoryVisible(!!catId)
}
}, [])
const onEditorClosed = useCallback(() => {
setEditorVisible(false)

View File

@ -13,9 +13,10 @@ import '@styles/index.css'
import Logo from '@images/Logo'
export const Login = memo(() => {
const [showLoader, setShowLoader] = useState(false)
const history = useHistory()
const location = useLocation()
const [showLoader, setShowLoader] = useState(false)
const handleLogin = useCallback((formData) => invokeWebApiWrapperAsync(
async () => {

View File

@ -1,5 +1,5 @@
import { Modal } from 'antd'
import { memo, useEffect, useState } from 'react'
import { memo, useMemo } from 'react'
import { Table } from '@components/Table'
import { formatDate } from '@utils'
@ -18,21 +18,18 @@ const dateColumn = {
}
export const InclinometryTable = memo(({ group, visible, onClose }) => {
const [tableColumns, setTableColumns] = useState([])
const [tableData, setTableData] = useState([])
useEffect(() => setTableColumns([
const tableColumns = useMemo(() => [
dateColumn,
...(group?.columns?.map((column) => ({
...column,
title: v(column.title)
})) ?? [])
]), [group?.columns])
], [group?.columns])
useEffect(() => setTableData(group?.values?.map(row => ({
const tableData = useMemo(() => group?.values?.map(row => ({
date: row.timestamp,
...row.data
}))), [group?.values])
})), [group?.values])
return !group?.columns ? null : (
<Modal

View File

@ -1,4 +1,4 @@
import { useState, useEffect, memo } from 'react'
import { useState, useEffect, memo, useMemo, useCallback } from 'react'
import { Button, Form, Input, Popconfirm, Timeline } from 'antd'
import {
CheckSquareOutlined,
@ -23,23 +23,20 @@ import '@styles/measure.css'
const createEditingColumns = (cols, renderDelegate) =>
cols.map(col => ({ render: renderDelegate, ...col }))
const disabled = !hasPermission('Measure.edit')
export const MeasureTable = memo(({ idWell, group, updateMeasuresFunc, additionalButtons }) => {
const [showLoader, setShowLoader] = useState(false)
const [displayedValues, setDisplayedValues] = useState({})
const [editingColumns, setEditingColumns] = useState(group.columns)
const [isTableEditing, setIsTableEditing] = useState(false)
const [editingActionName, setEditingActionName] = useState('')
const [data, setData] = useState([])
const [measuresForm] = Form.useForm()
useEffect(() => {
let data = [group.defaultValue]
if (group?.values?.length > 0)
data = group.values
setData(data)
setDisplayedValues(data.at(-1))
}, [group.defaultValue, group.values])
const data = useMemo(() => group?.values?.length > 0 ? group.values : [group?.defaultValue], [group?.defaultValue, group?.values])
useEffect(() => setDisplayedValues(data.at(-1)), [data])
useEffect(() => {
const switchableColumns = createEditingColumns(
@ -64,17 +61,15 @@ export const MeasureTable = memo(({ idWell, group, updateMeasuresFunc, additiona
`Не удалось удалить запись ${displayedValues.id} для скважины "${idWell}"`,
'Удаление записи для скважины'
)
const editingDisabled = useMemo(() => disabled || !!displayedValues?.isDefaultData, [displayedValues?.isDefaultData])
const deleteDisabled = useMemo(() => !hasPermission('Measure.delete') || !!displayedValues?.isDefaultData, [displayedValues?.isDefaultData])
const disabled = !hasPermission('Measure.edit')
const editingDisabled = disabled || !!displayedValues?.isDefaultData
const deleteDisabled = !hasPermission('Measure.delete') || !!displayedValues?.isDefaultData
const editTable = (action) => {
const editTable = useCallback((action) => {
setEditingActionName(action)
setIsTableEditing(true)
}
}, [])
const handleSubmitMeasuresForm = async (formData) => await invokeWebApiWrapperAsync(
const handleSubmitMeasuresForm = useCallback(async (formData) => await invokeWebApiWrapperAsync(
async () => {
measuresForm.validateFields()
@ -99,7 +94,7 @@ export const MeasureTable = memo(({ idWell, group, updateMeasuresFunc, additiona
setShowLoader,
`Не удалось добавить/изменить запись для скаважины "${idWell}"`,
'Добавление/изменение записи по скважине'
)
), [displayedValues?.id, displayedValues?.timestamp, editingActionName, group.idCategory, idWell, measuresForm, updateMeasuresFunc])
return (
<>

View File

@ -1,5 +1,5 @@
import moment from 'moment'
import { useState, useEffect, memo } from 'react'
import { useState, useEffect, memo, useCallback } from 'react'
import { Table, Select, DatePicker, Input } from 'antd'
import { MessageService } from '@api'
@ -65,6 +65,8 @@ const filterOptions = [
{ value: 3, label: 'Информация' },
]
const children = filterOptions.map((line) => <Option key={line.value}>{line.label}</Option>)
// Данные для таблицы
export const Messages = memo(({ idWell }) => {
const [messages, setMessages] = useState([])
@ -75,9 +77,7 @@ export const Messages = memo(({ idWell }) => {
const [searchString, setSearchString] = useState('')
const [showLoader, setShowLoader] = useState(false)
const children = filterOptions.map((line) => <Option key={line.value}>{line.label}</Option>)
const onChangeSearchString = (message) => setSearchString(message.length > 2 ? message : '')
const onChangeSearchString = useCallback((message) => setSearchString(message.length > 2 ? message : ''), [])
useEffect(() => invokeWebApiWrapperAsync(
async () => {

View File

@ -1,6 +1,6 @@
import 'moment/locale/ru'
import moment from 'moment'
import { useState, useEffect, memo } from 'react'
import { useState, useEffect, memo, useCallback } from 'react'
import { Radio, Button, Select, notification } from 'antd'
import { ReportService } from '@api'
@ -43,7 +43,7 @@ export const Report = memo(({ idWell }) => {
const [pagesCount, setPagesCount] = useState(0)
const [showLoader, setShowLoader] = useState(false)
const handleReportCreation = async () => await invokeWebApiWrapperAsync(
const handleReportCreation = useCallback(async () => await invokeWebApiWrapperAsync(
async () => {
const taskId = await ReportService.createReport(
idWell,
@ -81,9 +81,11 @@ export const Report = memo(({ idWell }) => {
${filterDateRange[0].format(dateTimeFormat)} по
${filterDateRange[1].format(dateTimeFormat)}`,
'Создание отчёта по скважине'
)
), [filterDateRange, format, idWell, step])
const disabledDate = (current) => !current.isBetween(aviableDateRange[0], aviableDateRange[1], 'seconds', '[]')
const disabledDate = useCallback((current) =>
!current.isBetween(aviableDateRange[0], aviableDateRange[1], 'seconds', '[]')
, [aviableDateRange])
useEffect(() => invokeWebApiWrapperAsync(
async () => {

View File

@ -1,4 +1,4 @@
import { memo } from 'react'
import { memo, useMemo } from 'react'
import {
FolderOutlined,
FundViewOutlined,
@ -30,7 +30,7 @@ const { Content } = Layout
export const Well = memo(() => {
const { idWell, tab } = useParams()
const rootPath = `/well/${idWell}`
const rootPath = useMemo(() => `/well/${idWell}`, [idWell])
return (
<Layout>

View File

@ -1,4 +1,4 @@
import { useState, useEffect, useCallback, memo } from 'react'
import { useState, useEffect, useCallback, memo, useMemo } from 'react'
import {
EditableTable,
@ -54,15 +54,15 @@ export const WellDrillParams = memo(({ idWell }) => {
await updateParams()
})(), [idWell, updateParams])
const handlerProps = {
const handlerProps = useMemo(() => ({
service: DrillParamsService,
setLoader: setShowLoader,
errorMsg: `Не удалось выполнить операцию`,
onComplete: updateParams,
idWell
}
}), [idWell, updateParams])
const recordParser = (record) => ({ ...record, idWell })
const recordParser = useCallback((record) => ({ ...record, idWell }), [idWell])
return (
<LoaderPortal show={showLoader}>

View File

@ -135,13 +135,13 @@ export const WellOperationsEditor = memo(({ idWell, idType, showNpt, ...other })
useEffect(updateOperations, [updateOperations])
const handlerProps = {
const handlerProps = useMemo(() => ({
service: WellOperationService,
setLoader: setShowLoader,
errorMsg: `Не удалось выполнить операцию`,
onComplete: updateOperations,
idWell
}
}), [idWell, updateOperations])
const onRow = useCallback((record) => {
if (selectedIds?.includes(record.id))