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

     1  /**
     2   * Copyright 2021 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 { orderBy } from 'lodash-es';
    17  import * as React from 'react';
    18  
    19  import { useKeyspaces } from '../../hooks/api';
    20  import { useDocumentTitle } from '../../hooks/useDocumentTitle';
    21  import { useSyncedURLParam } from '../../hooks/useSyncedURLParam';
    22  import { DataCell } from '../dataTable/DataCell';
    23  import { DataTable } from '../dataTable/DataTable';
    24  import { Pip } from '../pips/Pip';
    25  import { filterNouns } from '../../util/filterNouns';
    26  import { getShardsByState } from '../../util/keyspaces';
    27  import { ContentContainer } from '../layout/ContentContainer';
    28  import { WorkspaceHeader } from '../layout/WorkspaceHeader';
    29  import { WorkspaceTitle } from '../layout/WorkspaceTitle';
    30  import { DataFilter } from '../dataTable/DataFilter';
    31  import { KeyspaceLink } from '../links/KeyspaceLink';
    32  import KeyspaceActions from './keyspaces/KeyspaceActions';
    33  import { ReadOnlyGate } from '../ReadOnlyGate';
    34  import { isReadOnlyMode } from '../../util/env';
    35  import { Link } from 'react-router-dom';
    36  import { QueryLoadingPlaceholder } from '../placeholders/QueryLoadingPlaceholder';
    37  
    38  export const Keyspaces = () => {
    39      useDocumentTitle('Keyspaces');
    40      const { value: filter, updateValue: updateFilter } = useSyncedURLParam('filter');
    41  
    42      const keyspacesQuery = useKeyspaces();
    43  
    44      const ksRows = React.useMemo(() => {
    45          const mapped = (keyspacesQuery.data || []).map((k) => {
    46              const shardsByState = getShardsByState(k);
    47  
    48              return {
    49                  clusterID: k.cluster?.id,
    50                  cluster: k.cluster?.name,
    51                  name: k.keyspace?.name,
    52                  servingShards: shardsByState.serving.length,
    53                  nonservingShards: shardsByState.nonserving.length,
    54              };
    55          });
    56          const filtered = filterNouns(filter, mapped);
    57          return orderBy(filtered, ['cluster', 'name']);
    58      }, [keyspacesQuery.data, filter]);
    59  
    60      const renderRows = (rows: typeof ksRows) =>
    61          rows.map((row, idx) => (
    62              <tr key={idx}>
    63                  <DataCell>
    64                      <KeyspaceLink clusterID={row.clusterID} name={row.name}>
    65                          <div className="font-bold">{row.name}</div>
    66                          <div className="text-sm text-secondary">{row.cluster}</div>
    67                      </KeyspaceLink>
    68                  </DataCell>
    69                  <DataCell>
    70                      {!!row.servingShards && (
    71                          <div>
    72                              <Pip state="success" /> {row.servingShards} {row.servingShards === 1 ? 'shard' : 'shards'}
    73                          </div>
    74                      )}
    75                      {!!row.nonservingShards && (
    76                          <div className="font-bold">
    77                              <Pip state="danger" /> {row.nonservingShards}{' '}
    78                              {row.nonservingShards === 1 ? 'shard' : 'shards'} not serving
    79                          </div>
    80                      )}
    81                  </DataCell>
    82                  <ReadOnlyGate>
    83                      <DataCell>
    84                          <KeyspaceActions keyspace={row.name as string} clusterID={row.clusterID as string} />
    85                      </DataCell>
    86                  </ReadOnlyGate>
    87              </tr>
    88          ));
    89  
    90      return (
    91          <div>
    92              <WorkspaceHeader>
    93                  <div className="flex items-top justify-between max-w-screen-md">
    94                      <WorkspaceTitle>Keyspaces</WorkspaceTitle>
    95                      <ReadOnlyGate>
    96                          <div>
    97                              <Link className="btn btn-secondary btn-md" to="/keyspaces/create">
    98                                  Create a Keyspace
    99                              </Link>
   100                          </div>
   101                      </ReadOnlyGate>
   102                  </div>
   103              </WorkspaceHeader>
   104              <ContentContainer>
   105                  <DataFilter
   106                      autoFocus
   107                      onChange={(e) => updateFilter(e.target.value)}
   108                      onClear={() => updateFilter('')}
   109                      placeholder="Filter keyspaces"
   110                      value={filter || ''}
   111                  />
   112                  <div className="max-w-screen-md">
   113                      <DataTable
   114                          columns={isReadOnlyMode() ? ['Keyspace', 'Shards'] : ['Keyspace', 'Shards', 'Actions']}
   115                          data={ksRows}
   116                          renderRows={renderRows}
   117                      />
   118                      <QueryLoadingPlaceholder query={keyspacesQuery} />
   119                  </div>
   120              </ContentContainer>
   121          </div>
   122      );
   123  };