diff --git a/ui/src/components/ActiveTasksTable.tsx b/ui/src/components/ActiveTasksTable.tsx index 60108d4..51c91ac 100644 --- a/ui/src/components/ActiveTasksTable.tsx +++ b/ui/src/components/ActiveTasksTable.tsx @@ -1,22 +1,12 @@ import Checkbox from "@material-ui/core/Checkbox"; import IconButton from "@material-ui/core/IconButton"; -import Paper from "@material-ui/core/Paper"; -import { makeStyles } from "@material-ui/core/styles"; -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; -import TableContainer from "@material-ui/core/TableContainer"; -import TableFooter from "@material-ui/core/TableFooter"; -import TableHead from "@material-ui/core/TableHead"; -import TablePagination from "@material-ui/core/TablePagination"; import TableRow from "@material-ui/core/TableRow"; import Tooltip from "@material-ui/core/Tooltip"; import CancelIcon from "@material-ui/icons/Cancel"; import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined"; import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; -import Alert from "@material-ui/lab/Alert"; -import AlertTitle from "@material-ui/lab/AlertTitle"; -import React, { useCallback, useState } from "react"; +import React from "react"; import { connect, ConnectedProps } from "react-redux"; import { useHistory } from "react-router-dom"; import { taskRowsPerPageChange } from "../actions/settingsActions"; @@ -26,33 +16,12 @@ import { cancelAllActiveTasksAsync, listActiveTasksAsync, } from "../actions/tasksActions"; -import { usePolling } from "../hooks"; import { taskDetailsPath } from "../paths"; -import { ActiveTaskExtended } from "../reducers/tasksReducer"; import { AppState } from "../store"; import { TableColumn } from "../types/table"; import { durationBefore, prettifyPayload, timeAgo, uuidPrefix } from "../utils"; import SyntaxHighlighter from "./SyntaxHighlighter"; -import TableActions from "./TableActions"; -import TablePaginationActions, { - rowsPerPageOptions, -} from "./TablePaginationActions"; - -const useStyles = makeStyles((theme) => ({ - table: { - minWidth: 650, - }, - stickyHeaderCell: { - background: theme.palette.background.paper, - }, - alert: { - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - }, - pagination: { - border: "none", - }, -})); +import TasksTable, { RowProps, useRowStyles } from "./TasksTable"; function mapStateToProps(state: AppState) { return { @@ -67,10 +36,10 @@ function mapStateToProps(state: AppState) { } const mapDispatchToProps = { - listActiveTasksAsync, - cancelActiveTaskAsync, - batchCancelActiveTasksAsync, - cancelAllActiveTasksAsync, + listTasks: listActiveTasksAsync, + cancelTask: cancelActiveTaskAsync, + batchCancelTasks: batchCancelActiveTasksAsync, + cancelAllTasks: cancelAllActiveTasksAsync, taskRowsPerPageChange, }; @@ -93,223 +62,6 @@ interface Props { totalTaskCount: number; // total number of active tasks } -function ActiveTasksTable(props: Props & ReduxProps) { - const { pollInterval, listActiveTasksAsync, queue, pageSize } = props; - const classes = useStyles(); - const [page, setPage] = useState(0); - const [selectedIds, setSelectedIds] = useState([]); - const [activeTaskId, setActiveTaskId] = useState(""); - - const handlePageChange = ( - event: React.MouseEvent | null, - newPage: number - ) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = ( - event: React.ChangeEvent - ) => { - props.taskRowsPerPageChange(parseInt(event.target.value, 10)); - setPage(0); - }; - - const handleSelectAllClick = (event: React.ChangeEvent) => { - if (event.target.checked) { - const newSelected = props.tasks.map((t) => t.id); - setSelectedIds(newSelected); - } else { - setSelectedIds([]); - } - }; - - const handleCancelAllClick = () => { - props.cancelAllActiveTasksAsync(queue); - }; - - const handleBatchCancelClick = () => { - props - .batchCancelActiveTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const fetchData = useCallback(() => { - const pageOpts = { page: page + 1, size: pageSize }; - listActiveTasksAsync(queue, pageOpts); - }, [page, pageSize, queue, listActiveTasksAsync]); - - usePolling(fetchData, pollInterval); - - if (props.error.length > 0) { - return ( - - Error - {props.error} - - ); - } - - if (props.tasks.length === 0) { - return ( - - Info - No active tasks at this time. - - ); - } - - const rowCount = props.tasks.length; - const numSelected = selectedIds.length; - return ( -
- {!window.READ_ONLY && ( - 0} - iconButtonActions={[ - { - tooltip: "Cancel", - icon: , - onClick: handleBatchCancelClick, - disabled: props.batchActionPending, - }, - ]} - menuItemActions={[ - { - label: "Cancel All", - onClick: handleCancelAllClick, - disabled: props.allActionPending, - }, - ]} - /> - )} - - - - - {!window.READ_ONLY && ( - - - 0 && numSelected < rowCount} - checked={rowCount > 0 && numSelected === rowCount} - onChange={handleSelectAllClick} - inputProps={{ - "aria-label": "select all tasks shown in the table", - }} - /> - - - )} - {columns - .filter((col) => { - // Filter out actions column in readonly mode. - return !window.READ_ONLY || col.key !== "actions"; - }) - .map((col) => ( - - {col.label} - - ))} - - - - {/* TODO: loading and empty state */} - {props.tasks.map((task) => ( - { - if (checked) { - setSelectedIds(selectedIds.concat(task.id)); - } else { - setSelectedIds(selectedIds.filter((id) => id !== task.id)); - } - }} - onCancelClick={() => { - props.cancelActiveTaskAsync(queue, task.id); - }} - onActionCellEnter={() => setActiveTaskId(task.id)} - onActionCellLeave={() => setActiveTaskId("")} - showActions={activeTaskId === task.id} - /> - ))} - - - - - - -
-
-
- ); -} - -const useRowStyles = makeStyles((theme) => ({ - root: { - cursor: "pointer", - "& #copy-button": { - display: "none", - }, - "&:hover": { - boxShadow: theme.shadows[2], - }, - "&:hover $copyButton": { - display: "inline-block", - }, - "&:hover .MuiTableCell-root": { - borderBottomColor: theme.palette.background.paper, - }, - }, - idCell: { - width: "200px", - }, - copyButton: { - display: "none", - }, - IdGroup: { - display: "flex", - alignItems: "center", - }, -})); - -interface RowProps { - task: ActiveTaskExtended; - isSelected: boolean; - onSelectChange: (checked: boolean) => void; - onCancelClick: () => void; - showActions: boolean; - onActionCellEnter: () => void; - onActionCellLeave: () => void; -} - function Row(props: RowProps) { const { task } = props; const classes = useRowStyles(); @@ -408,4 +160,15 @@ function Row(props: RowProps) { ); } +function ActiveTasksTable(props: Props & ReduxProps) { + return ( + } + {...props} + /> + ); +} + export default connector(ActiveTasksTable); diff --git a/ui/src/components/ArchivedTasksTable.tsx b/ui/src/components/ArchivedTasksTable.tsx index dabf647..349ea48 100644 --- a/ui/src/components/ArchivedTasksTable.tsx +++ b/ui/src/components/ArchivedTasksTable.tsx @@ -1,23 +1,13 @@ import Checkbox from "@material-ui/core/Checkbox"; import IconButton from "@material-ui/core/IconButton"; -import Paper from "@material-ui/core/Paper"; -import { makeStyles } from "@material-ui/core/styles"; -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; -import TableContainer from "@material-ui/core/TableContainer"; -import TableFooter from "@material-ui/core/TableFooter"; -import TableHead from "@material-ui/core/TableHead"; -import TablePagination from "@material-ui/core/TablePagination"; import TableRow from "@material-ui/core/TableRow"; import Tooltip from "@material-ui/core/Tooltip"; import DeleteIcon from "@material-ui/icons/Delete"; import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined"; import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; import PlayArrowIcon from "@material-ui/icons/PlayArrow"; -import Alert from "@material-ui/lab/Alert"; -import AlertTitle from "@material-ui/lab/AlertTitle"; -import React, { useCallback, useState } from "react"; +import React from "react"; import { connect, ConnectedProps } from "react-redux"; import { useHistory } from "react-router-dom"; import { taskRowsPerPageChange } from "../actions/settingsActions"; @@ -30,36 +20,12 @@ import { runAllArchivedTasksAsync, runArchivedTaskAsync, } from "../actions/tasksActions"; -import { usePolling } from "../hooks"; import { taskDetailsPath } from "../paths"; -import { TaskInfoExtended } from "../reducers/tasksReducer"; import { AppState } from "../store"; import { TableColumn } from "../types/table"; import { prettifyPayload, timeAgo, uuidPrefix } from "../utils"; import SyntaxHighlighter from "./SyntaxHighlighter"; -import TableActions from "./TableActions"; -import TablePaginationActions, { - rowsPerPageOptions, -} from "./TablePaginationActions"; - -const useStyles = makeStyles((theme) => ({ - table: { - minWidth: 650, - }, - stickyHeaderCell: { - background: theme.palette.background.paper, - }, - alert: { - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - }, - pagination: { - border: "none", - }, - idCell: { - width: "200px", - }, -})); +import TasksTable, { RowProps, useRowStyles } from "./TasksTable"; function mapStateToProps(state: AppState) { return { @@ -74,13 +40,13 @@ function mapStateToProps(state: AppState) { } const mapDispatchToProps = { - listArchivedTasksAsync, - runArchivedTaskAsync, - runAllArchivedTasksAsync, - deleteArchivedTaskAsync, - deleteAllArchivedTasksAsync, - batchRunArchivedTasksAsync, - batchDeleteArchivedTasksAsync, + listTasks: listArchivedTasksAsync, + runTask: runArchivedTaskAsync, + runAllTasks: runAllArchivedTasksAsync, + deleteTask: deleteArchivedTaskAsync, + deleteAllTasks: deleteAllArchivedTasksAsync, + batchRunTasks: batchRunArchivedTasksAsync, + batchDeleteTasks: batchDeleteArchivedTasksAsync, taskRowsPerPageChange, }; @@ -93,263 +59,14 @@ interface Props { totalTaskCount: number; // totoal number of archived tasks. } -function ArchivedTasksTable(props: Props & ReduxProps) { - const { pollInterval, listArchivedTasksAsync, queue, pageSize } = props; - const classes = useStyles(); - const [page, setPage] = useState(0); - const [selectedIds, setSelectedIds] = useState([]); - const [activeTaskId, setActiveTaskId] = useState(""); - - const handlePageChange = ( - event: React.MouseEvent | null, - newPage: number - ) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = ( - event: React.ChangeEvent - ) => { - props.taskRowsPerPageChange(parseInt(event.target.value, 10)); - setPage(0); - }; - - const handleSelectAllClick = (event: React.ChangeEvent) => { - if (event.target.checked) { - const newSelected = props.tasks.map((t) => t.id); - setSelectedIds(newSelected); - } else { - setSelectedIds([]); - } - }; - - const handleRunAllClick = () => { - props.runAllArchivedTasksAsync(queue); - }; - - const handleDeleteAllClick = () => { - props.deleteAllArchivedTasksAsync(queue); - }; - - const handleBatchRunClick = () => { - props - .batchRunArchivedTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const handleBatchDeleteClick = () => { - props - .batchDeleteArchivedTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const fetchData = useCallback(() => { - const pageOpts = { page: page + 1, size: pageSize }; - listArchivedTasksAsync(queue, pageOpts); - }, [page, pageSize, queue, listArchivedTasksAsync]); - - usePolling(fetchData, pollInterval); - - if (props.error.length > 0) { - return ( - - Error - {props.error} - - ); - } - if (props.tasks.length === 0) { - return ( - - Info - No archived tasks at this time. - - ); - } - - const columns: TableColumn[] = [ - { key: "id", label: "ID", align: "left" }, - { key: "type", label: "Type", align: "left" }, - { key: "payload", label: "Payload", align: "left" }, - { key: "last_failed", label: "Last Failed", align: "left" }, - { key: "last_error", label: "Last Error", align: "left" }, - { key: "actions", label: "Actions", align: "center" }, - ]; - - const rowCount = props.tasks.length; - const numSelected = selectedIds.length; - return ( -
- {!window.READ_ONLY && ( - 0} - iconButtonActions={[ - { - tooltip: "Delete", - icon: , - onClick: handleBatchDeleteClick, - disabled: props.batchActionPending, - }, - { - tooltip: "Run", - icon: , - onClick: handleBatchRunClick, - disabled: props.batchActionPending, - }, - ]} - menuItemActions={[ - { - label: "Delete All", - onClick: handleDeleteAllClick, - disabled: props.allActionPending, - }, - { - label: "Run All", - onClick: handleRunAllClick, - disabled: props.allActionPending, - }, - ]} - /> - )} - - - - - {!window.READ_ONLY && ( - - - 0 && numSelected < rowCount} - checked={rowCount > 0 && numSelected === rowCount} - onChange={handleSelectAllClick} - inputProps={{ - "aria-label": "select all tasks shown in the table", - }} - /> - - - )} - {columns - .filter((col) => { - // Filter out actions column in readonly mode. - return !window.READ_ONLY || col.key !== "actions"; - }) - .map((col) => ( - - {col.label} - - ))} - - - - {props.tasks.map((task) => ( - { - if (checked) { - setSelectedIds(selectedIds.concat(task.id)); - } else { - setSelectedIds(selectedIds.filter((id) => id !== task.id)); - } - }} - onRunClick={() => { - props.runArchivedTaskAsync(queue, task.id); - }} - onDeleteClick={() => { - props.deleteArchivedTaskAsync(queue, task.id); - }} - allActionPending={props.allActionPending} - onActionCellEnter={() => setActiveTaskId(task.id)} - onActionCellLeave={() => setActiveTaskId("")} - showActions={activeTaskId === task.id} - /> - ))} - - - - - - -
-
-
- ); -} - -const useRowStyles = makeStyles((theme) => ({ - root: { - cursor: "pointer", - "& #copy-button": { - display: "none", - }, - "&:hover": { - boxShadow: theme.shadows[2], - }, - "&:hover $copyButton": { - display: "inline-block", - }, - "&:hover .MuiTableCell-root": { - borderBottomColor: theme.palette.background.paper, - }, - }, - actionCell: { - width: "96px", - }, - actionButton: { - marginLeft: 3, - marginRight: 3, - }, - idCell: { - width: "200px", - }, - copyButton: { - display: "none", - }, - IdGroup: { - display: "flex", - alignItems: "center", - }, -})); - -interface RowProps { - task: TaskInfoExtended; - isSelected: boolean; - onSelectChange: (checked: boolean) => void; - onRunClick: () => void; - onDeleteClick: () => void; - allActionPending: boolean; - showActions: boolean; - onActionCellEnter: () => void; - onActionCellLeave: () => void; -} +const columns: TableColumn[] = [ + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", align: "left" }, + { key: "payload", label: "Payload", align: "left" }, + { key: "last_failed", label: "Last Failed", align: "left" }, + { key: "last_error", label: "Last Error", align: "left" }, + { key: "actions", label: "Actions", align: "center" }, +]; function Row(props: RowProps) { const { task } = props; @@ -444,4 +161,15 @@ function Row(props: RowProps) { ); } +function ArchivedTasksTable(props: Props & ReduxProps) { + return ( + } + {...props} + /> + ); +} + export default connector(ArchivedTasksTable); diff --git a/ui/src/components/CompletedTasksTable.tsx b/ui/src/components/CompletedTasksTable.tsx index bc4dac1..0dab080 100644 --- a/ui/src/components/CompletedTasksTable.tsx +++ b/ui/src/components/CompletedTasksTable.tsx @@ -1,22 +1,12 @@ import Checkbox from "@material-ui/core/Checkbox"; import IconButton from "@material-ui/core/IconButton"; -import Paper from "@material-ui/core/Paper"; -import { makeStyles } from "@material-ui/core/styles"; -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; -import TableContainer from "@material-ui/core/TableContainer"; -import TableFooter from "@material-ui/core/TableFooter"; -import TableHead from "@material-ui/core/TableHead"; -import TablePagination from "@material-ui/core/TablePagination"; import TableRow from "@material-ui/core/TableRow"; import Tooltip from "@material-ui/core/Tooltip"; import DeleteIcon from "@material-ui/icons/Delete"; import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined"; import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; -import Alert from "@material-ui/lab/Alert"; -import AlertTitle from "@material-ui/lab/AlertTitle"; -import React, { useCallback, useState } from "react"; +import React from "react"; import { connect, ConnectedProps } from "react-redux"; import { useHistory } from "react-router-dom"; import { taskRowsPerPageChange } from "../actions/settingsActions"; @@ -26,9 +16,7 @@ import { deleteCompletedTaskAsync, listCompletedTasksAsync, } from "../actions/tasksActions"; -import { usePolling } from "../hooks"; import { taskDetailsPath } from "../paths"; -import { TaskInfoExtended } from "../reducers/tasksReducer"; import { AppState } from "../store"; import { TableColumn } from "../types/table"; import { @@ -39,26 +27,7 @@ import { uuidPrefix, } from "../utils"; import SyntaxHighlighter from "./SyntaxHighlighter"; -import TableActions from "./TableActions"; -import TablePaginationActions, { - rowsPerPageOptions, -} from "./TablePaginationActions"; - -const useStyles = makeStyles((theme) => ({ - table: { - minWidth: 650, - }, - stickyHeaderCell: { - background: theme.palette.background.paper, - }, - alert: { - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - }, - pagination: { - border: "none", - }, -})); +import TasksTable, { RowProps, useRowStyles } from "./TasksTable"; function mapStateToProps(state: AppState) { return { @@ -73,10 +42,10 @@ function mapStateToProps(state: AppState) { } const mapDispatchToProps = { - listCompletedTasksAsync, - deleteCompletedTaskAsync, - deleteAllCompletedTasksAsync, - batchDeleteCompletedTasksAsync, + listTasks: listCompletedTasksAsync, + deleteTask: deleteCompletedTaskAsync, + deleteAllTasks: deleteAllCompletedTasksAsync, + batchDeleteTasks: batchDeleteCompletedTasksAsync, taskRowsPerPageChange, }; @@ -89,242 +58,15 @@ interface Props { totalTaskCount: number; // totoal number of completed tasks. } -function CompletedTasksTable(props: Props & ReduxProps) { - const { pollInterval, listCompletedTasksAsync, queue, pageSize } = props; - const classes = useStyles(); - const [page, setPage] = useState(0); - const [selectedIds, setSelectedIds] = useState([]); - const [activeTaskId, setActiveTaskId] = useState(""); - - const handlePageChange = ( - event: React.MouseEvent | null, - newPage: number - ) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = ( - event: React.ChangeEvent - ) => { - props.taskRowsPerPageChange(parseInt(event.target.value, 10)); - setPage(0); - }; - - const handleSelectAllClick = (event: React.ChangeEvent) => { - if (event.target.checked) { - const newSelected = props.tasks.map((t) => t.id); - setSelectedIds(newSelected); - } else { - setSelectedIds([]); - } - }; - - const handleDeleteAllClick = () => { - props.deleteAllCompletedTasksAsync(queue); - }; - - const handleBatchDeleteClick = () => { - props - .batchDeleteCompletedTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const fetchData = useCallback(() => { - const pageOpts = { page: page + 1, size: pageSize }; - listCompletedTasksAsync(queue, pageOpts); - }, [page, pageSize, queue, listCompletedTasksAsync]); - - usePolling(fetchData, pollInterval); - - if (props.error.length > 0) { - return ( - - Error - {props.error} - - ); - } - if (props.tasks.length === 0) { - return ( - - Info - No completed tasks at this time. - - ); - } - - const columns: TableColumn[] = [ - { key: "id", label: "ID", align: "left" }, - { key: "type", label: "Type", align: "left" }, - { key: "payload", label: "Payload", align: "left" }, - { key: "completed_at", label: "Completed", align: "left" }, - { key: "result", label: "Result", align: "left" }, - { key: "ttl", label: "TTL", align: "left" }, - { key: "actions", label: "Actions", align: "center" }, - ]; - - const rowCount = props.tasks.length; - const numSelected = selectedIds.length; - return ( -
- {!window.READ_ONLY && ( - 0} - iconButtonActions={[ - { - tooltip: "Delete", - icon: , - onClick: handleBatchDeleteClick, - disabled: props.batchActionPending, - }, - ]} - menuItemActions={[ - { - label: "Delete All", - onClick: handleDeleteAllClick, - disabled: props.allActionPending, - }, - ]} - /> - )} - - - - - {!window.READ_ONLY && ( - - - 0 && numSelected < rowCount} - checked={rowCount > 0 && numSelected === rowCount} - onChange={handleSelectAllClick} - inputProps={{ - "aria-label": "select all tasks shown in the table", - }} - /> - - - )} - {columns - .filter((col) => { - // Filter out actions column in readonly mode. - return !window.READ_ONLY || col.key !== "actions"; - }) - .map((col) => ( - - {col.label} - - ))} - - - - {props.tasks.map((task) => ( - { - if (checked) { - setSelectedIds(selectedIds.concat(task.id)); - } else { - setSelectedIds(selectedIds.filter((id) => id !== task.id)); - } - }} - onDeleteClick={() => { - props.deleteCompletedTaskAsync(queue, task.id); - }} - allActionPending={props.allActionPending} - onActionCellEnter={() => setActiveTaskId(task.id)} - onActionCellLeave={() => setActiveTaskId("")} - showActions={activeTaskId === task.id} - /> - ))} - - - - - - -
-
-
- ); -} - -const useRowStyles = makeStyles((theme) => ({ - root: { - cursor: "pointer", - "& #copy-button": { - display: "none", - }, - "&:hover": { - boxShadow: theme.shadows[2], - "& #copy-button": { - display: "inline-block", - }, - }, - "&:hover $copyButton": { - display: "inline-block", - }, - "&:hover .MuiTableCell-root": { - borderBottomColor: theme.palette.background.paper, - }, - }, - actionCell: { - width: "96px", - }, - actionButton: { - marginLeft: 3, - marginRight: 3, - }, - idCell: { - width: "200px", - }, - copyButton: { - display: "none", - }, - IdGroup: { - display: "flex", - alignItems: "center", - }, -})); - -interface RowProps { - task: TaskInfoExtended; - isSelected: boolean; - onSelectChange: (checked: boolean) => void; - onDeleteClick: () => void; - allActionPending: boolean; - showActions: boolean; - onActionCellEnter: () => void; - onActionCellLeave: () => void; -} +const columns: TableColumn[] = [ + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", align: "left" }, + { key: "payload", label: "Payload", align: "left" }, + { key: "completed_at", label: "Completed", align: "left" }, + { key: "result", label: "Result", align: "left" }, + { key: "ttl", label: "TTL", align: "left" }, + { key: "actions", label: "Actions", align: "center" }, +]; function Row(props: RowProps) { const { task } = props; @@ -421,4 +163,14 @@ function Row(props: RowProps) { ); } +function CompletedTasksTable(props: Props & ReduxProps) { + return ( + } + {...props} + /> + ); +} export default connector(CompletedTasksTable); diff --git a/ui/src/components/PendingTasksTable.tsx b/ui/src/components/PendingTasksTable.tsx index c057e84..a6dc657 100644 --- a/ui/src/components/PendingTasksTable.tsx +++ b/ui/src/components/PendingTasksTable.tsx @@ -1,23 +1,13 @@ import Checkbox from "@material-ui/core/Checkbox"; import IconButton from "@material-ui/core/IconButton"; -import Paper from "@material-ui/core/Paper"; -import { makeStyles } from "@material-ui/core/styles"; -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; -import TableContainer from "@material-ui/core/TableContainer"; -import TableFooter from "@material-ui/core/TableFooter"; -import TableHead from "@material-ui/core/TableHead"; -import TablePagination from "@material-ui/core/TablePagination"; import TableRow from "@material-ui/core/TableRow"; import Tooltip from "@material-ui/core/Tooltip"; import ArchiveIcon from "@material-ui/icons/Archive"; import DeleteIcon from "@material-ui/icons/Delete"; import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined"; import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; -import Alert from "@material-ui/lab/Alert"; -import AlertTitle from "@material-ui/lab/AlertTitle"; -import React, { useCallback, useState } from "react"; +import React from "react"; import { connect, ConnectedProps } from "react-redux"; import { useHistory } from "react-router-dom"; import { taskRowsPerPageChange } from "../actions/settingsActions"; @@ -30,33 +20,12 @@ import { deletePendingTaskAsync, listPendingTasksAsync, } from "../actions/tasksActions"; -import { usePolling } from "../hooks"; import { taskDetailsPath } from "../paths"; -import { TaskInfoExtended } from "../reducers/tasksReducer"; import { AppState } from "../store"; import { TableColumn } from "../types/table"; import { prettifyPayload, uuidPrefix } from "../utils"; import SyntaxHighlighter from "./SyntaxHighlighter"; -import TableActions from "./TableActions"; -import TablePaginationActions, { - rowsPerPageOptions, -} from "./TablePaginationActions"; - -const useStyles = makeStyles((theme) => ({ - table: { - minWidth: 650, - }, - stickyHeaderCell: { - background: theme.palette.background.paper, - }, - alert: { - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - }, - pagination: { - border: "none", - }, -})); +import TasksTable, { RowProps, useRowStyles } from "./TasksTable"; function mapStateToProps(state: AppState) { return { @@ -71,13 +40,13 @@ function mapStateToProps(state: AppState) { } const mapDispatchToProps = { - listPendingTasksAsync, - deletePendingTaskAsync, - batchDeletePendingTasksAsync, - deleteAllPendingTasksAsync, - archivePendingTaskAsync, - batchArchivePendingTasksAsync, - archiveAllPendingTasksAsync, + listTasks: listPendingTasksAsync, + deleteTask: deletePendingTaskAsync, + batchDeleteTasks: batchDeletePendingTasksAsync, + deleteAllTasks: deleteAllPendingTasksAsync, + archiveTask: archivePendingTaskAsync, + batchArchiveTasks: batchArchivePendingTasksAsync, + archiveAllTasks: archiveAllPendingTasksAsync, taskRowsPerPageChange, }; @@ -90,268 +59,14 @@ interface Props { totalTaskCount: number; // total number of pending tasks } -function PendingTasksTable(props: Props & ReduxProps) { - const { pollInterval, listPendingTasksAsync, queue, pageSize } = props; - const classes = useStyles(); - const [page, setPage] = useState(0); - const [selectedIds, setSelectedIds] = useState([]); - const [activeTaskId, setActiveTaskId] = useState(""); - const handlePageChange = ( - event: React.MouseEvent | null, - newPage: number - ) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = ( - event: React.ChangeEvent - ) => { - props.taskRowsPerPageChange(parseInt(event.target.value, 10)); - setPage(0); - }; - - const handleSelectAllClick = (event: React.ChangeEvent) => { - if (event.target.checked) { - const newSelected = props.tasks.map((t) => t.id); - setSelectedIds(newSelected); - } else { - setSelectedIds([]); - } - }; - - const handleDeleteAllClick = () => { - props.deleteAllPendingTasksAsync(queue); - }; - - const handleArchiveAllClick = () => { - props.archiveAllPendingTasksAsync(queue); - }; - - const handleBatchDeleteClick = () => { - props - .batchDeletePendingTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const handleBatchArchiveClick = () => { - props - .batchArchivePendingTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const fetchData = useCallback(() => { - const pageOpts = { page: page + 1, size: pageSize }; - listPendingTasksAsync(queue, pageOpts); - }, [page, pageSize, queue, listPendingTasksAsync]); - - usePolling(fetchData, pollInterval); - - if (props.error.length > 0) { - return ( - - Error - {props.error} - - ); - } - if (props.tasks.length === 0) { - return ( - - Info - No pending tasks at this time. - - ); - } - - const columns: TableColumn[] = [ - { key: "id", label: "ID", align: "left" }, - { key: "type", label: "Type", align: "left" }, - { key: "paylod", label: "Payload", align: "left" }, - { key: "retried", label: "Retried", align: "right" }, - { key: "max_retry", label: "Max Retry", align: "right" }, - { key: "actions", label: "Actions", align: "center" }, - ]; - - const rowCount = props.tasks.length; - const numSelected = selectedIds.length; - return ( -
- {!window.READ_ONLY && ( - 0} - iconButtonActions={[ - { - tooltip: "Delete", - icon: , - onClick: handleBatchDeleteClick, - disabled: props.batchActionPending, - }, - { - tooltip: "Archive", - icon: , - onClick: handleBatchArchiveClick, - disabled: props.batchActionPending, - }, - ]} - menuItemActions={[ - { - label: "Delete All", - onClick: handleDeleteAllClick, - disabled: props.allActionPending, - }, - { - label: "Archive All", - onClick: handleArchiveAllClick, - disabled: props.allActionPending, - }, - ]} - /> - )} - - - - - {!window.READ_ONLY && ( - - - 0 && numSelected < rowCount} - checked={rowCount > 0 && numSelected === rowCount} - onChange={handleSelectAllClick} - inputProps={{ - "aria-label": "select all tasks shown in the table", - }} - /> - - - )} - {columns - .filter((col) => { - // Filter out actions column in readonly mode. - return !window.READ_ONLY || col.key !== "actions"; - }) - .map((col) => ( - - {col.label} - - ))} - - - - {props.tasks.map((task) => ( - { - if (checked) { - setSelectedIds(selectedIds.concat(task.id)); - } else { - setSelectedIds(selectedIds.filter((id) => id !== task.id)); - } - }} - allActionPending={props.allActionPending} - onDeleteClick={() => - props.deletePendingTaskAsync(queue, task.id) - } - onArchiveClick={() => { - props.archivePendingTaskAsync(queue, task.id); - }} - onActionCellEnter={() => setActiveTaskId(task.id)} - onActionCellLeave={() => setActiveTaskId("")} - showActions={activeTaskId === task.id} - /> - ))} - - - - - - -
-
-
- ); -} - -const useRowStyles = makeStyles((theme) => ({ - root: { - cursor: "pointer", - "& #copy-button": { - display: "none", - }, - "&:hover": { - boxShadow: theme.shadows[2], - "& #copy-button": { - display: "inline-block", - }, - }, - "&:hover $copyButton": { - display: "inline-block", - }, - "&:hover .MuiTableCell-root": { - borderBottomColor: theme.palette.background.paper, - }, - }, - - actionCell: { - width: "96px", - }, - actionButton: { - marginLeft: 3, - marginRight: 3, - }, - idCell: { - width: "200px", - }, - copyButton: { - display: "none", - }, - IdGroup: { - display: "flex", - alignItems: "center", - }, -})); - -interface RowProps { - task: TaskInfoExtended; - isSelected: boolean; - onSelectChange: (checked: boolean) => void; - onDeleteClick: () => void; - onArchiveClick: () => void; - allActionPending: boolean; - showActions: boolean; - onActionCellEnter: () => void; - onActionCellLeave: () => void; -} +const columns: TableColumn[] = [ + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", align: "left" }, + { key: "paylod", label: "Payload", align: "left" }, + { key: "retried", label: "Retried", align: "right" }, + { key: "max_retry", label: "Max Retry", align: "right" }, + { key: "actions", label: "Actions", align: "center" }, +]; function Row(props: RowProps) { const { task } = props; @@ -446,4 +161,15 @@ function Row(props: RowProps) { ); } +function PendingTasksTable(props: Props & ReduxProps) { + return ( + } + {...props} + /> + ); +} + export default connector(PendingTasksTable); diff --git a/ui/src/components/RetryTasksTable.tsx b/ui/src/components/RetryTasksTable.tsx index 5eeb21c..3b95c50 100644 --- a/ui/src/components/RetryTasksTable.tsx +++ b/ui/src/components/RetryTasksTable.tsx @@ -1,14 +1,6 @@ import Checkbox from "@material-ui/core/Checkbox"; import IconButton from "@material-ui/core/IconButton"; -import Paper from "@material-ui/core/Paper"; -import { makeStyles } from "@material-ui/core/styles"; -import Table from "@material-ui/core/Table"; -import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; -import TableContainer from "@material-ui/core/TableContainer"; -import TableFooter from "@material-ui/core/TableFooter"; -import TableHead from "@material-ui/core/TableHead"; -import TablePagination from "@material-ui/core/TablePagination"; import TableRow from "@material-ui/core/TableRow"; import Tooltip from "@material-ui/core/Tooltip"; import ArchiveIcon from "@material-ui/icons/Archive"; @@ -16,9 +8,7 @@ import DeleteIcon from "@material-ui/icons/Delete"; import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined"; import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; import PlayArrowIcon from "@material-ui/icons/PlayArrow"; -import Alert from "@material-ui/lab/Alert"; -import AlertTitle from "@material-ui/lab/AlertTitle"; -import React, { useCallback, useState } from "react"; +import React from "react"; import { connect, ConnectedProps } from "react-redux"; import { useHistory } from "react-router-dom"; import { taskRowsPerPageChange } from "../actions/settingsActions"; @@ -34,33 +24,12 @@ import { runAllRetryTasksAsync, runRetryTaskAsync, } from "../actions/tasksActions"; -import { usePolling } from "../hooks"; +import TasksTable, { RowProps, useRowStyles } from "./TasksTable"; import { taskDetailsPath } from "../paths"; -import { TaskInfoExtended } from "../reducers/tasksReducer"; import { AppState } from "../store"; import { TableColumn } from "../types/table"; import { durationBefore, prettifyPayload, uuidPrefix } from "../utils"; import SyntaxHighlighter from "./SyntaxHighlighter"; -import TableActions from "./TableActions"; -import TablePaginationActions, { - rowsPerPageOptions, -} from "./TablePaginationActions"; - -const useStyles = makeStyles((theme) => ({ - table: { - minWidth: 650, - }, - stickyHeaderCell: { - background: theme.palette.background.paper, - }, - alert: { - borderTopLeftRadius: 0, - borderTopRightRadius: 0, - }, - pagination: { - border: "none", - }, -})); function mapStateToProps(state: AppState) { return { @@ -75,16 +44,16 @@ function mapStateToProps(state: AppState) { } const mapDispatchToProps = { - batchDeleteRetryTasksAsync, - batchRunRetryTasksAsync, - batchArchiveRetryTasksAsync, - deleteAllRetryTasksAsync, - runAllRetryTasksAsync, - archiveAllRetryTasksAsync, - listRetryTasksAsync, - deleteRetryTaskAsync, - runRetryTaskAsync, - archiveRetryTaskAsync, + batchDeleteTasks: batchDeleteRetryTasksAsync, + batchRunTasks: batchRunRetryTasksAsync, + batchArchiveTasks: batchArchiveRetryTasksAsync, + deleteAllTasks: deleteAllRetryTasksAsync, + runAllTasks: runAllRetryTasksAsync, + archiveAllTasks: archiveAllRetryTasksAsync, + listTasks: listRetryTasksAsync, + deleteTask: deleteRetryTaskAsync, + runTask: runRetryTaskAsync, + archiveTask: archiveRetryTaskAsync, taskRowsPerPageChange, }; @@ -97,293 +66,16 @@ interface Props { totalTaskCount: number; // totoal number of scheduled tasks. } -function RetryTasksTable(props: Props & ReduxProps) { - const { pollInterval, listRetryTasksAsync, queue, pageSize } = props; - const classes = useStyles(); - const [page, setPage] = useState(0); - const [selectedIds, setSelectedIds] = useState([]); - const [activeTaskId, setActiveTaskId] = useState(""); - - const handlePageChange = ( - event: React.MouseEvent | null, - newPage: number - ) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = ( - event: React.ChangeEvent - ) => { - props.taskRowsPerPageChange(parseInt(event.target.value, 10)); - setPage(0); - }; - - const handleSelectAllClick = (event: React.ChangeEvent) => { - if (event.target.checked) { - const newSelected = props.tasks.map((t) => t.id); - setSelectedIds(newSelected); - } else { - setSelectedIds([]); - } - }; - - const handleRunAllClick = () => { - props.runAllRetryTasksAsync(queue); - }; - - const handleDeleteAllClick = () => { - props.deleteAllRetryTasksAsync(queue); - }; - - const handleArchiveAllClick = () => { - props.archiveAllRetryTasksAsync(queue); - }; - - const handleBatchRunClick = () => { - props - .batchRunRetryTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const handleBatchDeleteClick = () => { - props - .batchDeleteRetryTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const handleBatchArchiveClick = () => { - props - .batchArchiveRetryTasksAsync(queue, selectedIds) - .then(() => setSelectedIds([])); - }; - - const fetchData = useCallback(() => { - const pageOpts = { page: page + 1, size: pageSize }; - listRetryTasksAsync(queue, pageOpts); - }, [page, pageSize, queue, listRetryTasksAsync]); - - usePolling(fetchData, pollInterval); - - if (props.error.length > 0) { - return ( - - Error - {props.error} - - ); - } - if (props.tasks.length === 0) { - return ( - - Info - No retry tasks at this time. - - ); - } - - const columns: TableColumn[] = [ - { key: "id", label: "ID", align: "left" }, - { key: "type", label: "Type", align: "left" }, - { key: "payload", label: "Payload", align: "left" }, - { key: "retry_in", label: "Retry In", align: "left" }, - { key: "last_error", label: "Last Error", align: "left" }, - { key: "retried", label: "Retried", align: "right" }, - { key: "max_retry", label: "Max Retry", align: "right" }, - { key: "actions", label: "Actions", align: "center" }, - ]; - - const rowCount = props.tasks.length; - const numSelected = selectedIds.length; - return ( -
- {!window.READ_ONLY && ( - 0} - iconButtonActions={[ - { - tooltip: "Delete", - icon: , - onClick: handleBatchDeleteClick, - disabled: props.batchActionPending, - }, - { - tooltip: "Archive", - icon: , - onClick: handleBatchArchiveClick, - disabled: props.batchActionPending, - }, - { - tooltip: "Run", - icon: , - onClick: handleBatchRunClick, - disabled: props.batchActionPending, - }, - ]} - menuItemActions={[ - { - label: "Delete All", - onClick: handleDeleteAllClick, - disabled: props.allActionPending, - }, - { - label: "Archive All", - onClick: handleArchiveAllClick, - disabled: props.allActionPending, - }, - { - label: "Run All", - onClick: handleRunAllClick, - disabled: props.allActionPending, - }, - ]} - /> - )} - - - - - {!window.READ_ONLY && ( - - - 0 && numSelected < rowCount} - checked={rowCount > 0 && numSelected === rowCount} - onChange={handleSelectAllClick} - inputProps={{ - "aria-label": "select all tasks shown in the table", - }} - /> - - - )} - {columns - .filter((col) => { - // Filter out actions column in readonly mode. - return !window.READ_ONLY || col.key !== "actions"; - }) - .map((col) => ( - - {col.label} - - ))} - - - - {props.tasks.map((task) => ( - { - if (checked) { - setSelectedIds(selectedIds.concat(task.id)); - } else { - setSelectedIds(selectedIds.filter((id) => id !== task.id)); - } - }} - onRunClick={() => { - props.runRetryTaskAsync(task.queue, task.id); - }} - onDeleteClick={() => { - props.deleteRetryTaskAsync(task.queue, task.id); - }} - onArchiveClick={() => { - props.archiveRetryTaskAsync(task.queue, task.id); - }} - onActionCellEnter={() => setActiveTaskId(task.id)} - onActionCellLeave={() => setActiveTaskId("")} - showActions={activeTaskId === task.id} - /> - ))} - - - - - - -
-
-
- ); -} - -const useRowStyles = makeStyles((theme) => ({ - root: { - cursor: "pointer", - "& #copy-button": { - display: "none", - }, - "&:hover": { - boxShadow: theme.shadows[2], - "& #copy-button": { - display: "inline-block", - }, - }, - "&:hover $copyButton": { - display: "inline-block", - }, - "&:hover .MuiTableCell-root": { - borderBottomColor: theme.palette.background.paper, - }, - }, - actionCell: { - width: "140px", - }, - actionButton: { - marginLeft: 3, - marginRight: 3, - }, - idCell: { - width: "200px", - }, - copyButton: { - display: "none", - }, - IdGroup: { - display: "flex", - alignItems: "center", - }, -})); - -interface RowProps { - task: TaskInfoExtended; - isSelected: boolean; - onSelectChange: (checked: boolean) => void; - onDeleteClick: () => void; - onRunClick: () => void; - onArchiveClick: () => void; - allActionPending: boolean; - showActions: boolean; - onActionCellEnter: () => void; - onActionCellLeave: () => void; -} +const columns: TableColumn[] = [ + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", align: "left" }, + { key: "payload", label: "Payload", align: "left" }, + { key: "retry_in", label: "Retry In", align: "left" }, + { key: "last_error", label: "Last Error", align: "left" }, + { key: "retried", label: "Retried", align: "right" }, + { key: "max_retry", label: "Max Retry", align: "right" }, + { key: "actions", label: "Actions", align: "center" }, +]; function Row(props: RowProps) { const { task } = props; @@ -491,4 +183,15 @@ function Row(props: RowProps) { ); } +function RetryTasksTable(props: Props & ReduxProps) { + return ( + } + {...props} + /> + ); +} + export default connector(RetryTasksTable); diff --git a/ui/src/components/ScheduledTasksTable.tsx b/ui/src/components/ScheduledTasksTable.tsx index a77f003..58d3a83 100644 --- a/ui/src/components/ScheduledTasksTable.tsx +++ b/ui/src/components/ScheduledTasksTable.tsx @@ -74,17 +74,6 @@ const columns: TableColumn[] = [ { key: "actions", label: "Actions", align: "center" }, ]; -function ScheduledTasksTable(props: Props & ReduxProps) { - return ( - } - {...props} - /> - ); -} - function Row(props: RowProps) { const { task } = props; const classes = useRowStyles(); @@ -187,4 +176,15 @@ function Row(props: RowProps) { ); } +function ScheduledTasksTable(props: Props & ReduxProps) { + return ( + } + {...props} + /> + ); +} + export default connector(ScheduledTasksTable); diff --git a/ui/src/components/TasksTable.tsx b/ui/src/components/TasksTable.tsx index 303828c..02ca62f 100644 --- a/ui/src/components/TasksTable.tsx +++ b/ui/src/components/TasksTable.tsx @@ -14,6 +14,7 @@ import IconButton from "@material-ui/core/IconButton"; import PlayArrowIcon from "@material-ui/icons/PlayArrow"; import DeleteIcon from "@material-ui/icons/Delete"; import ArchiveIcon from "@material-ui/icons/Archive"; +import CancelIcon from "@material-ui/icons/Cancel"; import Alert from "@material-ui/lab/Alert"; import AlertTitle from "@material-ui/lab/AlertTitle"; import TablePaginationActions, { @@ -60,12 +61,15 @@ interface Props { batchDeleteTasks?: (qname: string, taskIds: string[]) => Promise; batchRunTasks?: (qname: string, taskIds: string[]) => Promise; batchArchiveTasks?: (qname: string, taskIds: string[]) => Promise; + batchCancelTasks?: (qname: string, taskIds: string[]) => Promise; deleteAllTasks?: (qname: string) => Promise; runAllTasks?: (qname: string) => Promise; archiveAllTasks?: (qname: string) => Promise; + cancelAllTasks?: (qname: string) => Promise; deleteTask?: (qname: string, taskId: string) => Promise; runTask?: (qname: string, taskId: string) => Promise; archiveTask?: (qname: string, taskId: string) => Promise; + cancelTask?: (qname: string, taskId: string) => Promise; taskRowsPerPageChange: (n: number) => void; renderRow: (rowProps: RowProps) => JSX.Element; @@ -140,6 +144,13 @@ export default function TasksTable(props: Props) { disabled: props.allActionPending, }); } + if (props.cancelAllTasks) { + allActions.push({ + label: "Cancel All", + onClick: createAllTasksHandler(props.cancelAllTasks), + disabled: props.allActionPending, + }); + } let batchActions = []; if (props.batchDeleteTasks) { @@ -166,6 +177,14 @@ export default function TasksTable(props: Props) { onClick: createBatchTasksHandler(props.batchRunTasks), }); } + if (props.batchCancelTasks) { + batchActions.push({ + tooltip: "Cancel", + icon: , + disabled: props.batchActionPending, + onClick: createBatchTasksHandler(props.batchCancelTasks), + }); + } const fetchData = useCallback(() => { const pageOpts = { page: page + 1, size: pageSize }; @@ -267,6 +286,9 @@ export default function TasksTable(props: Props) { onArchiveClick: props.archiveTask ? createTaskAction(props.archiveTask, task.id) : undefined, + onCancelClick: props.cancelTask + ? createTaskAction(props.cancelTask, task.id) + : undefined, onActionCellEnter: () => setActiveTaskId(task.id), onActionCellLeave: () => setActiveTaskId(""), showActions: activeTaskId === task.id, @@ -318,7 +340,7 @@ export const useRowStyles = makeStyles((theme) => ({ }, }, actionCell: { - width: "140px", + width: "140px", // TODO: This was 96px for pending/archived/completed row }, actionButton: { marginLeft: 3, @@ -344,6 +366,7 @@ export interface RowProps { onRunClick?: () => void; onDeleteClick?: () => void; onArchiveClick?: () => void; + onCancelClick?: () => void; allActionPending: boolean; showActions: boolean; onActionCellEnter: () => void; diff --git a/ui/src/reducers/tasksReducer.ts b/ui/src/reducers/tasksReducer.ts index 801ad55..07ed131 100644 --- a/ui/src/reducers/tasksReducer.ts +++ b/ui/src/reducers/tasksReducer.ts @@ -162,20 +162,16 @@ import { } from "../actions/tasksActions"; import { TaskInfo } from "../api"; -export interface ActiveTaskExtended extends TaskInfo { +export interface TaskInfoExtended extends TaskInfo { // Indicates that a request has been sent for this // task and awaiting for a response. requestPending: boolean; // Incidates that a cancelation signal has been // published for this task. - canceling: boolean; -} - -export interface TaskInfoExtended extends TaskInfo { - // Indicates that a request has been sent for this - // task and awaiting for a response. - requestPending: boolean; + // + // Only applies to active tasks + canceling?: boolean; } interface TasksState { @@ -184,7 +180,7 @@ interface TasksState { batchActionPending: boolean; allActionPending: boolean; error: string; - data: ActiveTaskExtended[]; + data: TaskInfoExtended[]; }; pendingTasks: { loading: boolean;