209 lines
5.8 KiB
TypeScript
209 lines
5.8 KiB
TypeScript
"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";
|
|
|
|
import { useMediaQuery, useTheme } from "@mui/material";
|
|
|
|
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 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");
|
|
|
|
const theme = useTheme();
|
|
const is_phone = useMediaQuery(theme.breakpoints.down("sm"));
|
|
|
|
// 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 aria-labelledby="tableTitle" size={dense ? "small" : "medium"}>
|
|
<EnhancedTableHead
|
|
order={order}
|
|
orderBy={orderBy}
|
|
onRequestSort={handleRequestSort}
|
|
rowCount={tableData.length}
|
|
/>
|
|
<TableBody>
|
|
{visibleRows.map((row, index) => {
|
|
return (
|
|
<NodeRow
|
|
key={row.name}
|
|
row={row}
|
|
selected={selected}
|
|
setSelected={setSelected}
|
|
/>
|
|
);
|
|
})}
|
|
{emptyRows > 0 && (
|
|
<TableRow
|
|
style={{
|
|
height: (dense ? 33 : 53) * emptyRows,
|
|
}}
|
|
>
|
|
<TableCell colSpan={6} />
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
);
|
|
}
|