Extract TableActions component

This commit is contained in:
Ken Hibino 2020-12-18 12:48:05 -08:00
parent 95bb6051d0
commit 68738ec962
5 changed files with 281 additions and 201 deletions

View File

@ -5,7 +5,6 @@ import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import Button from "@material-ui/core/Button";
import ButtonGroup from "@material-ui/core/ButtonGroup";
import Checkbox from "@material-ui/core/Checkbox";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
@ -14,14 +13,11 @@ import Paper from "@material-ui/core/Paper";
import Box from "@material-ui/core/Box";
import Collapse from "@material-ui/core/Collapse";
import IconButton from "@material-ui/core/IconButton";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp";
import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
import Typography from "@material-ui/core/Typography";
import TableFooter from "@material-ui/core/TableFooter";
import TablePagination from "@material-ui/core/TablePagination";
import MoreHorizIcon from "@material-ui/icons/MoreHoriz";
import Alert from "@material-ui/lab/Alert";
import AlertTitle from "@material-ui/lab/AlertTitle";
import SyntaxHighlighter from "react-syntax-highlighter";
@ -40,6 +36,7 @@ import TablePaginationActions, {
defaultPageSize,
rowsPerPageOptions,
} from "./TablePaginationActions";
import TableActions from "./TableActions";
import { timeAgo } from "../timeutil";
import { usePolling } from "../hooks";
import { DeadTaskExtended } from "../reducers/tasksReducer";
@ -48,12 +45,6 @@ const useStyles = makeStyles({
table: {
minWidth: 650,
},
actionsContainer: {
padding: "4px",
},
moreIcon: {
marginRight: "8px",
},
});
const useRowStyles = makeStyles({
@ -99,7 +90,6 @@ function DeadTasksTable(props: Props & ReduxProps) {
const [page, setPage] = useState(0);
const [pageSize, setPageSize] = useState(defaultPageSize);
const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
const [menuAnchor, setMenuAnchor] = useState<null | HTMLElement>(null);
const handleChangePage = (
event: React.MouseEvent<HTMLButtonElement> | null,
@ -124,20 +114,24 @@ function DeadTasksTable(props: Props & ReduxProps) {
}
};
const handleMenuClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setMenuAnchor(event.currentTarget);
};
const handleMenuClose = () => setMenuAnchor(null);
const handleRunAllClick = () => {
props.runAllDeadTasksAsync(queue);
setMenuAnchor(null);
};
const handleDeleteAllClick = () => {
props.deleteAllDeadTasksAsync(queue);
setMenuAnchor(null);
};
const handleBatchRunClick = () => {
props
.batchDeleteDeadTasksAsync(queue, selectedKeys)
.then(() => setSelectedKeys([]));
};
const handleBatchDeleteClick = () => {
props
.batchRunDeadTasksAsync(queue, selectedKeys)
.then(() => setSelectedKeys([]));
};
const fetchData = useCallback(() => {
@ -169,64 +163,15 @@ function DeadTasksTable(props: Props & ReduxProps) {
const numSelected = selectedKeys.length;
return (
<div>
<div className={classes.actionsContainer}>
<IconButton
aria-label="actions"
className={classes.moreIcon}
onClick={handleMenuClick}
>
<MoreHorizIcon />
</IconButton>
<Menu
id="action-menu"
keepMounted
anchorEl={menuAnchor}
open={Boolean(menuAnchor)}
onClose={handleMenuClose}
>
<MenuItem
onClick={handleRunAllClick}
disabled={props.allActionPending}
>
Run All
</MenuItem>
<MenuItem
onClick={handleDeleteAllClick}
disabled={props.allActionPending}
>
Delete All
</MenuItem>
</Menu>
{numSelected > 0 && (
<ButtonGroup
variant="text"
color="primary"
aria-label="text primary button group"
>
<Button
disabled={props.batchActionPending}
onClick={() =>
props
.batchRunDeadTasksAsync(queue, selectedKeys)
.then(() => setSelectedKeys([]))
}
>
Run
</Button>
<Button>Kill</Button>
<Button
disabled={props.batchActionPending}
onClick={() =>
props
.batchDeleteDeadTasksAsync(queue, selectedKeys)
.then(() => setSelectedKeys([]))
}
>
Delete
</Button>
</ButtonGroup>
)}
</div>
<TableActions
allActionPending={props.allActionPending}
batchActionPending={props.batchActionPending}
showBatchActions={numSelected > 0}
onRunAllClick={handleRunAllClick}
onDeleteAllClick={handleDeleteAllClick}
onBatchRunClick={handleBatchRunClick}
onBatchDeleteClick={handleBatchDeleteClick}
/>
<TableContainer component={Paper}>
<Table
stickyHeader={true}

View File

@ -31,6 +31,7 @@ import TablePaginationActions, {
defaultPageSize,
rowsPerPageOptions,
} from "./TablePaginationActions";
import TableActions from "./TableActions";
import { durationBefore } from "../timeutil";
import { usePolling } from "../hooks";
import { RetryTaskExtended } from "../reducers/tasksReducer";
@ -45,6 +46,8 @@ function mapStateToProps(state: AppState) {
return {
loading: state.tasks.retryTasks.loading,
tasks: state.tasks.retryTasks.data,
batchActionPending: state.tasks.retryTasks.batchActionPending,
allActionPending: state.tasks.retryTasks.allActionPending,
pollInterval: state.settings.pollInterval,
};
}
@ -120,71 +123,82 @@ function RetryTasksTable(props: Props & ReduxProps) {
const rowCount = props.tasks.length;
const numSelected = selectedKeys.length;
return (
<TableContainer component={Paper}>
<Table
stickyHeader={true}
className={classes.table}
aria-label="retry tasks table"
size="small"
>
<TableHead>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={handleSelectAllClick}
inputProps={{
"aria-label": "select all tasks shown in the table",
<div>
<TableActions
allActionPending={props.allActionPending}
batchActionPending={props.batchActionPending}
showBatchActions={numSelected > 0}
onRunAllClick={() => console.log("TODO")}
onDeleteAllClick={() => console.log("TODO")}
onBatchRunClick={() => console.log("TODO")}
onBatchDeleteClick={() => console.log("TODO")}
/>
<TableContainer component={Paper}>
<Table
stickyHeader={true}
className={classes.table}
aria-label="retry tasks table"
size="small"
>
<TableHead>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={handleSelectAllClick}
inputProps={{
"aria-label": "select all tasks shown in the table",
}}
/>
</TableCell>
{columns.map((col) => (
<TableCell key={col.label}>{col.label}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{props.tasks.map((task) => (
<Row
key={task.id}
task={task}
isSelected={selectedKeys.includes(task.key)}
onSelectChange={(checked: boolean) => {
if (checked) {
setSelectedKeys(selectedKeys.concat(task.key));
} else {
setSelectedKeys(
selectedKeys.filter((key) => key !== task.key)
);
}
}}
onDeleteClick={() => {
props.deleteRetryTaskAsync(task.queue, task.key);
}}
/>
</TableCell>
{columns.map((col) => (
<TableCell key={col.label}>{col.label}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{props.tasks.map((task) => (
<Row
key={task.id}
task={task}
isSelected={selectedKeys.includes(task.key)}
onSelectChange={(checked: boolean) => {
if (checked) {
setSelectedKeys(selectedKeys.concat(task.key));
} else {
setSelectedKeys(
selectedKeys.filter((key) => key !== task.key)
);
}
}}
onDeleteClick={() => {
props.deleteRetryTaskAsync(task.queue, task.key);
}}
/>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={rowsPerPageOptions}
colSpan={columns.length + 1}
count={props.totalTaskCount}
rowsPerPage={pageSize}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={rowsPerPageOptions}
colSpan={columns.length + 1}
count={props.totalTaskCount}
rowsPerPage={pageSize}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
</div>
);
}

View File

@ -31,6 +31,7 @@ import TablePaginationActions, {
defaultPageSize,
rowsPerPageOptions,
} from "./TablePaginationActions";
import TableActions from "./TableActions";
import { durationBefore } from "../timeutil";
import { usePolling } from "../hooks";
import { ScheduledTaskExtended } from "../reducers/tasksReducer";
@ -45,6 +46,8 @@ function mapStateToProps(state: AppState) {
return {
loading: state.tasks.scheduledTasks.loading,
tasks: state.tasks.scheduledTasks.data,
batchActionPending: state.tasks.scheduledTasks.batchActionPending,
allActionPending: state.tasks.scheduledTasks.allActionPending,
pollInterval: state.settings.pollInterval,
};
}
@ -120,71 +123,82 @@ function ScheduledTasksTable(props: Props & ReduxProps) {
const rowCount = props.tasks.length;
const numSelected = selectedKeys.length;
return (
<TableContainer component={Paper}>
<Table
stickyHeader={true}
className={classes.table}
aria-label="scheduled tasks table"
size="small"
>
<TableHead>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={handleSelectAllClick}
inputProps={{
"aria-label": "select all tasks shown in the table",
<div>
<TableActions
allActionPending={props.allActionPending}
batchActionPending={props.batchActionPending}
showBatchActions={numSelected > 0}
onRunAllClick={() => console.log("TODO")}
onDeleteAllClick={() => console.log("TODO")}
onBatchRunClick={() => console.log("TODO")}
onBatchDeleteClick={() => console.log("TODO")}
/>
<TableContainer component={Paper}>
<Table
stickyHeader={true}
className={classes.table}
aria-label="scheduled tasks table"
size="small"
>
<TableHead>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={handleSelectAllClick}
inputProps={{
"aria-label": "select all tasks shown in the table",
}}
/>
</TableCell>
{columns.map((col) => (
<TableCell key={col.label}>{col.label}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{props.tasks.map((task) => (
<Row
key={task.id}
task={task}
isSelected={selectedKeys.includes(task.key)}
onSelectChange={(checked: boolean) => {
if (checked) {
setSelectedKeys(selectedKeys.concat(task.key));
} else {
setSelectedKeys(
selectedKeys.filter((key) => key !== task.key)
);
}
}}
onDeleteClick={() => {
props.deleteScheduledTaskAsync(queue, task.key);
}}
/>
</TableCell>
{columns.map((col) => (
<TableCell key={col.label}>{col.label}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{props.tasks.map((task) => (
<Row
key={task.id}
task={task}
isSelected={selectedKeys.includes(task.key)}
onSelectChange={(checked: boolean) => {
if (checked) {
setSelectedKeys(selectedKeys.concat(task.key));
} else {
setSelectedKeys(
selectedKeys.filter((key) => key !== task.key)
);
}
}}
onDeleteClick={() => {
props.deleteScheduledTaskAsync(queue, task.key);
}}
/>
))}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={rowsPerPageOptions}
colSpan={columns.length + 1}
count={props.totalTaskCount}
rowsPerPage={pageSize}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={rowsPerPageOptions}
colSpan={columns.length + 1}
count={props.totalTaskCount}
rowsPerPage={pageSize}
page={page}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
</div>
);
}

View File

@ -0,0 +1,97 @@
import React, { useState } from "react";
import { makeStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";
import ButtonGroup from "@material-ui/core/ButtonGroup";
import IconButton from "@material-ui/core/IconButton";
import Menu from "@material-ui/core/Menu";
import MenuItem from "@material-ui/core/MenuItem";
import MoreHorizIcon from "@material-ui/icons/MoreHoriz";
const useStyles = makeStyles({
actionsContainer: {
padding: "4px",
},
moreIcon: {
marginRight: "8px",
},
});
interface Props {
allActionPending: boolean;
onRunAllClick: () => void;
onDeleteAllClick: () => void;
showBatchActions: boolean;
batchActionPending: boolean;
onBatchRunClick: () => void;
onBatchDeleteClick: () => void;
}
export default function TableActions(props: Props) {
const classes = useStyles();
const [menuAnchor, setMenuAnchor] = useState<null | HTMLElement>(null);
const handleMenuClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setMenuAnchor(event.currentTarget);
};
const closeMenu = () => setMenuAnchor(null);
return (
<div className={classes.actionsContainer}>
<IconButton
aria-label="actions"
className={classes.moreIcon}
onClick={handleMenuClick}
>
<MoreHorizIcon />
</IconButton>
<Menu
id="action-menu"
keepMounted
anchorEl={menuAnchor}
open={Boolean(menuAnchor)}
onClose={closeMenu}
>
<MenuItem
onClick={() => {
props.onRunAllClick();
closeMenu();
}}
disabled={props.allActionPending}
>
Run All
</MenuItem>
<MenuItem
onClick={() => {
props.onDeleteAllClick();
closeMenu();
}}
disabled={props.allActionPending}
>
Delete All
</MenuItem>
</Menu>
{props.showBatchActions && (
<ButtonGroup
variant="text"
color="primary"
aria-label="text primary button group"
>
<Button
disabled={props.batchActionPending}
onClick={props.onBatchRunClick}
>
Run
</Button>
<Button>Kill</Button>
<Button
disabled={props.batchActionPending}
onClick={props.onBatchDeleteClick}
>
Delete
</Button>
</ButtonGroup>
)}
</div>
);
}

View File

@ -92,11 +92,15 @@ interface TasksState {
};
scheduledTasks: {
loading: boolean;
batchActionPending: boolean;
allActionPending: boolean;
error: string;
data: ScheduledTaskExtended[];
};
retryTasks: {
loading: boolean;
batchActionPending: boolean;
allActionPending: boolean;
error: string;
data: RetryTaskExtended[];
};
@ -122,11 +126,15 @@ const initialState: TasksState = {
},
scheduledTasks: {
loading: false,
batchActionPending: false,
allActionPending: false,
error: "",
data: [],
},
retryTasks: {
loading: false,
batchActionPending: false,
allActionPending: false,
error: "",
data: [],
},
@ -222,6 +230,7 @@ function tasksReducer(
return {
...state,
scheduledTasks: {
...state.scheduledTasks,
loading: false,
error: "",
data: action.payload.tasks.map((task) => ({
@ -255,6 +264,7 @@ function tasksReducer(
return {
...state,
retryTasks: {
...state.retryTasks,
loading: false,
error: "",
data: action.payload.tasks.map((task) => ({