diff --git a/Client/src/Components/CircularCount/index.jsx b/Client/src/Components/CircularCount/index.jsx
new file mode 100644
index 000000000..5c09ad162
--- /dev/null
+++ b/Client/src/Components/CircularCount/index.jsx
@@ -0,0 +1,29 @@
+import { Box } from "@mui/material";
+import PropTypes from "prop-types";
+import { useTheme } from "@emotion/react";
+const CircularCount = ({ count }) => {
+ const theme = useTheme();
+ return (
+
+ {count}
+
+ );
+};
+
+CircularCount.propTypes = {
+ count: PropTypes.number,
+};
+
+export default CircularCount;
diff --git a/Client/src/Components/StatBox/index.jsx b/Client/src/Components/StatBox/index.jsx
index 953d378ec..b476a1c33 100644
--- a/Client/src/Components/StatBox/index.jsx
+++ b/Client/src/Components/StatBox/index.jsx
@@ -1,7 +1,7 @@
import { Box, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import PropTypes from "prop-types";
-import useUtils from "../../Pages/Uptime/utils";
+import useUtils from "../../Pages/Uptime/Home/Hooks/useUtils";
/**
* StatBox Component
diff --git a/Client/src/Pages/Infrastructure/Details/index.jsx b/Client/src/Pages/Infrastructure/Details/index.jsx
index acd1bafb1..5da921e30 100644
--- a/Client/src/Pages/Infrastructure/Details/index.jsx
+++ b/Client/src/Pages/Infrastructure/Details/index.jsx
@@ -8,7 +8,7 @@ import AreaChart from "../../../Components/Charts/AreaChart";
import { useSelector } from "react-redux";
import { networkService } from "../../../main";
import PulseDot from "../../../Components/Animated/PulseDot";
-import useUtils from "../../Uptime/utils";
+import useUtils from "../../Uptime/Home/Hooks/useUtils";
import { useNavigate } from "react-router-dom";
import Empty from "./empty";
import { logger } from "../../../Utils/Logger";
diff --git a/Client/src/Pages/Infrastructure/index.jsx b/Client/src/Pages/Infrastructure/index.jsx
index fbfae6a3c..51a254300 100644
--- a/Client/src/Pages/Infrastructure/index.jsx
+++ b/Client/src/Pages/Infrastructure/index.jsx
@@ -2,7 +2,7 @@ import { useEffect, useState, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { /* useDispatch, */ useSelector } from "react-redux";
import { useTheme } from "@emotion/react";
-import useUtils from "../Uptime/utils.jsx";
+import useUtils from "../Uptime/Home/Hooks/useUtils.jsx";
import { jwtDecode } from "jwt-decode";
import SkeletonLayout from "./skeleton";
import Fallback from "../../Components/Fallback";
@@ -17,7 +17,7 @@ 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";
-import Host from "../Uptime/Home/host.jsx";
+import Host from "../Uptime/Home/Components/Host";
import { useIsAdmin } from "../../Hooks/useIsAdmin.js";
import { InfrastructureMenu } from "./components/Menu";
diff --git a/Client/src/Pages/PageSpeed/Configure/index.jsx b/Client/src/Pages/PageSpeed/Configure/index.jsx
index f7829ef35..eee6dfcc9 100644
--- a/Client/src/Pages/PageSpeed/Configure/index.jsx
+++ b/Client/src/Pages/PageSpeed/Configure/index.jsx
@@ -23,7 +23,7 @@ import PulseDot from "../../../Components/Animated/PulseDot";
import LoadingButton from "@mui/lab/LoadingButton";
import PlayCircleOutlineRoundedIcon from "@mui/icons-material/PlayCircleOutlineRounded";
import SkeletonLayout from "./skeleton";
-import useUtils from "../../Uptime/utils";
+import useUtils from "../../Uptime/Home/Hooks/useUtils";
import "./index.css";
import Dialog from "../../../Components/Dialog";
diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx
index c5badbab8..9192faaa9 100644
--- a/Client/src/Pages/PageSpeed/Details/index.jsx
+++ b/Client/src/Pages/PageSpeed/Details/index.jsx
@@ -19,7 +19,7 @@ import PulseDot from "../../../Components/Animated/PulseDot";
import PagespeedDetailsAreaChart from "./Charts/AreaChart";
import Checkbox from "../../../Components/Inputs/Checkbox";
import PieChart from "./Charts/PieChart";
-import useUtils from "../../Uptime/utils";
+import useUtils from "../../Uptime/Home/Hooks/useUtils";
import "./index.css";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import StatBox from "../../../Components/StatBox";
diff --git a/Client/src/Pages/PageSpeed/card.jsx b/Client/src/Pages/PageSpeed/card.jsx
index f5b0d7110..f7f6c4249 100644
--- a/Client/src/Pages/PageSpeed/card.jsx
+++ b/Client/src/Pages/PageSpeed/card.jsx
@@ -7,7 +7,7 @@ import { useTheme } from "@emotion/react";
import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts";
import { useSelector } from "react-redux";
import { formatDateWithTz, formatDurationSplit } from "../../Utils/timeUtils";
-import useUtils from "../Uptime/utils";
+import useUtils from "../Uptime/Home/Hooks/useUtils";
import { useState } from "react";
import IconBox from "../../Components/IconBox";
/**
diff --git a/Client/src/Pages/Uptime/Details/index.jsx b/Client/src/Pages/Uptime/Details/index.jsx
index d5be671d3..be589c1ba 100644
--- a/Client/src/Pages/Uptime/Details/index.jsx
+++ b/Client/src/Pages/Uptime/Details/index.jsx
@@ -19,7 +19,7 @@ import PulseDot from "../../../Components/Animated/PulseDot";
import { ChartBox } from "./styled";
import SkeletonLayout from "./skeleton";
import "./index.css";
-import useUtils from "../utils";
+import useUtils from "../Home/Hooks/useUtils";
import { formatDateWithTz, formatDurationSplit } from "../../../Utils/timeUtils";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import IconBox from "../../../Components/IconBox";
diff --git a/Client/src/Pages/Uptime/Home/actionsMenu.jsx b/Client/src/Pages/Uptime/Home/Components/ActionsMenu/index.jsx
similarity index 94%
rename from Client/src/Pages/Uptime/Home/actionsMenu.jsx
rename to Client/src/Pages/Uptime/Home/Components/ActionsMenu/index.jsx
index 493c5f25e..f0561d6e6 100644
--- a/Client/src/Pages/Uptime/Home/actionsMenu.jsx
+++ b/Client/src/Pages/Uptime/Home/Components/ActionsMenu/index.jsx
@@ -2,16 +2,16 @@ import { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { useTheme } from "@emotion/react";
import { useNavigate } from "react-router-dom";
-import { createToast } from "../../../Utils/toastUtils";
-import { logger } from "../../../Utils/Logger";
+import { createToast } from "../../../../../Utils/toastUtils";
+import { logger } from "../../../../../Utils/Logger";
import { IconButton, Menu, MenuItem } from "@mui/material";
import {
deleteUptimeMonitor,
pauseUptimeMonitor,
-} from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
-import Settings from "../../../assets/icons/settings-bold.svg?react";
+} from "../../../../../Features/UptimeMonitors/uptimeMonitorsSlice";
+import Settings from "../../../../../assets/icons/settings-bold.svg?react";
import PropTypes from "prop-types";
-import Dialog from "../../../Components/Dialog";
+import Dialog from "../../../../../Components/Dialog";
const ActionsMenu = ({
monitor,
diff --git a/Client/src/Pages/Uptime/Home/host.jsx b/Client/src/Pages/Uptime/Home/Components/Host/index.jsx
similarity index 52%
rename from Client/src/Pages/Uptime/Home/host.jsx
rename to Client/src/Pages/Uptime/Home/Components/Host/index.jsx
index ec78875f4..9238f9889 100644
--- a/Client/src/Pages/Uptime/Home/host.jsx
+++ b/Client/src/Pages/Uptime/Home/Components/Host/index.jsx
@@ -1,5 +1,6 @@
-import { Box, Typography } from "@mui/material";
+import { Stack, Box, Typography } from "@mui/material";
import PropTypes from "prop-types";
+import { useTheme } from "@emotion/react";
/**
* Host component.
* This subcomponent receives a params object and displays the host details.
@@ -13,44 +14,43 @@ import PropTypes from "prop-types";
* @returns {React.ElementType} Returns a div element with the host details.
*/
const Host = ({ url, title, percentageColor, percentage }) => {
- const noTitle = title === undefined || title === url;
+ const theme = useTheme();
+ console.log(url, title);
return (
-
-
+
{title}
-
- {percentageColor && percentage && (
-
- {percentage}%
-
- )}
- {!noTitle && {url}}
-
+ {percentageColor && percentage && (
+ <>
+
+
+ {percentage}%
+
+ >
+ )}
+
+ {url}
+
);
};
diff --git a/Client/src/Pages/Uptime/Home/Components/LoadingSpinner/index.jsx b/Client/src/Pages/Uptime/Home/Components/LoadingSpinner/index.jsx
new file mode 100644
index 000000000..a649473af
--- /dev/null
+++ b/Client/src/Pages/Uptime/Home/Components/LoadingSpinner/index.jsx
@@ -0,0 +1,46 @@
+import { CircularProgress, Box } from "@mui/material";
+import { useTheme } from "@emotion/react";
+import PropTypes from "prop-types";
+const LoadingSpinner = ({ shouldRender }) => {
+ const theme = useTheme();
+ if (shouldRender === false) {
+ return;
+ }
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+LoadingSpinner.propTypes = {
+ shouldRender: PropTypes.bool,
+};
+
+export default LoadingSpinner;
diff --git a/Client/src/Pages/Uptime/Home/Components/SearchComponent/index.jsx b/Client/src/Pages/Uptime/Home/Components/SearchComponent/index.jsx
new file mode 100644
index 000000000..d93e6672b
--- /dev/null
+++ b/Client/src/Pages/Uptime/Home/Components/SearchComponent/index.jsx
@@ -0,0 +1,43 @@
+import { useState } from "react";
+import Search from "../../../../../Components/Inputs/Search";
+import { Box } from "@mui/material";
+import useDebounce from "../../Hooks/useDebounce";
+import { useEffect } from "react";
+import PropTypes from "prop-types";
+
+const SearchComponent = ({ monitors, onSearchChange, setIsSearching }) => {
+ const [localSearch, setLocalSearch] = useState("");
+ const debouncedSearch = useDebounce(localSearch, 500);
+ useEffect(() => {
+ onSearchChange(debouncedSearch);
+ setIsSearching(false);
+ }, [debouncedSearch, onSearchChange, setIsSearching]);
+
+ const handleSearch = (value) => {
+ setLocalSearch(value);
+ setIsSearching(true);
+ };
+
+ return (
+
+
+
+ );
+};
+
+SearchComponent.propTypes = {
+ monitors: PropTypes.array,
+ onSearchChange: PropTypes.func,
+ setIsSearching: PropTypes.func,
+};
+
+export default SearchComponent;
diff --git a/Client/src/Pages/Uptime/Home/skeleton.jsx b/Client/src/Pages/Uptime/Home/Components/Skeleton/index.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/Home/skeleton.jsx
rename to Client/src/Pages/Uptime/Home/Components/Skeleton/index.jsx
diff --git a/Client/src/Pages/Uptime/Home/Components/StatusBoxes/index.jsx b/Client/src/Pages/Uptime/Home/Components/StatusBoxes/index.jsx
new file mode 100644
index 000000000..ac9eca47f
--- /dev/null
+++ b/Client/src/Pages/Uptime/Home/Components/StatusBoxes/index.jsx
@@ -0,0 +1,36 @@
+import PropTypes from "prop-types";
+import { Stack } from "@mui/material";
+import StatusBox from "./statusBox";
+import { useTheme } from "@emotion/react";
+import SkeletonLayout from "./skeleton";
+
+const StatusBoxes = ({ shouldRender, monitorsSummary }) => {
+ const theme = useTheme();
+ if (!shouldRender) return ;
+ return (
+
+
+
+
+
+ );
+};
+
+StatusBoxes.propTypes = {
+ monitorsSummary: PropTypes.object.isRequired,
+};
+
+export default StatusBoxes;
diff --git a/Client/src/Pages/Uptime/Home/Components/StatusBoxes/skeleton.jsx b/Client/src/Pages/Uptime/Home/Components/StatusBoxes/skeleton.jsx
new file mode 100644
index 000000000..df6e08ccd
--- /dev/null
+++ b/Client/src/Pages/Uptime/Home/Components/StatusBoxes/skeleton.jsx
@@ -0,0 +1,31 @@
+import { Skeleton, Stack } from "@mui/material";
+import { useTheme } from "@emotion/react";
+
+const SkeletonLayout = () => {
+ const theme = useTheme();
+ return (
+
+
+
+
+
+ );
+};
+
+export default SkeletonLayout;
diff --git a/Client/src/Pages/Uptime/Home/StatusBox.jsx b/Client/src/Pages/Uptime/Home/Components/StatusBoxes/statusBox.jsx
similarity index 60%
rename from Client/src/Pages/Uptime/Home/StatusBox.jsx
rename to Client/src/Pages/Uptime/Home/Components/StatusBoxes/statusBox.jsx
index d04ebdf5f..b157857ea 100644
--- a/Client/src/Pages/Uptime/Home/StatusBox.jsx
+++ b/Client/src/Pages/Uptime/Home/Components/StatusBoxes/statusBox.jsx
@@ -1,9 +1,9 @@
import PropTypes from "prop-types";
import { useTheme } from "@emotion/react";
import { Box, Stack, Typography } from "@mui/material";
-import Arrow from "../../../assets/icons/top-right-arrow.svg?react";
-import Background from "../../../assets/Images/background-grid.svg?react";
-import ClockSnooze from "../../../assets/icons/clock-snooze.svg?react";
+import Arrow from "../../../../../assets/icons/top-right-arrow.svg?react";
+import Background from "../../../../../assets/Images/background-grid.svg?react";
+import ClockSnooze from "../../../../../assets/icons/clock-snooze.svg?react";
const StatusBox = ({ title, value }) => {
const theme = useTheme();
@@ -52,47 +52,48 @@ const StatusBox = ({ title, value }) => {
overflow="hidden"
>
-
- {title}
-
- {icon}
-
- {value}
-
-
+
+
+ {title}
+
+ {icon}
+
+
- #
-
+ {value}
+
+
+ #
+
+
);
diff --git a/Client/src/Pages/Uptime/Home/UptimeDataTable/index.jsx b/Client/src/Pages/Uptime/Home/Components/UptimeDataTable/index.jsx
similarity index 61%
rename from Client/src/Pages/Uptime/Home/UptimeDataTable/index.jsx
rename to Client/src/Pages/Uptime/Home/Components/UptimeDataTable/index.jsx
index 6961c6d5c..eaf08046d 100644
--- a/Client/src/Pages/Uptime/Home/UptimeDataTable/index.jsx
+++ b/Client/src/Pages/Uptime/Home/Components/UptimeDataTable/index.jsx
@@ -1,57 +1,51 @@
// Components
import { Box, Stack, CircularProgress } from "@mui/material";
-import Search from "../../../../Components/Inputs/Search";
-import { Heading } from "../../../../Components/Heading";
-import DataTable from "../../../../Components/Table";
+import { Heading } from "../../../../../Components/Heading";
+import DataTable from "../../../../../Components/Table";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded";
-import Host from "../host";
-import { StatusLabel } from "../../../../Components/Label";
-import BarChart from "../../../../Components/Charts/BarChart";
-import ActionsMenu from "../actionsMenu";
-
+import Host from "../Host";
+import { StatusLabel } from "../../../../../Components/Label";
+import BarChart from "../../../../../Components/Charts/BarChart";
+import ActionsMenu from "../ActionsMenu";
+import { useState } from "react";
+import SearchComponent from "../SearchComponent";
+import CircularCount from "../../../../../Components/CircularCount";
+import LoadingSpinner from "../LoadingSpinner";
+import UptimeDataTableSkeleton from "./skeleton";
// Utils
import { useTheme } from "@emotion/react";
-import useUtils from "../../utils";
-import { useState, memo, useCallback } from "react";
+import useUtils from "../../Hooks/useUtils";
import { useNavigate } from "react-router-dom";
-import "../index.css";
import PropTypes from "prop-types";
-const SearchComponent = memo(
- ({ monitors, debouncedSearch, onSearchChange, setIsSearching }) => {
- const [localSearch, setLocalSearch] = useState(debouncedSearch);
- const handleSearch = useCallback(
- (value) => {
- setIsSearching(true);
- setLocalSearch(value);
- onSearchChange(value);
- },
- [onSearchChange, setIsSearching]
- );
+const MonitorDataTable = ({ shouldRender, isSearching, headers, filteredMonitors }) => {
+ const theme = useTheme();
+ const navigate = useNavigate();
- return (
-
-
-
- );
- }
-);
-SearchComponent.displayName = "SearchComponent";
-SearchComponent.propTypes = {
- monitors: PropTypes.array,
- debouncedSearch: PropTypes.string,
- onSearchChange: PropTypes.func,
- setIsSearching: PropTypes.func,
+ if (!shouldRender) return null;
+ return (
+
+
+ {
+ navigate(`/uptime/${row.id}`);
+ },
+ emptyView: "No monitors found",
+ }}
+ />
+
+ );
};
/**
@@ -79,31 +73,32 @@ SearchComponent.propTypes = {
* @param {string} props.search - Current search query
* @param {Function} props.setSearch - Callback to update search query
* @param {boolean} props.isSearching - Whether a search is in progress
- * @param {Function} props.setIsSearching - Callback to update search state
* @param {Function} props.setIsLoading - Callback to update loading state
* @param {Function} props.triggerUpdate - Callback to trigger a data refresh
* @returns {JSX.Element} Rendered component
*/
-const UptimeDataTable = ({
- isAdmin,
- isLoading,
- monitors,
- filteredMonitors,
- monitorCount,
- sort,
- setSort,
- debouncedSearch,
- setSearch,
- isSearching,
- setIsSearching,
- setIsLoading,
- triggerUpdate,
-}) => {
- const { determineState } = useUtils();
+const UptimeDataTable = (props) => {
+ // Utils
+ const {
+ isAdmin,
+ setIsLoading,
+ monitors,
+ filteredMonitors,
+ monitorCount,
+ sort,
+ setSort,
+ setSearch,
+ triggerUpdate,
+ monitorsAreLoading,
+ } = props;
+ const { determineState } = useUtils();
const theme = useTheme();
const navigate = useNavigate();
+ // Local state
+ const [isSearching, setIsSearching] = useState(false);
+ // Handlers
const handleSort = (field) => {
let order = "";
if (sort.field !== field) {
@@ -212,7 +207,7 @@ const UptimeDataTable = ({
),
},
];
-
+ console.log("rendering");
return (
Uptime monitors
- {/* TODO Same as the one in Infrastructure. Create component */}
-
- {monitorCount}
-
-
+
-
- {(isSearching || isLoading) && (
- <>
-
-
-
-
- >
- )}
- {
- navigate(`/uptime/${row.id}`);
- },
- emptyView: "No monitors found",
- }}
- />
-
+
+
+
);
};
-const MemoizedUptimeDataTable = memo(UptimeDataTable);
-export default MemoizedUptimeDataTable;
-
UptimeDataTable.propTypes = {
+ isSearching: PropTypes.bool,
+ setIsSearching: PropTypes.func,
+ setSort: PropTypes.func,
+ setSearch: PropTypes.func,
+ setIsLoading: PropTypes.func,
+ triggerUpdate: PropTypes.func,
+ debouncedSearch: PropTypes.string,
+ onSearchChange: PropTypes.func,
isAdmin: PropTypes.bool,
isLoading: PropTypes.bool,
monitors: PropTypes.array,
@@ -315,11 +258,6 @@ UptimeDataTable.propTypes = {
field: PropTypes.string,
order: PropTypes.oneOf(["asc", "desc"]),
}),
- setSort: PropTypes.func,
- debouncedSearch: PropTypes.string,
- setSearch: PropTypes.func,
- isSearching: PropTypes.bool,
- setIsSearching: PropTypes.func,
- setIsLoading: PropTypes.func,
- triggerUpdate: PropTypes.func,
};
+
+export default UptimeDataTable;
diff --git a/Client/src/Pages/Uptime/Home/Components/UptimeDataTable/skeleton.jsx b/Client/src/Pages/Uptime/Home/Components/UptimeDataTable/skeleton.jsx
new file mode 100644
index 000000000..303eb48c8
--- /dev/null
+++ b/Client/src/Pages/Uptime/Home/Components/UptimeDataTable/skeleton.jsx
@@ -0,0 +1,21 @@
+import { Skeleton } from "@mui/material";
+import PropTypes from "prop-types";
+
+const UptimeDataTableSkeleton = ({ shouldRender }) => {
+ if (!shouldRender) return null;
+
+ return (
+
+ );
+};
+
+UptimeDataTableSkeleton.propTypes = {
+ shouldRender: PropTypes.bool.isRequired,
+};
+
+export default UptimeDataTableSkeleton;
diff --git a/Client/src/Pages/Uptime/Home/Hooks/useDebounce.jsx b/Client/src/Pages/Uptime/Home/Hooks/useDebounce.jsx
new file mode 100644
index 000000000..11d639f14
--- /dev/null
+++ b/Client/src/Pages/Uptime/Home/Hooks/useDebounce.jsx
@@ -0,0 +1,18 @@
+import { useState, useEffect } from "react";
+
+const useDebounce = (value, delay) => {
+ const [debouncedValue, setDebouncedValue] = useState(value);
+
+ useEffect(() => {
+ const handler = setTimeout(() => {
+ setDebouncedValue(value);
+ }, delay);
+
+ return () => {
+ clearTimeout(handler);
+ };
+ }, [value, delay]);
+ return debouncedValue;
+};
+
+export default useDebounce;
diff --git a/Client/src/Pages/Uptime/Home/Hooks/useMonitorFetch.jsx b/Client/src/Pages/Uptime/Home/Hooks/useMonitorFetch.jsx
new file mode 100644
index 000000000..d4b7e5ba7
--- /dev/null
+++ b/Client/src/Pages/Uptime/Home/Hooks/useMonitorFetch.jsx
@@ -0,0 +1,98 @@
+import { useEffect, useState } from "react";
+import { networkService } from "../../../../main";
+import { createToast } from "../../../../Utils/toastUtils";
+import { useTheme } from "@emotion/react";
+const getMonitorWithPercentage = (monitor, theme) => {
+ let uptimePercentage = "";
+ let percentageColor = "";
+
+ if (monitor.uptimePercentage !== undefined) {
+ uptimePercentage =
+ monitor.uptimePercentage === 0 ? "0" : (monitor.uptimePercentage * 100).toFixed(2);
+
+ percentageColor =
+ monitor.uptimePercentage < 0.25
+ ? theme.palette.error.main
+ : monitor.uptimePercentage < 0.5
+ ? theme.palette.warning.main
+ : monitor.uptimePercentage < 0.75
+ ? theme.palette.success.main
+ : theme.palette.success.main;
+ }
+
+ return {
+ id: monitor._id,
+ name: monitor.name,
+ url: monitor.url,
+ title: monitor.name,
+ percentage: uptimePercentage,
+ percentageColor,
+ monitor: monitor,
+ };
+};
+
+export const useMonitorFetch = ({
+ authToken,
+ teamId,
+ limit,
+ page,
+ rowsPerPage,
+ filter,
+ field,
+ order,
+ triggerUpdate,
+}) => {
+ const [monitorsAreLoading, setMonitorsAreLoading] = useState(false);
+ const [monitors, setMonitors] = useState([]);
+ const [filteredMonitors, setFilteredMonitors] = useState([]);
+ const [monitorsSummary, setMonitorsSummary] = useState({});
+
+ const theme = useTheme();
+
+ useEffect(() => {
+ const fetchMonitors = async () => {
+ try {
+ setMonitorsAreLoading(true);
+ const res = await networkService.getMonitorsByTeamId({
+ authToken,
+ teamId,
+ limit,
+ types: ["http", "ping", "docker", "port"],
+ page,
+ rowsPerPage,
+ filter,
+ field,
+ order,
+ });
+ const { monitors, filteredMonitors, summary } = res.data.data;
+ const mappedMonitors = filteredMonitors.map((monitor) =>
+ getMonitorWithPercentage(monitor, theme)
+ );
+ setMonitors(monitors);
+ setFilteredMonitors(mappedMonitors);
+ setMonitorsSummary(summary);
+ } catch (error) {
+ createToast({
+ body: error.message,
+ });
+ } finally {
+ setMonitorsAreLoading(false);
+ }
+ };
+ fetchMonitors();
+ }, [
+ authToken,
+ teamId,
+ limit,
+ field,
+ filter,
+ order,
+ page,
+ rowsPerPage,
+ theme,
+ triggerUpdate,
+ ]);
+ return { monitors, filteredMonitors, monitorsSummary, monitorsAreLoading };
+};
+
+export default useMonitorFetch;
diff --git a/Client/src/Pages/Uptime/utils.jsx b/Client/src/Pages/Uptime/Home/Hooks/useUtils.jsx
similarity index 100%
rename from Client/src/Pages/Uptime/utils.jsx
rename to Client/src/Pages/Uptime/Home/Hooks/useUtils.jsx
diff --git a/Client/src/Pages/Uptime/Home/UptimeDataTable/Skeleton/index.jsx b/Client/src/Pages/Uptime/Home/UptimeDataTable/Skeleton/index.jsx
deleted file mode 100644
index c17f6d74b..000000000
--- a/Client/src/Pages/Uptime/Home/UptimeDataTable/Skeleton/index.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Skeleton } from "@mui/material";
-import DataTable from "../../../../../Components/Table";
-const ROWS_NUMBER = 7;
-const ROWS_ARRAY = Array.from({ length: ROWS_NUMBER }, (_, i) => i);
-
-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 (
-
- );
-};
-
-export { TableSkeleton };
diff --git a/Client/src/Pages/Uptime/Home/fallback.jsx b/Client/src/Pages/Uptime/Home/fallback.jsx
deleted file mode 100644
index 2606ca95f..000000000
--- a/Client/src/Pages/Uptime/Home/fallback.jsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Box, Button, Stack, Typography } from "@mui/material";
-import { useTheme } from "@emotion/react";
-import { useNavigate } from "react-router-dom";
-import { useSelector } from "react-redux";
-import PlaceholderLight from "../../../assets/Images/data_placeholder.svg?react";
-import PlaceholderDark from "../../../assets/Images/data_placeholder_dark.svg?react";
-import PropTypes from "prop-types";
-
-const Fallback = ({ isAdmin }) => {
- const theme = useTheme();
- const navigate = useNavigate();
- const mode = useSelector((state) => state.ui.mode);
-
- return (
-
-
- {mode === "light" ? : }
-
-
- No monitors found to display
-
-
- It looks like you don’t have any monitors set up yet.
-
- {isAdmin && (
-
- )}
-
- );
-};
-
-Fallback.propTypes = {
- isAdmin: PropTypes.bool,
-};
-
-export default Fallback;
diff --git a/Client/src/Pages/Uptime/Home/index.css b/Client/src/Pages/Uptime/Home/index.css
deleted file mode 100644
index adb1d8674..000000000
--- a/Client/src/Pages/Uptime/Home/index.css
+++ /dev/null
@@ -1,6 +0,0 @@
-/* TODO take these from here and declare using emotion. Plus, the values should live in the theme */
-.monitors .MuiStack-root > button:not(.MuiIconButton-root) {
- font-size: var(--env-var-font-size-medium);
- height: var(--env-var-height-2);
- min-width: fit-content;
-}
diff --git a/Client/src/Pages/Uptime/Home/index.jsx b/Client/src/Pages/Uptime/Home/index.jsx
index 446702ebb..c300118e0 100644
--- a/Client/src/Pages/Uptime/Home/index.jsx
+++ b/Client/src/Pages/Uptime/Home/index.jsx
@@ -1,128 +1,70 @@
// Components
-import { Box, Stack, Button } from "@mui/material";
+import Breadcrumbs from "../../../Components/Breadcrumbs";
import Greeting from "../../../Utils/greeting";
-import SkeletonLayout from "./skeleton";
-import Fallback from "./fallback";
-import StatusBox from "./StatusBox";
-import UptimeDataTable from "./UptimeDataTable";
+import StatusBoxes from "./Components/StatusBoxes";
+import UptimeDataTable from "./Components/UptimeDataTable";
import Pagination from "../../../Components/Table/TablePagination";
+// MUI Components
+import { Stack, Box, Button } from "@mui/material";
+
// Utils
-import { useTheme } from "@emotion/react";
-import { useEffect, useState, useCallback, useMemo } from "react";
-import { setRowsPerPage } from "../../../Features/UI/uiSlice";
+import { useState, useCallback } from "react";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
-import { useSelector, useDispatch } from "react-redux";
+import { useTheme } from "@emotion/react";
import { useNavigate } from "react-router-dom";
-import { createToast } from "../../../Utils/toastUtils";
-import Breadcrumbs from "../../../Components/Breadcrumbs";
-import useDebounce from "../../../Utils/debounce";
-import { networkService } from "../../../main";
+import useMonitorFetch from "./Hooks/useMonitorFetch";
+import { useSelector, useDispatch } from "react-redux";
+import { setRowsPerPage } from "../../../Features/UI/uiSlice";
+import PropTypes from "prop-types";
const BREADCRUMBS = [{ name: `Uptime`, path: "/uptime" }];
+const CreateMonitorButton = ({ shouldRender }) => {
+ // Utils
+ const navigate = useNavigate();
+ if (shouldRender === false) {
+ return;
+ }
+
+ return (
+
+
+
+ );
+};
+
+CreateMonitorButton.propTypes = {
+ shouldRender: PropTypes.bool,
+};
+
const UptimeMonitors = () => {
// Redux state
+ const { authToken, user } = useSelector((state) => state.auth);
const rowsPerPage = useSelector((state) => state.ui.monitors.rowsPerPage);
+
// Local state
- const [sort, setSort] = useState({});
const [search, setSearch] = useState("");
const [page, setPage] = useState(0);
+ const [sort, setSort] = useState({});
const [isSearching, setIsSearching] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [monitorUpdateTrigger, setMonitorUpdateTrigger] = useState(false);
- const [monitors, setMonitors] = useState([]);
- const [filteredMonitors, setFilteredMonitors] = useState([]);
- const [monitorsSummary, setMonitorsSummary] = useState({});
// Utils
- const debouncedFilter = useDebounce(search, 500);
- const dispatch = useDispatch();
const theme = useTheme();
- const navigate = useNavigate();
const isAdmin = useIsAdmin();
+ const dispatch = useDispatch();
- const authState = useSelector((state) => state.auth);
-
- const fetchParams = useMemo(
- () => ({
- authToken: authState.authToken,
- teamId: authState.user.teamId,
- sort: { field: sort.field, order: sort.order },
- filter: debouncedFilter,
- page,
- rowsPerPage,
- }),
- [authState.authToken, authState.user.teamId, sort, debouncedFilter, page, rowsPerPage]
- );
-
- const getMonitorWithPercentage = useCallback((monitor, theme) => {
- let uptimePercentage = "";
- let percentageColor = "";
-
- if (monitor.uptimePercentage !== undefined) {
- uptimePercentage =
- monitor.uptimePercentage === 0
- ? "0"
- : (monitor.uptimePercentage * 100).toFixed(2);
-
- percentageColor =
- monitor.uptimePercentage < 0.25
- ? theme.palette.error.main
- : monitor.uptimePercentage < 0.5
- ? theme.palette.warning.main
- : monitor.uptimePercentage < 0.75
- ? theme.palette.success.main
- : theme.palette.success.main;
- }
-
- return {
- id: monitor._id,
- name: monitor.name,
- url: monitor.url,
- title: monitor.name,
- percentage: uptimePercentage,
- percentageColor,
- monitor: monitor,
- };
- }, []);
-
- const fetchMonitors = useCallback(async () => {
- try {
- setIsLoading(true);
- const config = fetchParams;
- const res = await networkService.getMonitorsByTeamId({
- authToken: config.authToken,
- teamId: config.teamId,
- limit: 25,
- types: ["http", "ping", "docker", "port"],
- page: config.page,
- rowsPerPage: config.rowsPerPage,
- filter: config.filter,
- field: config.sort.field,
- order: config.sort.order,
- });
- const { monitors, filteredMonitors, summary } = res.data.data;
- const mappedMonitors = filteredMonitors.map((monitor) =>
- getMonitorWithPercentage(monitor, theme)
- );
- setMonitors(monitors);
- setFilteredMonitors(mappedMonitors);
- setMonitorsSummary(summary);
- } catch (error) {
- createToast({
- body: error.message,
- });
- } finally {
- setIsLoading(false);
- setIsSearching(false);
- }
- }, [fetchParams, getMonitorWithPercentage, theme]);
-
- useEffect(() => {
- fetchMonitors();
- }, [fetchMonitors, monitorUpdateTrigger]);
-
+ // Handlers
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
@@ -140,103 +82,58 @@ const UptimeMonitors = () => {
const triggerUpdate = useCallback(() => {
setMonitorUpdateTrigger((prev) => !prev);
}, []);
+
+ const teamId = user.teamId;
+
+ const { monitorsAreLoading, monitors, filteredMonitors, monitorsSummary } =
+ useMonitorFetch({
+ authToken,
+ teamId,
+ limit: 25,
+ page,
+ rowsPerPage: rowsPerPage,
+ filter: search,
+ field: sort.field,
+ order: sort.order,
+ triggerUpdate: monitorUpdateTrigger,
+ });
const totalMonitors = monitorsSummary?.totalMonitors ?? 0;
- const hasMonitors = totalMonitors > 0;
- const canAddMonitor = isAdmin && hasMonitors;
return (
-
-
-
- {canAddMonitor && (
- <>
-
- >
- )}
-
-
-
- {
- <>
- {!isLoading && !hasMonitors && }
- {isLoading ? (
-
- ) : (
- hasMonitors && (
- <>
-
-
-
-
-
-
-
- >
- )
- )}
- >
- }
+
+
+
+
+
+
);
};
diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js
index 86c0fb840..ccd8ec26f 100644
--- a/Server/controllers/monitorController.js
+++ b/Server/controllers/monitorController.js
@@ -436,7 +436,7 @@ class MonitorController {
monitor.isActive = !monitor.isActive;
monitor.status = undefined;
monitor.save();
- return res.ssuccess({
+ return res.success({
msg: monitor.isActive
? successMessages.MONITOR_RESUME
: successMessages.MONITOR_PAUSE,
diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js
index 4c60e0f22..cad9bd68f 100644
--- a/Server/db/mongo/modules/monitorModule.js
+++ b/Server/db/mongo/modules/monitorModule.js
@@ -497,7 +497,7 @@ const getMonitorById = async (monitorId) => {
const getMonitorsByTeamId = async (req) => {
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
-
+ console.log("req.query", req.query);
limit = parseInt(limit);
page = parseInt(page);
rowsPerPage = parseInt(rowsPerPage);
@@ -512,8 +512,16 @@ const getMonitorsByTeamId = async (req) => {
}
const skip = page && rowsPerPage ? page * rowsPerPage : 0;
-
const sort = { [field]: order === "asc" ? 1 : -1 };
+
+ console.log("limit", limit);
+ console.log("page", page);
+ console.log("rowsPerPage", rowsPerPage);
+ console.log("filter", filter);
+ console.log("field", field);
+ console.log("order", order);
+ console.log("skip", skip);
+ console.log("sort", sort);
const results = await Monitor.aggregate([
{ $match: matchStage },
{
@@ -675,6 +683,7 @@ const getMonitorsByTeamId = async (req) => {
]);
let { monitors, filteredMonitors, summary } = results[0];
+ console.log("filteredMonitors", filteredMonitors);
filteredMonitors = filteredMonitors.map((monitor) => {
if (!monitor.checks) {
return monitor;
@@ -839,3 +848,22 @@ export {
groupChecksByTime,
calculateGroupStats,
};
+
+// limit 25
+// page 1
+// rowsPerPage 25
+// filter undefined
+// field name
+// order asc
+// skip 25
+// sort { name: 1 }
+// filteredMonitors []
+
+// limit 25
+// page NaN
+// rowsPerPage 25
+// filter undefined
+// field name
+// order asc
+// skip 0
+// sort { name: 1 }