Merge pull request #330 from bluewave-labs/feat/basic-table

Implemented a basic table that takes a standard arrangement of data
This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-07-13 15:03:10 -07:00
commit 00390ce889
3 files changed
+181 -108

No files matched your search

Whitespace-only changes.
+146
View File
@@ -0,0 +1,146 @@
import {
TableContainer,
Paper,
Table,
TableHead,
TableRow,
TableCell,
TableBody,
TablePagination,
} from "@mui/material";
import React, { useState } from "react";
import PropTypes from "prop-types";
/**
* BasicTable Component
* Renders a table with optional pagination.
*
* @component
* @param {Object} props - Component props.
* @param {Array} props.headers - Array of header titles for the table.
* @param {Array} props.rowItems - Array of row data objects for the table.
* @param {boolean} props.paginated - Flag to enable pagination.
*
* @example
*
* Headers is an array of objets that have a unique ID and a name
* Rows is an array of objects that have a unique ID and some data to display
* Row data can be any valid React node
* Rows are split into groups according to the number of headers, so if you have 3 headers then
* Rows will go under the index % 3 column.
* ie: Row 0 -> col 0 % 3 = 0
* ie: Row 1 -> col 1 % 3 = 1
* ie: Row 2 -> col 2 % 3 = 2
* ie: Row 3 -> col 3 % 3 = 0
*
* Thus items are put in their correct columns
*
*
* const headers = [
* { id: 1, name: "ID" },
* { id: 2, name: "Name" },
* { id: 3, name: "Age" },
* ];
* const rows = [
* { id: 1, data: <span>999</span> },
* { id: 2, data: <div>Alex Holliday</div> },
* { id: 3, data: <div>5</div> },
* // More rows...
* ];
* <BasicTable headers={headers} rows={rows} paginated={true} />
*/
const BasicTable = ({ headers, rowItems, paginated }) => {
// Add headers to props validation
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0); // Reset to first page after changing rows per page
};
const numOfCols = headers.length;
const groupedRowItems = rowItems.reduce((acc, item, idx) => {
const rowIndex = Math.floor((item.id - 1) / numOfCols);
if (!acc[rowIndex]) acc[rowIndex] = { items: [], idx };
acc[rowIndex].items.push({ item });
return acc;
}, {});
const paginatedRowsKeys = Object.keys(groupedRowItems).slice(
page * rowsPerPage,
page * rowsPerPage + rowsPerPage
);
return (
<>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
{headers.map((header) => {
return <TableCell key={header.id}>{header.name}</TableCell>;
})}
</TableRow>
</TableHead>
<TableBody>
{paginated === false &&
Object.keys(groupedRowItems).map((key) => {
return (
<TableRow key={groupedRowItems[key].idx}>
{groupedRowItems[key].items.map(({ item }) => {
return <TableCell key={item.id}>{item.data}</TableCell>;
})}
</TableRow>
);
})}
{paginated === true &&
paginatedRowsKeys.map((key) => (
<TableRow key={groupedRowItems[key].idx}>
{groupedRowItems[key].items.map(({ item }) => (
<TableCell key={item.id}>{item.data}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
{paginated === true && (
<TablePagination
component="div"
count={Object.keys(groupedRowItems).length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
rowsPerPageOptions={[1, 5, 10]}
/>
)}
</>
);
};
BasicTable.propTypes = {
headers: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
name: PropTypes.string.isRequired,
})
).isRequired,
rowItems: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
data: PropTypes.node,
})
).isRequired,
paginated: PropTypes.bool,
};
export default BasicTable;
+35 -108
View File
@@ -1,23 +1,8 @@
import "./index.css";
import { useState } from "react";
import PropTypes from "prop-types";
import ResponseTimeChart from "../Charts/ResponseTimeChart";
import {
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
Pagination,
PaginationItem,
} from "@mui/material";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
import BasicTable from "../BasicTable";
import OpenInNewPage from "../../assets/icons/open-in-new-page.svg?react";
import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded";
import { useNavigate } from "react-router-dom";
/**
* Host component.
@@ -95,100 +80,42 @@ const Status = ({ params }) => {
* @returns {React.Component} Returns a table with the monitor data.
*/
const MonitorTable = ({ monitors = [] }) => {
const navigate = useNavigate();
const [page, setPage] = useState(0);
const rowsPerPage = 5;
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const mappedRows = monitors
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((monitor) => {
const params = {
url: monitor.url,
title: monitor.name,
precentage: 100,
percentageColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
status: monitor.status === true ? "up" : "down",
backgroundColor:
monitor.status === true
? "var(--env-var-color-20)"
: "var(--env-var-color-21)",
statusDotColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
};
return (
<TableRow
sx={{ cursor: "pointer" }}
key={monitor._id}
onClick={() => {
navigate(`/monitors/${monitor._id}`);
}}
>
<TableCell>
<Host params={params} />
</TableCell>
<TableCell>
<Status params={params} />
</TableCell>
<TableCell>
<ResponseTimeChart checks={monitor.checks} />
</TableCell>
<TableCell>TODO Add Actions</TableCell>
</TableRow>
);
const headers = [
{ id: 1, name: "Host" },
{ id: 2, name: "Status" },
{ id: 3, name: "Response Time" },
{ id: 4, name: "Actions" },
];
const data = [];
monitors.forEach((monitor) => {
const params = {
url: monitor.url,
title: monitor.name,
precentage: 100,
percentageColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
status: monitor.status === true ? "up" : "down",
backgroundColor:
monitor.status === true
? "var(--env-var-color-20)"
: "var(--env-var-color-21)",
statusDotColor:
monitor.status === true
? "var(--env-var-color-17)"
: "var(--env-var-color-19)",
};
data.push({ id: data.length + 1, data: <Host params={params} /> });
data.push({ id: data.length + 1, data: <Status params={params} /> });
data.push({
id: data.length + 1,
data: <ResponseTimeChart checks={monitor.checks} />,
});
data.push({ id: data.length + 1, data: "TODO" });
});
return (
<>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Host</TableCell>
<TableCell>
Status
<span>
<ArrowDownwardRoundedIcon />
</span>
</TableCell>
<TableCell>Response Time</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>{mappedRows}</TableBody>
</Table>
</TableContainer>
<Pagination
count={Math.ceil(monitors?.length / rowsPerPage)}
page={page + 1}
onChange={(event, value) => handleChangePage(event, value - 1)}
shape="rounded"
renderItem={(item) => (
<PaginationItem
slots={{
previous: ArrowBackRoundedIcon,
next: ArrowForwardRoundedIcon,
}}
{...item}
sx={{
"&:focus": {
outline: "none",
},
}}
/>
)}
/>
</>
);
return <BasicTable headers={headers} rowItems={data} paginated={true} />;
};
MonitorTable.propTypes = {