diff --git a/Client/src/Components/BasicTable/index.css b/Client/src/Components/BasicTable/index.css
deleted file mode 100644
index 3c1b0f2cd..000000000
--- a/Client/src/Components/BasicTable/index.css
+++ /dev/null
@@ -1,130 +0,0 @@
-.MuiTable-root .host {
- width: fit-content;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
-}
-.MuiTable-root .host span {
- font-size: 11px;
-}
-
-.MuiTable-root .label {
- line-height: 1;
- border-radius: var(--env-var-radius-2);
- padding: 7px;
- font-size: var(--env-var-font-size-small-plus);
-}
-
-.MuiPaper-root:has(table.MuiTable-root) {
- box-shadow: none;
-}
-.MuiTable-root .MuiTableBody-root .MuiTableRow-root:last-child .MuiTableCell-root {
- border: none;
-}
-
-.MuiTable-root .MuiTableHead-root .MuiTableCell-root,
-.MuiTable-root .MuiTableBody-root .MuiTableCell-root {
- font-size: var(--env-var-font-size-medium);
-}
-.MuiTable-root .MuiTableHead-root .MuiTableCell-root {
- padding: var(--env-var-spacing-1);
- font-weight: 500;
-}
-.MuiTable-root .MuiTableHead-root span {
- display: inline-block;
- height: 17px;
- width: 20px;
- overflow: hidden;
- margin-bottom: -3px;
- margin-left: 3px;
-}
-.MuiTable-root .MuiTableHead-root span svg {
- width: 20px;
- height: 20px;
-}
-.MuiTable-root .MuiTableBody-root .MuiTableCell-root {
- padding: var(--env-var-spacing-1);
-}
-.MuiTable-root .MuiTableBody-root .MuiTableRow-root {
- height: 50px;
-}
-
-.MuiPaper-root + .MuiPagination-root {
- border-radius: var(--env-var-radius-1);
- padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2);
-}
-.MuiPaper-root + .MuiPagination-root ul {
- justify-content: center;
-}
-.MuiPaper-root + .MuiPagination-root button {
- font-size: var(--env-var-font-size-medium);
- font-weight: 500;
-}
-.MuiPaper-root + .MuiPagination-root ul li:first-child {
- margin-right: auto;
-}
-.MuiPaper-root + .MuiPagination-root ul li:last-child {
- margin-left: auto;
-}
-.MuiPaper-root + .MuiPagination-root ul li:first-child button {
- padding: 0 var(--env-var-spacing-1) 0 var(--env-var-spacing-1-plus);
-}
-.MuiPaper-root + .MuiPagination-root ul li:last-child button {
- padding: 0 var(--env-var-spacing-1-plus) 0 var(--env-var-spacing-1);
-}
-.MuiPaper-root + .MuiPagination-root ul li:first-child button::after,
-.MuiPaper-root + .MuiPagination-root ul li:last-child button::before {
- position: relative;
- display: inline-block;
-}
-.MuiPaper-root + .MuiPagination-root ul li:first-child button::after {
- content: "Previous";
- margin-left: 15px;
-}
-.MuiPaper-root + .MuiPagination-root ul li:last-child button::before {
- content: "Next";
- margin-right: 15px;
-}
-.MuiPaper-root + .MuiPagination-root div.MuiPaginationItem-root {
- user-select: none;
-}
-
-.MuiTablePagination-root p {
- font-weight: 500;
- font-size: var(--env-var-font-size-small-plus);
-}
-.MuiTablePagination-root .MuiTablePagination-select.MuiSelect-select {
- text-align: left;
- text-align-last: left;
-}
-.MuiTablePagination-root button {
- min-width: 0;
- padding: 4px;
- margin-left: 5px;
-}
-.MuiTablePagination-root svg {
- width: 22px;
- height: 22px;
-}
-.MuiTablePagination-root .MuiSelect-icon {
- width: 16px;
- height: 16px;
- top: 50%;
- right: 8%;
- transform: translateY(-50%);
-}
-.MuiTablePagination-root button.Mui-disabled {
- opacity: 0.4;
-}
-
-.table-container .MuiTable-root .MuiTableHead-root .MuiTableCell-root {
- text-transform: uppercase;
- opacity: 0.8;
- font-size: var(--env-var-font-size-small-plus);
- font-weight: 400;
-}
-.monitors .MuiTableCell-root:not(:first-of-type):not(:last-of-type),
-.monitors .MuiTableCell-root:not(:first-of-type):not(:last-of-type) {
- padding-left: var(--env-var-spacing-1);
- padding-right: var(--env-var-spacing-1);
-}
diff --git a/Client/src/Components/BasicTable/index.jsx b/Client/src/Components/BasicTable/index.jsx
deleted file mode 100644
index cfc3030b0..000000000
--- a/Client/src/Components/BasicTable/index.jsx
+++ /dev/null
@@ -1,335 +0,0 @@
-import PropTypes from "prop-types";
-import { useState, useEffect } from "react";
-import { useTheme } from "@emotion/react";
-import {
- TableContainer,
- Paper,
- Table,
- TableHead,
- TableRow,
- TableCell,
- TableBody,
- TablePagination,
- Box,
- Typography,
- Stack,
- Button,
-} from "@mui/material";
-import { useDispatch, useSelector } from "react-redux";
-import { setRowsPerPage } from "../../Features/UI/uiSlice";
-import LeftArrowDouble from "../../assets/icons/left-arrow-double.svg?react";
-import RightArrowDouble from "../../assets/icons/right-arrow-double.svg?react";
-import LeftArrow from "../../assets/icons/left-arrow.svg?react";
-import RightArrow from "../../assets/icons/right-arrow.svg?react";
-import SelectorVertical from "../../assets/icons/selector-vertical.svg?react";
-import "./index.css";
-/**
- * Component for pagination actions (first, previous, next, last).
- *
- * @component
- * @param {Object} props
- * @param {number} props.count - Total number of items.
- * @param {number} props.page - Current page number.
- * @param {number} props.rowsPerPage - Number of rows per page.
- * @param {function} props.onPageChange - Callback function to handle page change.
- *
- * @returns {JSX.Element} Pagination actions component.
- */
-const TablePaginationActions = (props) => {
- const { count, page, rowsPerPage, onPageChange } = props;
-
- const handleFirstPageButtonClick = (event) => {
- onPageChange(event, 0);
- };
- const handleBackButtonClick = (event) => {
- onPageChange(event, page - 1);
- };
- const handleNextButtonClick = (event) => {
- onPageChange(event, page + 1);
- };
- const handleLastPageButtonClick = (event) => {
- onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
- };
-
- return (
-
-
-
-
-
-
- );
-};
-
-TablePaginationActions.propTypes = {
- count: PropTypes.number.isRequired,
- page: PropTypes.number.isRequired,
- rowsPerPage: PropTypes.number.isRequired,
- onPageChange: PropTypes.func.isRequired,
-};
-
-/**
- * BasicTable Component
- * Renders a table with optional pagination.
- *
- * @component
- * @param {Object} props - Component props.
- * @param {Object} props.data - Data for the table including columns and rows.
- * @param {Array} props.data.cols - Array of objects for column headers.
- * @param {number} props.data.cols[].id - Unique identifier for the column.
- * @param {string} props.data.cols[].name - Name of the column to display as header.
- * @param {Array} props.data.rows - Array of row objects.
- * @param {number} props.data.rows[].id - Unique identifier for the row.
- * @param {Array} props.data.rows[].data - Array of cell data objects for the row.
- * @param {number} props.data.rows[].data[].id - Unique identifier for the cell.
- * @param {JSX.Element} props.data.rows[].data[].data - The content to display in the cell.
- * @param {function} props.data.rows.data.handleClick - Function to call when the row is clicked.
- * @param {boolean} [props.paginated=false] - Flag to enable pagination.
- * @param {boolean} [props.reversed=false] - Flag to enable reverse order.
- * @param {number} props.rowsPerPage- Number of rows per page (table).
- * @param {string} props.emptyMessage - Message to display when there is no data.
- * @example
- * const data = {
- * cols: [
- * { id: 1, name: "First Col" },
- * { id: 2, name: "Second Col" },
- * { id: 3, name: "Third Col" },
- * { id: 4, name: "Fourth Col" },
- * ],
- * rows: [
- * {
- * id: 1,
- * data: [
- * { id: 1, data:
Data for Row 1 Col 1
},
- * { id: 2, data: Data for Row 1 Col 2
},
- * { id: 3, data: Data for Row 1 Col 3
},
- * { id: 4, data: Data for Row 1 Col 4
},
- * ],
- * },
- * {
- * id: 2,
- * data: [
- * { id: 5, data: Data for Row 2 Col 1
},
- * { id: 6, data: Data for Row 2 Col 2
},
- * { id: 7, data: Data for Row 2 Col 3
},
- * { id: 8, data: Data for Row 2 Col 4
},
- * ],
- * },
- * ],
- * };
- *
- *
- */
-
-const BasicTable = ({ data, paginated, reversed, table, emptyMessage = "No data" }) => {
- const DEFAULT_ROWS_PER_PAGE = 5;
- const theme = useTheme();
- const dispatch = useDispatch();
- const uiState = useSelector((state) => state.ui);
- let rowsPerPage = uiState?.[table]?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE;
- const [page, setPage] = useState(0);
-
- useEffect(() => {
- setPage(0);
- }, [data]);
-
- const handleChangePage = (event, newPage) => {
- setPage(newPage);
- };
-
- const handleChangeRowsPerPage = (event) => {
- dispatch(
- setRowsPerPage({
- value: parseInt(event.target.value, 10),
- table: table,
- })
- );
- setPage(0);
- };
-
- let displayData = [];
-
- if (data && data.rows) {
- let rows = reversed ? [...data.rows].reverse() : data.rows;
- displayData = paginated
- ? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
- : rows;
- }
-
- if (!data || !data.cols || !data.rows) {
- return No data
;
- }
-
- /**
- * Helper function to calculate the range of displayed rows.
- * @returns {string}
- */
- const getRange = () => {
- let start = page * rowsPerPage + 1;
- let end = Math.min(page * rowsPerPage + rowsPerPage, data.rows.length);
- return `${start} - ${end}`;
- };
-
- return (
- <>
-
-
-
-
- {data.cols.map((col) => (
- {col.name}
- ))}
-
-
-
- {displayData.map((row) => {
- return (
-
- {row.data.map((cell) => {
- return {cell.data};
- })}
-
- );
- })}
- {displayData.length === 0 && (
-
-
- {emptyMessage}
-
-
- )}
-
-
-
- {paginated && (
-
-
- Showing {getRange()} of {data.rows.length} monitor(s)
-
-
- `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
- }
- slotProps={{
- select: {
- MenuProps: {
- keepMounted: true,
- PaperProps: {
- className: "pagination-dropdown",
- sx: {
- mt: 0,
- mb: theme.spacing(2),
- },
- },
- transformOrigin: { vertical: "bottom", horizontal: "left" },
- anchorOrigin: { vertical: "top", horizontal: "left" },
- sx: { mt: theme.spacing(-2) },
- },
- inputProps: { id: "pagination-dropdown" },
- IconComponent: SelectorVertical,
- sx: {
- ml: theme.spacing(4),
- mr: theme.spacing(12),
- minWidth: theme.spacing(20),
- textAlign: "left",
- "&.Mui-focused > div": {
- backgroundColor: theme.palette.background.main,
- },
- },
- },
- }}
- sx={{
- mt: theme.spacing(6),
- color: theme.palette.text.secondary,
- "& svg path": {
- stroke: theme.palette.text.tertiary,
- strokeWidth: 1.3,
- },
- "& .MuiSelect-select": {
- border: 1,
- borderColor: theme.palette.border.light,
- borderRadius: theme.shape.borderRadius,
- },
- }}
- />
-
- )}
- >
- );
-};
-
-BasicTable.propTypes = {
- data: PropTypes.object.isRequired,
- paginated: PropTypes.bool,
- reversed: PropTypes.bool,
- rowPerPage: PropTypes.number,
- table: PropTypes.string,
- emptyMessage: PropTypes.string,
-};
-
-export default BasicTable;
diff --git a/Client/src/Components/TabPanels/Account/TeamPanel.jsx b/Client/src/Components/TabPanels/Account/TeamPanel.jsx
index bbc6c4305..73d63e4b9 100644
--- a/Client/src/Components/TabPanels/Account/TeamPanel.jsx
+++ b/Client/src/Components/TabPanels/Account/TeamPanel.jsx
@@ -7,11 +7,10 @@ import { credentials } from "../../../Validation/validation";
import { networkService } from "../../../main";
import { createToast } from "../../../Utils/toastUtils";
import { useSelector } from "react-redux";
-import BasicTable from "../../BasicTable";
import Select from "../../Inputs/Select";
import LoadingButton from "@mui/lab/LoadingButton";
import { GenericDialog } from "../../Dialog/genericDialog";
-
+import DataTable from "../../Table/";
/**
* TeamPanel component manages the organization and team members,
* providing functionalities like renaming the organization, managing team members,
@@ -21,34 +20,47 @@ import { GenericDialog } from "../../Dialog/genericDialog";
*/
const TeamPanel = () => {
- const roleMap = {
- superadmin: "Super admin",
- admin: "Admin",
- user: "Team member",
- demo: "Demo User",
- };
-
const theme = useTheme();
const SPACING_GAP = theme.spacing(12);
- const { authToken, user } = useSelector((state) => state.auth);
- //TODO
- // const [orgStates, setOrgStates] = useState({
- // name: "Bluewave Labs",
- // isEdit: false,
- // });
+ const { authToken } = useSelector((state) => state.auth);
const [toInvite, setToInvite] = useState({
email: "",
role: ["0"],
});
- const [tableData, setTableData] = useState({});
+ const [data, setData] = useState([]);
const [members, setMembers] = useState([]);
const [filter, setFilter] = useState("all");
const [isDisabled, setIsDisabled] = useState(true);
const [errors, setErrors] = useState({});
const [isSendingInvite, setIsSendingInvite] = useState(false);
+ const headers = [
+ {
+ id: "name",
+ content: "Name",
+ render: (row) => {
+ return (
+
+
+ {row.firstName + " " + row.lastName}
+
+
+ Created {new Date(row.createdAt).toLocaleDateString()}
+
+
+ );
+ },
+ },
+ { id: "email", content: "Email", render: (row) => row.email },
+ {
+ id: "role",
+ content: "Role",
+ render: (row) => row.role,
+ },
+ ];
+
useEffect(() => {
const fetchTeam = async () => {
try {
@@ -67,6 +79,12 @@ const TeamPanel = () => {
}, [authToken]);
useEffect(() => {
+ const ROLE_MAP = {
+ superadmin: "Super admin",
+ admin: "Admin",
+ user: "Team member",
+ demo: "Demo User",
+ };
let team = members;
if (filter !== "all")
team = members.filter((member) => {
@@ -76,42 +94,14 @@ const TeamPanel = () => {
return member.role.includes(filter);
});
- const data = {
- cols: [
- { id: 1, name: "NAME" },
- { id: 2, name: "EMAIL" },
- { id: 3, name: "ROLE" },
- ],
- rows: team?.map((member, idx) => {
- const roles = member.role.map((role) => roleMap[role]).join(",");
- return {
- id: member._id,
- data: [
- {
- id: idx,
- data: (
-
-
- {member.firstName + " " + member.lastName}
-
-
- Created {new Date(member.createdAt).toLocaleDateString()}
-
-
- ),
- },
- { id: idx + 1, data: member.email },
- {
- id: idx + 2,
- data: roles,
- },
- ],
- };
- }),
- };
+ team = team.map((member) => ({
+ ...member,
+ id: member._id,
+ role: member.role.map((role) => ROLE_MAP[role]).join(","),
+ }));
+ setData(team);
+ }, [filter, members]);
- setTableData(data);
- }, [filter, members, roleMap, theme]);
useEffect(() => {
setIsDisabled(Object.keys(errors).length !== 0 || toInvite.email === "");
}, [errors, toInvite.email]);
@@ -248,12 +238,11 @@ const TeamPanel = () => {
Invite a team member
-
diff --git a/Client/src/Pages/Infrastructure/components/TablePagination/Actions/index.jsx b/Client/src/Components/Table/TablePagination/Actions/index.jsx
similarity index 85%
rename from Client/src/Pages/Infrastructure/components/TablePagination/Actions/index.jsx
rename to Client/src/Components/Table/TablePagination/Actions/index.jsx
index c684da145..c607fcb8b 100644
--- a/Client/src/Pages/Infrastructure/components/TablePagination/Actions/index.jsx
+++ b/Client/src/Components/Table/TablePagination/Actions/index.jsx
@@ -1,9 +1,9 @@
import PropTypes from "prop-types";
import { Box, Button } from "@mui/material";
-import LeftArrowDouble from "../../../../../assets/icons/left-arrow-double.svg?react";
-import RightArrowDouble from "../../../../../assets/icons/right-arrow-double.svg?react";
-import LeftArrow from "../../../../../assets/icons/left-arrow.svg?react";
-import RightArrow from "../../../../../assets/icons/right-arrow.svg?react";
+import LeftArrowDouble from "../../../../assets/icons/left-arrow-double.svg?react";
+import RightArrowDouble from "../../../../assets/icons/right-arrow-double.svg?react";
+import LeftArrow from "../../../../assets/icons/left-arrow.svg?react";
+import RightArrow from "../../../../assets/icons/right-arrow.svg?react";
TablePaginationActions.propTypes = {
count: PropTypes.number.isRequired,
diff --git a/Client/src/Pages/Infrastructure/components/TablePagination/index.jsx b/Client/src/Components/Table/TablePagination/index.jsx
similarity index 97%
rename from Client/src/Pages/Infrastructure/components/TablePagination/index.jsx
rename to Client/src/Components/Table/TablePagination/index.jsx
index bb618add8..4435454bc 100644
--- a/Client/src/Pages/Infrastructure/components/TablePagination/index.jsx
+++ b/Client/src/Components/Table/TablePagination/index.jsx
@@ -2,7 +2,7 @@ import PropTypes from "prop-types";
import { useTheme } from "@emotion/react";
import { Stack, TablePagination, Typography } from "@mui/material";
import { TablePaginationActions } from "./Actions";
-import SelectorVertical from "../../../../assets/icons/selector-vertical.svg?react";
+import SelectorVertical from "../../../assets/icons/selector-vertical.svg?react";
Pagination.propTypes = {
monitorCount: PropTypes.number.isRequired, // Total number of items for pagination.
diff --git a/Client/src/Components/Table/index.jsx b/Client/src/Components/Table/index.jsx
new file mode 100644
index 000000000..3cef178f1
--- /dev/null
+++ b/Client/src/Components/Table/index.jsx
@@ -0,0 +1,113 @@
+import {
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+} from "@mui/material";
+import PropTypes from "prop-types";
+import { useTheme } from "@emotion/react";
+
+/**
+ * @typedef {Object} Header
+ * @property {number|string} id - The unique identifier for the header.
+ * @property {React.ReactNode} content - The content to display in the header cell.
+ * @property {Function} render - A function to render the cell content for a given row.
+ */
+
+/**
+ * @typedef {Object} Config
+ * @property {Function} onRowClick - A function to be called when a row is clicked, receiving the row data as an argument.
+ * @property {Object} rowSX - Style object for the table row.
+ */
+
+/**
+ * DataTable component renders a table with headers and data.
+ *
+ * @param {Object} props - The component props.
+ * @param {Header[]} props.headers - An array of header objects, each containing an `id`, `content`, and `render` function.
+ * @param {Array} props.data - An array of data objects, each representing a row.
+ * @returns {JSX.Element} The rendered table component.
+ */
+
+const DataTable = ({ headers, data, config = { emptyView: "No data" } }) => {
+ const theme = useTheme();
+ if ((headers?.length ?? 0) === 0) {
+ return "No data";
+ }
+
+ return (
+
+
+
+
+ {headers.map((header, index) => (
+
+ {header.content}
+
+ ))}
+
+
+
+ {(data?.length ?? 0) === 0 ? (
+
+
+ {config.emptyView}
+
+
+ ) : (
+ data.map((row) => {
+ return (
+ config?.onRowClick(row)}
+ >
+ {headers.map((header, index) => {
+ return (
+
+ {header.render(row)}
+
+ );
+ })}
+
+ );
+ })
+ )}
+
+
+
+ );
+};
+
+DataTable.propTypes = {
+ headers: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+ content: PropTypes.node.isRequired,
+ render: PropTypes.func.isRequired,
+ })
+ ).isRequired,
+ data: PropTypes.array.isRequired,
+ config: PropTypes.shape({
+ onRowClick: PropTypes.func.isRequired,
+ rowSX: PropTypes.object,
+ emptyView: PropTypes.node,
+ }),
+};
+
+export default DataTable;
diff --git a/Client/src/Pages/Incidents/IncidentTable/index.jsx b/Client/src/Pages/Incidents/IncidentTable/index.jsx
index 1d0be7666..b4ef9fdb8 100644
--- a/Client/src/Pages/Incidents/IncidentTable/index.jsx
+++ b/Client/src/Pages/Incidents/IncidentTable/index.jsx
@@ -1,17 +1,5 @@
import PropTypes from "prop-types";
-import {
- TableContainer,
- Table,
- TableHead,
- TableRow,
- TableCell,
- TableBody,
- Pagination,
- PaginationItem,
- Paper,
- Typography,
- Box,
-} from "@mui/material";
+import { Pagination, PaginationItem, Typography, Box } from "@mui/material";
import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded";
@@ -27,7 +15,7 @@ import PlaceholderDark from "../../../assets/Images/data_placeholder_dark.svg?re
import { HttpStatusLabel } from "../../../Components/HttpStatusLabel";
import { Empty } from "./Empty/Empty";
import { IncidentSkeleton } from "./Skeleton/Skeleton";
-
+import DataTable from "../../../Components/Table";
const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
const uiTimezone = useSelector((state) => state.ui.timezone);
@@ -106,6 +94,46 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
});
};
+ const headers = [
+ {
+ id: "monitorName",
+ content: "Monitor Name",
+ render: (row) => monitors[row.monitorId]?.name ?? "N/A",
+ },
+ {
+ id: "status",
+ content: "Status",
+ render: (row) => {
+ const status = row.status === true ? "up" : "down";
+ return (
+
+ );
+ },
+ },
+ {
+ id: "dateTime",
+ content: "Date & Time",
+ render: (row) => {
+ const formattedDate = formatDateWithTz(
+ row.createdAt,
+ "YYYY-MM-DD HH:mm:ss A",
+ uiTimezone
+ );
+ return formattedDate;
+ },
+ },
+ {
+ id: "statusCode",
+ content: "Status Code",
+ render: (row) => ,
+ },
+ { id: "message", content: "Message", render: (row) => row.message },
+ ];
+
let paginationComponent = <>>;
if (checksCount > paginationController.rowsPerPage) {
paginationComponent = (
@@ -166,47 +194,11 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
) : (
<>
-
-
-
-
- Monitor Name
- Status
- Date & Time
- Status Code
- Message
-
-
-
- {checks.map((check) => {
- const status = check.status === true ? "up" : "down";
- const formattedDate = formatDateWithTz(
- check.createdAt,
- "YYYY-MM-DD HH:mm:ss A",
- uiTimezone
- );
-
- return (
-
- {monitors[check.monitorId]?.name}
-
-
-
- {formattedDate}
-
-
-
- {check.message}
-
- );
- })}
-
-
-
+
+
{paginationComponent}
>
)}
diff --git a/Client/src/Pages/Infrastructure/index.jsx b/Client/src/Pages/Infrastructure/index.jsx
index aba4369e6..be7d7e9ed 100644
--- a/Client/src/Pages/Infrastructure/index.jsx
+++ b/Client/src/Pages/Infrastructure/index.jsx
@@ -8,23 +8,12 @@ import SkeletonLayout from "./skeleton";
import Fallback from "../../Components/Fallback";
// import GearIcon from "../../Assets/icons/settings-bold.svg?react";
import CPUChipIcon from "../../assets/icons/cpu-chip.svg?react";
-import {
- Box,
- Button,
- IconButton,
- Paper,
- Stack,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
-} from "@mui/material";
+import DataTable from "../../Components/Table";
+import { Box, Button, IconButton, Stack } from "@mui/material";
import Breadcrumbs from "../../Components/Breadcrumbs";
import { StatusLabel } from "../../Components/Label";
import { Heading } from "../../Components/Heading";
-import { Pagination } from "./components/TablePagination";
+import { Pagination } from "../../Components/Table/TablePagination/index.jsx";
// import { getInfrastructureMonitorsByTeamId } from "../../Features/InfrastructureMonitors/infrastructureMonitorsSlice";
import { networkService } from "../../Utils/NetworkService.js";
import CustomGauge from "../../Components/Charts/CustomGauge/index.jsx";
@@ -32,42 +21,8 @@ import Host from "../Uptime/Home/host.jsx";
import { useIsAdmin } from "../../Hooks/useIsAdmin.js";
import { InfrastructureMenu } from "./components/Menu";
-const columns = [
- { label: "Host" },
- { label: "Status" },
- { label: "Frequency" },
- { label: "CPU" },
- { label: "Mem" },
- { label: "Disk" },
- { label: "Actions" },
-];
-
const BREADCRUMBS = [{ name: `infrastructure`, path: "/infrastructure" }];
-/* TODO
-Create reusable table component.
-It should receive as a parameter the following object:
-tableData = [
- columns = [
- {
- id: example,
- label: Example Extendable,
- align: "center" | "left" (default)
- }
- ],
- rows: [
- {
- **Number of keys will be equal to number of columns**
- key1: string,
- key2: number,
- key3: Component
- }
- ]
-]
-Apply to Monitor Table, and Account/Team.
-Analyze existing BasicTable
-*/
-
/**
* This is the Infrastructure monitoring page. This is a work in progress
*
@@ -139,6 +94,71 @@ function Infrastructure() {
fetchMonitors();
}
+ const headers = [
+ {
+ id: "host",
+ content: "Host",
+ render: (row) => (
+
+ ),
+ },
+ {
+ id: "status",
+ content: "Status",
+ render: (row) => (
+
+ ),
+ },
+ {
+ id: "frequency",
+ content: "Frequency",
+ render: (row) => (
+
+
+ {row.processor}
+
+ ),
+ },
+ { id: "cpu", content: "CPU", render: (row) => },
+ { id: "mem", content: "Mem", render: (row) => },
+ { id: "disk", content: "Disk", render: (row) => },
+ {
+ id: "actions",
+ content: "Actions",
+ render: (row) => (
+
+
+
+ ),
+ },
+ ];
+
const monitorsAsRows = monitors.map((monitor) => {
const processor =
((monitor.checks[0]?.cpu?.usage_frequency ?? 0) / 1000).toFixed(2) + " GHz";
@@ -237,103 +257,20 @@ function Infrastructure() {
{totalMonitors}
-
-
-
-
- {columns.map((column, index) => (
-
- {column.label}
-
- ))}
-
-
-
- {monitorsAsRows.map((row) => {
- return (
- openDetails(row.id)}
- sx={{
- cursor: "pointer",
- "&:hover": {
- backgroundColor: theme.palette.background.accent,
- },
- }}
- >
- {/* TODO iterate over column and get column id, applying row[column.id] */}
-
-
-
-
-
-
-
-
-
- {row.processor}
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Get ActionsMenu from Monitor Table and create a component */}
-
-
- {/* */}
-
-
-
- );
- })}
-
-
-
+
+ openDetails(row.id),
+ }}
+ headers={headers}
+ data={monitorsAsRows}
+ />
i);
-const TableBodySkeleton = () => {
+const TableSkeleton = () => {
/* TODO Skeleton does not follow light and dark theme */
+
+ const headers = [
+ {
+ id: "name",
+
+ content: "Host",
+
+ render: () => ,
+ },
+ {
+ id: "status",
+ content: "Status",
+ render: () => ,
+ },
+ {
+ id: "responseTime",
+ content: "Response Time",
+ render: () => ,
+ },
+ {
+ id: "type",
+ content: "Type",
+ render: () => ,
+ },
+ {
+ id: "actions",
+ content: "Actions",
+ render: () => ,
+ },
+ ];
+
return (
- <>
- {ROWS_ARRAY.map((row) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ))}
- >
+
);
};
-export { TableBodySkeleton };
+export { TableSkeleton };
diff --git a/Client/src/Pages/Uptime/Home/UptimeTable/index.jsx b/Client/src/Pages/Uptime/Home/UptimeTable/index.jsx
index a19da2a43..0aa0eef90 100644
--- a/Client/src/Pages/Uptime/Home/UptimeTable/index.jsx
+++ b/Client/src/Pages/Uptime/Home/UptimeTable/index.jsx
@@ -10,27 +10,18 @@ import { logger } from "../../../../Utils/Logger";
import { jwtDecode } from "jwt-decode";
import { networkService } from "../../../../main";
-import {
- TableContainer,
- Table,
- TableHead,
- TableRow,
- TableCell,
- TableBody,
- Paper,
- Box,
- CircularProgress,
-} from "@mui/material";
+import { Box, CircularProgress } from "@mui/material";
import ActionsMenu from "../actionsMenu";
import Host from "../host";
import { StatusLabel } from "../../../../Components/Label";
-import { TableBodySkeleton } from "./Skeleton";
+import { TableSkeleton } from "./Skeleton";
import BarChart from "../../../../Components/Charts/BarChart";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded";
-import { Pagination } from "../../../Infrastructure/components/TablePagination";
+import { Pagination } from "../../../../Components/Table/TablePagination";
+import DataTable from "../../../../Components/Table";
const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching, handlePause }) => {
const theme = useTheme();
@@ -46,7 +37,95 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching, handlePaus
const [monitorCount, setMonitorCount] = useState(0);
const [updateTrigger, setUpdateTrigger] = useState(false);
const [sort, setSort] = useState({});
+ const [data, setData] = useState([]);
const prevFilter = useRef(filter);
+ const headers = [
+ {
+ id: "name",
+ content: (
+ handleSort("name")}>
+ Host
+
+ {sort.order === "asc" ? (
+
+ ) : (
+
+ )}
+
+
+ ),
+ render: (row) => (
+
+ ),
+ },
+ {
+ id: "status",
+ content: (
+ handleSort("status")}
+ >
+ {" "}
+ Status
+
+ {sort.order === "asc" ? (
+
+ ) : (
+
+ )}
+
+
+ ),
+ render: (row) => {
+ const status = determineState(row.monitor);
+ return (
+
+ );
+ },
+ },
+ {
+ id: "responseTime",
+ content: "Response Time",
+ render: (row) => ,
+ },
+ {
+ id: "type",
+ content: "Type",
+ render: (row) => (
+ {row.monitor.type}
+ ),
+ },
+ {
+ id: "actions",
+ content: "Actions",
+ render: (row) => (
+
+ ),
+ },
+ ];
const handleRowUpdate = () => {
setUpdateTrigger((prev) => !prev);
@@ -144,7 +223,41 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching, handlePaus
setMonitors(res?.data?.data?.monitors ?? []);
setMonitorCount(res?.data?.data?.monitorCount ?? 0);
};
- /* TODO Apply component basic table? */
+
+ useEffect(() => {
+ const mappedMonitors = monitors.map((monitor) => {
+ let uptimePercentage = "";
+ let percentageColor = theme.palette.percentage.uptimeExcellent;
+
+ // Determine uptime percentage and color based on the monitor's uptimePercentage value
+ if (monitor.uptimePercentage !== undefined) {
+ uptimePercentage =
+ monitor.uptimePercentage === 0
+ ? "0"
+ : (monitor.uptimePercentage * 100).toFixed(2);
+
+ percentageColor =
+ monitor.uptimePercentage < 0.25
+ ? theme.palette.percentage.uptimePoor
+ : monitor.uptimePercentage < 0.5
+ ? theme.palette.percentage.uptimeFair
+ : monitor.uptimePercentage < 0.75
+ ? theme.palette.percentage.uptimeGood
+ : theme.palette.percentage.uptimeExcellent;
+ }
+
+ return {
+ id: monitor._id,
+ url: monitor.url,
+ title: monitor.name,
+ percentage: uptimePercentage,
+ percentageColor,
+ monitor: monitor,
+ };
+ });
+ setData(mappedMonitors);
+ }, [monitors, theme]);
+
return (
{isSearching && (
@@ -177,141 +290,37 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching, handlePaus
>
)}
-
-
-
-
- handleSort("name")}
- >
-
- Host
-
- {sort.order === "asc" ? (
-
- ) : (
-
- )}
-
-
-
- handleSort("status")}
- >
- {" "}
-
- {" "}
- Status
-
- {sort.order === "asc" ? (
-
- ) : (
-
- )}
-
-
-
- Response Time
- Type
- Actions
-
-
-
- {/* TODO add empty state. Check if is searching, and empty => skeleton. Is empty, not searching => skeleton */}
- {monitors.length > 0 ? (
- monitors.map((monitor) => {
- let uptimePercentage = "";
- let percentageColor = theme.palette.percentage.uptimeExcellent;
-
- // Determine uptime percentage and color based on the monitor's uptimePercentage value
- if (monitor.uptimePercentage !== undefined) {
- uptimePercentage =
- monitor.uptimePercentage === 0
- ? "0"
- : (monitor.uptimePercentage * 100).toFixed(2);
-
- percentageColor =
- monitor.uptimePercentage < 0.25
- ? theme.palette.percentage.uptimePoor
- : monitor.uptimePercentage < 0.5
- ? theme.palette.percentage.uptimeFair
- : monitor.uptimePercentage < 0.75
- ? theme.palette.percentage.uptimeGood
- : theme.palette.percentage.uptimeExcellent;
- }
-
- const params = {
- url: monitor.url,
- title: monitor.name,
- percentage: uptimePercentage,
- percentageColor,
- status: determineState(monitor),
- };
-
- return (
- {
- navigate(`/uptime/${monitor._id}`);
- }}
- >
-
-
-
-
-
-
-
-
-
-
- {monitor.type}
-
-
-
-
-
- );
- })
- ) : (
-
- )}
-
-
-
+ {/*
+ This is the original SX for the row, doesn't match infrastructure table
+ rowSX: {
+ cursor: "pointer",
+ "&:hover": {
+ filter: "brightness(.75)",
+ opacity: 0.75,
+ transition: "filter 0.3s ease, opacity 0.3s ease",
+ },
+ },
+ */}
+ {monitors.length > 0 ? (
+ {
+ navigate(`/uptime/${row.id}`);
+ },
+ emptyView: "No monitors found",
+ }}
+ />
+ ) : (
+
+ )}