diff --git a/Client/src/Components/Fallback/index.css b/Client/src/Components/Fallback/index.css
index 9b4d6f653..cad46300b 100644
--- a/Client/src/Components/Fallback/index.css
+++ b/Client/src/Components/Fallback/index.css
@@ -19,6 +19,22 @@
font-size: var(--env-var-font-size-medium);
}
+[class*="fallback__"] .background-pattern-svg {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 0;
+
+ width: 100%;
+ max-width: 800px;
+ height: 100%;
+ max-height: 800px;
+
+ background-position: center;
+ background-size: cover;
+ background-repeat: no-repeat;
+}
.fallback__status > .MuiStack-root {
margin-left: var(--env-var-spacing-2);
}
diff --git a/Client/src/Components/Layouts/HomeLayout/index.css b/Client/src/Components/Layouts/HomeLayout/index.css
index e08904f8e..ef6f38abd 100644
--- a/Client/src/Components/Layouts/HomeLayout/index.css
+++ b/Client/src/Components/Layouts/HomeLayout/index.css
@@ -26,20 +26,3 @@
min-height: calc(100vh - var(--env-var-spacing-2) * 2);
flex: 1;
}
-
-.home-layout > div:has(> [class*="fallback__"]) .background-pattern-svg {
- position: absolute;
- top: 0;
- left: 50%;
- transform: translate(-50%, -50%);
- z-index: 0;
-
- width: 100%;
- max-width: 800px;
- height: 100%;
- max-height: 800px;
-
- background-position: center;
- background-size: cover;
- background-repeat: no-repeat;
-}
diff --git a/Client/src/Components/MonitorStatusHeader/index.jsx b/Client/src/Components/MonitorStatusHeader/index.jsx
index 6a7188775..c246a928f 100644
--- a/Client/src/Components/MonitorStatusHeader/index.jsx
+++ b/Client/src/Components/MonitorStatusHeader/index.jsx
@@ -8,7 +8,7 @@ import ConfigButton from "./ConfigButton";
import SkeletonLayout from "./skeleton";
import PropTypes from "prop-types";
-const MonitorHeader = ({ shouldRender = true, isAdmin, monitor }) => {
+const MonitorStatusHeader = ({ shouldRender = true, isAdmin, monitor }) => {
const theme = useTheme();
const { statusColor, statusMsg, determineState } = useUtils();
if (!shouldRender) {
@@ -21,7 +21,7 @@ const MonitorHeader = ({ shouldRender = true, isAdmin, monitor }) => {
justifyContent="space-between"
>
- {monitor.name}
+ {monitor?.name}
{
);
};
-MonitorHeader.propTypes = {
+MonitorStatusHeader.propTypes = {
shouldRender: PropTypes.bool,
isAdmin: PropTypes.bool,
monitor: PropTypes.object,
};
-export default MonitorHeader;
+export default MonitorStatusHeader;
diff --git a/Client/src/Components/NetworkErrorFallback/index.jsx b/Client/src/Components/NetworkErrorFallback/index.jsx
new file mode 100644
index 000000000..1039b256b
--- /dev/null
+++ b/Client/src/Components/NetworkErrorFallback/index.jsx
@@ -0,0 +1,85 @@
+import { useTheme } from "@emotion/react";
+import { Box, Stack, Typography } from "@mui/material";
+import Skeleton from "../../assets/Images/create-placeholder.svg?react";
+import SkeletonDark from "../../assets/Images/create-placeholder-dark.svg?react";
+import Background from "../../assets/Images/background-grid.svg?react";
+import { useSelector } from "react-redux";
+
+/**
+ * Fallback component to display a fallback UI for network errors
+ *
+ * @returns {JSX.Element} The rendered fallback UI.
+ */
+
+const NetworkErrorFallback = () => {
+ const theme = useTheme();
+ const mode = useSelector((state) => state.ui.mode);
+
+ return (
+
+
+ {mode === "light" ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ Network error
+
+ Please check your connection
+
+
+
+ );
+};
+
+export default NetworkErrorFallback;
diff --git a/Client/src/Components/Table/TablePagination/index.jsx b/Client/src/Components/Table/TablePagination/index.jsx
index 518c18599..014e2405f 100644
--- a/Client/src/Components/Table/TablePagination/index.jsx
+++ b/Client/src/Components/Table/TablePagination/index.jsx
@@ -6,8 +6,8 @@ import SelectorVertical from "../../../assets/icons/selector-vertical.svg?react"
Pagination.propTypes = {
paginationLabel: PropTypes.string, // Label for the pagination.
- itemCount: PropTypes.number.isRequired, // Total number of items for pagination.
- page: PropTypes.number.isRequired, // Current page index.
+ itemCount: PropTypes.number, // Total number of items for pagination.
+ page: PropTypes.number, // 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, // Function to handle changes in rows per page.
@@ -29,9 +29,9 @@ const ROWS_PER_PAGE_OPTIONS = [5, 10, 15, 25];
*/
function Pagination({
paginationLabel,
- itemCount,
- page,
- rowsPerPage,
+ itemCount = 0,
+ page = 0,
+ rowsPerPage = 5,
handleChangePage,
handleChangeRowsPerPage,
}) {
diff --git a/Client/src/Components/Table/index.jsx b/Client/src/Components/Table/index.jsx
index 7a7048ab8..54e47ed1f 100644
--- a/Client/src/Components/Table/index.jsx
+++ b/Client/src/Components/Table/index.jsx
@@ -32,7 +32,14 @@ import { useTheme } from "@emotion/react";
* @returns {JSX.Element} The rendered table component.
*/
-const DataTable = ({ headers, data, config = { emptyView: "No data" } }) => {
+const DataTable = ({
+ headers = [],
+ data = [],
+ config = {
+ emptyView: "No data",
+ onRowClick: () => {},
+ },
+}) => {
const theme = useTheme();
if ((headers?.length ?? 0) === 0) {
return "No data";
@@ -117,9 +124,9 @@ DataTable.propTypes = {
render: PropTypes.func.isRequired,
})
).isRequired,
- data: PropTypes.array.isRequired,
+ data: PropTypes.array,
config: PropTypes.shape({
- onRowClick: PropTypes.func.isRequired,
+ onRowClick: PropTypes.func,
rowSX: PropTypes.object,
emptyView: PropTypes.node,
}),
diff --git a/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx b/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx
index aad697f9f..c9204c38a 100644
--- a/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx
+++ b/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx
@@ -118,8 +118,6 @@ const PieChart = ({ audits }) => {
const [highlightedItem, setHighLightedItem] = useState(null);
const [expand, setExpand] = useState(false);
- if (typeof audits === "undefined") return null;
-
/**
* Retrieves color properties based on the performance value.
*
@@ -166,6 +164,8 @@ const PieChart = ({ audits }) => {
*/
let performance = 0;
const getPieData = (audits) => {
+ if (typeof audits === "undefined") return undefined;
+
let data = [];
let startAngle = 0;
const padding = 3; // padding between arcs
diff --git a/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx b/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx
index c7612821f..010716b88 100644
--- a/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx
+++ b/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx
@@ -7,84 +7,84 @@ import PropTypes from "prop-types";
const PieChartLegend = ({ audits }) => {
const theme = useTheme();
- if (typeof audits === "undefined") return null;
return (
}
header="Performance metrics"
sx={{ flex: 1 }}
>
- {Object.keys(audits).map((key) => {
- if (key === "_id") return;
+ {typeof audits !== "undefined" &&
+ Object.keys(audits).map((key) => {
+ if (key === "_id") return;
- let audit = audits[key];
- let score = audit.score * 100;
- let bg =
- score >= 90
- ? theme.palette.success.main
- : score >= 50
- ? theme.palette.warning.main
- : score >= 0
- ? theme.palette.error.main
- : theme.palette.tertiary.main;
+ let audit = audits[key];
+ let score = audit.score * 100;
+ let bg =
+ score >= 90
+ ? theme.palette.success.main
+ : score >= 50
+ ? theme.palette.warning.main
+ : score >= 0
+ ? theme.palette.error.main
+ : theme.palette.tertiary.main;
- // Find the position where the number ends and the unit begins
- const match = audit.displayValue.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/);
- let value;
- let unit;
- if (match) {
- value = match[1];
- match[2] === "s" ? (unit = "seconds") : (unit = match[2]);
- } else {
- value = audit.displayValue;
- }
+ // Find the position where the number ends and the unit begins
+ const match = audit.displayValue.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/);
+ let value;
+ let unit;
+ if (match) {
+ value = match[1];
+ match[2] === "s" ? (unit = "seconds") : (unit = match[2]);
+ } else {
+ value = audit.displayValue;
+ }
- return (
-
-
-
- {audit.title}
-
-
- {value}
+ return (
+
+
+
+ {audit.title}
+
- {unit}
+ {value}
+
+ {unit}
+
-
-
-
-
- );
- })}
+
+
+
+ );
+ })}
);
};
diff --git a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx
index c818d04a3..582648e23 100644
--- a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx
+++ b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx
@@ -15,10 +15,6 @@ const PageSpeedAreaChart = ({ shouldRender, monitor, metrics, handleMetrics }) =
return ;
}
- if (typeof monitor === "undefined") {
- return null;
- }
-
const data = monitor?.checks ? [...monitor.checks].reverse() : [];
return (
diff --git a/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx b/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx
index 2db40e126..cd220a73c 100644
--- a/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx
+++ b/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx
@@ -5,18 +5,14 @@ import { Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import PieChartLegend from "../Charts/PieChartLegend";
import SkeletonLayout from "./skeleton";
+
const PerformanceReport = ({ shouldRender, audits }) => {
- console.log("shouldRender", shouldRender);
const theme = useTheme();
if (!shouldRender) {
return ;
}
- if (typeof audits === "undefined") {
- return null;
- }
-
return (
}
diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx
index 43eb0ed7b..6fa4aa82c 100644
--- a/Client/src/Pages/PageSpeed/Details/index.jsx
+++ b/Client/src/Pages/PageSpeed/Details/index.jsx
@@ -1,11 +1,10 @@
// Components
-import { Stack, Typography } from "@mui/material";
+import { Stack, Typography, Skeleton } from "@mui/material";
import Breadcrumbs from "../../../Components/Breadcrumbs";
import MonitorStatusHeader from "../../../Components/MonitorStatusHeader";
import PageSpeedStatusBoxes from "./Components/PageSpeedStatusBoxes";
import PageSpeedAreaChart from "./Components/PageSpeedAreaChart";
import PerformanceReport from "./Components/PerformanceReport";
-import Fallback from "../../../Components/Fallback";
// Utils
import { useTheme } from "@emotion/react";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
@@ -31,8 +30,6 @@ const PageSpeedDetails = () => {
monitorId,
});
- console.log(monitor, audits, isLoading);
-
const [metrics, setMetrics] = useState({
accessibility: true,
bestPractices: true,
diff --git a/Client/src/Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx b/Client/src/Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx
index 0e61c9c15..6266ebdc9 100644
--- a/Client/src/Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx
+++ b/Client/src/Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx
@@ -2,8 +2,6 @@ import { Grid, Grid2 } from "@mui/material";
import Card from "../Card";
const MonitorGrid = ({ shouldRender, monitors }) => {
- if (!shouldRender) return null;
-
return (
{
const [isLoading, setIsLoading] = useState(true);
const [monitors, setMonitors] = useState([]);
const [summary, setSummary] = useState({});
+ const [networkError, setNetworkError] = useState(false);
useEffect(() => {
const fetchMonitors = async () => {
@@ -27,6 +28,7 @@ const useMonitorsFetch = ({ authToken, teamId }) => {
setSummary(res.data.data.summary);
}
} catch (error) {
+ setNetworkError(true);
createToast({
body: error.message,
});
@@ -37,7 +39,7 @@ const useMonitorsFetch = ({ authToken, teamId }) => {
fetchMonitors();
}, [authToken, teamId]);
- return { isLoading, monitors, summary };
+ return { isLoading, monitors, summary, networkError };
};
export default useMonitorsFetch;
diff --git a/Client/src/Pages/PageSpeed/Monitors/index.jsx b/Client/src/Pages/PageSpeed/Monitors/index.jsx
index e921bc15a..a0c24d48e 100644
--- a/Client/src/Pages/PageSpeed/Monitors/index.jsx
+++ b/Client/src/Pages/PageSpeed/Monitors/index.jsx
@@ -11,6 +11,7 @@ import { useTheme } from "@emotion/react";
import { useSelector } from "react-redux";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import useMonitorsFetch from "./Hooks/useMonitorsFetch";
+import NetworkErrorFallback from "../../../Components/NetworkErrorFallback";
// Constants
const BREADCRUMBS = [{ name: `pagespeed`, path: "/pagespeed" }];
@@ -20,11 +21,15 @@ const PageSpeed = () => {
const isAdmin = useIsAdmin();
const { user, authToken } = useSelector((state) => state.auth);
- const { isLoading, monitors, summary } = useMonitorsFetch({
+ const { isLoading, monitors, summary, networkError } = useMonitorsFetch({
authToken: authToken,
teamId: user.teamId,
});
+ if (networkError === true) {
+ return ;
+ }
+
if (!isLoading && monitors?.length === 0) {
return (
}
header="Average Response Time"
>
-
+
);
@@ -133,7 +133,7 @@ export default ChartBoxes;
ChartBoxes.propTypes = {
shouldRender: PropTypes.bool,
- monitor: PropTypes.object.isRequired,
+ monitor: PropTypes.object,
dateRange: PropTypes.string.isRequired,
uiTimezone: PropTypes.string.isRequired,
dateFormat: PropTypes.string.isRequired,
diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx
index dcd3a06be..5d6c48b58 100644
--- a/Client/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx
+++ b/Client/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx
@@ -19,7 +19,7 @@ const DownBarChart = memo(({ monitor, type, onBarHover }) => {
{
setChartHovered(true);
onBarHover({ time: null, totalChecks: 0 });
diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx
index 8be1125c2..c262d4cce 100644
--- a/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx
+++ b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx
@@ -15,7 +15,7 @@ const ResponseTImeChart = ({ shouldRender = true, monitor, dateRange }) => {
header="Response Times"
>
diff --git a/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx b/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx
index 1f5a19203..c03115af7 100644
--- a/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx
+++ b/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx
@@ -8,7 +8,7 @@ import { formatDateWithTz } from "../../../../../Utils/timeUtils";
import SkeletonLayout from "./skeleton";
const ResponseTable = ({
shouldRender = true,
- checks,
+ checks = [],
checksCount,
uiTimezone,
page,
@@ -77,8 +77,8 @@ const ResponseTable = ({
ResponseTable.propTypes = {
shouldRender: PropTypes.bool,
- checks: PropTypes.array.isRequired,
- checksCount: PropTypes.number.isRequired,
+ checks: PropTypes.array,
+ checksCount: PropTypes.number,
uiTimezone: PropTypes.string.isRequired,
page: PropTypes.number.isRequired,
setPage: PropTypes.func.isRequired,
diff --git a/Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx b/Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx
index ad4620396..5597ce2ac 100644
--- a/Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx
+++ b/Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx
@@ -8,6 +8,7 @@ import useUtils from "../../../Monitors/Hooks/useUtils";
const UptimeStatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => {
const theme = useTheme();
const { determineState } = useUtils();
+
const { time: streakTime, units: streakUnits } = getHumanReadableDuration(
monitor?.uptimeStreak
);
diff --git a/Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx b/Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx
index 9e1c3b44f..ce0b4f7af 100644
--- a/Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx
+++ b/Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx
@@ -10,7 +10,7 @@ const useCertificateFetch = ({
certificateDateFormat,
uiTimezone,
}) => {
- const [certificateExpiry, setCertificateExpiry] = useState("N/A");
+ const [certificateExpiry, setCertificateExpiry] = useState(undefined);
const [certificateIsLoading, setCertificateIsLoading] = useState(false);
useEffect(() => {
diff --git a/Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx b/Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx
index 2a50c2172..541c29f6e 100644
--- a/Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx
+++ b/Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx
@@ -10,8 +10,8 @@ export const useChecksFetch = ({
page,
rowsPerPage,
}) => {
- const [checks, setChecks] = useState([]);
- const [checksCount, setChecksCount] = useState(0);
+ const [checks, setChecks] = useState(undefined);
+ const [checksCount, setChecksCount] = useState(undefined);
const [checksAreLoading, setChecksAreLoading] = useState(false);
useEffect(() => {
diff --git a/Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx b/Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx
index 067545f51..ca4643fd4 100644
--- a/Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx
+++ b/Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx
@@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom";
export const useMonitorFetch = ({ authToken, monitorId, dateRange }) => {
const [monitorIsLoading, setMonitorsIsLoading] = useState(false);
- const [monitor, setMonitor] = useState({});
+ const [monitor, setMonitor] = useState(undefined);
const navigate = useNavigate();
useEffect(() => {
diff --git a/Client/src/Pages/Uptime/Monitors/Components/SearchComponent/index.jsx b/Client/src/Pages/Uptime/Monitors/Components/SearchComponent/index.jsx
index d93e6672b..749f921a8 100644
--- a/Client/src/Pages/Uptime/Monitors/Components/SearchComponent/index.jsx
+++ b/Client/src/Pages/Uptime/Monitors/Components/SearchComponent/index.jsx
@@ -2,13 +2,18 @@ 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 { useEffect, useRef } from "react";
import PropTypes from "prop-types";
-const SearchComponent = ({ monitors, onSearchChange, setIsSearching }) => {
+const SearchComponent = ({ monitors = [], onSearchChange, setIsSearching }) => {
+ const isFirstRender = useRef(true);
const [localSearch, setLocalSearch] = useState("");
const debouncedSearch = useDebounce(localSearch, 500);
useEffect(() => {
+ if (isFirstRender.current === true) {
+ isFirstRender.current = false;
+ return;
+ }
onSearchChange(debouncedSearch);
setIsSearching(false);
}, [debouncedSearch, onSearchChange, setIsSearching]);
diff --git a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx
index 1940c5543..8a9fdcbe9 100644
--- a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx
+++ b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx
@@ -7,12 +7,9 @@ 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";
-import { Heading } from "../../../../../Components/Heading";
// Utils
import { useTheme } from "@emotion/react";
@@ -20,35 +17,6 @@ import useUtils from "../../Hooks/useUtils";
import { useNavigate } from "react-router-dom";
import PropTypes from "prop-types";
-const MonitorDataTable = ({ shouldRender, isSearching, headers, filteredMonitors }) => {
- const theme = useTheme();
- const navigate = useNavigate();
-
- if (!shouldRender) return null;
- return (
-
-
- {
- navigate(`/uptime/${row.id}`);
- },
- emptyView: "No monitors found",
- }}
- />
-
- );
-};
-
/**
* UptimeDataTable displays a table of uptime monitors with sorting, searching, and action capabilities
* @param {Object} props - Component props
@@ -78,34 +46,29 @@ const MonitorDataTable = ({ shouldRender, isSearching, headers, filteredMonitors
* @param {Function} props.triggerUpdate - Callback to trigger a data refresh
* @returns {JSX.Element} Rendered component
*/
-const UptimeDataTable = (props) => {
+const UptimeDataTable = ({
+ isAdmin,
+ isSearching,
+ setIsLoading,
+ filteredMonitors,
+ sort,
+ setSort,
+ triggerUpdate,
+ monitorsAreLoading,
+}) => {
// Utils
-
- const {
- isAdmin,
- setIsLoading,
- monitors,
- filteredMonitors,
- monitorCount,
- sort,
- setSort,
- setSearch,
- triggerUpdate,
- monitorsAreLoading,
- } = props;
+ const navigate = useNavigate();
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) {
+ if (sort?.field !== field) {
order = "desc";
} else {
- order = sort.order === "asc" ? "desc" : "asc";
+ order = sort?.order === "asc" ? "desc" : "asc";
}
setSort({ field, order });
};
@@ -124,10 +87,10 @@ const UptimeDataTable = (props) => {
- {sort.order === "asc" ? (
+ {sort?.order === "asc" ? (
) : (
@@ -160,10 +123,10 @@ const UptimeDataTable = (props) => {
- {sort.order === "asc" ? (
+ {sort?.order === "asc" ? (
) : (
@@ -210,41 +173,37 @@ const UptimeDataTable = (props) => {
),
},
];
- return (
-
-
- Uptime monitors
-
-
-
- ;
+ }
+
+ return (
+
+
+ {
+ navigate(`/uptime/${row.id}`);
+ },
+ emptyView: "No monitors found",
+ }}
/>
-
);
};
UptimeDataTable.propTypes = {
isSearching: PropTypes.bool,
- setIsSearching: PropTypes.func,
setSort: PropTypes.func,
setSearch: PropTypes.func,
setIsLoading: PropTypes.func,
diff --git a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/skeleton.jsx b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/skeleton.jsx
index 303eb48c8..e3c4fa2e2 100644
--- a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/skeleton.jsx
+++ b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/skeleton.jsx
@@ -1,9 +1,6 @@
import { Skeleton } from "@mui/material";
-import PropTypes from "prop-types";
-
-const UptimeDataTableSkeleton = ({ shouldRender }) => {
- if (!shouldRender) return null;
+const UptimeDataTableSkeleton = () => {
return (
{
);
};
-UptimeDataTableSkeleton.propTypes = {
- shouldRender: PropTypes.bool.isRequired,
-};
-
export default UptimeDataTableSkeleton;
diff --git a/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx b/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx
index d4b7e5ba7..ec768e9f8 100644
--- a/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx
+++ b/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx
@@ -43,9 +43,10 @@ export const useMonitorFetch = ({
triggerUpdate,
}) => {
const [monitorsAreLoading, setMonitorsAreLoading] = useState(false);
- const [monitors, setMonitors] = useState([]);
- const [filteredMonitors, setFilteredMonitors] = useState([]);
- const [monitorsSummary, setMonitorsSummary] = useState({});
+ const [monitors, setMonitors] = useState(undefined);
+ const [filteredMonitors, setFilteredMonitors] = useState(undefined);
+ const [monitorsSummary, setMonitorsSummary] = useState(undefined);
+ const [networkError, setNetworkError] = useState(false);
const theme = useTheme();
@@ -72,6 +73,7 @@ export const useMonitorFetch = ({
setFilteredMonitors(mappedMonitors);
setMonitorsSummary(summary);
} catch (error) {
+ setNetworkError(true);
createToast({
body: error.message,
});
@@ -92,7 +94,13 @@ export const useMonitorFetch = ({
theme,
triggerUpdate,
]);
- return { monitors, filteredMonitors, monitorsSummary, monitorsAreLoading };
+ return {
+ monitors,
+ filteredMonitors,
+ monitorsSummary,
+ monitorsAreLoading,
+ networkError,
+ };
};
export default useMonitorFetch;
diff --git a/Client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx b/Client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx
index 0e5fc62ec..ff3288cdf 100644
--- a/Client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx
+++ b/Client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx
@@ -2,6 +2,7 @@ import { useTheme } from "@mui/material";
const useUtils = () => {
const determineState = (monitor) => {
+ if (typeof monitor === "undefined") return "pending";
if (monitor.isActive === false) return "paused";
if (monitor?.status === undefined) return "pending";
return monitor?.status == true ? "up" : "down";
diff --git a/Client/src/Pages/Uptime/Monitors/index.jsx b/Client/src/Pages/Uptime/Monitors/index.jsx
index d60b5c495..1356d9e7c 100644
--- a/Client/src/Pages/Uptime/Monitors/index.jsx
+++ b/Client/src/Pages/Uptime/Monitors/index.jsx
@@ -6,6 +6,11 @@ import UptimeDataTable from "./Components/UptimeDataTable";
import Pagination from "../../../Components/Table/TablePagination";
import CreateMonitorHeader from "../../../Components/CreateMonitorHeader";
import Fallback from "../../../Components/Fallback";
+import NetworkErrorFallback from "../../../Components/NetworkErrorFallback";
+import SearchComponent from "./Components/SearchComponent";
+
+import MonitorCountHeader from "../../../Components/MonitorCountHeader";
+
// MUI Components
import { Stack, Box, Button } from "@mui/material";
@@ -53,9 +58,9 @@ const UptimeMonitors = () => {
const rowsPerPage = useSelector((state) => state.ui.monitors.rowsPerPage);
// Local state
- const [search, setSearch] = useState("");
- const [page, setPage] = useState(0);
- const [sort, setSort] = useState({});
+ const [search, setSearch] = useState(undefined);
+ const [page, setPage] = useState(undefined);
+ const [sort, setSort] = useState(undefined);
const [isSearching, setIsSearching] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [monitorUpdateTrigger, setMonitorUpdateTrigger] = useState(false);
@@ -85,22 +90,28 @@ const UptimeMonitors = () => {
}, []);
const teamId = user.teamId;
-
- const { monitorsAreLoading, monitors, filteredMonitors, monitorsSummary } =
- useMonitorsFetch({
- authToken,
- teamId,
- limit: 25,
- page,
- rowsPerPage: rowsPerPage,
- filter: search,
- field: sort.field,
- order: sort.order,
- triggerUpdate: monitorUpdateTrigger,
- });
+ const {
+ monitorsAreLoading,
+ monitors,
+ filteredMonitors,
+ monitorsSummary,
+ networkError,
+ } = useMonitorsFetch({
+ authToken,
+ teamId,
+ limit: 25,
+ page: page,
+ rowsPerPage: rowsPerPage,
+ filter: search,
+ field: sort?.field,
+ order: sort?.order,
+ triggerUpdate: monitorUpdateTrigger,
+ });
const totalMonitors = monitorsSummary?.totalMonitors ?? 0;
- if (!isLoading && !monitorsAreLoading && monitors?.length === 0) {
+ if (networkError) return ;
+
+ if (!isLoading && !monitorsAreLoading && totalMonitors === 0) {
return (
{
monitorsSummary={monitorsSummary}
shouldRender={!monitorsAreLoading}
/>
+
+
+ 0 && !monitorsAreLoading}
+ monitorCount={totalMonitors}
+ heading={"Uptime monitors"}
+ >
+
+
{
setSort={setSort}
setSearch={setSearch}
isSearching={isSearching}
- setIsSearching={setIsSearching}
monitorsAreLoading={monitorsAreLoading}
triggerUpdate={triggerUpdate}
/>
diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js
index d040ff2fd..7dc505e7f 100644
--- a/Client/src/Utils/NetworkService.js
+++ b/Client/src/Utils/NetworkService.js
@@ -3,7 +3,6 @@ const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL;
const FALLBACK_BASE_URL = "http://localhost:5000/api/v1";
import { clearAuthState } from "../Features/Auth/authSlice";
import { clearUptimeMonitorState } from "../Features/UptimeMonitors/uptimeMonitorsSlice";
-import { createToast } from "./toastUtils";
class NetworkService {
constructor(store, dispatch, navigate) {