Merge pull request 'Fixed mobile layout in Pie Chart and in table' (#141) from Qubasa-Qubasa-main into main
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { ChangeEvent, useState } from "react";
|
import { ChangeEvent, useState } from "react";
|
||||||
|
|
||||||
@@ -38,8 +39,21 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const userPrefersDarkmode = useMediaQuery("(prefers-color-scheme: dark)");
|
const userPrefersDarkmode = useMediaQuery("(prefers-color-scheme: dark)");
|
||||||
|
const theme = useTheme();
|
||||||
|
const is_small = useMediaQuery(theme.breakpoints.down("sm"));
|
||||||
|
|
||||||
let [useDarkTheme, setUseDarkTheme] = useState(false);
|
let [useDarkTheme, setUseDarkTheme] = useState(false);
|
||||||
let [showSidebar, setShowSidebar] = useState(true);
|
let [showSidebar, setShowSidebar] = useState(true);
|
||||||
|
|
||||||
|
// If the screen is small, hide the sidebar
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (is_small) {
|
||||||
|
setShowSidebar(false);
|
||||||
|
} else {
|
||||||
|
setShowSidebar(true);
|
||||||
|
}
|
||||||
|
}, [is_small]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (useDarkTheme !== userPrefersDarkmode) {
|
if (useDarkTheme !== userPrefersDarkmode) {
|
||||||
// Enable dark theme if the user prefers dark mode
|
// Enable dark theme if the user prefers dark mode
|
||||||
|
|||||||
@@ -27,18 +27,21 @@ import Stack from "@mui/material/Stack/Stack";
|
|||||||
import ModeIcon from "@mui/icons-material/Mode";
|
import ModeIcon from "@mui/icons-material/Mode";
|
||||||
import ClearIcon from "@mui/icons-material/Clear";
|
import ClearIcon from "@mui/icons-material/Clear";
|
||||||
import Fade from "@mui/material/Fade/Fade";
|
import Fade from "@mui/material/Fade/Fade";
|
||||||
|
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||||
|
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
||||||
import NodePieChart, { PieData } from "./NodePieChart";
|
import NodePieChart, { PieData } from "./NodePieChart";
|
||||||
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
|
Collapse,
|
||||||
Container,
|
Container,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import hexRgb from "hex-rgb";
|
import hexRgb from "hex-rgb";
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
import { NodeStatus, TableData } from "@/data/nodeData";
|
import { NodeStatus, NodeStatusKeys, TableData } from "@/data/nodeData";
|
||||||
|
|
||||||
interface HeadCell {
|
interface HeadCell {
|
||||||
disablePadding: boolean;
|
disablePadding: boolean;
|
||||||
@@ -111,52 +114,6 @@ function stableSort<T>(
|
|||||||
return stabilizedThis.map((el) => el[0]);
|
return stabilizedThis.map((el) => el[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
|
||||||
{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 EnhancedTableToolbarProps {
|
interface EnhancedTableToolbarProps {
|
||||||
selected: string | undefined;
|
selected: string | undefined;
|
||||||
tableData: TableData[];
|
tableData: TableData[];
|
||||||
@@ -194,9 +151,9 @@ function EnhancedTableToolbar(props: EnhancedTableToolbarProps) {
|
|||||||
).length;
|
).length;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ name: "Online", value: online, color: "#2E7D32" },
|
{ name: "Online", value: online, color: theme.palette.success.main },
|
||||||
{ name: "Offline", value: offline, color: "#db3927" },
|
{ name: "Offline", value: offline, color: theme.palette.error.main },
|
||||||
{ name: "Pending", value: pending, color: "#FFBB28" },
|
{ name: "Pending", value: pending, color: theme.palette.warning.main },
|
||||||
];
|
];
|
||||||
}, [tableData]);
|
}, [tableData]);
|
||||||
|
|
||||||
@@ -230,7 +187,7 @@ function EnhancedTableToolbar(props: EnhancedTableToolbarProps) {
|
|||||||
height: 110,
|
height: 110,
|
||||||
backgroundColor: hexRgb(pieItem.color, {
|
backgroundColor: hexRgb(pieItem.color, {
|
||||||
format: "css",
|
format: "css",
|
||||||
alpha: 0.18,
|
alpha: 0.25,
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -337,8 +294,8 @@ function EnhancedTableToolbar(props: EnhancedTableToolbarProps) {
|
|||||||
{/* Pie Chart Grid */}
|
{/* Pie Chart Grid */}
|
||||||
<Grid2
|
<Grid2
|
||||||
key="PieChart"
|
key="PieChart"
|
||||||
lg={6}
|
md={6}
|
||||||
sm={12}
|
xs={12}
|
||||||
display="flex"
|
display="flex"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
@@ -353,7 +310,7 @@ function EnhancedTableToolbar(props: EnhancedTableToolbarProps) {
|
|||||||
key="CardStack"
|
key="CardStack"
|
||||||
lg={6}
|
lg={6}
|
||||||
display="flex"
|
display="flex"
|
||||||
sx={{ display: { lg: "flex", sm: "none" } }}
|
sx={{ display: { lg: "flex", xs: "none", md: "flex" } }}
|
||||||
>
|
>
|
||||||
{cardStack}
|
{cardStack}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
@@ -366,28 +323,63 @@ function EnhancedTableToolbar(props: EnhancedTableToolbarProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderLastSeen(last_seen: number) {
|
export interface NodeTableProps {
|
||||||
|
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 (
|
return (
|
||||||
<Typography component="div" align="left" variant="body1">
|
<TableHead>
|
||||||
{last_seen} days ago
|
<TableRow>
|
||||||
</Typography>
|
<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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderName(name: string, id: string) {
|
function Row(props: {
|
||||||
return (
|
row: TableData;
|
||||||
<Stack>
|
selected: string | undefined;
|
||||||
<Typography component="div" align="left" variant="body1">
|
setSelected: (a: string | undefined) => void;
|
||||||
{name}
|
}) {
|
||||||
</Typography>
|
function renderStatus(status: NodeStatusKeys) {
|
||||||
<Typography color="grey" component="div" align="left" variant="body2">
|
|
||||||
{id}
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderStatus(status: NodeStatus) {
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NodeStatus.Online:
|
case NodeStatus.Online:
|
||||||
return (
|
return (
|
||||||
@@ -420,12 +412,123 @@ function renderStatus(status: NodeStatus) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeTableProps {
|
const { row, selected, setSelected } = props;
|
||||||
tableData: TableData[];
|
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<unknown>, 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 (
|
||||||
|
<React.Fragment>
|
||||||
|
{/* Rendered Row */}
|
||||||
|
<TableRow
|
||||||
|
hover
|
||||||
|
role="checkbox"
|
||||||
|
aria-checked={isSelected}
|
||||||
|
tabIndex={-1}
|
||||||
|
key={row.name}
|
||||||
|
selected={isSelected}
|
||||||
|
sx={{ cursor: "pointer" }}
|
||||||
|
>
|
||||||
|
<TableCell padding="none">
|
||||||
|
<IconButton
|
||||||
|
aria-label="expand row"
|
||||||
|
size="small"
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
>
|
||||||
|
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
|
||||||
|
</IconButton>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
component="th"
|
||||||
|
scope="row"
|
||||||
|
onClick={(event) => handleClick(event, row.name)}
|
||||||
|
>
|
||||||
|
<Stack>
|
||||||
|
<Typography component="div" align="left" variant="body1">
|
||||||
|
{row.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
color="grey"
|
||||||
|
component="div"
|
||||||
|
align="left"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{row.id}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
align="right"
|
||||||
|
onClick={(event) => handleClick(event, row.name)}
|
||||||
|
>
|
||||||
|
{renderStatus(row.status)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
align="right"
|
||||||
|
onClick={(event) => handleClick(event, row.name)}
|
||||||
|
>
|
||||||
|
<Typography component="div" align="left" variant="body1">
|
||||||
|
{row.last_seen} days ago
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
{/* Row Expansion */}
|
||||||
|
<TableRow>
|
||||||
|
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
|
||||||
|
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||||
|
<Box sx={{ margin: 1 }}>
|
||||||
|
<Typography variant="h6" gutterBottom component="div">
|
||||||
|
Metadata
|
||||||
|
</Typography>
|
||||||
|
<Grid2 container spacing={2} paddingLeft={0}>
|
||||||
|
<Grid2 xs={6} style={{ ...debugSx }} justifyContent="left" display="flex" paddingRight={3}>
|
||||||
|
<Box >Hello1</Box>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 xs={6} style={{ ...debugSx }} paddingLeft={4}>
|
||||||
|
<Box>Hello2</Box>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
</Grid2>
|
||||||
|
</Box>
|
||||||
|
</Collapse>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NodeTable(props: NodeTableProps) {
|
export default function NodeTable(props: NodeTableProps) {
|
||||||
let { tableData } = props;
|
let { tableData } = props;
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const is_xs = useMediaQuery(theme.breakpoints.only("xs"));
|
||||||
|
|
||||||
const [order, setOrder] = React.useState<Order>("asc");
|
const [order, setOrder] = React.useState<Order>("asc");
|
||||||
const [orderBy, setOrderBy] = React.useState<keyof TableData>("status");
|
const [orderBy, setOrderBy] = React.useState<keyof TableData>("status");
|
||||||
const [selected, setSelected] = React.useState<string | undefined>(undefined);
|
const [selected, setSelected] = React.useState<string | undefined>(undefined);
|
||||||
@@ -442,15 +545,6 @@ export default function NodeTable(props: NodeTableProps) {
|
|||||||
setOrderBy(property);
|
setOrderBy(property);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<unknown>, name: string) => {
|
|
||||||
// Speed optimization. We compare string pointers here instead of the string content.
|
|
||||||
if (selected == name) {
|
|
||||||
setSelected(undefined);
|
|
||||||
} else {
|
|
||||||
setSelected(name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleChangePage = (event: unknown, newPage: number) => {
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
setPage(newPage);
|
setPage(newPage);
|
||||||
};
|
};
|
||||||
@@ -462,9 +556,6 @@ export default function NodeTable(props: NodeTableProps) {
|
|||||||
setPage(0);
|
setPage(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Speed optimization. We compare string pointers here instead of the string content.
|
|
||||||
const isSelected = (name: string) => name == selected;
|
|
||||||
|
|
||||||
// Avoid a layout jump when reaching the last page with empty rows.
|
// Avoid a layout jump when reaching the last page with empty rows.
|
||||||
const emptyRows =
|
const emptyRows =
|
||||||
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - tableData.length) : 0;
|
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - tableData.length) : 0;
|
||||||
@@ -479,7 +570,6 @@ export default function NodeTable(props: NodeTableProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper elevation={1} sx={{ margin: 5 }}>
|
|
||||||
<Box sx={{ width: "100%" }}>
|
<Box sx={{ width: "100%" }}>
|
||||||
<Paper sx={{ width: "100%", mb: 2 }}>
|
<Paper sx={{ width: "100%", mb: 2 }}>
|
||||||
<EnhancedTableToolbar
|
<EnhancedTableToolbar
|
||||||
@@ -501,30 +591,15 @@ export default function NodeTable(props: NodeTableProps) {
|
|||||||
/>
|
/>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{visibleRows.map((row, index) => {
|
{visibleRows.map((row, index) => {
|
||||||
const isItemSelected = isSelected(row.name);
|
|
||||||
const labelId = `enhanced-table-checkbox-${index}`;
|
const labelId = `enhanced-table-checkbox-${index}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<Row
|
||||||
hover
|
key={row.id}
|
||||||
onClick={(event) => handleClick(event, row.name)}
|
row={row}
|
||||||
role="checkbox"
|
selected={selected}
|
||||||
aria-checked={isItemSelected}
|
setSelected={setSelected}
|
||||||
tabIndex={-1}
|
/>
|
||||||
key={row.name}
|
|
||||||
selected={isItemSelected}
|
|
||||||
sx={{ cursor: "pointer" }}
|
|
||||||
>
|
|
||||||
<TableCell component="th" id={labelId} scope="row">
|
|
||||||
{renderName(row.name, row.id)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="right">
|
|
||||||
{renderStatus(row.status)}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align="right">
|
|
||||||
{renderLastSeen(row.last_seen)}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{emptyRows > 0 && (
|
{emptyRows > 0 && (
|
||||||
@@ -542,6 +617,7 @@ export default function NodeTable(props: NodeTableProps) {
|
|||||||
{/* 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]}
|
||||||
|
labelRowsPerPage={is_xs ? "Rows" : "Rows per page:"}
|
||||||
component="div"
|
component="div"
|
||||||
count={tableData.length}
|
count={tableData.length}
|
||||||
rowsPerPage={rowsPerPage}
|
rowsPerPage={rowsPerPage}
|
||||||
@@ -551,6 +627,5 @@ export default function NodeTable(props: NodeTableProps) {
|
|||||||
/>
|
/>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,20 @@ export default function NodePieChart(props: Props) {
|
|||||||
dataKey="value"
|
dataKey="value"
|
||||||
nameKey="name"
|
nameKey="name"
|
||||||
label={showLabels}
|
label={showLabels}
|
||||||
|
legendType="square"
|
||||||
|
cx="50%"
|
||||||
|
cy="50%"
|
||||||
|
startAngle={0}
|
||||||
|
endAngle={360}
|
||||||
|
paddingAngle={0}
|
||||||
|
labelLine={true}
|
||||||
|
hide={false}
|
||||||
|
minAngle={0}
|
||||||
|
isAnimationActive={true}
|
||||||
|
animationBegin={0}
|
||||||
|
animationDuration={1000}
|
||||||
|
animationEasing="ease-in"
|
||||||
|
blendStroke={true}
|
||||||
>
|
>
|
||||||
{data.map((entry, index) => (
|
{data.map((entry, index) => (
|
||||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||||
|
|||||||
@@ -4,15 +4,12 @@ import NodeList from "./NodeList";
|
|||||||
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import { tableData } from "@/data/nodeData";
|
import { tableData } from "@/data/nodeData";
|
||||||
|
import { StrictMode } from "react";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<Box
|
<StrictMode>
|
||||||
sx={{ backgroundColor: "#e9ecf5", height: "100%", width: "100%" }}
|
|
||||||
display="inline-block"
|
|
||||||
id="rootBox"
|
|
||||||
>
|
|
||||||
<NodeList tableData={tableData} />
|
<NodeList tableData={tableData} />
|
||||||
</Box>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,30 @@
|
|||||||
import { createTheme } from "@mui/material/styles";
|
import { createTheme } from "@mui/material/styles";
|
||||||
|
|
||||||
export const darkTheme = createTheme({
|
export const darkTheme = createTheme({
|
||||||
|
breakpoints: {
|
||||||
|
values: {
|
||||||
|
xs: 0,
|
||||||
|
sm: 400,
|
||||||
|
md: 900,
|
||||||
|
lg: 1200,
|
||||||
|
xl: 1536,
|
||||||
|
},
|
||||||
|
},
|
||||||
palette: {
|
palette: {
|
||||||
mode: "dark",
|
mode: "dark",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const lightTheme = createTheme({
|
export const lightTheme = createTheme({
|
||||||
|
breakpoints: {
|
||||||
|
values: {
|
||||||
|
xs: 0,
|
||||||
|
sm: 400,
|
||||||
|
md: 900,
|
||||||
|
lg: 1200,
|
||||||
|
xl: 1536,
|
||||||
|
},
|
||||||
|
},
|
||||||
palette: {
|
palette: {
|
||||||
mode: "light",
|
mode: "light",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
export interface TableData {
|
export interface TableData {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
status: NodeStatus;
|
status: NodeStatusKeys;
|
||||||
last_seen: number;
|
last_seen: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NodeStatus {
|
export const NodeStatus = {
|
||||||
Online,
|
Online: "Online",
|
||||||
Offline,
|
Offline: "Offline",
|
||||||
Pending,
|
Pending: "Pending",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NodeStatusKeys = typeof NodeStatus[keyof typeof NodeStatus];
|
||||||
|
|
||||||
function createData(
|
function createData(
|
||||||
name: string,
|
name: string,
|
||||||
id: string,
|
id: string,
|
||||||
status: NodeStatus,
|
status: NodeStatusKeys,
|
||||||
last_seen: number,
|
last_seen: number,
|
||||||
): TableData {
|
): TableData {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user