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

     1  import React, { useMemo, useEffect } from "react";
     2  import { connect, ConnectedProps } from "react-redux";
     3  import { useHistory } from "react-router-dom";
     4  import { makeStyles } from "@material-ui/core/styles";
     5  import Container from "@material-ui/core/Container";
     6  import Grid from "@material-ui/core/Grid";
     7  import Paper from "@material-ui/core/Paper";
     8  import Typography from "@material-ui/core/Typography";
     9  import Button from "@material-ui/core/Button";
    10  import Alert from "@material-ui/lab/Alert";
    11  import AlertTitle from "@material-ui/lab/AlertTitle";
    12  import ArrowBackIcon from "@material-ui/icons/ArrowBack";
    13  import { useParams } from "react-router-dom";
    14  import QueueBreadCrumb from "../components/QueueBreadcrumb";
    15  import { AppState } from "../store";
    16  import { getTaskInfoAsync } from "../actions/tasksActions";
    17  import { TaskDetailsRouteParams } from "../paths";
    18  import { usePolling } from "../hooks";
    19  import { listQueuesAsync } from "../actions/queuesActions";
    20  import SyntaxHighlighter from "../components/SyntaxHighlighter";
    21  import { durationFromSeconds, stringifyDuration, timeAgo, prettifyPayload } from "../utils";
    22  
    23  function mapStateToProps(state: AppState) {
    24    return {
    25      loading: state.tasks.taskInfo.loading,
    26      error: state.tasks.taskInfo.error,
    27      taskInfo: state.tasks.taskInfo.data,
    28      pollInterval: state.settings.pollInterval,
    29      queues: state.queues.data.map((q) => q.name), // FIXME: This data may not be available
    30    };
    31  }
    32  
    33  const connector = connect(mapStateToProps, {
    34    getTaskInfoAsync,
    35    listQueuesAsync,
    36  });
    37  
    38  const useStyles = makeStyles((theme) => ({
    39    container: {
    40      paddingTop: theme.spacing(2),
    41    },
    42    alert: {
    43      borderTopLeftRadius: 0,
    44      borderTopRightRadius: 0,
    45    },
    46    paper: {
    47      padding: theme.spacing(2),
    48      marginTop: theme.spacing(2),
    49    },
    50    breadcrumbs: {
    51      marginBottom: theme.spacing(2),
    52    },
    53    infoRow: {
    54      display: "flex",
    55      alignItems: "center",
    56      paddingTop: theme.spacing(1),
    57    },
    58    infoKeyCell: {
    59      width: "140px",
    60    },
    61    infoValueCell: {
    62      width: "auto",
    63    },
    64    footer: {
    65      paddingTop: theme.spacing(3),
    66      paddingBottom: theme.spacing(3),
    67    },
    68  }));
    69  
    70  type Props = ConnectedProps<typeof connector>;
    71  
    72  function TaskDetailsView(props: Props) {
    73    const classes = useStyles();
    74    const { qname, taskId } = useParams<TaskDetailsRouteParams>();
    75    const { getTaskInfoAsync, pollInterval, listQueuesAsync, taskInfo } = props;
    76    const history = useHistory();
    77  
    78    const fetchTaskInfo = useMemo(() => {
    79      return () => {
    80        getTaskInfoAsync(qname, taskId);
    81      };
    82    }, [qname, taskId, getTaskInfoAsync]);
    83  
    84    usePolling(fetchTaskInfo, pollInterval);
    85  
    86    // Fetch queues data to populate props.queues
    87    useEffect(() => {
    88      listQueuesAsync();
    89    }, [listQueuesAsync]);
    90  
    91    return (
    92      <Container maxWidth="lg" className={classes.container}>
    93        <Grid container spacing={0}>
    94          <Grid item xs={12} className={classes.breadcrumbs}>
    95            <QueueBreadCrumb
    96              queues={props.queues}
    97              queueName={qname}
    98              taskId={taskId}
    99            />
   100          </Grid>
   101          <Grid item xs={12} md={6}>
   102            {props.error ? (
   103              <Alert severity="error" className={classes.alert}>
   104                <AlertTitle>Error</AlertTitle>
   105                {props.error}
   106              </Alert>
   107            ) : (
   108              <Paper className={classes.paper} variant="outlined">
   109                <Typography variant="h6">Task Info</Typography>
   110                <div>
   111                  <div className={classes.infoRow}>
   112                    <Typography
   113                      variant="subtitle2"
   114                      className={classes.infoKeyCell}
   115                    >
   116                      ID:{" "}
   117                    </Typography>
   118                    <Typography className={classes.infoValueCell}>
   119                      {taskInfo?.id}
   120                    </Typography>
   121                  </div>
   122                  <div className={classes.infoRow}>
   123                    <Typography
   124                      variant="subtitle2"
   125                      className={classes.infoKeyCell}
   126                    >
   127                      Type:{" "}
   128                    </Typography>
   129                    <Typography className={classes.infoValueCell}>
   130                      {taskInfo?.type}
   131                    </Typography>
   132                  </div>
   133                  <div className={classes.infoRow}>
   134                    <Typography
   135                      variant="subtitle2"
   136                      className={classes.infoKeyCell}
   137                    >
   138                      State:{" "}
   139                    </Typography>
   140                    <Typography className={classes.infoValueCell}>
   141                      {taskInfo?.state}
   142                    </Typography>
   143                  </div>
   144                  <div className={classes.infoRow}>
   145                    <Typography
   146                      variant="subtitle2"
   147                      className={classes.infoKeyCell}
   148                    >
   149                      Queue:{" "}
   150                    </Typography>
   151                    <Typography className={classes.infoValueCell}>
   152                      {taskInfo?.queue}
   153                    </Typography>
   154                  </div>
   155                  <div className={classes.infoRow}>
   156                    <Typography
   157                      variant="subtitle2"
   158                      className={classes.infoKeyCell}
   159                    >
   160                      Retry:{" "}
   161                    </Typography>
   162                    <Typography className={classes.infoValueCell}>
   163                      {taskInfo?.retried}/{taskInfo?.max_retry}
   164                    </Typography>
   165                  </div>
   166                  <div className={classes.infoRow}>
   167                    <Typography
   168                      variant="subtitle2"
   169                      className={classes.infoKeyCell}
   170                    >
   171                      Last Failure:{" "}
   172                    </Typography>
   173                    <Typography className={classes.infoValueCell}>
   174                      {taskInfo?.last_failed_at ? (
   175                        <Typography>
   176                          {taskInfo?.error_message} ({taskInfo?.last_failed_at})
   177                        </Typography>
   178                      ) : (
   179                        <Typography> - </Typography>
   180                      )}
   181                    </Typography>
   182                  </div>
   183                  <div className={classes.infoRow}>
   184                    <Typography
   185                      variant="subtitle2"
   186                      className={classes.infoKeyCell}
   187                    >
   188                      Next Process Time:{" "}
   189                    </Typography>
   190                    {taskInfo?.next_process_at ? (
   191                      <Typography>{taskInfo?.next_process_at}</Typography>
   192                    ) : (
   193                      <Typography> - </Typography>
   194                    )}
   195                  </div>
   196                </div>
   197                <div className={classes.infoRow}>
   198                  <Typography variant="subtitle2" className={classes.infoKeyCell}>
   199                    Timeout:{" "}
   200                  </Typography>
   201                  <Typography className={classes.infoValueCell}>
   202                    {taskInfo?.timeout_seconds ? (
   203                      <Typography>{taskInfo?.timeout_seconds} seconds</Typography>
   204                    ) : (
   205                      <Typography> - </Typography>
   206                    )}
   207                  </Typography>
   208                </div>
   209                <div className={classes.infoRow}>
   210                  <Typography variant="subtitle2" className={classes.infoKeyCell}>
   211                    Deadline:{" "}
   212                  </Typography>
   213                  <Typography className={classes.infoValueCell}>
   214                    {taskInfo?.deadline ? (
   215                      <Typography>{taskInfo?.deadline}</Typography>
   216                    ) : (
   217                      <Typography> - </Typography>
   218                    )}
   219                  </Typography>
   220                </div>
   221                <div className={classes.infoRow}>
   222                  <Typography variant="subtitle2" className={classes.infoKeyCell}>
   223                    Payload:{" "}
   224                  </Typography>
   225                  <div className={classes.infoValueCell}>
   226                    {taskInfo?.payload && (
   227                      <SyntaxHighlighter
   228                        language="json"
   229                        customStyle={{ margin: 0, maxWidth: 400 }}
   230                      >
   231                        {prettifyPayload(taskInfo.payload)}
   232                      </SyntaxHighlighter>
   233                    )}
   234                  </div>
   235                </div>
   236                {
   237                  /* Completed Task Only */ taskInfo?.state === "completed" && (
   238                    <>
   239                      <div className={classes.infoRow}>
   240                        <Typography
   241                          variant="subtitle2"
   242                          className={classes.infoKeyCell}
   243                        >
   244                          Completed:{" "}
   245                        </Typography>
   246                        <div className={classes.infoValueCell}>
   247                          <Typography>
   248                            {timeAgo(taskInfo.completed_at)} (
   249                            {taskInfo.completed_at})
   250                          </Typography>
   251                        </div>
   252                      </div>
   253                      <div className={classes.infoRow}>
   254                        <Typography
   255                          variant="subtitle2"
   256                          className={classes.infoKeyCell}
   257                        >
   258                          Result:{" "}
   259                        </Typography>
   260                        <div className={classes.infoValueCell}>
   261                          <SyntaxHighlighter
   262                            language="json"
   263                            customStyle={{ margin: 0, maxWidth: 400 }}
   264                          >
   265                            {prettifyPayload(taskInfo.result)}
   266                          </SyntaxHighlighter>
   267                        </div>
   268                      </div>
   269                      <div className={classes.infoRow}>
   270                        <Typography
   271                          variant="subtitle2"
   272                          className={classes.infoKeyCell}
   273                        >
   274                          TTL:{" "}
   275                        </Typography>
   276                        <Typography className={classes.infoValueCell}>
   277                          <Typography>
   278                            {taskInfo.ttl_seconds > 0
   279                              ? `${stringifyDuration(
   280                                  durationFromSeconds(taskInfo.ttl_seconds)
   281                                )} left`
   282                              : "expired"}
   283                          </Typography>
   284                        </Typography>
   285                      </div>
   286                    </>
   287                  )
   288                }
   289              </Paper>
   290            )}
   291            <div className={classes.footer}>
   292              <Button
   293                startIcon={<ArrowBackIcon />}
   294                onClick={() => history.goBack()}
   295              >
   296                Go Back
   297              </Button>
   298            </div>
   299          </Grid>
   300        </Grid>
   301      </Container>
   302    );
   303  }
   304  
   305  export default connector(TaskDetailsView);