"use client"; import * as React from "react"; import { alpha } from "@mui/material/styles"; import Box from "@mui/material/Box"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; import TableCell from "@mui/material/TableCell"; import TableContainer from "@mui/material/TableContainer"; import TableHead from "@mui/material/TableHead"; import TablePagination from "@mui/material/TablePagination"; import TableRow from "@mui/material/TableRow"; import TableSortLabel from "@mui/material/TableSortLabel"; import Toolbar from "@mui/material/Toolbar"; import Typography from "@mui/material/Typography"; import Paper from "@mui/material/Paper"; import IconButton from "@mui/material/IconButton"; import Tooltip from "@mui/material/Tooltip"; import FormControlLabel from "@mui/material/FormControlLabel"; import Switch from "@mui/material/Switch"; import DeleteIcon from "@mui/icons-material/Delete"; import FilterListIcon from "@mui/icons-material/FilterList"; import SpeedDial, { CloseReason, OpenReason } from "@mui/material/SpeedDial"; import SpeedDialIcon from "@mui/material/SpeedDialIcon"; import SpeedDialAction from "@mui/material/SpeedDialAction"; import { visuallyHidden } from "@mui/utils"; import CircleIcon from "@mui/icons-material/Circle"; import Stack from "@mui/material/Stack/Stack"; import EditIcon from "@mui/icons-material/ModeEdit"; import SearchIcon from "@mui/icons-material/Search"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import NodePieChart, { PieData } from "./NodePieChart"; import Fab from "@mui/material/Fab"; import AddIcon from "@mui/icons-material/Add"; import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2 import { Card, CardContent, Collapse, Container, FormGroup, useTheme, } from "@mui/material"; import hexRgb from "hex-rgb"; import useMediaQuery from "@mui/material/useMediaQuery"; import { NodeStatus, NodeStatusKeys, TableData } from "@/data/nodeData"; interface HeadCell { disablePadding: boolean; id: keyof TableData; label: string; alignRight: boolean; } const headCells: readonly HeadCell[] = [ { id: "name", alignRight: false, disablePadding: false, label: "DISPLAY NAME & ID", }, { id: "status", alignRight: false, disablePadding: false, label: "STATUS", }, { id: "last_seen", alignRight: false, disablePadding: false, label: "LAST SEEN", }, ]; function descendingComparator(a: T, b: T, orderBy: keyof T) { if (b[orderBy] < a[orderBy]) { return -1; } if (b[orderBy] > a[orderBy]) { return 1; } return 0; } type Order = "asc" | "desc"; function getComparator( order: Order, orderBy: Key, ): ( a: { [key in Key]: number | string | boolean }, b: { [key in Key]: number | string | boolean }, ) => number { return order === "desc" ? (a, b) => descendingComparator(a, b, orderBy) : (a, b) => -descendingComparator(a, b, orderBy); } // Since 2020 all major browsers ensure sort stability with Array.prototype.sort(). // stableSort() brings sort stability to non-modern browsers (notably IE11). If you // only support modern browsers you can replace stableSort(exampleArray, exampleComparator) // with exampleArray.slice().sort(exampleComparator) function stableSort( array: readonly T[], comparator: (a: T, b: T) => number, ) { const stabilizedThis = array.map((el, index) => [el, index] as [T, number]); stabilizedThis.sort((a, b) => { const order = comparator(a[0], b[0]); if (order !== 0) { return order; } return a[1] - b[1]; }); return stabilizedThis.map((el) => el[0]); } function BasicSpeedDial() { const [open, setOpen] = React.useState(false); function handleClose(event: any, reason: CloseReason) { if (reason === "toggle" || reason === "escapeKeyDown") { setOpen(false); } } function handleOpen(event: any, reason: OpenReason) { if (reason === "toggle") { setOpen(true); } } return ( } direction="down" onClose={handleClose} onOpen={handleOpen} open={open} > } tooltipTitle="Edit" /> } tooltipTitle="Add" /> } tooltipTitle="Delete" /> ); } interface EnhancedTableToolbarProps { selected: string | undefined; tableData: TableData[]; onClear: () => void; } function EnhancedTableToolbar(props: EnhancedTableToolbarProps) { const { selected, onClear, tableData } = props; const theme = useTheme(); const is_lg = useMediaQuery(theme.breakpoints.down("lg")); const is_sm = useMediaQuery(theme.breakpoints.down("sm")); const isSelected = selected != undefined; const [debug, setDebug] = React.useState(false); const debugSx = debug ? { "--Grid-borderWidth": "1px", borderTop: "var(--Grid-borderWidth) solid", borderLeft: "var(--Grid-borderWidth) solid", borderColor: "divider", "& > div": { borderRight: "var(--Grid-borderWidth) solid", borderBottom: "var(--Grid-borderWidth) solid", borderColor: "divider", }, } : {}; const pieData = React.useMemo(() => { const online = tableData.filter( (row) => row.status === NodeStatus.Online, ).length; const offline = tableData.filter( (row) => row.status === NodeStatus.Offline, ).length; const pending = tableData.filter( (row) => row.status === NodeStatus.Pending, ).length; return [ { name: "Online", value: online, color: theme.palette.success.main }, { name: "Offline", value: offline, color: theme.palette.error.main }, { name: "Pending", value: pending, color: theme.palette.warning.main }, ]; }, [tableData]); const cardData = React.useMemo(() => { return pieData .filter((pieItem) => pieItem.value > 0) .concat({ name: "Total", value: pieData.reduce((a, b) => a + b.value, 0), color: "#000000", }); }, [pieData]); const cardStack = ( {cardData.map((pieItem) => ( {pieItem.value} {pieItem.name} ))} ); return ( NODES {/* Debug Controls */} { setDebug(!debug); }} checked={debug} /> } label="Debug" /> {/* Pie Chart Grid */} {/* Card Stack Grid */} {cardStack} {/*Toolbar Grid */} ); } export interface NodeTableProps { tableData: TableData[]; } interface EnhancedTableProps { onRequestSort: ( event: React.MouseEvent, property: keyof TableData, ) => void; order: Order; orderBy: string; rowCount: number; } function EnhancedTableHead(props: EnhancedTableProps) { const { order, orderBy, onRequestSort } = props; const createSortHandler = (property: keyof TableData) => (event: React.MouseEvent) => { onRequestSort(event, property); }; return ( {headCells.map((headCell) => ( {headCell.label} {orderBy === headCell.id ? ( {order === "desc" ? "sorted descending" : "sorted ascending"} ) : null} ))} ); } function Row(props: { row: TableData; selected: string | undefined; setSelected: (a: string | undefined) => void; }) { function renderStatus(status: NodeStatusKeys) { switch (status) { case NodeStatus.Online: return ( Online ); case NodeStatus.Offline: return ( Offline ); case NodeStatus.Pending: return ( Pending ); } } const { row, selected, setSelected } = props; const [open, setOpen] = React.useState(false); //const labelId = `enhanced-table-checkbox-${index}`; // Speed optimization. We compare string pointers here instead of the string content. const isSelected = selected == row.name; const handleClick = (event: React.MouseEvent, name: string) => { if (isSelected) { setSelected(undefined); } else { setSelected(name); } }; const debug = true; const debugSx = debug ? { "--Grid-borderWidth": "1px", borderTop: "var(--Grid-borderWidth) solid", borderLeft: "var(--Grid-borderWidth) solid", borderColor: "divider", "& > div": { borderRight: "var(--Grid-borderWidth) solid", borderBottom: "var(--Grid-borderWidth) solid", borderColor: "divider", }, } : {}; return ( {/* Rendered Row */} setOpen(!open)} > {open ? : } handleClick(event, row.name)} > {row.name} {row.id} handleClick(event, row.name)} > {renderStatus(row.status)} handleClick(event, row.name)} > {row.last_seen} days ago {/* Row Expansion */} Metadata Hello1 Hello2 ); } export default function NodeTable(props: NodeTableProps) { let { tableData } = props; const theme = useTheme(); const is_xs = useMediaQuery(theme.breakpoints.only("xs")); const [order, setOrder] = React.useState("asc"); const [orderBy, setOrderBy] = React.useState("status"); const [selected, setSelected] = React.useState(undefined); const [page, setPage] = React.useState(0); const [dense, setDense] = React.useState(false); const [rowsPerPage, setRowsPerPage] = React.useState(5); const handleRequestSort = ( event: React.MouseEvent, property: keyof TableData, ) => { const isAsc = orderBy === property && order === "asc"; setOrder(isAsc ? "desc" : "asc"); setOrderBy(property); }; const handleChangePage = (event: unknown, newPage: number) => { setPage(newPage); }; const handleChangeRowsPerPage = ( event: React.ChangeEvent, ) => { setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); }; // Avoid a layout jump when reaching the last page with empty rows. const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - tableData.length) : 0; const visibleRows = React.useMemo( () => stableSort(tableData, getComparator(order, orderBy)).slice( page * rowsPerPage, page * rowsPerPage + rowsPerPage, ), [order, orderBy, page, rowsPerPage, tableData], ); return ( setSelected(undefined)} /> {visibleRows.map((row, index) => { const labelId = `enhanced-table-checkbox-${index}`; return ( ); })} {emptyRows > 0 && ( )}
{/* TODO: This creates the error Warning: Prop `id` did not match. Server: ":RspmmcqH1:" Client: ":R3j6qpj9H1:" */}
); }