UI: Splitted NideList into multiple components. Generated random user data for benchmarking
This commit is contained in:
@@ -1,56 +1,21 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { alpha } from "@mui/material/styles";
|
|
||||||
import Box from "@mui/material/Box";
|
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 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 TableRow from "@mui/material/TableRow";
|
||||||
import TableSortLabel from "@mui/material/TableSortLabel";
|
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Paper from "@mui/material/Paper";
|
|
||||||
import IconButton from "@mui/material/IconButton";
|
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 CircleIcon from "@mui/icons-material/Circle";
|
||||||
import Stack from "@mui/material/Stack/Stack";
|
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 KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||||
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
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 Link from "next/link";
|
|
||||||
|
|
||||||
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
||||||
import {
|
import { Collapse } from "@mui/material";
|
||||||
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";
|
import { NodeStatus, NodeStatusKeys, TableData } from "@/data/nodeData";
|
||||||
import StickySpeedDial from "./StickySpeedDial";
|
|
||||||
import { jsx } from "@emotion/react";
|
|
||||||
|
|
||||||
export default function Row(props: {
|
export default function NodeRow(props: {
|
||||||
row: TableData;
|
row: TableData;
|
||||||
selected: string | undefined;
|
selected: string | undefined;
|
||||||
setSelected: (a: string | undefined) => void;
|
setSelected: (a: string | undefined) => void;
|
||||||
@@ -93,13 +58,13 @@ export default function Row(props: {
|
|||||||
//const labelId = `enhanced-table-checkbox-${index}`;
|
//const labelId = `enhanced-table-checkbox-${index}`;
|
||||||
|
|
||||||
// Speed optimization. We compare string pointers here instead of the string content.
|
// Speed optimization. We compare string pointers here instead of the string content.
|
||||||
const isSelected = selected == row.name;
|
const isSelected = selected == row.id;
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<unknown>, name: string) => {
|
const handleClick = (event: React.MouseEvent<unknown>, id: string) => {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
setSelected(undefined);
|
setSelected(undefined);
|
||||||
} else {
|
} else {
|
||||||
setSelected(name);
|
setSelected(id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -142,7 +107,7 @@ export default function Row(props: {
|
|||||||
<TableCell
|
<TableCell
|
||||||
component="th"
|
component="th"
|
||||||
scope="row"
|
scope="row"
|
||||||
onClick={(event) => handleClick(event, row.name)}
|
onClick={(event) => handleClick(event, row.id)}
|
||||||
>
|
>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Typography component="div" align="left" variant="body1">
|
<Typography component="div" align="left" variant="body1">
|
||||||
@@ -1,126 +1,19 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { alpha } from "@mui/material/styles";
|
|
||||||
import Box from "@mui/material/Box";
|
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 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 Paper from "@mui/material/Paper";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
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 SearchIcon from "@mui/icons-material/Search";
|
||||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
import NodeTableContainer from "./NodeTableContainer";
|
||||||
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 Link from "next/link";
|
|
||||||
import Row from "./Row";
|
|
||||||
|
|
||||||
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
import { useTheme } from "@mui/material";
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Collapse,
|
|
||||||
Container,
|
|
||||||
FormGroup,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import hexRgb from "hex-rgb";
|
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
import { NodeStatus, NodeStatusKeys, TableData } from "@/data/nodeData";
|
import { TableData } from "@/data/nodeData";
|
||||||
import EnhancedTableToolbar from "./EnhancedTableToolbar";
|
import EnhancedTableToolbar from "./EnhancedTableToolbar";
|
||||||
import { jsx } from "@emotion/react";
|
import { table } from "console";
|
||||||
|
|
||||||
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<T>(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<Key extends keyof any>(
|
|
||||||
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<T>(
|
|
||||||
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 SearchBar() {
|
function SearchBar() {
|
||||||
const [search, setSearch] = React.useState<string | undefined>(undefined);
|
const [search, setSearch] = React.useState<string | undefined>(undefined);
|
||||||
@@ -145,81 +38,15 @@ export interface NodeTableProps {
|
|||||||
tableData: TableData[];
|
tableData: TableData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EnhancedTableProps {
|
|
||||||
onRequestSort: (
|
|
||||||
event: React.MouseEvent<unknown>,
|
|
||||||
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<unknown>) => {
|
|
||||||
onRequestSort(event, property);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell id="dropdown" colSpan={1} />
|
|
||||||
{headCells.map((headCell) => (
|
|
||||||
<TableCell
|
|
||||||
key={headCell.id}
|
|
||||||
align={headCell.alignRight ? "right" : "left"}
|
|
||||||
padding={headCell.disablePadding ? "none" : "normal"}
|
|
||||||
sortDirection={orderBy === headCell.id ? order : false}
|
|
||||||
>
|
|
||||||
<TableSortLabel
|
|
||||||
active={orderBy === headCell.id}
|
|
||||||
direction={orderBy === headCell.id ? order : "asc"}
|
|
||||||
onClick={createSortHandler(headCell.id)}
|
|
||||||
>
|
|
||||||
{headCell.label}
|
|
||||||
{orderBy === headCell.id ? (
|
|
||||||
<Box component="span" sx={visuallyHidden}>
|
|
||||||
{order === "desc" ? "sorted descending" : "sorted ascending"}
|
|
||||||
</Box>
|
|
||||||
) : null}
|
|
||||||
</TableSortLabel>
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function NodeTable(props: NodeTableProps) {
|
export default function NodeTable(props: NodeTableProps) {
|
||||||
let { tableData } = props;
|
let { tableData } = props;
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const is_xs = useMediaQuery(theme.breakpoints.only("xs"));
|
const is_xs = useMediaQuery(theme.breakpoints.only("xs"));
|
||||||
|
|
||||||
const [order, setOrder] = React.useState<Order>("asc");
|
|
||||||
const [orderBy, setOrderBy] = React.useState<keyof TableData>("status");
|
|
||||||
const [selected, setSelected] = React.useState<string | undefined>(undefined);
|
const [selected, setSelected] = React.useState<string | undefined>(undefined);
|
||||||
const [page, setPage] = React.useState(0);
|
const [page, setPage] = React.useState(0);
|
||||||
const [dense, setDense] = React.useState(false);
|
|
||||||
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
||||||
const [search, setSearch] = React.useState<string>("");
|
|
||||||
|
|
||||||
const filteredTableData = React.useMemo(() => {
|
|
||||||
return tableData.filter((row) => {
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}, [search]);
|
|
||||||
|
|
||||||
const handleRequestSort = (
|
|
||||||
event: React.MouseEvent<unknown>,
|
|
||||||
property: keyof TableData,
|
|
||||||
) => {
|
|
||||||
const isAsc = orderBy === property && order === "asc";
|
|
||||||
setOrder(isAsc ? "desc" : "asc");
|
|
||||||
setOrderBy(property);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChangePage = (event: unknown, newPage: number) => {
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
setPage(newPage);
|
setPage(newPage);
|
||||||
@@ -232,60 +59,18 @@ export default function NodeTable(props: NodeTableProps) {
|
|||||||
setPage(0);
|
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 (
|
return (
|
||||||
<Box sx={{ width: "100%" }}>
|
<Box sx={{ width: "100%" }}>
|
||||||
<Paper sx={{ width: "100%", mb: 2 }}>
|
<Paper sx={{ width: "100%", mb: 2 }}>
|
||||||
<EnhancedTableToolbar tableData={tableData} selected={selected} />
|
<EnhancedTableToolbar tableData={tableData} selected={selected} />
|
||||||
<TableContainer>
|
<NodeTableContainer
|
||||||
<Table
|
tableData={tableData}
|
||||||
sx={{ minWidth: 750 }}
|
page={page}
|
||||||
aria-labelledby="tableTitle"
|
rowsPerPage={rowsPerPage}
|
||||||
size={dense ? "small" : "medium"}
|
dense={false}
|
||||||
>
|
selected={selected}
|
||||||
<EnhancedTableHead
|
setSelected={setSelected}
|
||||||
order={order}
|
/>
|
||||||
orderBy={orderBy}
|
|
||||||
onRequestSort={handleRequestSort}
|
|
||||||
rowCount={tableData.length}
|
|
||||||
/>
|
|
||||||
<TableBody>
|
|
||||||
{visibleRows.map((row, index) => {
|
|
||||||
const labelId = `enhanced-table-checkbox-${index}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row
|
|
||||||
key={row.id}
|
|
||||||
row={row}
|
|
||||||
selected={selected}
|
|
||||||
setSelected={setSelected}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{emptyRows > 0 && (
|
|
||||||
<TableRow
|
|
||||||
style={{
|
|
||||||
height: (dense ? 33 : 53) * emptyRows,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TableCell colSpan={6} />
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
{/* TODO: This creates the error Warning: Prop `id` did not match. Server: ":RspmmcqH1:" Client: ":R3j6qpj9H1:" */}
|
{/* TODO: This creates the error Warning: Prop `id` did not match. Server: ":RspmmcqH1:" Client: ":R3j6qpj9H1:" */}
|
||||||
<TablePagination
|
<TablePagination
|
||||||
rowsPerPageOptions={[5, 10, 25]}
|
rowsPerPageOptions={[5, 10, 25]}
|
||||||
|
|||||||
209
pkgs/ui/src/app/nodes/NodeTableContainer.tsx
Normal file
209
pkgs/ui/src/app/nodes/NodeTableContainer.tsx
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
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 TableRow from "@mui/material/TableRow";
|
||||||
|
import TableSortLabel from "@mui/material/TableSortLabel";
|
||||||
|
import { visuallyHidden } from "@mui/utils";
|
||||||
|
import NodeRow from "./NodeRow";
|
||||||
|
|
||||||
|
import { 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<T>(a: T, b: T, orderBy: keyof T) {
|
||||||
|
if (b[orderBy] < a[orderBy]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (b[orderBy] > a[orderBy]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NodeOrder = "asc" | "desc";
|
||||||
|
|
||||||
|
function getComparator<Key extends keyof any>(
|
||||||
|
order: NodeOrder,
|
||||||
|
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<T>(
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EnhancedTableProps {
|
||||||
|
onRequestSort: (
|
||||||
|
event: React.MouseEvent<unknown>,
|
||||||
|
property: keyof TableData,
|
||||||
|
) => void;
|
||||||
|
order: NodeOrder;
|
||||||
|
orderBy: string;
|
||||||
|
rowCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EnhancedTableHead(props: EnhancedTableProps) {
|
||||||
|
const { order, orderBy, onRequestSort } = props;
|
||||||
|
const createSortHandler =
|
||||||
|
(property: keyof TableData) => (event: React.MouseEvent<unknown>) => {
|
||||||
|
onRequestSort(event, property);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell id="dropdown" colSpan={1} />
|
||||||
|
{headCells.map((headCell) => (
|
||||||
|
<TableCell
|
||||||
|
key={headCell.id}
|
||||||
|
align={headCell.alignRight ? "right" : "left"}
|
||||||
|
padding={headCell.disablePadding ? "none" : "normal"}
|
||||||
|
sortDirection={orderBy === headCell.id ? order : false}
|
||||||
|
>
|
||||||
|
<TableSortLabel
|
||||||
|
active={orderBy === headCell.id}
|
||||||
|
direction={orderBy === headCell.id ? order : "asc"}
|
||||||
|
onClick={createSortHandler(headCell.id)}
|
||||||
|
>
|
||||||
|
{headCell.label}
|
||||||
|
{orderBy === headCell.id ? (
|
||||||
|
<Box component="span" sx={visuallyHidden}>
|
||||||
|
{order === "desc" ? "sorted descending" : "sorted ascending"}
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
</TableSortLabel>
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NodeTableContainerProps {
|
||||||
|
tableData: readonly TableData[];
|
||||||
|
page: number;
|
||||||
|
rowsPerPage: number;
|
||||||
|
dense: boolean;
|
||||||
|
selected: string | undefined;
|
||||||
|
setSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NodeTableContainer(props: NodeTableContainerProps) {
|
||||||
|
const { tableData, page, rowsPerPage, dense, selected, setSelected } = props;
|
||||||
|
const [order, setOrder] = React.useState<NodeOrder>("asc");
|
||||||
|
const [orderBy, setOrderBy] = React.useState<keyof TableData>("status");
|
||||||
|
|
||||||
|
// 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 handleRequestSort = (
|
||||||
|
event: React.MouseEvent<unknown>,
|
||||||
|
property: keyof TableData,
|
||||||
|
) => {
|
||||||
|
const isAsc = orderBy === property && order === "asc";
|
||||||
|
setOrder(isAsc ? "desc" : "asc");
|
||||||
|
setOrderBy(property);
|
||||||
|
};
|
||||||
|
|
||||||
|
const visibleRows = React.useMemo(
|
||||||
|
() =>
|
||||||
|
stableSort(tableData, getComparator(order, orderBy)).slice(
|
||||||
|
page * rowsPerPage,
|
||||||
|
page * rowsPerPage + rowsPerPage,
|
||||||
|
),
|
||||||
|
[order, orderBy, page, rowsPerPage, tableData],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<TableContainer>
|
||||||
|
<Table
|
||||||
|
sx={{ minWidth: 750 }}
|
||||||
|
aria-labelledby="tableTitle"
|
||||||
|
size={dense ? "small" : "medium"}
|
||||||
|
>
|
||||||
|
<EnhancedTableHead
|
||||||
|
order={order}
|
||||||
|
orderBy={orderBy}
|
||||||
|
onRequestSort={handleRequestSort}
|
||||||
|
rowCount={tableData.length}
|
||||||
|
/>
|
||||||
|
<TableBody>
|
||||||
|
{visibleRows.map((row, index) => {
|
||||||
|
const labelId = `enhanced-table-checkbox-${index}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodeRow
|
||||||
|
key={row.id}
|
||||||
|
row={row}
|
||||||
|
selected={selected}
|
||||||
|
setSelected={setSelected}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{emptyRows > 0 && (
|
||||||
|
<TableRow
|
||||||
|
style={{
|
||||||
|
height: (dense ? 33 : 53) * emptyRows,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TableCell colSpan={6} />
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,13 +3,13 @@
|
|||||||
import NodeTable from "./NodeTable";
|
import NodeTable from "./NodeTable";
|
||||||
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import { tableData } from "@/data/nodeData";
|
import { tableData, executeCreateData } from "@/data/nodeData";
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<NodeTable tableData={tableData} />
|
<NodeTable tableData={executeCreateData()} />
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ function createData(
|
|||||||
status: NodeStatusKeys,
|
status: NodeStatusKeys,
|
||||||
last_seen: number,
|
last_seen: number,
|
||||||
): TableData {
|
): TableData {
|
||||||
|
if (status == NodeStatus.Online) {
|
||||||
|
last_seen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
id,
|
id,
|
||||||
@@ -27,6 +31,67 @@ function createData(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A function to generate random names
|
||||||
|
function getRandomName(): string {
|
||||||
|
let names = [
|
||||||
|
"Alice",
|
||||||
|
"Bob",
|
||||||
|
"Charlie",
|
||||||
|
"David",
|
||||||
|
"Eve",
|
||||||
|
"Frank",
|
||||||
|
"Grace",
|
||||||
|
"Heidi",
|
||||||
|
"Ivan",
|
||||||
|
"Judy",
|
||||||
|
"Mallory",
|
||||||
|
"Oscar",
|
||||||
|
"Peggy",
|
||||||
|
"Sybil",
|
||||||
|
"Trent",
|
||||||
|
"Victor",
|
||||||
|
"Walter",
|
||||||
|
"Wendy",
|
||||||
|
"Zoe",
|
||||||
|
];
|
||||||
|
let index = Math.floor(Math.random() * names.length);
|
||||||
|
return names[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// A function to generate random IPv6 addresses
|
||||||
|
function getRandomId(): string {
|
||||||
|
let hex = "0123456789abcdef";
|
||||||
|
let id = "";
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
for (let j = 0; j < 4; j++) {
|
||||||
|
let index = Math.floor(Math.random() * hex.length);
|
||||||
|
id += hex[index];
|
||||||
|
}
|
||||||
|
if (i < 7) {
|
||||||
|
id += ":";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A function to generate random status keys
|
||||||
|
function getRandomStatus(): NodeStatusKeys {
|
||||||
|
let statusKeys = [NodeStatus.Online, NodeStatus.Offline, NodeStatus.Pending];
|
||||||
|
let index = Math.floor(Math.random() * statusKeys.length);
|
||||||
|
return statusKeys[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// A function to generate random last seen values
|
||||||
|
function getRandomLastSeen(status: NodeStatusKeys): number {
|
||||||
|
if (status === "online") {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
let min = 1; // One day ago
|
||||||
|
let max = 360; // One year ago
|
||||||
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const tableData = [
|
export const tableData = [
|
||||||
createData(
|
createData(
|
||||||
"Matchbox",
|
"Matchbox",
|
||||||
@@ -97,3 +162,19 @@ export const tableData = [
|
|||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// A function to execute the createData function with dummy data in a loop 100 times and return an array
|
||||||
|
export function executeCreateData(): TableData[] {
|
||||||
|
let result: TableData[] = [];
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
// Generate dummy data
|
||||||
|
let name = getRandomName();
|
||||||
|
let id = getRandomId();
|
||||||
|
let status = getRandomStatus();
|
||||||
|
let last_seen = getRandomLastSeen(status);
|
||||||
|
|
||||||
|
// Call the createData function and push the result to the array
|
||||||
|
result.push(createData(name, id, status, last_seen));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user