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 };