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