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

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(() => { export const ClusterController = memo(() => {
const [deposits, setDeposits] = useState([]) const [deposits, setDeposits] = useState([])
const [clusters, setClusters] = useState([]) const [clusters, setClusters] = useState([])
const [filteredClusters, setFilteredClusters] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('') 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(() => [ const clusterColumns = useMemo(() => [
makeSelectColumn('Месторождение', 'idDeposit', deposits, '--', { makeSelectColumn('Месторождение', 'idDeposit', deposits, '--', {
width: 200, width: 200,
@ -65,14 +71,6 @@ export const ClusterController = memo(() => {
useEffect(updateTable, [updateTable]) 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(() => ({ const handlerProps = useMemo(() => ({
service: AdminClusterService, service: AdminClusterService,
setLoader: setShowLoader, setLoader: setShowLoader,
@ -84,7 +82,7 @@ export const ClusterController = memo(() => {
<> <>
<Input.Search <Input.Search
style={{ margin: '15px 0' }} style={{ margin: '15px 0' }}
placeholder={'Введите текст для поиска...'} placeholder={'Введите текст для поиска (по полям: Название, Долгота, Широта)...'}
onChange={(e) => setSearchValue(e.target.value)} onChange={(e) => setSearchValue(e.target.value)}
value={searchValue} value={searchValue}
loading={showLoader} loading={showLoader}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { Button, Input, Tag } from 'antd' import { Button, Input, Tag } from 'antd'
import { UserSwitchOutlined } from '@ant-design/icons' 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 { BehaviorSubject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
import { import {
@ -172,12 +172,12 @@ export const UserController = memo(() => {
'Получение списка компаний' 'Получение списка компаний'
), []) ), [])
const handlerProps = { const handlerProps = useMemo(() => ({
service: AdminUserService, service: AdminUserService,
setLoader: setShowLoader, setLoader: setShowLoader,
errorMsg: `Не удалось выполнить операцию`, errorMsg: `Не удалось выполнить операцию`,
onComplete: updateTable, onComplete: updateTable,
} }), [updateTable])
const onSearchTextChange = useCallback((e) => subject?.next(e?.target?.value), [subject]) const onSearchTextChange = useCallback((e) => subject?.next(e?.target?.value), [subject])
@ -186,7 +186,7 @@ export const UserController = memo(() => {
<LoaderPortal show={showLoader}> <LoaderPortal show={showLoader}>
<Input.Search <Input.Search
allowClear allowClear
placeholder={'Поиск пользователей'} placeholder={'Введите текст для поиска (по всем полям за исключением ролей)...'}
onChange={onSearchTextChange} onChange={onSearchTextChange}
style={{ marginBottom: '15px' }} style={{ marginBottom: '15px' }}
loading={isSearching} 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 { Input } from 'antd'
import { invokeWebApiWrapperAsync } from '@components/factory' import { invokeWebApiWrapperAsync } from '@components/factory'
@ -12,17 +12,23 @@ const columns = [
makeColumn('Логин', 'login', { sorter: makeStringSorter('login') }), makeColumn('Логин', 'login', { sorter: makeStringSorter('login') }),
makeColumn('IP', 'ip', { sorter: makeStringSorter('ip') }), makeColumn('IP', 'ip', { sorter: makeStringSorter('ip') }),
makeColumn('Дата посещения', 'lastDate', { makeColumn('Дата посещения', 'lastDate', {
render: (date) => formatDate(date, false, 'DD MMM YYYY, HH:mm:ss'), render: (date) => formatDate(date, false),
sorter: makeDateSorter('lastDate'), sorter: makeDateSorter('lastDate'),
}), }),
] ]
export const VisitLog = memo(() => { export const VisitLog = memo(() => {
const [logData, setLogData] = useState([]) const [logData, setLogData] = useState([])
const [filteredLogData, setFilteredLogData] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [searchValue, setSearchValue] = useState('') 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( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const logData = arrayOrDefault(await RequestTrackerService.getUsersStat(logRecordCount)) 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 ( return (
<> <>
<Input.Search <Input.Search

View File

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

View File

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

View File

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

View File

@ -1,21 +1,14 @@
import { useEffect, useState } from 'react' import { memo, useMemo } from 'react'
import { Grid, GridItem } from '@components/Grid' import { Grid, GridItem } from '@components/Grid'
import { Column } from '@components/charts/Column' import { Column } from '@components/charts/Column'
export const ArchiveColumn = ({ lineGroup, data, interval, style, headerHeight, yStart }) => { export const ArchiveColumn = memo(({ lineGroup, data, interval, style, headerHeight, yStart }) => {
const [lineGroupWithoutShapes, setLineGroupWithoutShapes] = useState([]) const lgws = useMemo(() => lineGroup.filter(cfg => !cfg.isShape), [lineGroup])
const [pv, setPV] = useState([]) const pv = useMemo(() => lgws.filter(line => line.showLabels).map(line => ({
useEffect(() => {
const lgws = lineGroup.filter(cfg => !cfg.isShape)
setLineGroupWithoutShapes(lgws)
setPV(lgws.filter(line => line.showLabels).map(line => ({
color: line.color, color: line.color,
label: line.label label: line.label
}))) })), [lgws])
}, [lineGroup])
return ( return (
<div style={style}> <div style={style}>
@ -26,13 +19,13 @@ export const ArchiveColumn = ({ lineGroup, data, interval, style, headerHeight,
</Grid> </Grid>
<Column <Column
data={data} data={data}
lineGroup={lineGroupWithoutShapes} lineGroup={lgws}
interval={interval} interval={interval}
yDisplay={false} yDisplay={false}
yStart={yStart} yStart={yStart}
/> />
</div> </div>
) )
} })
export default ArchiveColumn 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' import { Grid, GridItem } from '@components/Grid'
@ -41,13 +41,9 @@ export const cutData = (data, beginDate, endDate) => {
return data return data
} }
export const ArchiveDisplay = ({data, startDate, interval, onWheel}) => { export const ArchiveDisplay = memo(({data, startDate, interval, onWheel}) => {
const [chartData, setChartData] = useState([]) const endDate = useMemo(() => new Date(+startDate + interval), [startDate, interval])
const chartData = useMemo(() => cutData(data, startDate, endDate), [data, startDate, endDate])
useEffect(() => {
const endDate = new Date(+startDate + interval)
setChartData(cutData(data, startDate, endDate))
}, [data, startDate, interval])
return ( return (
<Grid onWheel={onWheel}> <Grid onWheel={onWheel}>
@ -65,6 +61,6 @@ export const ArchiveDisplay = ({data, startDate, interval, onWheel}) => {
))} ))}
</Grid> </Grid>
) )
} })
export default ArchiveDisplay export default ArchiveDisplay

View File

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

View File

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

View File

@ -1,18 +1,19 @@
import { memo, useMemo } from 'react'
import { Table } from 'antd' import { Table } from 'antd'
import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table' import { makeTextColumn, makeNumericColumnPlanFact } from '@components/Table'
import { getPrecision } from '@utils/functions' import { getPrecision } from '@utils/functions'
export const WellOperationsTable = ({ wellOperations }) => { const columns = [
const columns = [ makeTextColumn('Конструкция секции', 'sectionType'),
makeTextColumn('Конструкция секции','sectionType'), makeTextColumn('Операция', 'operationName'),
makeTextColumn('Операция','operationName'),
makeNumericColumnPlanFact('Глубина забоя', 'depth', null, null, getPrecision), makeNumericColumnPlanFact('Глубина забоя', 'depth', null, null, getPrecision),
makeNumericColumnPlanFact('Часы', 'durationHours', null, null, getPrecision), makeNumericColumnPlanFact('Часы', 'durationHours', null, null, getPrecision),
makeNumericColumnPlanFact('Комментарий', 'comment', null, null, (text) => text ?? '-') 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, key: el.plan?.id ?? el.fact.id,
sectionType: el.plan?.wellSectionTypeName ?? el.fact?.wellSectionTypeName, sectionType: el.plan?.wellSectionTypeName ?? el.fact?.wellSectionTypeName,
operationName: `${el.plan?.categoryName ?? el.fact?.categoryName ?? ''} ${' '} operationName: `${el.plan?.categoryName ?? el.fact?.categoryName ?? ''} ${' '}
@ -23,7 +24,7 @@ export const WellOperationsTable = ({ wellOperations }) => {
durationHoursFact: el.fact?.durationHours, durationHoursFact: el.fact?.durationHours,
commentPlan: el.plan?.comment ?? '-', commentPlan: el.plan?.comment ?? '-',
commentFact: el.fact?.comment ?? '-' commentFact: el.fact?.comment ?? '-'
})) })), [wellOperations])
return ( return (
<Table <Table
@ -36,6 +37,6 @@ export const WellOperationsTable = ({ wellOperations }) => {
tableName={'well_operations'} tableName={'well_operations'}
/> />
) )
} })
export default WellOperationsTable 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 { useParams } from 'react-router-dom'
import { arrayOrDefault } from '@utils' import { arrayOrDefault } from '@utils'
@ -8,7 +8,7 @@ import { invokeWebApiWrapperAsync } from '@components/factory'
import ClusterWells from './ClusterWells' import ClusterWells from './ClusterWells'
export const Cluster = () => { export const Cluster = memo(() => {
const { idCluster } = useParams() const { idCluster } = useParams()
const [data, setData] = useState([]) const [data, setData] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -28,6 +28,6 @@ export const Cluster = () => {
<ClusterWells statsWells={data} /> <ClusterWells statsWells={data} />
</LoaderPortal> </LoaderPortal>
) )
} })
export default Cluster 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 { DatePicker, Button, Input } from 'antd'
import { FileService } from '@api' import { FileService } from '@api'
import { hasPermission } from '@utils/permissions'
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, makePaginationObject } from '@components/Table' import { EditableTable, makeColumn, makeDateColumn, makeNumericColumn, makePaginationObject } from '@components/Table'
import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory' import { invokeWebApiWrapperAsync, downloadFile, formatBytes } from '@components/factory'
import { formatDate } from '@utils' import { hasPermission } from '@utils/permissions'
const pageSize = 12 const pageSize = 12
const { RangePicker } = DatePicker const { RangePicker } = DatePicker
@ -24,26 +23,11 @@ const columns = [
{name} {name}
</Button> </Button>
), ),
}, { },
title: 'Дата загрузки', makeDateColumn('Дата загрузки', 'uploadDate'),
key: 'uploadDate', makeNumericColumn('Размер', 'size', null, null, formatBytes),
dataIndex: 'uploadDate', makeColumn('Автор', 'author', { render: item => <UserView user={item}/> }),
render: item => formatDate(item, false, 'DD MMM YYYY, HH:mm:ss'), makeColumn('Компания', 'company', { render: (_, record) => <CompanyView company={record?.author?.company}/> })
}, {
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}/>
}
] ]
export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, customColumns, beforeTable, onChange, tableName }) => { 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 [files, setFiles] = useState([])
const [showLoader, setShowLoader] = useState(false) 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) => { const update = useCallback(() => {
await FileService.delete(idWell, file.id)
update()
}
const hanleCompanySearch = (value, _) => setFilterCompanyName(value)
const hanleFileNameSearch = (value, _) => setFilterFileName(value)
const mergedColumns = [...columns, ...(customColumns ?? [])]
const update = () => {
let begin = null let begin = null
let end = null let end = null
if (filterDataRange?.length > 1) { if (filterDataRange?.length > 1) {
@ -101,13 +77,15 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
`Не удалось загрузить файлы по скважине "${idWell}"`, `Не удалось загрузить файлы по скважине "${idWell}"`,
'Загрузка файла по скважине' 'Загрузка файла по скважине'
) )
} }, [filterCompanyName, filterDataRange, filterFileName, idCategory, idWell, page])
useEffect(update, [idWell, idCategory, page, filterDataRange, filterCompanyName, filterFileName]) useEffect(update, [update])
useEffect(() => onChange?.(files), [files, onChange]) useEffect(() => onChange?.(files), [files, onChange])
const companies = [...new Set(files.map(file => file.company))].filter(company => company) const handleFileDelete = useMemo(() => hasPermission(`File.edit${idCategory}`) && (async (file) => {
const filenames = [...new Set(files.map(file => file.name))].filter(name => name) await FileService.delete(idWell, file.id)
update()
}), [idWell, idCategory, update])
return ( return (
<LoaderPortal show={showLoader}> <LoaderPortal show={showLoader}>
@ -124,7 +102,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
<Search <Search
list={'listCompanies'} list={'listCompanies'}
placeholder={'Фильтр по компании'} placeholder={'Фильтр по компании'}
onSearch={hanleCompanySearch} onSearch={setFilterCompanyName}
/> />
<datalist id={'listCompanies'}> <datalist id={'listCompanies'}>
{companies.map((company) => ( {companies.map((company) => (
@ -138,7 +116,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
<Search <Search
list={'listFileNames'} list={'listFileNames'}
placeholder={'Фильтр по имени файла'} placeholder={'Фильтр по имени файла'}
onSearch={hanleFileNameSearch} onSearch={setFilterFileName}
/> />
<datalist id={'listFileNames'}> <datalist id={'listFileNames'}>
{filenames.map((name) => ( {filenames.map((name) => (
@ -154,7 +132,7 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
url={uploadUrl} url={uploadUrl}
accept={accept} accept={accept}
onUploadStart={() => setShowLoader(true)} onUploadStart={() => setShowLoader(true)}
onUploadComplete={handleUploadComplete} onUploadComplete={update}
/> />
</div> </div>
)} )}
@ -169,9 +147,9 @@ export const DocumentsTemplate = ({ idCategory, idWell, accept, headerChild, cus
pagination={{ pagination={{
...pagination, ...pagination,
showSizeChanger: false, showSizeChanger: false,
onChange: (page) => setPage(page), onChange: setPage,
}} }}
onRowDelete={hasPermission(`File.edit${idCategory}`) && handleFileDelete} onRowDelete={handleFileDelete}
rowKey={(record) => record.id} rowKey={(record) => record.id}
tableName={tableName ?? `file_${idCategory}`} tableName={tableName ?? `file_${idCategory}`}
/> />

View File

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

View File

@ -1,5 +1,5 @@
import { Input, Modal, Radio } from 'antd' 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 { BehaviorSubject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
import { UserView } from '@components/views' import { UserView } from '@components/views'
@ -113,7 +113,7 @@ export const CategoryEditor = memo(({ idWell, visible, category, onClosed }) =>
`Изменение статуса пользователя` `Изменение статуса пользователя`
), [users, idWell, category.idFileCategory]) ), [users, idWell, category.idFileCategory])
const userColumns = [ const userColumns = useMemo(() => [
makeColumn('Пользователь', 'user', { makeColumn('Пользователь', 'user', {
sorter: (a, b) => (a?.user?.surname && b?.user?.surname) ? a.user.surname.localeCompare(b.user.surname) : 0, sorter: (a, b) => (a?.user?.surname && b?.user?.surname) ? a.user.surname.localeCompare(b.user.surname) : 0,
render: (user) => <UserView user={user} />, 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]) 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 { Button, Input, Popconfirm, Form } from 'antd'
import { import {
DeleteOutlined, DeleteOutlined,
@ -44,7 +44,7 @@ export const CategoryRender = memo(({ idWell, partData, onUpdate, onEdit, onHist
file // Информация о файле file // Информация о файле
} = partData ?? {} } = 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 [isUploading, setIsUploading] = useState(false)
const [isDeleting, setIsDeleting] = useState(false) const [isDeleting, setIsDeleting] = useState(false)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import 'moment/locale/ru' import 'moment/locale/ru'
import moment from 'moment' 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 { Radio, Button, Select, notification } from 'antd'
import { ReportService } from '@api' import { ReportService } from '@api'
@ -43,7 +43,7 @@ export const Report = memo(({ idWell }) => {
const [pagesCount, setPagesCount] = useState(0) const [pagesCount, setPagesCount] = useState(0)
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const handleReportCreation = async () => await invokeWebApiWrapperAsync( const handleReportCreation = useCallback(async () => await invokeWebApiWrapperAsync(
async () => { async () => {
const taskId = await ReportService.createReport( const taskId = await ReportService.createReport(
idWell, idWell,
@ -81,9 +81,11 @@ export const Report = memo(({ idWell }) => {
${filterDateRange[0].format(dateTimeFormat)} по ${filterDateRange[0].format(dateTimeFormat)} по
${filterDateRange[1].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( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {

View File

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

View File

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

View File

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