github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/webui/src/pages/repositories/repository/actions/index.jsx (about)

     1  import React, {useEffect, useState} from "react";
     2  import { useOutletContext } from "react-router-dom";
     3  import {
     4      ActionGroup,
     5      ActionsBar,
     6      AlertError,
     7      FormattedDate,
     8      Loading, Na, RefreshButton,
     9      TooltipButton
    10  } from "../../../../lib/components/controls";
    11  import {useRefs} from "../../../../lib/hooks/repo";
    12  import {useAPIWithPagination} from "../../../../lib/hooks/api";
    13  import {actions} from "../../../../lib/api";
    14  import {
    15      FilterIcon,
    16      XIcon
    17  } from "@primer/octicons-react";
    18  import {Table} from "react-bootstrap";
    19  import {Paginator} from "../../../../lib/components/pagination";
    20  import {ActionStatusIcon} from "../../../../lib/components/repository/actions";
    21  import {Link} from "../../../../lib/components/nav";
    22  import {useRouter} from "../../../../lib/hooks/router";
    23  import Alert from "react-bootstrap/Alert";
    24  import {RepoError} from "../error";
    25  
    26  
    27  const RunRow = ({ repo, run, onFilterBranch, onFilterCommit }) => {
    28      return (
    29          <tr>
    30              <td>
    31                  <ActionStatusIcon className="me-2" status={run.status}/>
    32                  {' '}
    33                  <Link href={{
    34                      pathname: '/repositories/:repoId/actions/:runId',
    35                      params: {repoId: repo.id, runId: run.run_id}
    36                  }}>
    37                      {run.run_id}
    38                  </Link>
    39              </td>
    40              <td>{run.event_type}</td>
    41              <td>
    42                  <Link className="me-2" href={{
    43                      pathname: '/repositories/:repoId/objects',
    44                      params: {repoId: repo.id},
    45                      query: {ref: run.branch}
    46                  }}>
    47                      {run.branch}
    48                  </Link>
    49                  <TooltipButton
    50                      onClick={() => onFilterBranch(run.branch)}
    51                      variant="link"
    52                      tooltip="filter by branch"
    53                      className="row-hover"
    54                      size="sm">
    55                      <FilterIcon size="small"/>
    56                  </TooltipButton>
    57              </td>
    58              <td><FormattedDate dateValue={run.start_time}/></td>
    59              <td>
    60                  {(!run.end_time) ? <Na/> :<FormattedDate dateValue={run.end_time}/>}
    61              </td>
    62              <td>
    63                  {(!run.commit_id) ? <Na/> : (
    64                      <>
    65                          <Link className="me-2" href={{
    66                              pathname: '/repositories/:repoId/commits/:commitId',
    67                              params: {repoId: repo.id, commitId: run.commit_id}
    68                          }}>
    69                              <code>{run.commit_id.substr(0, 12)}</code>
    70                          </Link>
    71                          <TooltipButton
    72                              onClick={() => onFilterCommit(run.commit_id)}
    73                              variant="link"
    74                              tooltip="filter by commit ID"
    75                              className="row-hover"
    76                              size="sm">
    77                              <FilterIcon size="small"/>
    78                          </TooltipButton>
    79                      </>
    80                  )}
    81              </td>
    82          </tr>
    83      )
    84  }
    85  
    86  const RunTable = ({ repo, runs, nextPage, after, onPaginate, onFilterBranch, onFilterCommit }) => {
    87      return (
    88          <>
    89              <Table>
    90                  <thead>
    91                      <tr>
    92                          <th>Run ID</th>
    93                          <th>Event</th>
    94                          <th>Branch</th>
    95                          <th>Start Time</th>
    96                          <th>End Time</th>
    97                          <th>Commit ID</th>
    98                      </tr>
    99                  </thead>
   100                  <tbody>
   101                  {runs.map(run => <RunRow
   102                      key={run.run_id}
   103                      repo={repo}
   104                      run={run}
   105                      onFilterBranch={onFilterBranch}
   106                      onFilterCommit={onFilterCommit}/>)}
   107                  </tbody>
   108              </Table>
   109              <Paginator onPaginate={onPaginate} after={after} nextPage={nextPage}/>
   110          </>
   111      )
   112  }
   113  
   114  const ActionsList = ({ repo, after, onPaginate, branch, commit, onFilterBranch, onFilterCommit }) => {
   115  
   116      const [refresh, setRefresh] = useState(false)
   117      const {results, loading, error, nextPage} = useAPIWithPagination(async () => {
   118          return await actions.listRuns(repo.id, branch, commit, after)
   119      }, [repo.id, after, refresh, branch, commit])
   120  
   121      const doRefresh = () => setRefresh(!refresh)
   122  
   123      let content;
   124      if (error) content = <AlertError error={error}/>
   125  
   126      else if (loading) content = <Loading/>
   127      else if (results.length === 0 && !nextPage) content = <Alert variant="info" className={"mt-3"}>No action runs have been logged yet.</Alert>
   128      else content = (
   129              <RunTable
   130                  repo={repo}
   131                  runs={results}
   132                  nextPage={nextPage}
   133                  after={after}
   134                  onPaginate={onPaginate}
   135                  onFilterCommit={onFilterCommit}
   136                  onFilterBranch={onFilterBranch}
   137              />
   138      )
   139  
   140      let filters = [];
   141      if (branch) {
   142          filters = [<TooltipButton key="branch" variant="light" tooltip="remove branch filter" onClick={() => onFilterBranch("")}>
   143              <XIcon/> {branch}
   144          </TooltipButton>]
   145      }
   146      if (commit) {
   147          filters = [...filters, <TooltipButton key="commit" variant="light" tooltip="remove commit filter" onClick={() => onFilterCommit("")}>
   148              <XIcon/>  {commit.substr(0, 12)}
   149          </TooltipButton> ]
   150      }
   151  
   152      return (
   153          <div className="mb-5">
   154              <ActionsBar>
   155                  <ActionGroup orientation="left">
   156                      {filters}
   157                  </ActionGroup>
   158  
   159                  <ActionGroup orientation="right">
   160                      <RefreshButton onClick={doRefresh}/>
   161                  </ActionGroup>
   162              </ActionsBar>
   163              {content}
   164              <div>
   165                  {/* eslint-disable-next-line react/jsx-no-target-blank */}
   166                  Actions can be configured to run when predefined events occur. <a href="https://docs.lakefs.io/howto/hooks/" target="_blank">Learn more.</a>
   167              </div>
   168          </div>
   169      )
   170  }
   171  
   172  
   173  const ActionsContainer = () => {
   174      const router = useRouter();
   175      const { after } = router.query;
   176      const commit = (router.query.commit) ? router.query.commit : "";
   177      const branch = (router.query.branch) ? router.query.branch : "";
   178  
   179      const { repo, loading, error } = useRefs();
   180  
   181      if (loading) return <Loading/>;
   182      if (error) return <RepoError error={error}/>;
   183  
   184      const params = {repoId: repo.id};
   185  
   186      return (
   187          <ActionsList
   188              repo={repo}
   189              after={after}
   190              onPaginate={after => {
   191                  const query = {after};
   192                  if (commit) query.commit = commit;
   193                  if (branch) query.branch = branch;
   194                  router.push({pathname: `/repositories/:repoId/actions`, query, params})
   195              }}
   196              branch={branch}
   197              commit={commit}
   198              onFilterBranch={branch => {
   199                  const query = {}; // will reset pagination
   200                  if (branch) query.branch = branch;
   201                  router.push({pathname: `/repositories/:repoId/actions`, query, params})
   202              }}
   203              onFilterCommit={commit => {
   204                  const query = {} // will reset pagination
   205                  if (commit) query.commit = commit;
   206                  router.push({pathname: `/repositories/:repoId/actions`, query, params})
   207              }}
   208          />
   209      );
   210  };
   211  
   212  export const RepositoryActionsPage = () => {
   213    const [setActivePage] = useOutletContext();
   214    useEffect(() => setActivePage("actions"), [setActivePage]);
   215    return <ActionsContainer/>;
   216  };
   217  
   218  export default RepositoryActionsPage;