@@ -1,16 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { tableData } from "@/data/nodeDataStatic";
|
||||
import { StrictMode } from "react";
|
||||
import { NodeTable } from "@/components/table";
|
||||
import { useMachines } from "@/components/hooks/useMachines";
|
||||
|
||||
export default function Page() {
|
||||
//const { data, isLoading } = useMachines();
|
||||
//console.log({ data, isLoading });
|
||||
return (
|
||||
<StrictMode>
|
||||
<NodeTable tableData={tableData} />
|
||||
<NodeTable />
|
||||
</StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
import React, { useMemo } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import Grid2 from "@mui/material/Unstable_Grid2";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import { useTheme } from "@mui/material";
|
||||
|
||||
import { NodeStatus, TableData } from "@/data/nodeData";
|
||||
import { PieCards } from "./pieCards";
|
||||
import { PieData, NodePieChart } from "./nodePieChart";
|
||||
import { Machine } from "@/api/model/machine";
|
||||
import { Status } from "@/api/model";
|
||||
|
||||
interface EnhancedTableToolbarProps {
|
||||
tableData: TableData[];
|
||||
tableData: readonly Machine[];
|
||||
}
|
||||
|
||||
export function EnhancedTableToolbar(
|
||||
@@ -24,13 +24,13 @@ export function EnhancedTableToolbar(
|
||||
|
||||
const pieData: PieData[] = useMemo(() => {
|
||||
const online = tableData.filter(
|
||||
(row) => row.status === NodeStatus.Online,
|
||||
(row) => row.status === Status.online,
|
||||
).length;
|
||||
const offline = tableData.filter(
|
||||
(row) => row.status === NodeStatus.Offline,
|
||||
(row) => row.status === Status.offline,
|
||||
).length;
|
||||
const pending = tableData.filter(
|
||||
(row) => row.status === NodeStatus.Pending,
|
||||
(row) => row.status === Status.unknown,
|
||||
).length;
|
||||
|
||||
return [
|
||||
|
||||
@@ -10,15 +10,13 @@ import CircleIcon from "@mui/icons-material/Circle";
|
||||
import Stack from "@mui/material/Stack/Stack";
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
||||
|
||||
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
||||
import { Collapse, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Collapse } from "@mui/material";
|
||||
import { Machine, Status } from "@/api/model";
|
||||
|
||||
import { NodeStatus, NodeStatusKeys, TableData } from "@/data/nodeData";
|
||||
|
||||
function renderStatus(status: NodeStatusKeys) {
|
||||
function renderStatus(status: Status) {
|
||||
switch (status) {
|
||||
case NodeStatus.Online:
|
||||
case Status.online:
|
||||
return (
|
||||
<Stack direction="row" alignItems="center" gap={1}>
|
||||
<CircleIcon color="success" style={{ fontSize: 15 }} />
|
||||
@@ -28,7 +26,7 @@ function renderStatus(status: NodeStatusKeys) {
|
||||
</Stack>
|
||||
);
|
||||
|
||||
case NodeStatus.Offline:
|
||||
case Status.offline:
|
||||
return (
|
||||
<Stack direction="row" alignItems="center" gap={1}>
|
||||
<CircleIcon color="error" style={{ fontSize: 15 }} />
|
||||
@@ -37,7 +35,7 @@ function renderStatus(status: NodeStatusKeys) {
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
case NodeStatus.Pending:
|
||||
case Status.unknown:
|
||||
return (
|
||||
<Stack direction="row" alignItems="center" gap={1}>
|
||||
<CircleIcon color="warning" style={{ fontSize: 15 }} />
|
||||
@@ -49,16 +47,12 @@ function renderStatus(status: NodeStatusKeys) {
|
||||
}
|
||||
}
|
||||
export function NodeRow(props: {
|
||||
row: TableData;
|
||||
row: Machine;
|
||||
selected: string | undefined;
|
||||
setSelected: (a: string | undefined) => void;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const is_phone = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
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;
|
||||
@@ -109,15 +103,6 @@ export function NodeRow(props: {
|
||||
>
|
||||
{renderStatus(row.status)}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
align="right"
|
||||
onClick={(event) => handleClick(event, row.name)}
|
||||
>
|
||||
<Typography component="div" align="left" variant="body1">
|
||||
{String(row.last_seen).padStart(3, "0")}{" "}
|
||||
{is_phone ? "days" : "days ago"}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
{/* Row Expansion */}
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
"use client";
|
||||
|
||||
import { useState, ChangeEvent, SetStateAction, Dispatch } from "react";
|
||||
import { useState, ChangeEvent, useMemo } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import TablePagination from "@mui/material/TablePagination";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import { useTheme } from "@mui/material";
|
||||
import { CircularProgress, Grid, useTheme } from "@mui/material";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
|
||||
import { TableData } from "@/data/nodeData";
|
||||
import { EnhancedTableToolbar } from "./enhancedTableToolbar";
|
||||
import { StickySpeedDial } from "./stickySpeedDial";
|
||||
import { NodeTableContainer } from "./nodeTableContainer";
|
||||
import { SearchBar } from "./searchBar";
|
||||
import Grid2 from "@mui/material/Unstable_Grid2/Grid2";
|
||||
import { useMachines } from "../hooks/useMachines";
|
||||
import { Machine } from "@/api/model/machine";
|
||||
|
||||
export interface NodeTableProps {
|
||||
tableData: TableData[];
|
||||
}
|
||||
|
||||
export function NodeTable(props: NodeTableProps) {
|
||||
let { tableData } = props;
|
||||
export function NodeTable() {
|
||||
const machines = useMachines();
|
||||
|
||||
const theme = useTheme();
|
||||
const is_xs = useMediaQuery(theme.breakpoints.only("xs"));
|
||||
@@ -30,7 +24,15 @@ export function NodeTable(props: NodeTableProps) {
|
||||
const [selected, setSelected] = useState<string | undefined>(undefined);
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
||||
const [filteredList, setFilteredList] = useState<TableData[]>(tableData);
|
||||
const [filteredList, setFilteredList] = useState<readonly Machine[]>([]);
|
||||
|
||||
const tableData = useMemo(() => {
|
||||
const tableData = machines.data.map((machine) => {
|
||||
return { name: machine.name, status: machine.status };
|
||||
});
|
||||
setFilteredList(tableData);
|
||||
return tableData;
|
||||
}, [machines.data]);
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
@@ -41,38 +43,53 @@ export function NodeTable(props: NodeTableProps) {
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Paper sx={{ width: "100%", mb: 2 }}>
|
||||
<StickySpeedDial selected={selected} />
|
||||
<EnhancedTableToolbar tableData={tableData}>
|
||||
<Grid2 xs={12}>
|
||||
<SearchBar
|
||||
tableData={tableData}
|
||||
setFilteredList={setFilteredList}
|
||||
/>
|
||||
</Grid2>
|
||||
</EnhancedTableToolbar>
|
||||
<NodeTableContainer
|
||||
tableData={filteredList}
|
||||
page={page}
|
||||
rowsPerPage={rowsPerPage}
|
||||
dense={false}
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
/>
|
||||
{/* TODO: This creates the error Warning: Prop `id` did not match. Server: ":RspmmcqH1:" Client: ":R3j6qpj9H1:" */}
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
labelRowsPerPage={is_xs ? "Rows" : "Rows per page:"}
|
||||
component="div"
|
||||
count={filteredList.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
if (machines.isLoading) {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
style={{ height: "100vh" }} // make the container fill the screen height
|
||||
alignItems="center" // center the items vertically
|
||||
justifyContent="center" // center the items horizontally
|
||||
>
|
||||
<CircularProgress size={80} color="secondary" />
|
||||
</Grid>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Paper sx={{ width: "100%", mb: 2 }}>
|
||||
<StickySpeedDial selected={selected} />
|
||||
<EnhancedTableToolbar tableData={tableData}>
|
||||
<Grid2 xs={12}>
|
||||
<SearchBar
|
||||
tableData={tableData}
|
||||
setFilteredList={setFilteredList}
|
||||
/>
|
||||
</Grid2>
|
||||
</EnhancedTableToolbar>
|
||||
|
||||
<NodeTableContainer
|
||||
tableData={filteredList}
|
||||
page={page}
|
||||
rowsPerPage={rowsPerPage}
|
||||
dense={false}
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
/>
|
||||
|
||||
{/* TODO: This creates the error Warning: Prop `id` did not match. Server: ":RspmmcqH1:" Client: ":R3j6qpj9H1:" */}
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
labelRowsPerPage={is_xs ? "Rows" : "Rows per page:"}
|
||||
component="div"
|
||||
count={filteredList.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,11 @@ 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";
|
||||
import { Machine } from "@/api/model/machine";
|
||||
|
||||
interface HeadCell {
|
||||
disablePadding: boolean;
|
||||
id: keyof TableData;
|
||||
id: keyof Machine;
|
||||
label: string;
|
||||
alignRight: boolean;
|
||||
}
|
||||
@@ -28,7 +26,7 @@ const headCells: readonly HeadCell[] = [
|
||||
id: "name",
|
||||
alignRight: false,
|
||||
disablePadding: false,
|
||||
label: "DISPLAY NAME & ID",
|
||||
label: "DOMAIN NAME",
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
@@ -36,12 +34,6 @@ const headCells: readonly HeadCell[] = [
|
||||
disablePadding: false,
|
||||
label: "STATUS",
|
||||
},
|
||||
{
|
||||
id: "last_seen",
|
||||
alignRight: false,
|
||||
disablePadding: false,
|
||||
label: "LAST SEEN",
|
||||
},
|
||||
];
|
||||
|
||||
function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
|
||||
@@ -90,7 +82,7 @@ function stableSort<T>(
|
||||
interface EnhancedTableProps {
|
||||
onRequestSort: (
|
||||
event: React.MouseEvent<unknown>,
|
||||
property: keyof TableData,
|
||||
property: keyof Machine,
|
||||
) => void;
|
||||
order: NodeOrder;
|
||||
orderBy: string;
|
||||
@@ -100,7 +92,7 @@ interface EnhancedTableProps {
|
||||
function EnhancedTableHead(props: EnhancedTableProps) {
|
||||
const { order, orderBy, onRequestSort } = props;
|
||||
const createSortHandler =
|
||||
(property: keyof TableData) => (event: React.MouseEvent<unknown>) => {
|
||||
(property: keyof Machine) => (event: React.MouseEvent<unknown>) => {
|
||||
onRequestSort(event, property);
|
||||
};
|
||||
|
||||
@@ -135,7 +127,7 @@ function EnhancedTableHead(props: EnhancedTableProps) {
|
||||
}
|
||||
|
||||
interface NodeTableContainerProps {
|
||||
tableData: readonly TableData[];
|
||||
tableData: readonly Machine[];
|
||||
page: number;
|
||||
rowsPerPage: number;
|
||||
dense: boolean;
|
||||
@@ -146,10 +138,7 @@ interface NodeTableContainerProps {
|
||||
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"));
|
||||
const [orderBy, setOrderBy] = React.useState<keyof Machine>("status");
|
||||
|
||||
// Avoid a layout jump when reaching the last page with empty rows.
|
||||
const emptyRows =
|
||||
@@ -157,7 +146,7 @@ export function NodeTableContainer(props: NodeTableContainerProps) {
|
||||
|
||||
const handleRequestSort = (
|
||||
event: React.MouseEvent<unknown>,
|
||||
property: keyof TableData,
|
||||
property: keyof Machine,
|
||||
) => {
|
||||
const isAsc = orderBy === property && order === "asc";
|
||||
setOrder(isAsc ? "desc" : "asc");
|
||||
@@ -185,7 +174,7 @@ export function NodeTableContainer(props: NodeTableContainerProps) {
|
||||
{visibleRows.map((row, index) => {
|
||||
return (
|
||||
<NodeRow
|
||||
key={row.name}
|
||||
key={index}
|
||||
row={row}
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
|
||||
@@ -1,34 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
SetStateAction,
|
||||
Dispatch,
|
||||
useState,
|
||||
useEffect,
|
||||
useRef,
|
||||
useMemo,
|
||||
ClassAttributes,
|
||||
JSX,
|
||||
Key,
|
||||
LiHTMLAttributes,
|
||||
} from "react";
|
||||
import { SetStateAction, Dispatch, useState, useEffect, useMemo } from "react";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import { useDebounce } from "../hooks/useDebounce";
|
||||
import { TableData } from "@/data/nodeData";
|
||||
import {
|
||||
Autocomplete,
|
||||
Box,
|
||||
Container,
|
||||
InputAdornment,
|
||||
Stack,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import { Autocomplete, InputAdornment, TextField } from "@mui/material";
|
||||
import { Machine } from "@/api/model/machine";
|
||||
|
||||
export interface SearchBarProps {
|
||||
tableData: TableData[];
|
||||
setFilteredList: Dispatch<SetStateAction<TableData[]>>;
|
||||
tableData: readonly Machine[];
|
||||
setFilteredList: Dispatch<SetStateAction<readonly Machine[]>>;
|
||||
}
|
||||
|
||||
export function SearchBar(props: SearchBarProps) {
|
||||
@@ -52,7 +33,7 @@ export function SearchBar(props: SearchBarProps) {
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedSearch) {
|
||||
const filtered: TableData[] = tableData.filter((row) => {
|
||||
const filtered: Machine[] = tableData.filter((row) => {
|
||||
return row.name.toLowerCase().includes(debouncedSearch.toLowerCase());
|
||||
});
|
||||
setFilteredList(filtered);
|
||||
|
||||
Reference in New Issue
Block a user