github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/webui/src/lib/components/auth/forms.jsx (about)

     1  import React, {useEffect, useRef, useState} from "react";
     2  import Modal from "react-bootstrap/Modal";
     3  import Badge from "react-bootstrap/Badge";
     4  import Form from "react-bootstrap/Form";
     5  import Button from "react-bootstrap/Button";
     6  import {FormControl, InputGroup} from "react-bootstrap";
     7  import {SearchIcon} from "@primer/octicons-react";
     8  
     9  import {useAPI} from "../../hooks/api";
    10  import {Checkbox, DataTable, DebouncedFormControl, AlertError, Loading} from "../controls";
    11  
    12  const resolveEntityDisplayName = (ent) => {
    13      // for users
    14      if (ent?.email?.length) return ent.email;
    15      // for groups
    16      if (ent?.name?.length) return ent.name;
    17      return ent.id;
    18  }
    19  
    20  export const AttachModal = ({ show, searchFn, onAttach, onHide, addText = "Add",
    21                            emptyState = 'No matches', modalTitle = 'Add', headers = ['Select', 'ID'],
    22                       filterPlaceholder = 'Filter...'}) => {
    23      const search = useRef(null);
    24      const [searchPrefix, setSearchPrefix] = useState("");
    25      const [selected, setSelected] = useState([]);
    26  
    27      useEffect(() => {
    28          if (!!search.current && search.current.value === "")
    29              search.current.focus();
    30      });
    31  
    32      const { response, error, loading } = useAPI(() => {
    33          return searchFn(searchPrefix);
    34      }, [searchPrefix]);
    35  
    36      let content;
    37      if (loading) content = <Loading/>;
    38      else if (error) content = <AlertError error={error}/>;
    39      else content = (
    40              <>
    41                  <DataTable
    42                      headers={headers}
    43                      keyFn={ent => ent.id}
    44                      emptyState={emptyState}
    45                      results={response}
    46                      rowFn={ent => [
    47                          <Checkbox
    48                              defaultChecked={selected.indexOf(ent.id) >= 0}
    49                              onAdd={() => setSelected([...selected, ent])}
    50                              onRemove={() => setSelected(selected.filter(selectedEnt => selectedEnt.id !== ent.id))}
    51                              name={'selected'}/>,
    52                          <strong>{resolveEntityDisplayName(ent)}</strong>
    53                      ]}/>
    54  
    55                  <div className="mt-3">
    56                      {(selected.length > 0) &&
    57                      <p>
    58                          <strong>Selected: </strong>
    59                          {(selected.map(item => (
    60                              <Badge key={item.id} pill variant="primary" className="me-1">
    61                                  {resolveEntityDisplayName(item)}
    62                              </Badge>
    63                          )))}
    64                      </p>
    65                      }
    66                  </div>
    67              </>
    68          );
    69  
    70      return (
    71          <Modal show={show} onHide={onHide}>
    72              <Modal.Header closeButton>
    73                  <Modal.Title>{modalTitle}</Modal.Title>
    74              </Modal.Header>
    75              <Modal.Body>
    76                  <Form onSubmit={e => { e.preventDefault() }}>
    77                      <InputGroup>
    78                          <InputGroup.Text>
    79                              <SearchIcon/>
    80                          </InputGroup.Text>
    81                          <DebouncedFormControl
    82                              ref={search}
    83                              placeholder={filterPlaceholder}
    84                              onChange={() => {setSearchPrefix(search.current.value)}}/>
    85                      </InputGroup>
    86                  </Form>
    87                  <div className="mt-2">
    88                      {content}
    89                  </div>
    90              </Modal.Body>
    91              <Modal.Footer>
    92                  <Button variant="success" disabled={selected.length === 0} onClick={() => {onAttach(selected)}}>
    93                      {addText}
    94                  </Button>
    95                  <Button variant="secondary" onClick={onHide}>Cancel</Button>
    96              </Modal.Footer>
    97          </Modal>
    98      );
    99  };
   100  
   101  export const EntityActionModal = ({ show, onHide, onAction, title, placeholder, actionName, validationFunction = null }) => {
   102      const [error, setError] = useState(null);
   103      const idField = useRef(null);
   104  
   105      useEffect(() => {
   106          if (!!idField.current && idField.current.value === "")
   107              idField.current.focus();
   108      });
   109  
   110      const onSubmit = () => {
   111          if (validationFunction) {
   112              const validationResult = validationFunction(idField.current.value);
   113              if (!validationResult.isValid) {
   114                  setError(validationResult.errorMessage);
   115                  return;
   116              }
   117          }
   118          onAction(idField.current.value).catch(err => setError(err));
   119      };
   120  
   121      return (
   122          <Modal show={show} onHide={onHide}>
   123              <Modal.Header closeButton>
   124                  <Modal.Title>{title}</Modal.Title>
   125              </Modal.Header>
   126  
   127              <Modal.Body>
   128                  <Form onSubmit={e => {
   129                      e.preventDefault()
   130                      onSubmit()
   131                  }}>
   132                      <FormControl ref={idField} autoFocus placeholder={placeholder} type="text"/>
   133                  </Form>
   134  
   135                  {(!!error) && <AlertError className="mt-3" error={error}/>}
   136  
   137              </Modal.Body>
   138  
   139              <Modal.Footer>
   140                  <Button onClick={onSubmit} variant="success">{actionName}</Button>
   141                  <Button onClick={onHide} variant="secondary">Cancel</Button>
   142              </Modal.Footer>
   143          </Modal>
   144      );
   145  };