vitess.io/vitess@v0.16.2/web/vtadmin/src/components/routes/topology/ClusterTopology.tsx (about)

     1  /**
     2   * Copyright 2022 The Vitess Authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  import React, { useEffect, useState } from 'react';
    17  
    18  import { useTopologyPath } from '../../../hooks/api';
    19  import { useDocumentTitle } from '../../../hooks/useDocumentTitle';
    20  import { ContentContainer } from '../../layout/ContentContainer';
    21  import { NavCrumbs } from '../../layout/NavCrumbs';
    22  import { WorkspaceHeader } from '../../layout/WorkspaceHeader';
    23  import { WorkspaceTitle } from '../../layout/WorkspaceTitle';
    24  import { Link, useParams } from 'react-router-dom';
    25  import { generateGraph, TopologyCell, TopologyCellChild } from './Nodes';
    26  
    27  import ReactFlow, {
    28      addEdge,
    29      MiniMap,
    30      Controls,
    31      Background,
    32      useNodesState,
    33      useEdgesState,
    34      Connection,
    35  } from 'react-flow-renderer';
    36  import { getTopologyPath } from '../../../api/http';
    37  
    38  export const ClusterTopology = () => {
    39      interface RouteParams {
    40          clusterID: string;
    41      }
    42      useDocumentTitle('Cluster Topolgy');
    43      const { clusterID } = useParams<RouteParams>();
    44      const { data } = useTopologyPath({ clusterID, path: '/' });
    45      const [topology, setTopology] = useState<{ cell: TopologyCell }>({ cell: data?.cell as TopologyCell });
    46  
    47      const [nodes, setNodes, onNodesChange] = useNodesState([]);
    48      const [edges, setEdges, onEdgesChange] = useEdgesState([]);
    49  
    50      const onConnect = (params: Connection) => setEdges((eds) => addEdge(params, eds));
    51      const onExpand = async (path: string) => {
    52          const { cell } = await getTopologyPath({ clusterID, path });
    53          const newTopo = { ...topology };
    54          newTopo.cell.children = placeCell(newTopo.cell, cell as TopologyCell);
    55          setTopology(newTopo);
    56      };
    57  
    58      const placeCell = (currentCell: TopologyCell, newCell: TopologyCell): TopologyCellChild[] => {
    59          const newChildren: TopologyCellChild[] = [];
    60          currentCell.children?.forEach((c) => {
    61              if (typeof c === 'string' && c === newCell?.name) {
    62                  newChildren.push(newCell as TopologyCell);
    63              }
    64              if (typeof c == 'string' && c !== newCell?.name) {
    65                  newChildren.push(c);
    66              }
    67              if (typeof c !== 'string') {
    68                  c.children = placeCell(c, newCell);
    69                  newChildren.push(c);
    70              }
    71          });
    72          return newChildren;
    73      };
    74  
    75      useEffect(() => {
    76          const { nodes: initialNodes, edges: initialEdges } = topology
    77              ? generateGraph(topology, onExpand)
    78              : { nodes: [], edges: [] };
    79          setNodes(initialNodes);
    80          setEdges(initialEdges);
    81          // eslint-disable-next-line react-hooks/exhaustive-deps
    82      }, [topology]);
    83  
    84      useEffect(() => {
    85          if (data?.cell) {
    86              setTopology({ cell: data?.cell as TopologyCell });
    87          }
    88          // eslint-disable-next-line react-hooks/exhaustive-deps
    89      }, [data]);
    90  
    91      if (!data) {
    92          return (
    93              <div>
    94                  <WorkspaceHeader>
    95                      <NavCrumbs>
    96                          <Link to="/topology">Topology</Link>
    97                      </NavCrumbs>
    98  
    99                      <WorkspaceTitle className="font-mono">{clusterID}</WorkspaceTitle>
   100                  </WorkspaceHeader>
   101  
   102                  <ContentContainer>404</ContentContainer>
   103              </div>
   104          );
   105      }
   106  
   107      return (
   108          <div>
   109              <WorkspaceHeader>
   110                  <NavCrumbs>
   111                      <Link to="/topology">Topology</Link>
   112                  </NavCrumbs>
   113  
   114                  <WorkspaceTitle className="font-mono">{clusterID}</WorkspaceTitle>
   115              </WorkspaceHeader>
   116  
   117              <ContentContainer className="lg:w-[1400px] lg:h-[1200px] md:w-[900px] md:h-[800px]">
   118                  <ReactFlow
   119                      nodes={nodes}
   120                      edges={edges}
   121                      onNodesChange={onNodesChange}
   122                      onEdgesChange={onEdgesChange}
   123                      onConnect={onConnect}
   124                      fitView
   125                      attributionPosition="top-right"
   126                  >
   127                      <MiniMap
   128                          nodeStrokeColor={(n) => {
   129                              if (n.style?.background) return n.style.background as string;
   130                              if (n.type === 'input') return '#0041d0';
   131                              if (n.type === 'output') return '#ff0072';
   132                              if (n.type === 'default') return '#1a192b';
   133  
   134                              return '#eee';
   135                          }}
   136                          nodeColor={(n) => {
   137                              if (n.style?.background) return n.style.background as string;
   138  
   139                              return '#fff';
   140                          }}
   141                          nodeBorderRadius={2}
   142                      />
   143                      <Controls />
   144                      <Background color="#aaa" gap={16} />
   145                  </ReactFlow>
   146              </ContentContainer>
   147          </div>
   148      );
   149  };