vitess.io/vitess@v0.16.2/web/vtadmin/src/components/ActionPanel.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  
    17  import React, { useState } from 'react';
    18  import { UseMutationResult } from 'react-query';
    19  
    20  import { Icon, Icons } from './Icon';
    21  import { TextInput } from './TextInput';
    22  
    23  type Mutation = UseMutationResult & {
    24      mutate: () => void;
    25  };
    26  
    27  export interface ActionPanelProps {
    28      confirmationValue?: string;
    29      danger?: boolean;
    30      description: React.ReactNode;
    31      disabled?: boolean;
    32      documentationLink: string;
    33      loadingText: string;
    34      loadedText: string;
    35      mutation: UseMutationResult;
    36      title: string;
    37      warnings?: React.ReactNodeArray;
    38      body?: React.ReactNode;
    39  }
    40  /**
    41   * ActionPanel is a panel used for initiating mutations on entity pages.
    42   * When rendering multiple ActionPanel components, ensure they are in
    43   * a surrounding <div> to ensure the first: and last: CSS selectors work.
    44   */
    45  const ActionPanel: React.FC<ActionPanelProps> = ({
    46      confirmationValue,
    47      danger,
    48      disabled,
    49      title,
    50      description,
    51      documentationLink,
    52      mutation,
    53      loadingText,
    54      loadedText,
    55      warnings = [],
    56      body,
    57  }) => {
    58      const [typedConfirmation, setTypedConfirmation] = useState('');
    59  
    60      const requiresConfirmation = typeof confirmationValue === 'string' && !!confirmationValue;
    61  
    62      const isDisabled =
    63          !!disabled || mutation.isLoading || (requiresConfirmation && typedConfirmation !== confirmationValue);
    64  
    65      return (
    66          <div
    67              className={`p-9 pb-12 last:border-b border ${
    68                  danger ? 'border-red-400' : 'border-gray-400'
    69              } border-b-0 first:rounded-t-lg last:rounded-b-lg`}
    70              title={title}
    71          >
    72              <div className="flex justify-between items-center">
    73                  <p className="text-base font-bold m-0 text-gray-900">{title}</p>
    74                  <a
    75                      href={documentationLink}
    76                      target="_blank"
    77                      rel="noreferrer"
    78                      className="text-gray-900 ml-1 inline-block"
    79                  >
    80                      <span className="text-sm font-semibold text-gray-900">Documentation</span>
    81                      <Icon icon={Icons.open} className="ml-1 h-6 w-6 text-gray-900 fill-current inline" />
    82                  </a>
    83              </div>
    84              <p className="text-base mt-0">{description}</p>
    85  
    86              {warnings.map(
    87                  (warning, i) =>
    88                      warning && (
    89                          <div className="text-danger flex items-center" key={i}>
    90                              <Icon icon={Icons.alertFail} className="fill-current text-danger inline mr-2" />
    91                              {warning}
    92                          </div>
    93                      )
    94              )}
    95  
    96              {/* Don't render the confirmation input if "disabled" prop is set */}
    97              {requiresConfirmation && !disabled && (
    98                  <>
    99                      <p className="text-base">
   100                          Please type <span className="font-bold">{confirmationValue}</span> confirm.
   101                      </p>
   102                      <div className="w-1/3">
   103                          <TextInput value={typedConfirmation} onChange={(e) => setTypedConfirmation(e.target.value)} />
   104                      </div>
   105                  </>
   106              )}
   107              {body}
   108              <button
   109                  className={`btn btn-secondary ${danger && 'btn-danger'} mt-4`}
   110                  disabled={isDisabled}
   111                  onClick={() => {
   112                      (mutation as Mutation).mutate();
   113                      setTypedConfirmation('');
   114                  }}
   115              >
   116                  {mutation.isLoading ? loadingText : loadedText}
   117              </button>
   118          </div>
   119      );
   120  };
   121  
   122  export default ActionPanel;