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:
3 files changed
+181
-108
No files matched your search
Whitespace-only changes.
@@ -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;
|
||||
@@ -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 = {
|
||||
|
||||
Reference in new issue
Block a user