import React from 'react'; import type {TNetNodeInfo, TNetNodePeerInfo} from '../../../../../types/api/netInfo'; import {cn} from '../../../../../utils/cn'; import {useWithProblemsQueryParam} from '../../../../../utils/hooks/useWithProblemsQueryParam'; import {getIllustration} from '../../../../../utils/illustrations'; import {NodeNetwork} from '../NodeNetwork/NodeNetwork'; import type {NodeTooltipData} from '../NodeTooltipPopup/NodeTooltipPopup'; import i18n from '../i18n'; import {getConnectedNodesCount, groupNodesByField} from '../utils'; const b = cn('network'); export interface NodesProps { nodes: Record; isRight?: boolean; showId?: boolean; showRacks?: boolean; clickedNode?: TNetNodeInfo; onClickNode: (node: TNetNodeInfo | undefined) => void; onShowNodeTooltip: (anchor: HTMLDivElement, data: NodeTooltipData) => void; onHideNodeTooltip: () => void; } interface NodeItemProps { nodeInfo: TNetNodeInfo | TNetNodePeerInfo; index: number; isRight?: boolean; showId?: boolean; clickedNode?: TNetNodeInfo; capacity?: number; connected?: number; onClickNode: (node: TNetNodeInfo | undefined) => void; onShowNodeTooltip: (anchor: HTMLDivElement, data: NodeTooltipData) => void; onHideNodeTooltip: () => void; } function NodeItem({ nodeInfo, index, isRight, showId, clickedNode, capacity, connected, onClickNode, onShowNodeTooltip, onHideNodeTooltip, }: NodeItemProps) { const handleClick = isRight ? undefined : () => { onClickNode( clickedNode && nodeInfo.NodeId === clickedNode.NodeId ? undefined : (nodeInfo as TNetNodeInfo), ); }; return ( ); } function getNodeCapacityAndConnected( nodeInfo: TNetNodeInfo | TNetNodePeerInfo, isRight?: boolean, useObjectKeysLength?: boolean, ) { let capacity: number | undefined; let connected: number | undefined; if (!isRight && 'Peers' in nodeInfo && nodeInfo.Peers) { capacity = useObjectKeysLength ? Object.keys(nodeInfo.Peers).length : nodeInfo.Peers.length; connected = getConnectedNodesCount(nodeInfo.Peers); } return {capacity, connected}; } function shouldShowNode( withProblems: boolean, isRight?: boolean, capacity?: number, connected?: number, ) { return (withProblems && capacity !== connected) || !withProblems || isRight; } interface RackColumnProps { rackKey: string; rackIndex: number; nodes: (TNetNodeInfo | TNetNodePeerInfo)[]; isRight?: boolean; showId?: boolean; clickedNode?: TNetNodeInfo; withProblems: boolean; onClickNode: (node: TNetNodeInfo | undefined) => void; onShowNodeTooltip: (anchor: HTMLDivElement, data: NodeTooltipData) => void; onHideNodeTooltip: () => void; } function RackColumn({ rackKey, rackIndex, nodes, isRight, showId, clickedNode, withProblems, onClickNode, onShowNodeTooltip, onHideNodeTooltip, }: RackColumnProps) { return (
{rackKey === 'undefined' ? '?' : rackKey}
{nodes.map((nodeInfo, index) => { const {capacity, connected} = getNodeCapacityAndConnected(nodeInfo, isRight, true); if (shouldShowNode(withProblems, isRight, capacity, connected)) { return ( ); } return null; })}
); } interface FlatNodesListProps { nodes: (TNetNodeInfo | TNetNodePeerInfo)[]; isRight?: boolean; showId?: boolean; clickedNode?: TNetNodeInfo; withProblems: boolean; onClickNode: (node: TNetNodeInfo | undefined) => void; onShowNodeTooltip: (anchor: HTMLDivElement, data: NodeTooltipData) => void; onHideNodeTooltip: () => void; } function FlatNodesList({ nodes, isRight, showId, clickedNode, withProblems, onClickNode, onShowNodeTooltip, onHideNodeTooltip, }: FlatNodesListProps) { return ( {nodes.map((nodeInfo, index) => { const peers = nodeInfo && 'Peers' in nodeInfo ? nodeInfo.Peers : undefined; const {capacity, connected} = getNodeCapacityAndConnected(nodeInfo, isRight, false); if (shouldShowNode(withProblems, isRight, capacity, connected)) { return ( ); } return null; })} ); } function countDisplayedNodes( nodes: NodesProps['nodes'], withProblems: boolean, isRight?: boolean, showRacks?: boolean, ) { let count = 0; Object.keys(nodes).forEach((nodeTypeKey) => { if (showRacks) { const nodesGroupedByRack = groupNodesByField(nodes[nodeTypeKey], 'Rack'); Object.keys(nodesGroupedByRack).forEach((rackKey) => { nodesGroupedByRack[rackKey].forEach((nodeInfo) => { const {capacity, connected} = getNodeCapacityAndConnected( nodeInfo, isRight, true, ); if (shouldShowNode(withProblems, isRight, capacity, connected)) { count++; } }); }); } else { nodes[nodeTypeKey].forEach((nodeInfo) => { const {capacity, connected} = getNodeCapacityAndConnected(nodeInfo, isRight, false); if (shouldShowNode(withProblems, isRight, capacity, connected)) { count++; } }); } }); return count; } export function Nodes({ nodes, isRight, showId, showRacks, clickedNode, onClickNode, onShowNodeTooltip, onHideNodeTooltip, }: NodesProps) { const SuccessImage = getIllustration('SuccessOperation'); const {withProblems} = useWithProblemsQueryParam(); const displayedNodesCount = React.useMemo( () => countDisplayedNodes(nodes, withProblems, isRight, showRacks), [nodes, withProblems, isRight, showRacks], ); const result = Object.keys(nodes).map((nodeTypeKey, j) => { const nodesGroupedByRack = groupNodesByField(nodes[nodeTypeKey], 'Rack'); return (
{i18n('label_nodes', {type: nodeTypeKey})}
{showRacks ? ( Object.keys(nodesGroupedByRack).map((rackKey, i) => ( )) ) : ( )}
); }); if (withProblems && displayedNodesCount === 0) { return ; } return result; }