asb_cloud_front/src/components/selectors/WellTreeSelector.tsx

183 lines
7.2 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

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

import { Button, Drawer, Skeleton, Tree, TreeProps, Typography } from 'antd'
import { DefaultValueType } from 'rc-tree-select/lib/interface'
import { useState, useEffect, ReactNode, useCallback, memo, Key } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
import { WellIcon, WellIconState } from '@components/icons'
import { invokeWebApiWrapperAsync } from '@components/factory'
import { DepositService, DepositDto, WellDto } from '@api'
import { isRawDate } from '@utils'
import { ReactComponent as DepositIcon } from '@images/DepositIcon.svg'
import { ReactComponent as ClusterIcon } from '@images/ClusterIcon.svg'
import '@styles/wellTreeSelect.css'
export const getWellState = (idState?: number): WellIconState => idState === 1 ? 'active' : 'unknown'
export const checkIsWellOnline = (lastTelemetryDate: unknown): boolean =>
isRawDate(lastTelemetryDate) && (Date.now() - +new Date(lastTelemetryDate) < 600_000)
export type TreeNodeData = {
title?: string | null
key?: string
value?: DefaultValueType
icon?: ReactNode
children?: TreeNodeData[]
}
const getKeyByUrl = (url?: string): [Key | null, string | null] => {
const result = url?.match(/^\/([^\/]+)\/([^\/?]+)/) // pattern "/:type/:id"
if (!result) return [null, null]
return [result[0], result[1]]
}
const getLabel = (wellsTree: TreeNodeData[], value?: string): string | undefined => {
const [url, type] = getKeyByUrl(value)
if (!url) return
let deposit: TreeNodeData | undefined
let cluster: TreeNodeData | undefined
let well: TreeNodeData | undefined
switch (type) {
case 'deposit':
deposit = wellsTree.find((deposit) => deposit.key === url)
if (deposit)
return `${deposit.title}`
return 'Ошибка! Месторождение не найдено!'
case 'cluster':
deposit = wellsTree.find((deposit) => (
cluster = deposit.children?.find((cluster: TreeNodeData) => cluster.key === url)
))
if (deposit && cluster)
return `${deposit.title} / ${cluster.title}`
return 'Ошибка! Куст не найден!'
case 'well':
deposit = wellsTree.find((deposit) => (
cluster = deposit.children?.find((cluster: TreeNodeData) => (
well = cluster.children?.find((well: TreeNodeData) => well.key === url)
))
))
if (deposit && cluster && well)
return `${deposit.title} / ${cluster.title} / ${well.title}`
return 'Ошибка! Скважина не найдена!'
default: break
}
}
const getWellSortScore = (well: WellDto) => {
let out = [1, 2, 0][well.idState ?? 2]
const timeout = Date.now() - +new Date(well.lastTelemetryDate || 0)
if (timeout < 600_000) out += 600_000 - timeout
return out
}
const sortWellsByActive = (a: WellDto, b: WellDto): number => {
const score = getWellSortScore(b) - getWellSortScore(a)
if (score !== 0) return score
return (a.caption || '')?.localeCompare(b.caption || '')
}
export const WellTreeSelector = memo(({ show, ...other }: TreeProps<TreeNodeData> & { show?: boolean }) => {
const [wellsTree, setWellsTree] = useState<TreeNodeData[]>([])
const [showLoader, setShowLoader] = useState<boolean>(false)
const [visible, setVisible] = useState<boolean>(false)
const [expanded, setExpanded] = useState<Key[]>([])
const [selected, setSelected] = useState<Key[]>([])
const [value, setValue] = useState<string>()
const navigate = useNavigate()
const location = useLocation()
useEffect(() => {
setVisible((prev) => show ?? prev)
setExpanded((prev) => {
if (typeof show === 'undefined') return prev
if (!show) return []
const out: Key[] = []
wellsTree.forEach((deposit) => {
if (deposit.key) out.push(deposit.key)
deposit.children?.forEach((cluster) => {
if (cluster.key) out.push(cluster.key)
})
})
return out
})
}, [wellsTree, show])
useEffect(() => {
invokeWebApiWrapperAsync(
async () => {
const deposits: Array<DepositDto> = await DepositService.getDeposits()
const wellsTree: TreeNodeData[] = deposits.map(deposit =>({
title: deposit.caption,
key: `/deposit/${deposit.id}`,
value: `/deposit/${deposit.id}`,
icon: <DepositIcon width={24} height={24}/>,
children: deposit.clusters?.map(cluster => {
const wells = cluster.wells ? cluster.wells.slice() : []
wells.sort(sortWellsByActive)
return {
title: cluster.caption,
key: `/cluster/${cluster.id}`,
value: `/cluster/${cluster.id}`,
icon: <ClusterIcon width={24} height={24}/>,
children: wells.map(well => ({
title: well.caption,
key: `/well/${well.id}`,
value: `/well/${well.id}`,
icon: <WellIcon
width={24}
height={24}
state={getWellState(well.idState)}
online={checkIsWellOnline(well.lastTelemetryDate)}
/>
})),
}
}),
}))
setWellsTree(wellsTree)
},
setShowLoader,
`Не удалось загрузить список скважин`,
{ actionName: 'Получить список скважин' }
)
}, [])
const onChange = useCallback((value?: string): void => {
const key = getKeyByUrl(value)[0]
setSelected(key ? [key] : [])
setValue(getLabel(wellsTree, value))
}, [wellsTree])
const onSelect = useCallback((value: Key[]): void => {
navigate(String(value), { state: { from: location.pathname }})
}, [navigate, location])
useEffect(() => onChange(location.pathname), [onChange, location])
return (
<>
<Button loading={showLoader} onClick={() => setVisible(true)}>{value ?? 'Выберите месторождение'}</Button>
<Drawer visible={visible} mask={false} onClose={() => setVisible(false)}>
<Typography.Title level={3}>Список скважин</Typography.Title>
<Skeleton active loading={showLoader}>
<Tree
{...other}
showIcon
selectedKeys={selected}
treeData={wellsTree}
onSelect={onSelect}
onExpand={setExpanded}
expandedKeys={expanded}
/>
</Skeleton>
</Drawer>
</>
)
})
export default WellTreeSelector