import React, { useCallback } from "react"; import { connect, ConnectedProps } from "react-redux"; import { makeStyles } from "@material-ui/core/styles"; import Checkbox from "@material-ui/core/Checkbox"; import IconButton from "@material-ui/core/IconButton"; import Paper from "@material-ui/core/Paper"; 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 MoreHorizIcon from "@material-ui/icons/MoreHoriz"; import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined"; import { useHistory } from "react-router-dom"; import { listGroupsAsync } from "../actions/groupsActions"; import GroupSelect from "./GroupSelect"; import TableActions from "./TableActions"; import SyntaxHighlighter from "./SyntaxHighlighter"; import { prettifyPayload, uuidPrefix } from "../utils"; import { usePolling } from "../hooks"; import { taskDetailsPath } from "../paths"; import { AppState } from "../store"; import { TaskInfoExtended } from "../reducers/tasksReducer"; import { GroupInfo } from "../api"; import { TableColumn } from "../types/table"; import TablePaginationActions, { rowsPerPageOptions, } from "./TablePaginationActions"; import { listAggregatingTasksAsync } from "../actions/tasksActions"; import { taskRowsPerPageChange } from "../actions/settingsActions"; const useStyles = makeStyles((theme) => ({ groupSelector: { paddingTop: theme.spacing(1), paddingBottom: theme.spacing(1), paddingLeft: theme.spacing(2), paddingRight: theme.spacing(2), }, table: { minWidth: 650, }, stickyHeaderCell: { background: theme.palette.background.paper, }, alert: { borderTopLeftRadius: 0, borderTopRightRadius: 0, }, pagination: { border: "none", }, })); function mapStateToProps(state: AppState) { return { groups: state.groups.data, groupsError: state.groups.error, loading: state.tasks.aggregatingTasks.loading, group: state.tasks.aggregatingTasks.group, tasks: state.tasks.aggregatingTasks.data, pollInterval: state.settings.pollInterval, pageSize: state.settings.taskRowsPerPage, }; } const mapDispatchToProps = { listGroupsAsync, listAggregatingTasksAsync, taskRowsPerPageChange, }; const connector = connect(mapStateToProps, mapDispatchToProps); interface Props { queue: string; } const columns: TableColumn[] = [ { key: "id", label: "ID", align: "left" }, { key: "type", label: "Type", align: "left" }, { key: "paylod", label: "Payload", align: "left" }, { key: "group", label: "Group", align: "left" }, { key: "actions", label: "Actions", align: "center" }, ]; function AggregatingTasksTable( props: Props & ConnectedProps ) { const [selectedGroup, setSelectedGroup] = React.useState( null ); const [page, setPage] = React.useState(0); const [selectedIds, setSelectedIds] = React.useState([]); const [activeTaskId, setActiveTaskId] = React.useState(""); const { pollInterval, listGroupsAsync, listAggregatingTasksAsync, queue, pageSize, } = props; const classes = useStyles(); 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 fetchGroups = useCallback(() => { listGroupsAsync(queue); }, [listGroupsAsync, queue]); const fetchTasks = useCallback(() => { const pageOpts = { page: page + 1, size: pageSize }; if (selectedGroup !== null) { listAggregatingTasksAsync(queue, selectedGroup.group, pageOpts); } }, [page, pageSize, queue, selectedGroup, listAggregatingTasksAsync]); usePolling(fetchGroups, pollInterval); usePolling(fetchTasks, pollInterval); const rowCount = props.tasks.length; const numSelected = selectedIds.length; return (
{!window.READ_ONLY && ( 0} iconButtonActions={[ { tooltip: "Delete", icon: , onClick: () => {}, // TODO: handleBatchDeleteClick, disabled: false, //TODO: props.batchActionPending, }, { tooltip: "Archive", icon: , onClick: () => {}, //TODO: handleBatchArchiveClick, disabled: false, //TODO: props.batchActionPending, }, ]} menuItemActions={[ { label: "Delete All", onClick: () => {}, //TODO: handleDeleteAllClick, disabled: false, // TODO: props.allActionPending, }, { label: "Archive All", onClick: () => {}, // TODO: handleArchiveAllClick, disabled: false, // TODO: 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.group === selectedGroup?.group && props.tasks.map((task) => ( { if (checked) { setSelectedIds(selectedIds.concat(task.id)); } else { setSelectedIds( selectedIds.filter((id) => id !== task.id) ); } }} allActionPending={false /* TODO: props.allActionPending */} onDeleteClick={ () => {} //TODO: props.deletePendingTaskAsync(queue, task.id) } onArchiveClick={() => { // TODO: 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; } function Row(props: RowProps) { const { task } = props; const classes = useRowStyles(); const history = useHistory(); return ( history.push(taskDetailsPath(task.queue, task.id))} > {!window.READ_ONLY && ( e.stopPropagation()}> ) => props.onSelectChange(event.target.checked) } checked={props.isSelected} /> )}
{uuidPrefix(task.id)} { e.stopPropagation(); navigator.clipboard.writeText(task.id); }} size="small" className={classes.copyButton} >
{task.type} {prettifyPayload(task.payload)} {task.group} {!window.READ_ONLY && ( e.stopPropagation()} > {props.showActions ? ( ) : ( )} )}
); } export default connector(AggregatingTasksTable);