vitess.io/vitess@v0.16.2/web/vtadmin/src/components/routes/Backups.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 17 import { orderBy } from 'lodash'; 18 import { useMemo } from 'react'; 19 20 import { useBackups } from '../../hooks/api'; 21 import { useDocumentTitle } from '../../hooks/useDocumentTitle'; 22 import { useSyncedURLParam } from '../../hooks/useSyncedURLParam'; 23 import { formatStatus } from '../../util/backups'; 24 import { formatAlias } from '../../util/tablets'; 25 import { formatDateTime, formatRelativeTime } from '../../util/time'; 26 import { DataCell } from '../dataTable/DataCell'; 27 import { DataFilter } from '../dataTable/DataFilter'; 28 import { DataTable } from '../dataTable/DataTable'; 29 import { ContentContainer } from '../layout/ContentContainer'; 30 import { WorkspaceHeader } from '../layout/WorkspaceHeader'; 31 import { WorkspaceTitle } from '../layout/WorkspaceTitle'; 32 import { TabletLink } from '../links/TabletLink'; 33 import { BackupStatusPip } from '../pips/BackupStatusPip'; 34 import { filterNouns } from '../../util/filterNouns'; 35 import { ShardLink } from '../links/ShardLink'; 36 37 const COLUMNS = ['Started at', 'Directory', 'Backup', 'Tablet', 'Status']; 38 39 export const Backups = () => { 40 useDocumentTitle('Backups'); 41 42 const { value: filter, updateValue: updateFilter } = useSyncedURLParam('filter'); 43 44 const { data: backups = [] } = useBackups(); 45 46 const rows = useMemo(() => { 47 const mapped = backups.map((b) => { 48 return { 49 clusterID: b.cluster?.id, 50 clusterName: b.cluster?.name, 51 directory: b.backup?.directory, 52 engine: b.backup?.engine, 53 keyspace: b.backup?.keyspace, 54 name: b.backup?.name, 55 shard: b.backup?.shard, 56 status: formatStatus(b.backup?.status), 57 tablet: formatAlias(b.backup?.tablet_alias), 58 time: b.backup?.time?.seconds, 59 _status: b.backup?.status, 60 }; 61 }); 62 63 const filtered = filterNouns(filter, mapped); 64 return orderBy(filtered, ['clusterID', 'keyspace', 'shard', 'name'], ['asc', 'asc', 'asc', 'desc']); 65 }, [backups, filter]); 66 67 const renderRows = (rs: typeof rows) => { 68 return rs.map((row) => { 69 return ( 70 <tr key={`${row.clusterID}-${row.directory}-${row.name}`}> 71 <DataCell> 72 {formatDateTime(row.time)} 73 <div className="text-sm text-secondary">{formatRelativeTime(row.time)}</div> 74 </DataCell> 75 <DataCell> 76 <ShardLink clusterID={row.clusterID} keyspace={row.keyspace} shard={row.shard}> 77 {row.directory} 78 </ShardLink> 79 <div className="text-sm text-secondary">{row.clusterName}</div> 80 </DataCell> 81 <DataCell>{row.name}</DataCell> 82 <DataCell> 83 <TabletLink alias={row.tablet} clusterID={row.clusterID}> 84 {row.tablet} 85 </TabletLink> 86 </DataCell> 87 <DataCell className="whitespace-nowrap"> 88 <BackupStatusPip status={row._status} /> {row.status} 89 </DataCell> 90 </tr> 91 ); 92 }); 93 }; 94 95 return ( 96 <div> 97 <WorkspaceHeader> 98 <WorkspaceTitle>Backups</WorkspaceTitle> 99 </WorkspaceHeader> 100 <ContentContainer> 101 <DataFilter 102 onChange={(e) => updateFilter(e.target.value)} 103 onClear={() => updateFilter('')} 104 placeholder="Filter backups" 105 value={filter || ''} 106 /> 107 <DataTable columns={COLUMNS} data={rows} renderRows={renderRows} /> 108 </ContentContainer> 109 </div> 110 ); 111 };