import { Drawer, Tree, TreeDataNode, TreeProps } from 'antd' import { useState, useEffect, useCallback, memo, Key, useMemo } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { useDepositList } from '@asb/context' import { WellIcon, WellIconState } from '@components/icons' import { 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/components/well_tree_select.css' /** * Для поиска в URL текущего раздела по шаблону `/{type}/{id}` * * Если найдено совпадение может вернуть 1 или 2 группы соответственно */ const URL_REGEX = /^\/([^\/?#]+)(?:\/([^\/?#]+))?/ 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) const getKeyByUrl = (url?: string): [Key | null, string | null, number | null] => { const result = url?.match(URL_REGEX) // pattern "/:type/:id" if (!result) return [null, null, null] return [result[0], result[1], result[2] && result[2] !== 'null' ? Number(result[2]) : null] } const getLabel = (wellsTree: TreeDataNode[], value?: string): string | undefined => { const [url, type, key] = getKeyByUrl(value) if (!url) return let deposit: TreeDataNode | undefined let cluster: TreeDataNode | undefined let well: TreeDataNode | undefined switch (type) { case 'deposit': if (key === null) return 'Месторождение не выбрано' deposit = wellsTree.find((deposit) => deposit.key === url) if (deposit) return `${deposit.title}` return 'Ошибка! Месторождение не найдено!' case 'cluster': if (key === null) return 'Куст не выбран' deposit = wellsTree.find((deposit) => ( cluster = deposit.children?.find((cluster: TreeDataNode) => cluster.key === url) )) if (deposit && cluster) return `${deposit.title} / ${cluster.title}` return 'Ошибка! Куст не найден!' case 'well': if (key === null) return 'Скважина не выбрана' deposit = wellsTree.find((deposit) => ( cluster = deposit.children?.find((cluster: TreeDataNode) => ( well = cluster.children?.find((well: TreeDataNode) => 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 type WellTreeSelectorProps = TreeProps & { show?: boolean expand?: boolean | Key[] current?: Key onClose?: () => void onChange?: (value: string | undefined) => void open?: boolean } const getExpandKeys = (treeData: TreeDataNode[], depositKeys?: Key[] | boolean): Key[] => { const out: Key[] = [] treeData.forEach((deposit) => { if (Array.isArray(depositKeys) && !depositKeys.includes(deposit.key)) return if (deposit.key) out.push(deposit.key) deposit.children?.forEach((cluster) => { if (cluster.key) out.push(cluster.key) }) }) return out } const makeWellsTreeData = (deposits: DepositDto[]): TreeDataNode[] => deposits.map(deposit =>({ title: deposit.caption, key: `/deposit/${deposit.id}`, value: `/deposit/${deposit.id}`, icon: , 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: , children: wells.map(well => ({ title: well.caption, key: `/well/${well.id}`, value: `/well/${well.id}`, icon: })), } }), })) export const WellTreeSelector = memo(({ expand, current, onChange, onClose, open, ...other }) => { const [expanded, setExpanded] = useState([]) const [selected, setSelected] = useState([]) const navigate = useNavigate() const location = useLocation() const deposits = useDepositList() const wellsTree = useMemo(() => makeWellsTreeData(deposits), [deposits]) const onValueChange = useCallback((value?: string): void => { const key = getKeyByUrl(value)[0] setSelected(key ? [key] : []) onChange?.(getLabel(wellsTree, value)) }, [wellsTree]) const onSelect = useCallback((value: Key[]): void => { const newRoot = URL_REGEX.exec(String(value)) const oldRoot = URL_REGEX.exec(location.pathname) if (!newRoot || !oldRoot) return let newPath = newRoot[0] if (oldRoot[1] === newRoot[1]) { /// Если типы страницы одинаковые (deposit, cluster, well), добавляем остаток старого пути const url = location.pathname.substring(oldRoot[0].length) newPath = newPath + url } navigate(newPath, { state: { from: location.pathname }}) }, [navigate, location]) useEffect(() => { if (current) setSelected([current]) }, [current]) useEffect(() => { setExpanded((prev) => expand ? getExpandKeys(wellsTree, expand) : prev) }, [wellsTree, expand]) useEffect(() => onValueChange(location.pathname), [onValueChange, location.pathname]) return ( ) }) export default WellTreeSelector