diff --git a/Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js b/Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js
index b593edff3..7e0739cbd 100644
--- a/Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js
+++ b/Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js
@@ -40,7 +40,7 @@ export const checkInfrastructureEndpointResolution = createAsyncThunk(
const res = await networkService.checkEndpointResolution({
authToken: authToken,
monitorURL: monitorURL,
- })
+ });
return res.data;
} catch (error) {
if (error.response && error.response.data) {
@@ -53,7 +53,7 @@ export const checkInfrastructureEndpointResolution = createAsyncThunk(
return thunkApi.rejectWithValue(payload);
}
}
-)
+);
export const getInfrastructureMonitorById = createAsyncThunk(
"infrastructureMonitors/getMonitorById",
@@ -87,6 +87,7 @@ export const getInfrastructureMonitorsByTeamId = createAsyncThunk(
authToken: token,
teamId: user.teamId,
types: ["hardware"],
+ limit: 1,
});
return res.data;
} catch (error) {
@@ -112,7 +113,7 @@ export const updateInfrastructureMonitor = createAsyncThunk(
description: monitor.description,
interval: monitor.interval,
notifications: monitor.notifications,
- threshold: monitor.threshold
+ threshold: monitor.threshold,
};
const res = await networkService.updateMonitor({
authToken: authToken,
@@ -307,7 +308,9 @@ const infrastructureMonitorsSlice = createSlice({
.addCase(getInfrastructureMonitorById.rejected, (state, action) => {
state.isLoading = false;
state.success = false;
- state.msg = action.payload ? action.payload.msg : "Failed to get infrastructure monitor";
+ state.msg = action.payload
+ ? action.payload.msg
+ : "Failed to get infrastructure monitor";
})
// *****************************************************
// update Monitor
@@ -401,6 +404,7 @@ const infrastructureMonitorsSlice = createSlice({
},
});
-export const { setInfrastructureMonitors, clearInfrastructureMonitorState } = infrastructureMonitorsSlice.actions;
+export const { setInfrastructureMonitors, clearInfrastructureMonitorState } =
+ infrastructureMonitorsSlice.actions;
export default infrastructureMonitorsSlice.reducer;
diff --git a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js
index d472a673a..00ef87bc0 100644
--- a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js
+++ b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js
@@ -221,6 +221,7 @@ export const addDemoMonitors = createAsyncThunk(
}
}
);
+
export const deleteAllMonitors = createAsyncThunk(
"monitors/deleteAllMonitors",
async (data, thunkApi) => {
diff --git a/Client/src/Pages/Infrastructure/components/TablePagination/Actions/index.jsx b/Client/src/Pages/Infrastructure/components/TablePagination/Actions/index.jsx
new file mode 100644
index 000000000..7e267c486
--- /dev/null
+++ b/Client/src/Pages/Infrastructure/components/TablePagination/Actions/index.jsx
@@ -0,0 +1,80 @@
+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";
+
+TablePaginationActions.propTypes = {
+ count: PropTypes.number.isRequired,
+ page: PropTypes.number.isRequired,
+ rowsPerPage: PropTypes.number.isRequired,
+ onPageChange: PropTypes.func.isRequired,
+};
+
+/**
+ * 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.
+ */
+
+function TablePaginationActions({ count, page, rowsPerPage, onPageChange }) {
+ 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 (
+
+
+
+
+
+
+ );
+}
+
+export { TablePaginationActions };
diff --git a/Client/src/Pages/Infrastructure/components/TablePagination/index.jsx b/Client/src/Pages/Infrastructure/components/TablePagination/index.jsx
new file mode 100644
index 000000000..8a935fec3
--- /dev/null
+++ b/Client/src/Pages/Infrastructure/components/TablePagination/index.jsx
@@ -0,0 +1,117 @@
+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";
+
+Pagination.propTypes = {
+ monitorCount: PropTypes.number.isRequired, // Total number of items for pagination.
+ page: PropTypes.number.isRequired, // Current page index.
+ rowsPerPage: PropTypes.number.isRequired, // Number of rows displayed per page.
+ handleChangePage: PropTypes.func.isRequired, // Function to handle page changes.
+ handleChangeRowsPerPage: PropTypes.func.isRequired, // Function to handle changes in rows per page.
+};
+
+const ROWS_PER_PAGE_OPTIONS = [5, 10, 15, 25];
+
+/**
+ * Pagination component for table navigation with customized styling and behavior.
+ *
+ * @param {object} props - Component properties.
+ * @param {number} props.monitorCount - Total number of monitors to paginate.
+ * @param {number} props.page - Current page index (0-based).
+ * @param {number} props.rowsPerPage - Number of rows to display per page.
+ * @param {function} props.handleChangePage - Callback for handling page changes.
+ * @param {function} props.handleChangeRowsPerPage - Callback for handling changes to rows per page.
+ * @returns {JSX.Element} The Pagination component.
+ */
+function Pagination({
+ monitorCount,
+ page,
+ rowsPerPage,
+ handleChangePage,
+ handleChangeRowsPerPage,
+}) {
+ const theme = useTheme();
+
+ const start = page * rowsPerPage + 1;
+ const end = Math.min(page * rowsPerPage + rowsPerPage, monitorCount);
+ const range = `${start} - ${end}`;
+
+ return (
+
+
+ Showing {range} of {monitorCount} monitor(s)
+
+
+ `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
+ }
+ slotProps={{
+ select: {
+ MenuProps: {
+ keepMounted: true,
+ disableScrollLock: 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={{
+ 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,
+ },
+ }}
+ />
+
+ );
+}
+
+export { Pagination };
diff --git a/Client/src/Pages/Infrastructure/index.jsx b/Client/src/Pages/Infrastructure/index.jsx
index 9735bf6b2..2d45fdb74 100644
--- a/Client/src/Pages/Infrastructure/index.jsx
+++ b/Client/src/Pages/Infrastructure/index.jsx
@@ -13,27 +13,22 @@ import {
// TablePagination,
// Typography,
} from "@mui/material";
-import { Heading } from "../../Components/Heading";
-
+import { useEffect } from "react";
+import { useNavigate } from "react-router-dom";
+import { useDispatch, useSelector } from "react-redux";
import { useTheme } from "@emotion/react";
import Greeting from "../../Utils/greeting";
-import Breadcrumbs from "../../Components/Breadcrumbs";
-import { StatusLabel } from "../../Components/Label";
-import { Gauge } from "../../Components/Charts/Gauge";
import GearIcon from "../../Assets/icons/settings-bold.svg?react";
import CPUChipIcon from "../../Assets/icons/cpu-chip.svg?react";
+import Breadcrumbs from "../../Components/Breadcrumbs";
+import { StatusLabel } from "../../Components/Label";
+import { Heading } from "../../Components/Heading";
+import { Gauge } from "../../Components/Charts/Gauge";
+import { getInfrastructureMonitorsByTeamId } from "../../Features/InfrastructureMonitors/infrastructureMonitorsSlice";
+import useUtils from "../Monitors/utils";
+import { Pagination } from "./components/TablePagination";
-const mockedData = {
- ip: "https://192.168.1.30",
- status: "up",
- processor: "2Ghz",
- cpu: 80,
- mem: 50,
- disk: 70,
-};
-
-const ROWS = Array.from(Array(20).keys()).map(() => mockedData);
-console.log(ROWS);
+// const ROWS = Array.from(Array(20).keys()).map(() => mockedData);
const columns = [
{ label: "Host" },
@@ -69,8 +64,6 @@ Apply to Monitor Table, and Account/Team.
Analyze existing BasicTable
*/
-import { useNavigate } from "react-router-dom";
-
/**
* This is the Infrastructure monitoring page. This is a work in progress
*
@@ -78,9 +71,33 @@ import { useNavigate } from "react-router-dom";
* @returns {JSX.Element} The infrastructure monitoring page.
*/
-function Infrastructure() {
+function Infrastructure(/* {isAdmin} */) {
const theme = useTheme();
+ const { determineState } = useUtils();
const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const { authToken } = useSelector((state) => state.auth);
+ const { isLoading, monitorsSummary, msg, success, ...rest } = useSelector(
+ (state) => state.infrastructureMonitors
+ );
+
+ const { monitorCounts = {}, monitors = [] } = monitorsSummary;
+ const { total: totalMonitors = 0 } = monitorCounts;
+ console.log({ monitors });
+ const monitorsAsRows = monitors.map((monitor) => ({
+ ip: monitor.name,
+ status: determineState(monitor),
+ processor: "2Ghz" /* How to retrieve that? */,
+ cpu: 80 /* How to retrieve that? */,
+ mem: 50 /* How to retrieve that? */,
+ disk: 70 /* How to retrieve that? */,
+ }));
+
+ useEffect(() => {
+ dispatch(getInfrastructureMonitorsByTeamId(authToken));
+ }, [authToken]);
+ // console.log({ isLoading, monitorsSummary, msg, success, rest });
+
return (
- 5
+ {totalMonitors}
- {ROWS.map((row, index) => (
-
- {/* TODO iterate over column and get column id, applying row[column.id] */}
- {row.ip}
-
-
-
-
-
- (
+
+ {/* TODO iterate over column and get column id, applying row[column.id] */}
+ {row.ip}
+
+
- {row.processor}
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Get ActionsMenu from Monitor Table and create a component */}
-
-
+
+
+
+ {row.processor}
+
+
+
+
-
-
-
- ))}
+
+
+
+
+
+
+
+
+ {/* Get ActionsMenu from Monitor Table and create a component */}
+
+
+
+
+
+ ))
+ }
- {/*
-
- Showing {getRange()} of {monitorCount} monitor(s)
-
-
- `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
- }
- slotProps={{
- select: {
- MenuProps: {
- keepMounted: true,
- disableScrollLock: 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={{
- 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,
- },
- }}
- />
- */}
+ {/*
+ TODO continue creating pagination component. It should change the current page, which will trigger refetch?
+
+ */}
+ {/* */}
);
diff --git a/Client/src/Pages/Monitors/Home/MonitorTable/index.jsx b/Client/src/Pages/Monitors/Home/MonitorTable/index.jsx
index edfb47da3..bb3f5a65a 100644
--- a/Client/src/Pages/Monitors/Home/MonitorTable/index.jsx
+++ b/Client/src/Pages/Monitors/Home/MonitorTable/index.jsx
@@ -1,4 +1,15 @@
import PropTypes from "prop-types";
+import { useState, useEffect, memo, useCallback, useRef } from "react";
+import { useNavigate } from "react-router";
+import { useDispatch, useSelector } from "react-redux";
+import { useTheme } from "@emotion/react";
+import useUtils from "../../utils";
+
+import { setRowsPerPage } from "../../../../Features/UI/uiSlice";
+import { logger } from "../../../../Utils/Logger";
+import { jwtDecode } from "jwt-decode";
+import { networkService } from "../../../../main";
+
import {
TableContainer,
Table,
@@ -8,106 +19,18 @@ import {
TableBody,
Paper,
Box,
- TablePagination,
- Stack,
- Typography,
- Button,
CircularProgress,
} from "@mui/material";
+import ActionsMenu from "../actionsMenu";
+import Host from "../host";
+import { StatusLabel } from "../../../../Components/Label";
+import { TableBodySkeleton } from "./Skeleton";
+import BarChart from "../../../../Components/Charts/BarChart";
+
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded";
-import { setRowsPerPage } from "../../../../Features/UI/uiSlice";
-import { useState, useEffect, memo, useCallback, useRef } from "react";
-import { useNavigate } from "react-router";
-import { logger } from "../../../../Utils/Logger";
-import Host from "../host";
-import { StatusLabel } from "../../../../Components/Label";
-import { jwtDecode } from "jwt-decode";
-import { useDispatch, useSelector } from "react-redux";
-import { networkService } from "../../../../main";
-import { useTheme } from "@emotion/react";
-import BarChart from "../../../../Components/Charts/BarChart";
-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 ActionsMenu from "../actionsMenu";
-import useUtils from "../../utils";
-import { TableBodySkeleton } from "./Skeleton";
-
-/**
- * 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,
-};
+import { Pagination } from "../../../Infrastructure/components/TablePagination";
const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
const theme = useTheme();
@@ -117,6 +40,7 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
const { rowsPerPage } = useSelector((state) => state.ui.monitors);
const authState = useSelector((state) => state.auth);
+
const [page, setPage] = useState(0);
const [monitors, setMonitors] = useState([]);
const [monitorCount, setMonitorCount] = useState(0);
@@ -195,11 +119,11 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
* 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, monitorCount);
- return `${start} - ${end}`;
- };
+ // const getRange = () => {
+ // let start = page * rowsPerPage + 1;
+ // let end = Math.min(page * rowsPerPage + rowsPerPage, monitorCount);
+ // return `${start} - ${end}`;
+ // };
const handleSort = async (field) => {
let order = "";
@@ -392,78 +316,13 @@ const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
-
-
- Showing {getRange()} of {monitorCount} monitor(s)
-
-
- `Page ${page + 1} of ${Math.max(0, Math.ceil(count / rowsPerPage))}`
- }
- slotProps={{
- select: {
- MenuProps: {
- keepMounted: true,
- disableScrollLock: 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={{
- 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,
- },
- }}
- />
-
+
);
};
diff --git a/Client/src/Pages/Monitors/Home/index.jsx b/Client/src/Pages/Monitors/Home/index.jsx
index 345a0a88e..59655af92 100644
--- a/Client/src/Pages/Monitors/Home/index.jsx
+++ b/Client/src/Pages/Monitors/Home/index.jsx
@@ -20,6 +20,8 @@ const Monitors = ({ isAdmin }) => {
const authState = useSelector((state) => state.auth);
const dispatch = useDispatch({});
+ console.log({ monitorState });
+
useEffect(() => {
dispatch(getUptimeMonitorsByTeamId(authState.authToken));
}, [authState.authToken, dispatch]);
diff --git a/Client/src/Pages/Monitors/utils.jsx b/Client/src/Pages/Monitors/utils.jsx
index 4dc4260cd..1c351d4ad 100644
--- a/Client/src/Pages/Monitors/utils.jsx
+++ b/Client/src/Pages/Monitors/utils.jsx
@@ -1,13 +1,15 @@
import { useTheme } from "@mui/material";
const useUtils = () => {
- const theme = useTheme();
const determineState = (monitor) => {
if (monitor.isActive === false) return "paused";
if (monitor?.status === undefined) return "pending";
return monitor?.status == true ? "up" : "down";
};
+ /* TODO Refactor: from here on shouldn't live in a custom hook, but on theme, or constants */
+ const theme = useTheme();
+
const statusColor = {
up: theme.palette.success.main,
down: theme.palette.error.main,