Qubasa-Qubasa-main (#262)
All checks were successful
checks-impure / test (push) Successful in 6s
checks / test (push) Successful in 49s

UI: Connected UI to API
This commit is contained in:
Luis Hebendanz 2023-09-11 12:59:24 +00:00
parent bf4db16209
commit aa5976f046
6 changed files with 93 additions and 125 deletions

View File

@ -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>
);
}

View File

@ -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 [

View File

@ -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 */}

View File

@ -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>
);
}
}

View File

@ -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}

View File

@ -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);