import React, { useState } from "react"; import { Link } from "react-router-dom"; import clsx from "clsx"; import { makeStyles } from "@material-ui/core/styles"; import Grid from "@material-ui/core/Grid"; import Box from "@material-ui/core/Box"; import Collapse from "@material-ui/core/Collapse"; import IconButton from "@material-ui/core/IconButton"; 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 TableHead from "@material-ui/core/TableHead"; import TableRow from "@material-ui/core/TableRow"; import TableSortLabel from "@material-ui/core/TableSortLabel"; import Tooltip from "@material-ui/core/Tooltip"; import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp"; 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 { ServerInfo } from "../api"; import { SortDirection, SortableTableColumn } from "../types/table"; import { timeAgo, uuidPrefix } from "../utils"; import { queueDetailsPath } from "../paths"; import Typography from "@material-ui/core/Typography"; const useStyles = makeStyles((theme) => ({ table: { minWidth: 650, }, fixedCell: { position: "sticky", zIndex: 1, left: 0, background: theme.palette.common.white, }, })); enum SortBy { HostPID, Status, ActiveWorkers, Queues, Started, } const colConfigs: SortableTableColumn[] = [ { label: "Host:PID", key: "host", sortBy: SortBy.HostPID, align: "left", }, { label: "Started", key: "started", sortBy: SortBy.Started, align: "left", }, { label: "Status", key: "status", sortBy: SortBy.Status, align: "left", }, { label: "Queues", key: "queues", sortBy: SortBy.Queues, align: "left", }, { label: "Active Workers", key: "workers", sortBy: SortBy.ActiveWorkers, align: "left", }, ]; // sortServers takes a array of server-infos and return a sorted array. // It returns a new array and leave the original array untouched. function sortServerInfos( entries: ServerInfo[], cmpFn: (first: ServerInfo, second: ServerInfo) => number ): ServerInfo[] { let copy = [...entries]; copy.sort(cmpFn); return copy; } interface Props { servers: ServerInfo[]; } export default function ServersTable(props: Props) { const classes = useStyles(); const [sortBy, setSortBy] = useState(SortBy.HostPID); const [sortDir, setSortDir] = useState(SortDirection.Asc); const createSortClickHandler = (sortKey: SortBy) => (e: React.MouseEvent) => { if (sortKey === sortBy) { // Toggle sort direction. const nextSortDir = sortDir === SortDirection.Asc ? SortDirection.Desc : SortDirection.Asc; setSortDir(nextSortDir); } else { // Change the sort key. setSortBy(sortKey); } }; const cmpFunc = (s1: ServerInfo, s2: ServerInfo): number => { let isS1Smaller = false; switch (sortBy) { case SortBy.HostPID: if (s1.host === s2.host && s1.pid === s2.pid) return 0; if (s1.host === s2.host) { isS1Smaller = s1.pid < s2.pid; } else { isS1Smaller = s1.host < s2.host; } break; case SortBy.Started: const s1StartTime = Date.parse(s1.start_time); const s2StartTime = Date.parse(s2.start_time); if (s1StartTime === s2StartTime) return 0; isS1Smaller = s1StartTime < s2StartTime; break; case SortBy.Status: if (s1.status === s2.status) return 0; isS1Smaller = s1.status < s2.status; break; case SortBy.Queues: const s1Queues = Object.keys(s1.queue_priorities).join(","); const s2Queues = Object.keys(s2.queue_priorities).join(","); if (s1Queues === s2Queues) return 0; isS1Smaller = s1Queues < s2Queues; break; case SortBy.ActiveWorkers: if (s1.active_workers.length === s2.active_workers.length) { return 0; } isS1Smaller = s1.active_workers.length < s2.active_workers.length; break; default: // eslint-disable-next-line no-throw-literal throw `Unexpected order by value: ${sortBy}`; } if (sortDir === SortDirection.Asc) { return isS1Smaller ? -1 : 1; } else { return isS1Smaller ? 1 : -1; } }; if (props.servers.length === 0) { return ( Info No servers found at this time. ); } return ( {colConfigs.map((cfg, i) => ( {cfg.label} ))} {sortServerInfos(props.servers, cmpFunc).map((srv) => ( ))}
); } interface RowProps { server: ServerInfo; } const useRowStyles = makeStyles((theme) => ({ rowRoot: { "& > *": { borderBottom: "unset", }, }, noBorder: { border: "none", }, })); function Row(props: RowProps) { const classes = useRowStyles(); const { server } = props; const [open, setOpen] = useState(false); const qnames = Object.keys(server.queue_priorities); return ( {server.host}:{server.pid} {timeAgo(server.start_time)} {server.status} {qnames.map((qname, idx) => ( {qname} {idx === qnames.length - 1 ? "" : ", "} ))} {server.active_workers.length}/{server.concurrency} setOpen(!open)} > {open ? : } Active Workers Task ID Task Payload Queue Started {server.active_workers.map((worker) => ( {uuidPrefix(worker.task.id)} {JSON.stringify(worker.task.payload)} {worker.task.queue} {timeAgo(worker.start_time)} ))}
Queue Priority Queue Priority {qnames.map((qname) => ( {qname} {server.queue_priorities[qname]} ))}
Strict Priority:{" "} {server.strict_priority_enabled ? "ON" : "OFF"}
); }