diff --git a/src/components/RunList/index.tsx b/src/components/RunList/index.tsx index 6a5eff4..19748ab 100644 --- a/src/components/RunList/index.tsx +++ b/src/components/RunList/index.tsx @@ -1,5 +1,8 @@ -import { useMemo } from 'react'; +import { SetStateAction, useMemo } from 'react'; +import { useState } from "react"; import OpenInNewIcon from "@mui/icons-material/OpenInNew"; +import Grid from '@mui/material/Grid'; +import Button from '@mui/material/Button'; import type { DecodedValueMap, QueryParamConfigMap, @@ -9,11 +12,13 @@ import { useDebounce } from "usehooks-ts"; import { useMaterialReactTable, MaterialReactTable, + MRT_TableHeadCellFilterContainer, type MRT_ColumnDef, type MRT_PaginationState, type MRT_Updater, type MRT_ColumnFiltersState, type MRT_TableOptions, + type MRT_TableInstance, } from 'material-react-table'; import { type Theme } from "@mui/material/styles"; import { parse } from "date-fns"; @@ -31,6 +36,10 @@ import { RunStatuses, } from "../../lib/paddles.d"; import useDefaultTableOptions from "../../lib/table"; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import Badge from '@mui/material/Badge'; +import Menu from '@mui/material/Menu'; const DEFAULT_PAGE_SIZE = 25; @@ -45,7 +54,6 @@ const _columns: MRT_ColumnDef[] = [ header: "link", maxSize: 12, enableColumnFilter: false, - enableColumnActions: false, Cell: ({ row }) => { return ( @@ -62,14 +70,14 @@ const _columns: MRT_ColumnDef[] = [ return row.original.status.replace("finished ", ""); }, filterSelectOptions: Object.values(RunStatuses), - size: 40, - enableColumnActions: false, + maxSize: 25, }, { accessorKey: "user", header: "user", maxSize: 45, enableColumnActions: false, + enableColumnFilter: false, }, { id: "scheduled", @@ -81,7 +89,7 @@ const _columns: MRT_ColumnDef[] = [ const date_: string[] = row.original.scheduled.split(" "); return
{date_[0]}
{date_[1]}
}, - size: 50, + size: 35, }, { id: "started", @@ -89,7 +97,7 @@ const _columns: MRT_ColumnDef[] = [ accessorFn: (row: Run) => formatDate(row.started), enableColumnFilter: false, sortingFn: "datetime", - size: 125, + size: 35, }, { id: "posted", @@ -97,7 +105,7 @@ const _columns: MRT_ColumnDef[] = [ accessorFn: (row: Run) => formatDate(row.posted), enableColumnFilter: false, sortingFn: "datetime", - size: 125, + maxSize: 35, }, { id: "runtime", @@ -115,16 +123,16 @@ const _columns: MRT_ColumnDef[] = [ { accessorKey: "suite", header: "suite", - size: 70, + size: 50, }, { accessorKey: "branch", header: "branch", - maxSize: 75, + maxSize: 70, }, { accessorKey: "machine_type", - header: "machine_type", + header: "machine type", size: 30, }, { @@ -200,6 +208,9 @@ type RunListProps = { } export default function RunList(props: RunListProps) { + const [openFilterMenu, setOpenFilterMenu] = useState(false); + const [dropMenuAnchorEl, setDropMenuAnchor] = useState(null); + const { params, setter, tableOptions } = props; const options = useDefaultTableOptions(); const debouncedParams = useDebounce(params, 500); @@ -220,6 +231,15 @@ export default function RunList(props: RunListProps) { pageIndex: params.page || 0, pageSize: params.pageSize || DEFAULT_PAGE_SIZE, }; + const toggleFilterMenu = (event: { currentTarget: SetStateAction; }) => { + if (dropMenuAnchorEl) { + setDropMenuAnchor(null); + setOpenFilterMenu(false); + } else { + setDropMenuAnchor(event.currentTarget); + setOpenFilterMenu(true); + } + } const onColumnFiltersChange = (updater: MRT_Updater) => { if ( ! ( updater instanceof Function ) ) return; const result: RunListParams = {pageSize: pagination.pageSize}; @@ -267,12 +287,19 @@ export default function RunList(props: RunListProps) { data: data, manualPagination: true, manualFiltering: true, + enableColumnActions: false, onPaginationChange, rowCount: Infinity, muiPaginationProps: { showLastButton: false, }, onColumnFiltersChange, + columnFilterDisplayMode: 'custom', + enableColumnFilters: false, + muiFilterTextFieldProps: ({ column }) => ({ + label: column.columnDef.header, + placeholder: '', + }), initialState: { ...options.initialState, columnVisibility: { @@ -297,8 +324,115 @@ export default function RunList(props: RunListProps) { if ( category ) return { className: category }; return {}; }, + renderTopToolbarCustomActions: ({ table }) => ( + + (filter.value ? count + 1 : count), 0)} + > + + + + ), ...tableOptions, }); + if (query.isError) return null; - return + return ( +
+
+ + { table.getState().columnFilters.map((column) => { + let filterValue = column.value; + if (column.id == "scheduled") { + const parsedDate = new Date(column.value as string); + filterValue = (parsedDate.toISOString().split('T')[0]) + } + return (column.value ? `${column.id}: '${filterValue}' ` : "") + } )} + + + + +
+ +
+ ) +} + + +// FilterMenu + +type FilterMenuProps = { + isOpen: boolean; + table: MRT_TableInstance; +}; + + +const FILTER_SECTIONS = ["run", "build", "result"] +const FILTER_SECTIONS_COLUMNS = [ + ["scheduled", "suite", "machine_type", "user"], + ["branch", "sha1"], + ["status"], +] + +function FilterMenu({ isOpen, table}: FilterMenuProps) { + if (!isOpen) { + return null; + } + + return ( + + {FILTER_SECTIONS_COLUMNS.map((_, sectionIndex) => ( + + + Filter by {FILTER_SECTIONS[sectionIndex]} details... + + + {table.getLeafHeaders().map((header) => { + if (FILTER_SECTIONS_COLUMNS[sectionIndex].includes(header.id)) { + return ( + + + + ); + } + return null; + })} + + + ))} + + ) } diff --git a/src/lib/paddles.ts b/src/lib/paddles.ts index 364a2d6..d35a643 100644 --- a/src/lib/paddles.ts +++ b/src/lib/paddles.ts @@ -56,12 +56,19 @@ function getURL(endpoint: string, params?: GetURLParams) { } function useRuns(params: GetURLParams): UseQueryResult { + let baseUrl = "/runs/"; const params_ = { ...params }; if (params_.pageSize) { params_.count = params.pageSize; delete params_.pageSize; } - const url = getURL("/runs/", params); + if (params_.machine_type) { + // this is to handle difference in machine_type filter + // in two endpoints /node/?machine_type=xyz and /runs/machine_type/xyz + baseUrl += "machine_type/" + params_.machine_type + "/"; + delete params_.machine_type; + } + const url = getURL(baseUrl, params); const query = useQuery(["runs", { url }], { select: (data: Run[]) => data.map((item) => { diff --git a/src/lib/table.ts b/src/lib/table.ts index 4db9d87..7cbc21f 100644 --- a/src/lib/table.ts +++ b/src/lib/table.ts @@ -24,6 +24,19 @@ export default function useDefaultTableOptions(): Par mrtTheme: { baseBackgroundColor: theme.palette.background.default, }, + muiTableHeadCellProps: { + sx: { + '& .Mui-TableHeadCell-Content': { + fontSize: "0.8em", + }, + '& .MuiTableSortLabel-root': { + display: "none", + }, + '&:hover .MuiTableSortLabel-root': { + display: "block", + }, + }, + }, muiTableBodyCellProps: { sx: { color: "black",