diff --git a/ui/src/components/ActiveTasksTable.tsx b/ui/src/components/ActiveTasksTable.tsx index 22c105d..4ccad26 100644 --- a/ui/src/components/ActiveTasksTable.tsx +++ b/ui/src/components/ActiveTasksTable.tsx @@ -6,18 +6,19 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import Alert from "@material-ui/lab/Alert"; import AlertTitle from "@material-ui/lab/AlertTitle"; -import Button from "@material-ui/core/Button"; import TableContainer from "@material-ui/core/TableContainer"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import TableFooter from "@material-ui/core/TableFooter"; import TablePagination from "@material-ui/core/TablePagination"; +import Tooltip from "@material-ui/core/Tooltip"; import Paper from "@material-ui/core/Paper"; import Box from "@material-ui/core/Box"; import Checkbox from "@material-ui/core/Checkbox"; import Collapse from "@material-ui/core/Collapse"; import IconButton from "@material-ui/core/IconButton"; import CancelIcon from "@material-ui/icons/Cancel"; +import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp"; import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; import Typography from "@material-ui/core/Typography"; @@ -38,6 +39,7 @@ import TableActions from "./TableActions"; import { usePolling } from "../hooks"; import { ActiveTaskExtended } from "../reducers/tasksReducer"; import { uuidPrefix } from "../utils"; +import { TableColumn } from "../types/table"; const useStyles = makeStyles({ table: { @@ -76,6 +78,7 @@ function ActiveTasksTable(props: Props & ReduxProps) { const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(defaultPageSize); const [selectedIds, setSelectedIds] = useState([]); + const [activeTaskId, setActiveTaskId] = useState(""); const handleChangePage = ( event: React.MouseEvent | null, @@ -126,11 +129,12 @@ function ActiveTasksTable(props: Props & ReduxProps) { ); } - const columns: { label: string; align: "left" | "center" | "right" }[] = [ - { label: "", align: "left" }, - { label: "ID", align: "left" }, - { label: "Type", align: "left" }, - { label: "Actions", align: "center" }, + const columns: TableColumn[] = [ + { key: "icon", label: "", align: "left" }, + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", align: "left" }, + { key: "status", label: "Status", align: "left" }, + { key: "actions", label: "Actions", align: "center" }, ]; const rowCount = props.tasks.length; @@ -175,7 +179,7 @@ function ActiveTasksTable(props: Props & ReduxProps) { /> {columns.map((col) => ( - + {col.label} ))} @@ -198,6 +202,9 @@ function ActiveTasksTable(props: Props & ReduxProps) { onCancelClick={() => { props.cancelActiveTaskAsync(queue, task.id); }} + onActionCellEnter={() => setActiveTaskId(task.id)} + onActionCellLeave={() => setActiveTaskId("")} + showActions={activeTaskId === task.id} /> ))} @@ -238,6 +245,9 @@ interface RowProps { isSelected: boolean; onSelectChange: (checked: boolean) => void; onCancelClick: () => void; + showActions: boolean; + onActionCellEnter: () => void; + onActionCellLeave: () => void; } function Row(props: RowProps) { @@ -272,14 +282,29 @@ function Row(props: RowProps) { {uuidPrefix(task.id)} {task.type} - - + {task.canceling ? "Canceling" : "Running"} + + {props.showActions ? ( + + + + + + + + ) : ( + + + + )} diff --git a/ui/src/components/DeadTasksTable.tsx b/ui/src/components/DeadTasksTable.tsx index ff069c1..0ac1490 100644 --- a/ui/src/components/DeadTasksTable.tsx +++ b/ui/src/components/DeadTasksTable.tsx @@ -1,20 +1,22 @@ import React, { useCallback, useState } from "react"; +import clsx from "clsx"; import { connect, ConnectedProps } from "react-redux"; 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 Button from "@material-ui/core/Button"; import Checkbox from "@material-ui/core/Checkbox"; import TableContainer from "@material-ui/core/TableContainer"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; +import Tooltip from "@material-ui/core/Tooltip"; 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 PlayArrowIcon from "@material-ui/icons/PlayArrow"; import DeleteIcon from "@material-ui/icons/Delete"; +import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp"; import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; import Typography from "@material-ui/core/Typography"; @@ -42,6 +44,7 @@ import TableActions from "./TableActions"; import { timeAgo, uuidPrefix } from "../utils"; import { usePolling } from "../hooks"; import { DeadTaskExtended } from "../reducers/tasksReducer"; +import { TableColumn } from "../types/table"; const useStyles = makeStyles({ table: { @@ -55,6 +58,13 @@ const useRowStyles = makeStyles({ borderBottom: "unset", }, }, + actionCell: { + width: "96px", + }, + activeActionCell: { + display: "flex", + justifyContent: "space-between", + }, }); function mapStateToProps(state: AppState) { @@ -92,6 +102,7 @@ function DeadTasksTable(props: Props & ReduxProps) { const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(defaultPageSize); const [selectedKeys, setSelectedKeys] = useState([]); + const [activeTaskId, setActiveTaskId] = useState(""); const handleChangePage = ( event: React.MouseEvent | null, @@ -152,13 +163,13 @@ function DeadTasksTable(props: Props & ReduxProps) { ); } - const columns = [ - { label: "" }, - { label: "ID" }, - { label: "Type" }, - { label: "Last Failed" }, - { label: "Last Error" }, - { label: "Actions" }, + const columns: TableColumn[] = [ + { key: "icon", label: "", align: "left" }, + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", 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; @@ -214,7 +225,9 @@ function DeadTasksTable(props: Props & ReduxProps) { /> {columns.map((col) => ( - {col.label} + + {col.label} + ))} @@ -240,6 +253,9 @@ function DeadTasksTable(props: Props & ReduxProps) { props.deleteDeadTaskAsync(queue, task.key); }} allActionPending={props.allActionPending} + onActionCellEnter={() => setActiveTaskId(task.id)} + onActionCellLeave={() => setActiveTaskId("")} + showActions={activeTaskId === task.id} /> ))} @@ -274,6 +290,9 @@ interface RowProps { onRunClick: () => void; onDeleteClick: () => void; allActionPending: boolean; + showActions: boolean; + onActionCellEnter: () => void; + onActionCellLeave: () => void; } function Row(props: RowProps) { @@ -310,19 +329,41 @@ function Row(props: RowProps) { {task.type} {timeAgo(task.last_failed_at)} {task.error_message} - - - + + {props.showActions ? ( + + + + + + + + + + + + + ) : ( + + + + )} diff --git a/ui/src/components/PendingTasksTable.tsx b/ui/src/components/PendingTasksTable.tsx index d7da089..eeb3000 100644 --- a/ui/src/components/PendingTasksTable.tsx +++ b/ui/src/components/PendingTasksTable.tsx @@ -6,7 +6,6 @@ import TableBody from "@material-ui/core/TableBody"; import TableCell from "@material-ui/core/TableCell"; import Alert from "@material-ui/lab/Alert"; import AlertTitle from "@material-ui/lab/AlertTitle"; -import Button from "@material-ui/core/Button"; import TableContainer from "@material-ui/core/TableContainer"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; @@ -30,6 +29,7 @@ import { AppState } from "../store"; import { PendingTask } from "../api"; import { usePolling } from "../hooks"; import { uuidPrefix } from "../utils"; +import { TableColumn } from "../types/table"; const useStyles = makeStyles({ table: { @@ -92,11 +92,10 @@ function PendingTasksTable(props: Props & ReduxProps) { ); } - const columns = [ - { label: "" }, - { label: "ID" }, - { label: "Type" }, - { label: "Actions" }, + const columns: TableColumn[] = [ + { key: "icon", label: "", align: "left" }, + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", align: "left" }, ]; return ( @@ -110,7 +109,9 @@ function PendingTasksTable(props: Props & ReduxProps) { {columns.map((col) => ( - {col.label} + + {col.label} + ))} @@ -170,9 +171,6 @@ function Row(props: { task: PendingTask }) { {uuidPrefix(task.id)} {task.type} - - - diff --git a/ui/src/components/QueuesOverviewTable.tsx b/ui/src/components/QueuesOverviewTable.tsx index 05cabdb..0dfec11 100644 --- a/ui/src/components/QueuesOverviewTable.tsx +++ b/ui/src/components/QueuesOverviewTable.tsx @@ -18,7 +18,7 @@ import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; import DeleteQueueConfirmationDialog from "./DeleteQueueConfirmationDialog"; import { Queue } from "../api"; import { queueDetailsPath } from "../paths"; -import { SortDirection, ColumnConfig } from "../types/table"; +import { SortDirection, SortableTableColumn } from "../types/table"; const useStyles = makeStyles((theme) => ({ table: { @@ -71,7 +71,7 @@ enum SortBy { None, // no sort support } -const colConfigs: ColumnConfig[] = [ +const colConfigs: SortableTableColumn[] = [ { label: "Queue", key: "queue", sortBy: SortBy.Queue, align: "left" }, { label: "Size", key: "size", sortBy: SortBy.Size, align: "right" }, { label: "Active", key: "active", sortBy: SortBy.Active, align: "right" }, diff --git a/ui/src/components/RetryTasksTable.tsx b/ui/src/components/RetryTasksTable.tsx index 2dc2685..2b2134d 100644 --- a/ui/src/components/RetryTasksTable.tsx +++ b/ui/src/components/RetryTasksTable.tsx @@ -4,7 +4,6 @@ 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 Button from "@material-ui/core/Button"; import TableContainer from "@material-ui/core/TableContainer"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; @@ -12,12 +11,14 @@ import TableFooter from "@material-ui/core/TableFooter"; import TablePagination from "@material-ui/core/TablePagination"; import Paper from "@material-ui/core/Paper"; import Box from "@material-ui/core/Box"; +import Tooltip from "@material-ui/core/Tooltip"; import Checkbox from "@material-ui/core/Checkbox"; import Collapse from "@material-ui/core/Collapse"; 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 MoreHorizIcon from "@material-ui/icons/MoreHoriz"; import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp"; import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; import Typography from "@material-ui/core/Typography"; @@ -46,6 +47,8 @@ import TableActions from "./TableActions"; import { durationBefore, uuidPrefix } from "../utils"; import { usePolling } from "../hooks"; import { RetryTaskExtended } from "../reducers/tasksReducer"; +import clsx from "clsx"; +import { TableColumn } from "../types/table"; const useStyles = makeStyles({ table: { @@ -91,6 +94,7 @@ function RetryTasksTable(props: Props & ReduxProps) { const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(defaultPageSize); const [selectedKeys, setSelectedKeys] = useState([]); + const [activeTaskId, setActiveTaskId] = useState(""); const handleChangePage = ( event: React.MouseEvent | null, @@ -161,15 +165,15 @@ function RetryTasksTable(props: Props & ReduxProps) { ); } - const columns = [ - { label: "" }, - { label: "ID" }, - { label: "Type" }, - { label: "Retry In" }, - { label: "Last Error" }, - { label: "Retried" }, - { label: "Max Retry" }, - { label: "Actions" }, + const columns: TableColumn[] = [ + { key: "icon", label: "", align: "left" }, + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", align: "left" }, + { key: "retry_in", label: "Retry In", align: "left" }, + { key: "last_error", label: "Last Error", align: "left" }, + { key: "retried", label: "Retried", align: "left" }, + { key: "max_retry", label: "Max Retry", align: "left" }, + { key: "actions", label: "Actions", align: "center" }, ]; const rowCount = props.tasks.length; @@ -236,7 +240,9 @@ function RetryTasksTable(props: Props & ReduxProps) { /> {columns.map((col) => ( - {col.label} + + {col.label} + ))} @@ -265,6 +271,9 @@ function RetryTasksTable(props: Props & ReduxProps) { onKillClick={() => { props.killRetryTaskAsync(task.queue, task.key); }} + onActionCellEnter={() => setActiveTaskId(task.id)} + onActionCellLeave={() => setActiveTaskId("")} + showActions={activeTaskId === task.id} /> ))} @@ -298,6 +307,13 @@ const useRowStyles = makeStyles({ borderBottom: "unset", }, }, + actionCell: { + width: "140px", + }, + activeActionCell: { + display: "flex", + justifyContent: "space-between", + }, }); interface RowProps { @@ -308,6 +324,9 @@ interface RowProps { onRunClick: () => void; onKillClick: () => void; allActionPending: boolean; + showActions: boolean; + onActionCellEnter: () => void; + onActionCellLeave: () => void; } function Row(props: RowProps) { @@ -346,25 +365,50 @@ function Row(props: RowProps) { {task.error_message} {task.retried} {task.max_retry} - - - - + + {props.showActions ? ( + + + + + + + + + + + + + + + + + + ) : ( + + + + )} diff --git a/ui/src/components/ScheduledTasksTable.tsx b/ui/src/components/ScheduledTasksTable.tsx index 8c07e8e..7521011 100644 --- a/ui/src/components/ScheduledTasksTable.tsx +++ b/ui/src/components/ScheduledTasksTable.tsx @@ -1,10 +1,10 @@ import React, { useState, useCallback } from "react"; +import clsx from "clsx"; import { connect, ConnectedProps } from "react-redux"; 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 Button from "@material-ui/core/Button"; import TableContainer from "@material-ui/core/TableContainer"; import TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; @@ -12,6 +12,7 @@ import TableFooter from "@material-ui/core/TableFooter"; import TablePagination from "@material-ui/core/TablePagination"; import Paper from "@material-ui/core/Paper"; import Box from "@material-ui/core/Box"; +import Tooltip from "@material-ui/core/Tooltip"; import Checkbox from "@material-ui/core/Checkbox"; import Collapse from "@material-ui/core/Collapse"; import IconButton from "@material-ui/core/IconButton"; @@ -20,6 +21,7 @@ import DeleteIcon from "@material-ui/icons/Delete"; import ArchiveIcon from "@material-ui/icons/Archive"; import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp"; import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; +import MoreHorizIcon from "@material-ui/icons/MoreHoriz"; import Typography from "@material-ui/core/Typography"; import Alert from "@material-ui/lab/Alert"; import AlertTitle from "@material-ui/lab/AlertTitle"; @@ -46,6 +48,7 @@ import TableActions from "./TableActions"; import { durationBefore, uuidPrefix } from "../utils"; import { usePolling } from "../hooks"; import { ScheduledTaskExtended } from "../reducers/tasksReducer"; +import { TableColumn } from "../types/table"; const useStyles = makeStyles({ table: { @@ -91,6 +94,7 @@ function ScheduledTasksTable(props: Props & ReduxProps) { const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(defaultPageSize); const [selectedKeys, setSelectedKeys] = useState([]); + const [activeTaskId, setActiveTaskId] = useState(""); const handleChangePage = ( event: React.MouseEvent | null, @@ -161,12 +165,12 @@ function ScheduledTasksTable(props: Props & ReduxProps) { ); } - const columns = [ - { label: "" }, - { label: "ID" }, - { label: "Type" }, - { label: "Process In" }, - { label: "Actions" }, + const columns: TableColumn[] = [ + { key: "icon", label: "", align: "left" }, + { key: "id", label: "ID", align: "left" }, + { key: "type", label: "Type", align: "left" }, + { key: "process_in", label: "Process In", align: "left" }, + { key: "actions", label: "Actions", align: "center" }, ]; const rowCount = props.tasks.length; @@ -233,7 +237,9 @@ function ScheduledTasksTable(props: Props & ReduxProps) { /> {columns.map((col) => ( - {col.label} + + {col.label} + ))} @@ -262,6 +268,9 @@ function ScheduledTasksTable(props: Props & ReduxProps) { onKillClick={() => { props.killScheduledTaskAsync(queue, task.key); }} + onActionCellEnter={() => setActiveTaskId(task.id)} + onActionCellLeave={() => setActiveTaskId("")} + showActions={activeTaskId === task.id} /> ))} @@ -295,6 +304,13 @@ const useRowStyles = makeStyles({ borderBottom: "unset", }, }, + actionCell: { + width: "140px", + }, + activeActionCell: { + display: "flex", + justifyContent: "space-between", + }, }); interface RowProps { @@ -305,6 +321,9 @@ interface RowProps { onDeleteClick: () => void; onKillClick: () => void; allActionPending: boolean; + showActions: boolean; + onActionCellEnter: () => void; + onActionCellLeave: () => void; } function Row(props: RowProps) { @@ -340,25 +359,50 @@ function Row(props: RowProps) { {task.type} {durationBefore(task.next_process_at)} - - - - + + {props.showActions ? ( + + + + + + + + + + + + + + + + + + ) : ( + + + + )} diff --git a/ui/src/components/SchedulerEntriesTable.tsx b/ui/src/components/SchedulerEntriesTable.tsx index a662fb0..4ced60d 100644 --- a/ui/src/components/SchedulerEntriesTable.tsx +++ b/ui/src/components/SchedulerEntriesTable.tsx @@ -11,7 +11,7 @@ import Alert from "@material-ui/lab/Alert"; import AlertTitle from "@material-ui/lab/AlertTitle"; import SyntaxHighlighter from "react-syntax-highlighter"; import syntaxHighlightStyle from "react-syntax-highlighter/dist/esm/styles/hljs/github"; -import { SortDirection, ColumnConfig } from "../types/table"; +import { SortDirection, SortableTableColumn } from "../types/table"; import TableSortLabel from "@material-ui/core/TableSortLabel"; import { SchedulerEntry } from "../api"; import { timeAgo, durationBefore } from "../utils"; @@ -41,7 +41,7 @@ enum SortBy { PrevEnqueue, } -const colConfigs: ColumnConfig[] = [ +const colConfigs: SortableTableColumn[] = [ { label: "Entry ID", key: "entry_id", diff --git a/ui/src/types/table.ts b/ui/src/types/table.ts index c928586..bba2da4 100644 --- a/ui/src/types/table.ts +++ b/ui/src/types/table.ts @@ -4,12 +4,17 @@ export enum SortDirection { Desc = "desc", } -// ColumnConfig is a config for a table column. -// -// T is the enum of sort keys. -export interface ColumnConfig { - label: string; +// TableColumn is a config for a table column. +export interface TableColumn { key: string; - sortBy: T; + label: string; align: "left" | "right" | "center"; } + +// SortableTableColumn is a config for a table column +// for table with sorting support. +// +// T is the enum of sort keys. +export interface SortableTableColumn extends TableColumn { + sortBy: T; +}