github.com/wfusion/gofusion@v1.1.14/common/infra/asynq/asynqmon/ui/src/components/TasksTableContainer.tsx (about)

     1  import React, { useState } from "react";
     2  import { connect, ConnectedProps } from "react-redux";
     3  import { makeStyles } from "@material-ui/core/styles";
     4  import Typography from "@material-ui/core/Typography";
     5  import Paper from "@material-ui/core/Paper";
     6  import Chip from "@material-ui/core/Chip";
     7  import InputBase from "@material-ui/core/InputBase";
     8  import SearchIcon from "@material-ui/icons/Search";
     9  import ActiveTasksTable from "./ActiveTasksTable";
    10  import PendingTasksTable from "./PendingTasksTable";
    11  import ScheduledTasksTable from "./ScheduledTasksTable";
    12  import RetryTasksTable from "./RetryTasksTable";
    13  import ArchivedTasksTable from "./ArchivedTasksTable";
    14  import CompletedTasksTable from "./CompletedTasksTable";
    15  import AggregatingTasksTableContainer from "./AggregatingTasksTableContainer";
    16  import { useHistory } from "react-router-dom";
    17  import { queueDetailsPath, taskDetailsPath } from "../paths";
    18  import { QueueInfo } from "../reducers/queuesReducer";
    19  import { AppState } from "../store";
    20  import { isDarkTheme } from "../theme";
    21  
    22  interface TabPanelProps {
    23    children?: React.ReactNode;
    24    selected: string; // currently selected value
    25    value: string; // tab panel will be shown if selected value equals to the value
    26  }
    27  
    28  function TabPanel(props: TabPanelProps) {
    29    const { children, value, selected, ...other } = props;
    30  
    31    return (
    32      <div
    33        role="tabpanel"
    34        hidden={value !== selected}
    35        id={`scrollable-auto-tabpanel-${selected}`}
    36        aria-labelledby={`scrollable-auto-tab-${selected}`}
    37        style={{ flex: 1, overflowY: "scroll" }}
    38        {...other}
    39      >
    40        {value === selected && children}
    41      </div>
    42    );
    43  }
    44  
    45  function mapStatetoProps(state: AppState, ownProps: Props) {
    46    // TODO: Add loading state for each queue.
    47    const queueInfo = state.queues.data.find(
    48      (q: QueueInfo) => q.name === ownProps.queue
    49    );
    50    const currentStats = queueInfo
    51      ? queueInfo.currentStats
    52      : {
    53          queue: ownProps.queue,
    54          paused: false,
    55          size: 0,
    56          groups: 0,
    57          active: 0,
    58          pending: 0,
    59          aggregating: 0,
    60          scheduled: 0,
    61          retry: 0,
    62          archived: 0,
    63          completed: 0,
    64          processed: 0,
    65          failed: 0,
    66          timestamp: "n/a",
    67        };
    68    return { currentStats };
    69  }
    70  
    71  const connector = connect(mapStatetoProps);
    72  
    73  type ReduxProps = ConnectedProps<typeof connector>;
    74  
    75  interface Props {
    76    queue: string;
    77    selected: string;
    78  }
    79  
    80  const useStyles = makeStyles((theme) => ({
    81    container: {
    82      width: "100%",
    83      height: "100%",
    84      background: theme.palette.background.paper,
    85    },
    86    header: {
    87      display: "flex",
    88      alignItems: "center",
    89      paddingTop: theme.spacing(1),
    90    },
    91    heading: {
    92      paddingTop: theme.spacing(1),
    93      paddingBottom: theme.spacing(1),
    94      paddingLeft: theme.spacing(2),
    95      paddingRight: theme.spacing(2),
    96    },
    97    chip: {
    98      marginLeft: theme.spacing(1),
    99    },
   100    taskcount: {
   101      fontSize: "12px",
   102      color: theme.palette.text.secondary,
   103      background: isDarkTheme(theme)
   104        ? "#303030"
   105        : theme.palette.background.default,
   106      textAlign: "center",
   107      padding: "3px 6px",
   108      borderRadius: "10px",
   109      marginLeft: "2px",
   110    },
   111    searchbar: {
   112      paddingLeft: theme.spacing(1),
   113      paddingRight: theme.spacing(1),
   114      marginRight: theme.spacing(1),
   115      flex: 1,
   116    },
   117    search: {
   118      position: "relative",
   119      maxWidth: 400,
   120      borderRadius: "18px",
   121      backgroundColor: isDarkTheme(theme) ? "#303030" : theme.palette.grey[100],
   122      "&:hover, &:focus": {
   123        backgroundColor: isDarkTheme(theme) ? "#303030" : theme.palette.grey[200],
   124      },
   125    },
   126    searchIcon: {
   127      padding: theme.spacing(0, 2),
   128      height: "100%",
   129      position: "absolute",
   130      pointerEvents: "none",
   131      display: "flex",
   132      alignItems: "center",
   133      justifyContent: "center",
   134    },
   135    inputRoot: {
   136      color: "inherit",
   137      width: "100%",
   138    },
   139    inputInput: {
   140      padding: theme.spacing(1, 1, 1, 0),
   141      // vertical padding + font size from searchIcon
   142      paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
   143      width: "100%",
   144      fontSize: "0.85rem",
   145    },
   146  }));
   147  
   148  function TasksTableContainer(props: Props & ReduxProps) {
   149    const { currentStats } = props;
   150    const classes = useStyles();
   151    const history = useHistory();
   152    const chips = [
   153      { key: "active", label: "Active", count: currentStats.active },
   154      { key: "pending", label: "Pending", count: currentStats.pending },
   155      {
   156        key: "aggregating",
   157        label: "Aggregating",
   158        count: currentStats.aggregating,
   159      },
   160      { key: "scheduled", label: "Scheduled", count: currentStats.scheduled },
   161      { key: "retry", label: "Retry", count: currentStats.retry },
   162      { key: "archived", label: "Archived", count: currentStats.archived },
   163      { key: "completed", label: "Completed", count: currentStats.completed },
   164    ];
   165  
   166    const [searchQuery, setSearchQuery] = useState<string>("");
   167  
   168    return (
   169      <Paper variant="outlined" className={classes.container}>
   170        <div className={classes.header}>
   171          <Typography color="textPrimary" className={classes.heading}>
   172            Tasks
   173          </Typography>
   174          <div>
   175            {chips.map((c) => (
   176              <Chip
   177                key={c.key}
   178                className={classes.chip}
   179                label={
   180                  <div>
   181                    {c.label} <span className={classes.taskcount}>{c.count}</span>
   182                  </div>
   183                }
   184                variant="outlined"
   185                color={props.selected === c.key ? "primary" : "default"}
   186                onClick={() => history.push(queueDetailsPath(props.queue, c.key))}
   187              />
   188            ))}
   189          </div>
   190          <div className={classes.searchbar}>
   191            <div className={classes.search}>
   192              <div className={classes.searchIcon}>
   193                <SearchIcon />
   194              </div>
   195              <InputBase
   196                placeholder="Search by ID"
   197                classes={{
   198                  root: classes.inputRoot,
   199                  input: classes.inputInput,
   200                }}
   201                value={searchQuery}
   202                onChange={(e) => {
   203                  setSearchQuery(e.target.value);
   204                }}
   205                inputProps={{
   206                  "aria-label": "search",
   207                  onKeyDown: (e) => {
   208                    if (e.key === "Enter") {
   209                      history.push(
   210                        taskDetailsPath(props.queue, searchQuery.trim())
   211                      );
   212                    }
   213                  },
   214                }}
   215              />
   216            </div>
   217          </div>
   218        </div>
   219        <TabPanel value="active" selected={props.selected}>
   220          <ActiveTasksTable
   221            queue={props.queue}
   222            totalTaskCount={currentStats.active}
   223          />
   224        </TabPanel>
   225        <TabPanel value="pending" selected={props.selected}>
   226          <PendingTasksTable
   227            queue={props.queue}
   228            totalTaskCount={currentStats.pending}
   229          />
   230        </TabPanel>
   231        <TabPanel value="aggregating" selected={props.selected}>
   232          <AggregatingTasksTableContainer queue={props.queue} />
   233        </TabPanel>
   234        <TabPanel value="scheduled" selected={props.selected}>
   235          <ScheduledTasksTable
   236            queue={props.queue}
   237            totalTaskCount={currentStats.scheduled}
   238          />
   239        </TabPanel>
   240        <TabPanel value="retry" selected={props.selected}>
   241          <RetryTasksTable
   242            queue={props.queue}
   243            totalTaskCount={currentStats.retry}
   244          />
   245        </TabPanel>
   246        <TabPanel value="archived" selected={props.selected}>
   247          <ArchivedTasksTable
   248            queue={props.queue}
   249            totalTaskCount={currentStats.archived}
   250          />
   251        </TabPanel>
   252        <TabPanel value="completed" selected={props.selected}>
   253          <CompletedTasksTable
   254            queue={props.queue}
   255            totalTaskCount={currentStats.completed}
   256          />
   257        </TabPanel>
   258      </Paper>
   259    );
   260  }
   261  
   262  export default connector(TasksTableContainer);