From 9e13d313cc08f41ab4a7478fe9da6d46bc005eca Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 14:55:10 -0800 Subject: [PATCH 1/9] add monitor utils hook --- Client/src/Hooks/useMonitorUtils.js | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Client/src/Hooks/useMonitorUtils.js diff --git a/Client/src/Hooks/useMonitorUtils.js b/Client/src/Hooks/useMonitorUtils.js new file mode 100644 index 000000000..1e235af68 --- /dev/null +++ b/Client/src/Hooks/useMonitorUtils.js @@ -0,0 +1,45 @@ +import { useCallback } from "react"; + +const useMonitorUtils = () => { + 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 determineState = useCallback((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"; + }, []); + + return { getMonitorWithPercentage, determineState }; +}; + +export { useMonitorUtils }; From 412284972372b1fef10746d8e315b0ff60f18e00 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 14:56:03 -0800 Subject: [PATCH 2/9] remove connection console log --- Client/src/Utils/NetworkService.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index 254138ff3..52edfa91f 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -905,9 +905,7 @@ class NetworkService { onOpen?.(); }; - this.eventSource.addEventListener("open", (e) => { - console.log("getDistributedUptimeMonitors connection opened:"); - }); + this.eventSource.addEventListener("open", (e) => {}); this.eventSource.onmessage = (event) => { const data = JSON.parse(event.data); From d4164417da9ba4e06826e612500c4ae453c9773d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 14:56:45 -0800 Subject: [PATCH 3/9] use new utils hook --- .../Monitors/Hooks/useMonitorsFetch.jsx | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx b/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx index ec768e9f8..b65de3525 100644 --- a/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx +++ b/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx @@ -2,34 +2,7 @@ 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, - }; -}; +import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils"; export const useMonitorFetch = ({ authToken, @@ -49,7 +22,7 @@ export const useMonitorFetch = ({ const [networkError, setNetworkError] = useState(false); const theme = useTheme(); - + const { getMonitorWithPercentage } = useMonitorUtils(); useEffect(() => { const fetchMonitors = async () => { try { From 68202dc88be092788fce7df4283e187e0245e9e2 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 14:57:18 -0800 Subject: [PATCH 4/9] use utils hook --- .../Status/Hooks/useStatusPageFetch.jsx | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/Client/src/Pages/StatusPage/Status/Hooks/useStatusPageFetch.jsx b/Client/src/Pages/StatusPage/Status/Hooks/useStatusPageFetch.jsx index 815b50a52..d2b2eae2a 100644 --- a/Client/src/Pages/StatusPage/Status/Hooks/useStatusPageFetch.jsx +++ b/Client/src/Pages/StatusPage/Status/Hooks/useStatusPageFetch.jsx @@ -3,30 +3,7 @@ import { networkService } from "../../../../main"; import { useSelector } from "react-redux"; 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 { - ...monitor, - percentage: uptimePercentage, - percentageColor, - }; -}; +import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils"; const useStatusPageFetch = (isCreate = false) => { const [isLoading, setIsLoading] = useState(true); @@ -35,7 +12,7 @@ const useStatusPageFetch = (isCreate = false) => { const [monitors, setMonitors] = useState(undefined); const { authToken } = useSelector((state) => state.auth); const theme = useTheme(); - + const { getMonitorWithPercentage } = useMonitorUtils(); const fetchStatusPage = useCallback(async () => { try { const response = await networkService.getStatusPage({ authToken }); From 1e7e7c736505009f8b67d6991daf3b98aa94e9e5 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 14:57:28 -0800 Subject: [PATCH 5/9] refactor DU monitors page --- .../Components/MonitorTable/index.jsx | 91 ++++++++ .../Monitors/Hooks/useSubscribeToMonitors.jsx | 58 +++++ .../DistributedUptime/Monitors/index.jsx | 221 +++++------------- 3 files changed, 202 insertions(+), 168 deletions(-) create mode 100644 Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx create mode 100644 Client/src/Pages/DistributedUptime/Monitors/Hooks/useSubscribeToMonitors.jsx diff --git a/Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx b/Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx new file mode 100644 index 000000000..ad0afe1a8 --- /dev/null +++ b/Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx @@ -0,0 +1,91 @@ +import DataTable from "../../../../../Components/Table"; +import BarChart from "../../../../../Components/Charts/BarChart"; +import { Box } from "@mui/material"; +import { StatusLabel } from "../../../../../Components/Label"; + +//REFACTOR +import Host from "../../../../Uptime/Monitors/Components/Host"; +import ActionsMenu from "../../../../Uptime/Monitors/Components/ActionsMenu"; +import { useTheme } from "@mui/material/styles"; +import { useNavigate } from "react-router-dom"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; +const TYPE_MAP = { + distributed_http: "Distributed HTTP", +}; + +const MonitorTable = ({ isLoading, monitors }) => { + const theme = useTheme(); + const navigate = useNavigate(); + const { determineState } = useMonitorUtils(); + + const headers = [ + { + id: "name", + content: Host, + render: (row) => ( + + ), + }, + { + id: "status", + content: Status, + render: (row) => { + const status = determineState(row?.monitor); + return ( + + ); + }, + }, + { + id: "responseTime", + content: "Response Time", + render: (row) => , + }, + { + id: "type", + content: "Type", + render: (row) => {TYPE_MAP[row.monitor.type]}, + }, + { + id: "actions", + content: "Actions", + render: (row) => ( + + ), + }, + ]; + + return ( + { + navigate(`/distributed-uptime/${row.id}`); + }, + }} + /> + ); +}; + +export default MonitorTable; diff --git a/Client/src/Pages/DistributedUptime/Monitors/Hooks/useSubscribeToMonitors.jsx b/Client/src/Pages/DistributedUptime/Monitors/Hooks/useSubscribeToMonitors.jsx new file mode 100644 index 000000000..1a1db1453 --- /dev/null +++ b/Client/src/Pages/DistributedUptime/Monitors/Hooks/useSubscribeToMonitors.jsx @@ -0,0 +1,58 @@ +import { useEffect, useState } from "react"; +import { networkService } from "../../../../main"; +import { useSelector } from "react-redux"; +import { useTheme } from "@emotion/react"; +import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils"; +import { createToast } from "../../../../Utils/toastUtils"; + +const useSubscribeToMonitors = () => { + // Redux + const { authToken, user } = useSelector((state) => state.auth); + + // Local state + const [isLoading, setIsLoading] = useState(true); + const [networkError, setNetworkError] = useState(false); + const [monitors, setMonitors] = useState(undefined); + const [monitorsSummary, setMonitorsSummary] = useState(undefined); + const [filteredMonitors, setFilteredMonitors] = useState(undefined); + + const theme = useTheme(); + const { getMonitorWithPercentage } = useMonitorUtils(); + + useEffect(() => { + try { + const cleanup = networkService.subscribeToDistributedUptimeMonitors({ + authToken: authToken, + teamId: user.teamId, + limit: 25, + types: ["distributed_http"], + page: 0, + rowsPerPage: 10, + filter: null, + field: null, + order: null, + onUpdate: (data) => { + const res = data.monitors; + const { monitors, filteredMonitors, summary } = res; + const mappedMonitors = filteredMonitors.map((monitor) => + getMonitorWithPercentage(monitor, theme) + ); + setMonitors(monitors); + setMonitorsSummary(summary); + setFilteredMonitors(mappedMonitors); + }, + }); + + return cleanup; + } catch (error) { + createToast({ + body: error.message, + }); + setNetworkError(true); + } finally { + setIsLoading(false); + } + }, [authToken, user, getMonitorWithPercentage, theme]); + return [isLoading, networkError, monitors, monitorsSummary, filteredMonitors]; +}; +export { useSubscribeToMonitors }; diff --git a/Client/src/Pages/DistributedUptime/Monitors/index.jsx b/Client/src/Pages/DistributedUptime/Monitors/index.jsx index 0d1288aa9..34b1a0f21 100644 --- a/Client/src/Pages/DistributedUptime/Monitors/index.jsx +++ b/Client/src/Pages/DistributedUptime/Monitors/index.jsx @@ -1,185 +1,70 @@ // Components -import { Stack, Box, Button } from "@mui/material"; -import DataTable from "../../../Components/Table"; +import { Stack, Typography } from "@mui/material"; import Breadcrumbs from "../../../Components/Breadcrumbs"; -import Host from "../../Uptime/Monitors/Components/Host"; -import BarChart from "../../../Components/Charts/BarChart"; -import ActionsMenu from "../../Uptime/Monitors/Components/ActionsMenu"; -import { StatusLabel } from "../../../Components/Label"; -// Utils -import { networkService } from "../../../main"; -import { useTheme } from "@mui/material/styles"; -import { useNavigate } from "react-router-dom"; -import useUtils from "../../Uptime/Monitors/Hooks/useUtils"; -import { useEffect, useState } from "react"; -import { useSelector } from "react-redux"; +import CreateMonitorHeader from "../../../Components/MonitorCreateHeader"; +import MonitorTable from "./Components/MonitorTable"; +import Fallback from "../../../Components/Fallback"; +import GenericFallback from "../../../Components/GenericFallback"; +// Utils +import { useTheme } from "@mui/material/styles"; +import { useSelector } from "react-redux"; +import { useIsAdmin } from "../../../Hooks/useIsAdmin"; +import { useSubscribeToMonitors } from "./Hooks/useSubscribeToMonitors"; // Constants const BREADCRUMBS = [{ name: `Distributed Uptime`, path: "/distributed-uptime" }]; -const TYPE_MAP = { - distributed_http: "Distributed HTTP", -}; const DistributedUptimeMonitors = () => { // Redux state const { authToken, user } = useSelector((state) => state.auth); // Local state - const [monitors, setMonitors] = useState([]); - const [filteredMonitors, setFilteredMonitors] = useState([]); - const [monitorsSummary, setMonitorsSummary] = useState({}); // Utils - - const { determineState } = useUtils(); const theme = useTheme(); - const navigate = useNavigate(); - const headers = [ - { - id: "name", - content: Host, - render: (row) => ( - - ), - }, - { - id: "status", - content: Status, - render: (row) => { - const status = determineState(row?.monitor); - return ( - - ); - }, - }, - { - id: "responseTime", - content: "Response Time", - render: (row) => , - }, - { - id: "type", - content: "Type", - render: (row) => {TYPE_MAP[row.monitor.type]}, - }, - { - id: "actions", - content: "Actions", - render: (row) => ( - - ), - }, - ]; + const isAdmin = useIsAdmin(); + const [isLoading, networkError, monitors, monitorsSummary, filteredMonitors] = + useSubscribeToMonitors(); - 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, - }; - }; - - useEffect(() => { - const cleanup = networkService.subscribeToDistributedUptimeMonitors({ - authToken: authToken, - teamId: user.teamId, - limit: 25, - types: ["distributed_http"], - page: 0, - rowsPerPage: 10, - filter: null, - field: null, - order: null, - onUpdate: (data) => { - const res = data.monitors; - const { monitors, filteredMonitors, summary } = res; - const mappedMonitors = filteredMonitors.map((monitor) => - getMonitorWithPercentage(monitor, theme) - ); - setMonitors(monitors); - setFilteredMonitors(mappedMonitors); - setMonitorsSummary(summary); - }, - }); - - return cleanup; - }, [user.teamId, authToken, theme]); - - return ( - - - - - - {monitors.length > 0 && ( - { - navigate(`/distributed-uptime/${row.id}`); - }, - }} - /> - )} + Network error + + Please check your connection + + ); + } + + if (typeof monitorsSummary !== "undefined" && monitorsSummary.totalMonitors === 0) { + return ( + + ); + } + return ( + + + + ); }; From e570b76d88f1c0189b3dc897748a52bade433346 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 15:06:37 -0800 Subject: [PATCH 6/9] refactor common components out --- .../Components/ActionsMenu/index.jsx | 10 ++++----- .../Monitors => }/Components/Host/index.jsx | 2 +- .../Components/MonitorTable/index.jsx | 21 +++++++++++-------- .../DistributedUptime/Monitors/index.jsx | 3 --- .../Components/MonitorsTable/index.jsx | 10 ++++++++- .../Status/Components/MonitorsList/index.jsx | 2 +- .../Components/UptimeDataTable/index.jsx | 4 ++-- 7 files changed, 30 insertions(+), 22 deletions(-) rename Client/src/{Pages/Uptime/Monitors => }/Components/ActionsMenu/index.jsx (94%) rename Client/src/{Pages/Uptime/Monitors => }/Components/Host/index.jsx (96%) diff --git a/Client/src/Pages/Uptime/Monitors/Components/ActionsMenu/index.jsx b/Client/src/Components/ActionsMenu/index.jsx similarity index 94% rename from Client/src/Pages/Uptime/Monitors/Components/ActionsMenu/index.jsx rename to Client/src/Components/ActionsMenu/index.jsx index f0561d6e6..a14e347fe 100644 --- a/Client/src/Pages/Uptime/Monitors/Components/ActionsMenu/index.jsx +++ b/Client/src/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/Monitors/Components/Host/index.jsx b/Client/src/Components/Host/index.jsx similarity index 96% rename from Client/src/Pages/Uptime/Monitors/Components/Host/index.jsx rename to Client/src/Components/Host/index.jsx index c2f082f3a..051e60331 100644 --- a/Client/src/Pages/Uptime/Monitors/Components/Host/index.jsx +++ b/Client/src/Components/Host/index.jsx @@ -1,7 +1,7 @@ import { Stack, Typography } from "@mui/material"; import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; -import Dot from "../../../../../Components/Dot"; +import Dot from "../Dot"; /** * Host component. * This subcomponent receives a params object and displays the host details. diff --git a/Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx b/Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx index ad0afe1a8..c0aa5b57c 100644 --- a/Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx +++ b/Client/src/Pages/DistributedUptime/Monitors/Components/MonitorTable/index.jsx @@ -1,14 +1,17 @@ +// Components import DataTable from "../../../../../Components/Table"; import BarChart from "../../../../../Components/Charts/BarChart"; import { Box } from "@mui/material"; import { StatusLabel } from "../../../../../Components/Label"; +import Host from "../../../../../Components/Host"; -//REFACTOR -import Host from "../../../../Uptime/Monitors/Components/Host"; -import ActionsMenu from "../../../../Uptime/Monitors/Components/ActionsMenu"; +// Utils import { useTheme } from "@mui/material/styles"; import { useNavigate } from "react-router-dom"; import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; +import PropTypes from "prop-types"; + +// Constants const TYPE_MAP = { distributed_http: "Distributed HTTP", }; @@ -59,12 +62,7 @@ const MonitorTable = ({ isLoading, monitors }) => { { id: "actions", content: "Actions", - render: (row) => ( - - ), + render: (row) => {"TODO"}, }, ]; @@ -88,4 +86,9 @@ const MonitorTable = ({ isLoading, monitors }) => { ); }; +MonitorTable.propTypes = { + isLoading: PropTypes.bool, + monitors: PropTypes.array, +}; + export default MonitorTable; diff --git a/Client/src/Pages/DistributedUptime/Monitors/index.jsx b/Client/src/Pages/DistributedUptime/Monitors/index.jsx index 34b1a0f21..13adeba25 100644 --- a/Client/src/Pages/DistributedUptime/Monitors/index.jsx +++ b/Client/src/Pages/DistributedUptime/Monitors/index.jsx @@ -8,15 +8,12 @@ import GenericFallback from "../../../Components/GenericFallback"; // Utils import { useTheme } from "@mui/material/styles"; -import { useSelector } from "react-redux"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; import { useSubscribeToMonitors } from "./Hooks/useSubscribeToMonitors"; // Constants const BREADCRUMBS = [{ name: `Distributed Uptime`, path: "/distributed-uptime" }]; const DistributedUptimeMonitors = () => { - // Redux state - const { authToken, user } = useSelector((state) => state.auth); // Local state // Utils const theme = useTheme(); diff --git a/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx b/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx index f44b844a7..3f1832889 100644 --- a/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx +++ b/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx @@ -1,6 +1,6 @@ // Components import DataTable from "../../../../../Components/Table"; -import Host from "../../../../Uptime/Monitors/Components/Host"; +import Host from "../../../../../Components/Host"; import { StatusLabel } from "../../../../../Components/Label"; import { Stack } from "@mui/material"; import { InfrastructureMenu } from "../MonitorsTableMenu"; @@ -12,6 +12,7 @@ import CustomGauge from "../../../../../Components/Charts/CustomGauge"; import { useTheme } from "@emotion/react"; import useUtils from "../../../../Uptime/Monitors/Hooks/useUtils"; import { useNavigate } from "react-router-dom"; +import PropTypes from "prop-types"; const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete }) => { // Utils @@ -131,4 +132,11 @@ const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete ); }; +MonitorsTable.propTypes = { + shouldRender: PropTypes.bool, + monitors: PropTypes.array, + isAdmin: PropTypes.bool, + handleActionMenuDelete: PropTypes.func, +}; + export default MonitorsTable; diff --git a/Client/src/Pages/StatusPage/Status/Components/MonitorsList/index.jsx b/Client/src/Pages/StatusPage/Status/Components/MonitorsList/index.jsx index 65a4a182a..ff7a18f97 100644 --- a/Client/src/Pages/StatusPage/Status/Components/MonitorsList/index.jsx +++ b/Client/src/Pages/StatusPage/Status/Components/MonitorsList/index.jsx @@ -1,6 +1,6 @@ // Components import { Stack, Box } from "@mui/material"; -import Host from "../../../../Uptime/Monitors/Components/Host"; +import Host from "../../../../../Components/Host"; import StatusPageBarChart from "../../../../../Components/Charts/StatusPageBarChart"; import { StatusLabel } from "../../../../../Components/Label"; diff --git a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx index 2824f80d6..323365031 100644 --- a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx +++ b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx @@ -3,10 +3,10 @@ import { Box, Stack } from "@mui/material"; import DataTable from "../../../../../Components/Table"; import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded"; import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded"; -import Host from "../Host"; +import Host from "../../../../../Components/Host"; import { StatusLabel } from "../../../../../Components/Label"; import BarChart from "../../../../../Components/Charts/BarChart"; -import ActionsMenu from "../ActionsMenu"; +import ActionsMenu from "../../../../../Components/ActionsMenu"; import LoadingSpinner from "../LoadingSpinner"; import TableSkeleton from "../../../../../Components/Table/skeleton"; From e5ade000c3be3919d7d12fad80e704f6a87fc545 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 16:07:39 -0800 Subject: [PATCH 7/9] extract network request to hook --- .../Details/Hooks/useSubscribeToDetails.jsx | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 Client/src/Pages/DistributedUptime/Details/Hooks/useSubscribeToDetails.jsx diff --git a/Client/src/Pages/DistributedUptime/Details/Hooks/useSubscribeToDetails.jsx b/Client/src/Pages/DistributedUptime/Details/Hooks/useSubscribeToDetails.jsx new file mode 100644 index 000000000..409e9cf89 --- /dev/null +++ b/Client/src/Pages/DistributedUptime/Details/Hooks/useSubscribeToDetails.jsx @@ -0,0 +1,119 @@ +import { useState, useEffect, useRef } from "react"; +import { networkService } from "../../../../main"; +import { useSelector } from "react-redux"; + +const getRandomDevice = () => { + const manufacturers = { + Apple: ["iPhone 15 Pro Max", "iPhone 15", "iPhone 14 Pro", "iPhone 14", "iPhone 13"], + Samsung: [ + "Galaxy S23 Ultra", + "Galaxy S23+", + "Galaxy S23", + "Galaxy Z Fold5", + "Galaxy Z Flip5", + ], + Google: ["Pixel 8 Pro", "Pixel 8", "Pixel 7a", "Pixel 7", "Pixel 6a"], + OnePlus: [ + "OnePlus 11", + "OnePlus 10T", + "OnePlus Nord 3", + "OnePlus 10 Pro", + "OnePlus Nord 2T", + ], + Xiaomi: ["13 Pro", "13", "Redmi Note 12", "POCO F5", "Redmi 12"], + Huawei: ["P60 Pro", "Mate X3", "Nova 11", "P50 Pro", "Mate 50"], + Sony: ["Xperia 1 V", "Xperia 5 V", "Xperia 10 V", "Xperia Pro-I", "Xperia 1 IV"], + Motorola: ["Edge 40 Pro", "Edge 40", "G84", "G54", "Razr 40 Ultra"], + ASUS: [ + "ROG Phone 7", + "Zenfone 10", + "ROG Phone 6", + "Zenfone 9", + "ROG Phone 7 Ultimate", + ], + }; + + const manufacturerNames = Object.keys(manufacturers); + const randomManufacturer = + manufacturerNames[Math.floor(Math.random() * manufacturerNames.length)]; + + const models = manufacturers[randomManufacturer]; + const randomModel = models[Math.floor(Math.random() * models.length)]; + + return { + manufacturer: randomManufacturer, + model: randomModel, + }; +}; +const useSubscribeToDetails = ({ monitorId, dateRange }) => { + const [isLoading, setIsLoading] = useState(true); + const [connectionStatus, setConnectionStatus] = useState(undefined); + const [retryCount, setRetryCount] = useState(0); + const [networkError, setNetworkError] = useState(false); + const [monitor, setMonitor] = useState(undefined); + const [lastUpdateTrigger, setLastUpdateTrigger] = useState(0); + const [devices, setDevices] = useState(Array.from({ length: 5 }, getRandomDevice)); + const authToken = useSelector((state) => state.auth.token); + + const prevDateRangeRef = useRef(dateRange); + + useEffect(() => { + try { + const cleanup = networkService.subscribeToDistributedUptimeDetails({ + authToken, + monitorId, + dateRange: dateRange, + normalize: true, + onUpdate: (data) => { + if (networkError === true) { + setNetworkError(false); + } + setLastUpdateTrigger(Date.now()); + const latestChecksWithDevice = data?.monitor?.latestChecks.map((check, idx) => { + check.device = devices[idx]; + return check; + }); + const monitorWithDevice = { + ...data.monitor, + latestChecks: latestChecksWithDevice, + }; + setMonitor(monitorWithDevice); + }, + onOpen: () => { + setConnectionStatus("up"); + setRetryCount(0); // Reset retry count on successful connection + }, + onError: () => { + setNetworkError(true); + setConnectionStatus("down"); + }, + }); + return cleanup; + } catch (error) { + setNetworkError(true); + } finally { + setIsLoading(false); + } + }, [ + authToken, + dateRange, + monitorId, + retryCount, + setConnectionStatus, + networkError, + devices, + ]); + + useEffect(() => { + const hasDateRangeChanged = prevDateRangeRef.current !== dateRange; + prevDateRangeRef.current = dateRange; // Update the ref to the current dateRange + + if (!hasDateRangeChanged) { + setDevices(Array.from({ length: 5 }, getRandomDevice)); + } + }, [dateRange]); + + return [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger]; +}; + +export default useSubscribeToDetails; From cceab2b705ef091bdfa67d8702bbe99e150a69e3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 16:07:57 -0800 Subject: [PATCH 8/9] extract subcomponents --- .../DistributedUptimeResponseChart/index.jsx | 102 ++++++++++-------- .../Components/MonitorHeader/index.jsx | 23 ++++ .../Details/Components/StatBoxes/index.jsx | 62 +++++++++++ 3 files changed, 140 insertions(+), 47 deletions(-) create mode 100644 Client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx create mode 100644 Client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx diff --git a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx b/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx index 56d2f9618..7a4c4b210 100644 --- a/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx +++ b/Client/src/Pages/DistributedUptime/Details/Components/DistributedUptimeResponseChart/index.jsx @@ -8,6 +8,8 @@ import { ResponsiveContainer, Text, } from "recharts"; +import ChartBox from "../../../../../Components/Charts/ChartBox"; +import ResponseTimeIcon from "../../../../../assets/icons/response-time-icon.svg?react"; import { Box, Stack, Typography } from "@mui/material"; import { useTheme } from "@emotion/react"; import { useState } from "react"; @@ -22,67 +24,73 @@ const CustomToolTip = ({ active, payload, label }) => { ? payload[0]?.payload?.originalAvgResponseTime : (payload[0]?.payload?.avgResponseTime ?? 0); return ( - } + header="Response Times" + sx={{ padding: 0 }} > - - {formatDateWithTz(label, "ddd, MMMM D, YYYY, h:mm A", uiTimezone)} - - - - - + + + - Response Time - {" "} - - {Math.floor(responseTime)} - {" "} - ms + Response Time + {" "} + + {Math.floor(responseTime)} + + {" "} + ms + - - + + + {/* Display original value */} - {/* Display original value */} - + ); } return null; diff --git a/Client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx b/Client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx new file mode 100644 index 000000000..53f36542f --- /dev/null +++ b/Client/src/Pages/DistributedUptime/Details/Components/MonitorHeader/index.jsx @@ -0,0 +1,23 @@ +import { Stack, Typography } from "@mui/material"; +import { useTheme } from "@mui/material/styles"; +import PropTypes from "prop-types"; + +const MonitorHeader = ({ monitor }) => { + const theme = useTheme(); + return ( + + + {monitor.name} + + Distributed Uptime Monitoring powered by DePIN + + + + ); +}; + +MonitorHeader.propTypes = { + monitor: PropTypes.object, +}; + +export default MonitorHeader; diff --git a/Client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx b/Client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx new file mode 100644 index 000000000..a4fd3b03d --- /dev/null +++ b/Client/src/Pages/DistributedUptime/Details/Components/StatBoxes/index.jsx @@ -0,0 +1,62 @@ +// Components +import { Stack } from "@mui/material"; +import StatBox from "../../../../../Components/StatBox"; +import LastUpdate from "../LastUpdate"; +import UptLogo from "../../../../../assets/icons/upt_logo.png"; + +// Utils +import { useTheme } from "@mui/material/styles"; +import PropTypes from "prop-types"; + +const StatBoxes = ({ monitor, lastUpdateTrigger }) => { + const theme = useTheme(); + + return ( + + + + + } + /> + + } + /> + + + + ); +}; + +StatBoxes.propTypes = { + monitor: PropTypes.object, + lastUpdateTrigger: PropTypes.number, +}; + +export default StatBoxes; From a436ca16f4a0e48dadca7a84c651641ba08537d9 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 6 Feb 2025 16:08:26 -0800 Subject: [PATCH 9/9] refactor to use components and hook --- .../Pages/DistributedUptime/Details/index.jsx | 358 ++++-------------- .../DistributedUptime/Monitors/index.jsx | 6 +- 2 files changed, 72 insertions(+), 292 deletions(-) diff --git a/Client/src/Pages/DistributedUptime/Details/index.jsx b/Client/src/Pages/DistributedUptime/Details/index.jsx index 6c921063f..ab39ec2ec 100644 --- a/Client/src/Pages/DistributedUptime/Details/index.jsx +++ b/Client/src/Pages/DistributedUptime/Details/index.jsx @@ -1,324 +1,102 @@ //Components import DistributedUptimeMap from "./Components/DistributedUptimeMap"; import Breadcrumbs from "../../../Components/Breadcrumbs"; -import { Stack, Typography, Box, Button, ButtonGroup } from "@mui/material"; -import ChartBox from "../../../Components/Charts/ChartBox"; -import StatBox from "../../../Components/StatBox"; -import ResponseTimeIcon from "../../../assets/icons/response-time-icon.svg?react"; +import { Stack, Typography } from "@mui/material"; import DeviceTicker from "./Components/DeviceTicker"; import DistributedUptimeResponseChart from "./Components/DistributedUptimeResponseChart"; -import UptLogo from "../../../assets/icons/upt_logo.png"; -import LastUpdate from "./Components/LastUpdate"; import NextExpectedCheck from "./Components/NextExpectedCheck"; import Footer from "./Components/Footer"; +import StatBoxes from "./Components/StatBoxes"; +import MonitorHeader from "./Components/MonitorHeader"; +import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader"; +import GenericFallback from "../../../Components/GenericFallback"; + //Utils -import { networkService } from "../../../main"; -import { useSelector } from "react-redux"; import { useTheme } from "@mui/material/styles"; -import { useEffect, useState, useCallback, useRef } from "react"; +import { useState } from "react"; import { useParams } from "react-router-dom"; - -//Constants -const BASE_BOX_PADDING_VERTICAL = 8; -const BASE_BOX_PADDING_HORIZONTAL = 8; -const MAX_RETRIES = 10; -const RETRY_DELAY = 5000; - -function getRandomDevice() { - const manufacturers = { - Apple: ["iPhone 15 Pro Max", "iPhone 15", "iPhone 14 Pro", "iPhone 14", "iPhone 13"], - Samsung: [ - "Galaxy S23 Ultra", - "Galaxy S23+", - "Galaxy S23", - "Galaxy Z Fold5", - "Galaxy Z Flip5", - ], - Google: ["Pixel 8 Pro", "Pixel 8", "Pixel 7a", "Pixel 7", "Pixel 6a"], - OnePlus: [ - "OnePlus 11", - "OnePlus 10T", - "OnePlus Nord 3", - "OnePlus 10 Pro", - "OnePlus Nord 2T", - ], - Xiaomi: ["13 Pro", "13", "Redmi Note 12", "POCO F5", "Redmi 12"], - Huawei: ["P60 Pro", "Mate X3", "Nova 11", "P50 Pro", "Mate 50"], - Sony: ["Xperia 1 V", "Xperia 5 V", "Xperia 10 V", "Xperia Pro-I", "Xperia 1 IV"], - Motorola: ["Edge 40 Pro", "Edge 40", "G84", "G54", "Razr 40 Ultra"], - ASUS: [ - "ROG Phone 7", - "Zenfone 10", - "ROG Phone 6", - "Zenfone 9", - "ROG Phone 7 Ultimate", - ], - }; - - const manufacturerNames = Object.keys(manufacturers); - const randomManufacturer = - manufacturerNames[Math.floor(Math.random() * manufacturerNames.length)]; - - const models = manufacturers[randomManufacturer]; - const randomModel = models[Math.floor(Math.random() * models.length)]; - - return { - manufacturer: randomManufacturer, - model: randomModel, - }; -} - -// export const StatBox = ({ heading, value, img, altTxt }) => { -// const theme = useTheme(); -// return ( -// -// {img && ( -// {altTxt} -// )} -// -// {heading} -// {value} -// -// -// ); -// }; +import useSubscribeToDetails from "./Hooks/useSubscribeToDetails"; const DistributedUptimeDetails = () => { - // Redux State - const { authToken } = useSelector((state) => state.auth); - const { mode } = useSelector((state) => state.ui); + const { monitorId } = useParams(); // Local State - // const [hoveredUptimeData, setHoveredUptimeData] = useState(null); - // const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null); - const [retryCount, setRetryCount] = useState(0); - const [connectionStatus, setConnectionStatus] = useState("down"); - const [lastUpdateTrigger, setLastUpdateTrigger] = useState(Date.now()); const [dateRange, setDateRange] = useState("day"); - const [monitor, setMonitor] = useState(null); - const [devices, setDevices] = useState([]); - - // Refs - const prevDateRangeRef = useRef(dateRange); // Utils const theme = useTheme(); - const { monitorId } = useParams(); - + const [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger] = + useSubscribeToDetails({ monitorId, dateRange }); // Constants const BREADCRUMBS = [ { name: "Distributed Uptime", path: "/distributed-uptime" }, { name: "Details", path: `/distributed-uptime/${monitorId}` }, ]; - useEffect(() => { - const hasDateRangeChanged = prevDateRangeRef.current !== dateRange; - prevDateRangeRef.current = dateRange; // Update the ref to the current dateRange + if (networkError) { + return ( + + + Network error + + Please check your connection + + ); + } - if (!hasDateRangeChanged) { - setDevices(Array.from({ length: 5 }, getRandomDevice)); - } - }, [dateRange]); - - const connectToService = useCallback(() => { - return networkService.subscribeToDistributedUptimeDetails({ - authToken, - monitorId, - dateRange: dateRange, - normalize: true, - onUpdate: (data) => { - setLastUpdateTrigger(Date.now()); - const latestChecksWithDevice = data?.monitor?.latestChecks.map((check, idx) => { - check.device = devices[idx]; - return check; - }); - const monitorWithDevice = { - ...data.monitor, - latestChecks: latestChecksWithDevice, - }; - setMonitor(monitorWithDevice); - }, - onOpen: () => { - setConnectionStatus("up"); - setRetryCount(0); // Reset retry count on successful connection - }, - onError: () => { - setConnectionStatus("down"); - console.log("Error, attempting reconnect..."); - - if (retryCount < MAX_RETRIES) { - setTimeout(() => { - setRetryCount((prev) => prev + 1); - connectToService(); - }, RETRY_DELAY); - } else { - console.log("Max retries reached"); - } - }, - }); - }, [authToken, monitorId, dateRange, retryCount, devices]); - - useEffect(() => { - const devices = Array.from({ length: 5 }, getRandomDevice); - const cleanup = connectToService(devices); - return cleanup; - }, [connectToService]); + if (typeof monitor === "undefined" || monitor.totalChecks === 0) { + return ( + + + + There is no check history for this monitor yet. + + + ); + } return ( - monitor && ( + + + + + + + - - {monitor?.url !== "https://jup.ag/" && - monitor?.url !== "https://explorer.solana.com/" && ( - - - {monitor.name} - - - )} - - - Distributed Uptime Monitoring powered by DePIN - - - - - - - } - /> - - } - /> - - - - - - - - - Showing statistics for past{" "} - {dateRange === "day" - ? "24 hours" - : dateRange === "week" - ? "7 days" - : "30 days"} - . - - - - - - - - } - header="Response Times" - sx={{ padding: 0 }} - > - - - - - - -