From 3d71e7759be7bc43471d2d0cf66492291eb3937f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 19 Mar 2025 12:15:04 -0700 Subject: [PATCH] update details page to use new queries --- src/Components/MonitorStatusHeader/index.jsx | 8 ++-- .../MonitorTimeFrameHeader/index.jsx | 6 +-- src/Hooks/useFetchUptimeMonitorDetails.js | 36 +++++++++++++++++ .../Components/StatusPagesTable/index.jsx | 2 +- .../Details/Components/ChartBoxes/index.jsx | 40 ++++++++----------- .../Components/Charts/DownBarChart.jsx | 12 +++--- .../Components/Charts/ResponseTimeChart.jsx | 10 ++--- .../Details/Components/Charts/UpBarChart.jsx | 12 +++--- .../Components/ResponseTable/index.jsx | 8 +++- .../Components/UptimeStatusBoxes/index.jsx | 38 +++++++++++++----- src/Pages/Uptime/Details/index.jsx | 40 +++++++++---------- 11 files changed, 131 insertions(+), 81 deletions(-) create mode 100644 src/Hooks/useFetchUptimeMonitorDetails.js diff --git a/src/Components/MonitorStatusHeader/index.jsx b/src/Components/MonitorStatusHeader/index.jsx index cbbba52b2..e1007bbf5 100644 --- a/src/Components/MonitorStatusHeader/index.jsx +++ b/src/Components/MonitorStatusHeader/index.jsx @@ -8,10 +8,10 @@ import ConfigButton from "./ConfigButton"; import SkeletonLayout from "./skeleton"; import PropTypes from "prop-types"; -const MonitorStatusHeader = ({ path, shouldRender = true, isAdmin, monitor }) => { +const MonitorStatusHeader = ({ path, isLoading = false, isAdmin, monitor }) => { const theme = useTheme(); - const { statusColor, statusMsg, determineState } = useUtils(); - if (!shouldRender) { + const { statusColor, determineState } = useUtils(); + if (isLoading) { return ; } @@ -48,7 +48,7 @@ const MonitorStatusHeader = ({ path, shouldRender = true, isAdmin, monitor }) => MonitorStatusHeader.propTypes = { path: PropTypes.string.isRequired, - shouldRender: PropTypes.bool, + isLoading: PropTypes.bool, isAdmin: PropTypes.bool, monitor: PropTypes.object, }; diff --git a/src/Components/MonitorTimeFrameHeader/index.jsx b/src/Components/MonitorTimeFrameHeader/index.jsx index 9ac8b8b6a..97f113897 100644 --- a/src/Components/MonitorTimeFrameHeader/index.jsx +++ b/src/Components/MonitorTimeFrameHeader/index.jsx @@ -4,14 +4,14 @@ import SkeletonLayout from "./skeleton"; import PropTypes from "prop-types"; const MonitorTimeFrameHeader = ({ - shouldRender = true, + isLoading = false, hasDateRange = true, dateRange, setDateRange, }) => { const theme = useTheme(); - if (!shouldRender) { + if (isLoading) { return ; } @@ -77,7 +77,7 @@ const MonitorTimeFrameHeader = ({ }; MonitorTimeFrameHeader.propTypes = { - shouldRender: PropTypes.bool, + isLoading: PropTypes.bool, hasDateRange: PropTypes.bool, dateRange: PropTypes.string, setDateRange: PropTypes.func, diff --git a/src/Hooks/useFetchUptimeMonitorDetails.js b/src/Hooks/useFetchUptimeMonitorDetails.js new file mode 100644 index 000000000..c8cb81e65 --- /dev/null +++ b/src/Hooks/useFetchUptimeMonitorDetails.js @@ -0,0 +1,36 @@ +import { useEffect, useState } from "react"; +import { networkService } from "../main"; +import { useNavigate } from "react-router-dom"; +import { createToast } from "../Utils/toastUtils"; + +export const useFetchUptimeMonitorDetails = ({ monitorId, dateRange }) => { + const [networkError, setNetworkError] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [monitor, setMonitor] = useState(undefined); + const [monitorStats, setMonitorStats] = useState(undefined); + const navigate = useNavigate(); + + useEffect(() => { + const fetchMonitors = async () => { + try { + const res = await networkService.getUptimeDetailsById({ + monitorId: monitorId, + dateRange: dateRange, + normalize: true, + }); + const { monitorData, monitorStats } = res?.data?.data ?? {}; + setMonitor(monitorData); + setMonitorStats(monitorStats); + } catch (error) { + setNetworkError(true); + createToast({ body: error.message }); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [dateRange, monitorId, navigate]); + return [monitor, monitorStats, isLoading, networkError]; +}; + +export default useFetchUptimeMonitorDetails; diff --git a/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx b/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx index 20226d6d3..6471f663d 100644 --- a/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx +++ b/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx @@ -27,7 +27,7 @@ const StatusPagesTable = ({ data }) => { row.type === "distributed" ? `/status/distributed/public/${row.url}` : `/status/uptime/public/${row.url}`; - window.open(url, "_blank", "noopener,noreferrer") + window.open(url, "_blank", "noopener,noreferrer"); } }, render: (row) => { diff --git a/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx b/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx index 1875ef760..9dc5c1544 100644 --- a/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx +++ b/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx @@ -12,29 +12,23 @@ import SkeletonLayout from "./skeleton"; import { formatDateWithTz } from "../../../../../Utils/timeUtils"; import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; +import { useState } from "react"; const ChartBoxes = ({ - shouldRender = true, - monitor, + isLoading = false, + monitorData, dateRange, uiTimezone, dateFormat, - hoveredUptimeData, - setHoveredUptimeData, - hoveredIncidentsData, - setHoveredIncidentsData, }) => { + // Local state + const [hoveredUptimeData, setHoveredUptimeData] = useState(null); + const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null); const theme = useTheme(); - - if (!shouldRender) { + if (isLoading) { return ; } - const totalUpChecks = monitor?.upChecks?.totalChecks ?? 0; - const totalDownChecks = monitor?.downChecks?.totalChecks ?? 0; - const denominator = - totalUpChecks + totalDownChecks > 0 ? totalUpChecks + totalDownChecks : 1; - const groupedUptimePercentage = (totalUpChecks / denominator) * 100; const noIncidentsMessage = "Great. No Incidents, yet!"; return ( @@ -46,7 +40,7 @@ const ChartBoxes = ({ } header="Uptime" - isEmpty={monitor?.uptimePercentage === 0 && !monitor?.upChecks?.length} + isEmpty={false} // TODO > {hoveredUptimeData !== null ? hoveredUptimeData.totalChecks - : (monitor?.groupedUpChecks?.reduce((count, checkGroup) => { + : (monitorData?.groupedUpChecks?.reduce((count, checkGroup) => { return count + checkGroup.totalChecks; }, 0) ?? 0)} @@ -81,7 +75,7 @@ const ChartBoxes = ({ {hoveredUptimeData !== null ? Math.floor(hoveredUptimeData?.avgResponseTime ?? 0) - : Math.floor(groupedUptimePercentage)} + : Math.floor(monitorData?.groupedUptimePercentage * 100 ?? 0)} {hoveredUptimeData !== null ? " ms" : " %"} @@ -89,7 +83,7 @@ const ChartBoxes = ({ @@ -98,14 +92,14 @@ const ChartBoxes = ({ icon={} header="Incidents" noDataMessage={noIncidentsMessage} - isEmpty={monitor?.groupedDownChecks?.length === 0} + isEmpty={monitorData?.groupedDownChecks?.length === 0} > {hoveredIncidentsData !== null ? hoveredIncidentsData.totalChecks - : (monitor?.groupedDownChecks?.reduce((count, checkGroup) => { + : (monitorData?.groupedDownChecks?.reduce((count, checkGroup) => { return count + checkGroup.totalChecks; }, 0) ?? 0)} @@ -123,7 +117,7 @@ const ChartBoxes = ({ @@ -132,7 +126,7 @@ const ChartBoxes = ({ icon={} header="Average Response Time" > - + ); @@ -141,8 +135,8 @@ const ChartBoxes = ({ export default ChartBoxes; ChartBoxes.propTypes = { - shouldRender: PropTypes.bool, - monitor: PropTypes.object, + isLoading: PropTypes.bool, + monitorData: PropTypes.object, dateRange: PropTypes.string.isRequired, uiTimezone: PropTypes.string.isRequired, dateFormat: PropTypes.string.isRequired, diff --git a/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx b/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx index 5d6c48b58..662abc831 100644 --- a/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx +++ b/src/Pages/Uptime/Details/Components/Charts/DownBarChart.jsx @@ -4,7 +4,7 @@ import { ResponsiveContainer, BarChart, XAxis, Bar, Cell } from "recharts"; import PropTypes from "prop-types"; import CustomLabels from "./CustomLabels"; -const DownBarChart = memo(({ monitor, type, onBarHover }) => { +const DownBarChart = memo(({ groupedDownChecks = [], type, onBarHover }) => { const theme = useTheme(); const [chartHovered, setChartHovered] = useState(false); @@ -19,7 +19,7 @@ const DownBarChart = memo(({ monitor, type, onBarHover }) => { { setChartHovered(true); onBarHover({ time: null, totalChecks: 0 }); @@ -40,10 +40,8 @@ const DownBarChart = memo(({ monitor, type, onBarHover }) => { y={0} width="100%" height="100%" - firstDataPoint={monitor?.groupedDownChecks?.[0] ?? {}} - lastDataPoint={ - monitor?.groupedDownChecks?.[monitor?.groupedDownChecks?.length - 1] ?? {} - } + firstDataPoint={groupedDownChecks?.[0] ?? {}} + lastDataPoint={groupedDownChecks?.[groupedDownChecks?.length - 1] ?? {}} type={type} /> } @@ -53,7 +51,7 @@ const DownBarChart = memo(({ monitor, type, onBarHover }) => { maxBarSize={7} background={{ fill: "transparent" }} > - {monitor?.groupedDownChecks?.map((entry, index) => { + {groupedDownChecks?.map((entry, index) => { return ( { - if (!shouldRender) { +const ResponseTImeChart = ({ isLoading = false, groupedChecks = [], dateRange }) => { + if (isLoading) { return ; } @@ -15,7 +15,7 @@ const ResponseTImeChart = ({ shouldRender = true, monitor, dateRange }) => { header="Response Times" > @@ -23,8 +23,8 @@ const ResponseTImeChart = ({ shouldRender = true, monitor, dateRange }) => { }; ResponseTImeChart.propTypes = { - shouldRender: PropTypes.bool, - monitor: PropTypes.object, + isLoading: PropTypes.bool, + groupedChecks: PropTypes.array, dateRange: PropTypes.string, }; diff --git a/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx b/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx index 1df79c26e..02df41f9a 100644 --- a/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx +++ b/src/Pages/Uptime/Details/Components/Charts/UpBarChart.jsx @@ -14,7 +14,7 @@ const getThemeColor = (responseTime) => { } }; -const UpBarChart = memo(({ monitor, type, onBarHover }) => { +const UpBarChart = memo(({ groupedUpChecks = [], type, onBarHover }) => { const theme = useTheme(); const [chartHovered, setChartHovered] = useState(false); const [hoveredBarIndex, setHoveredBarIndex] = useState(null); @@ -28,7 +28,7 @@ const UpBarChart = memo(({ monitor, type, onBarHover }) => { { setChartHovered(true); onBarHover({ time: null, totalChecks: 0, avgResponseTime: 0 }); @@ -49,10 +49,8 @@ const UpBarChart = memo(({ monitor, type, onBarHover }) => { y={0} width="100%" height="100%" - firstDataPoint={monitor?.groupedUpChecks?.[0]} - lastDataPoint={ - monitor?.groupedUpChecks?.[monitor?.groupedUpChecks?.length - 1] - } + firstDataPoint={groupedUpChecks?.[0]} + lastDataPoint={groupedUpChecks?.[groupedUpChecks?.length - 1]} type={type} /> } @@ -62,7 +60,7 @@ const UpBarChart = memo(({ monitor, type, onBarHover }) => { maxBarSize={7} background={{ fill: "transparent" }} > - {monitor?.groupedUpChecks?.map((entry, index) => { + {groupedUpChecks?.map((entry, index) => { const themeColor = getThemeColor(entry.avgResponseTime); return ( { +<<<<<<< Updated upstream const { t } = useTranslation(); if (!shouldRender) { +======= + if (isLoading) { +>>>>>>> Stashed changes return ; } @@ -78,7 +82,7 @@ const ResponseTable = ({ }; ResponseTable.propTypes = { - shouldRender: PropTypes.bool, + isLoading: PropTypes.bool, checks: PropTypes.array, checksCount: PropTypes.number, uiTimezone: PropTypes.string.isRequired, diff --git a/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx b/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx index 5597ce2ac..66c021aa2 100644 --- a/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx +++ b/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx @@ -1,23 +1,36 @@ import StatusBoxes from "../../../../../Components/StatusBoxes"; import StatBox from "../../../../../Components/StatBox"; + +import PropTypes from "prop-types"; import { getHumanReadableDuration } from "../../../../../Utils/timeUtils"; import { useTheme } from "@mui/material/styles"; import { Typography } from "@mui/material"; import useUtils from "../../../Monitors/Hooks/useUtils"; -const UptimeStatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => { +const UptimeStatusBoxes = ({ + isLoading = false, + monitor, + monitorStats, + certificateExpiry, +}) => { const theme = useTheme(); const { determineState } = useUtils(); - const { time: streakTime, units: streakUnits } = getHumanReadableDuration( - monitor?.uptimeStreak - ); + // Determine time since last failure + const timeOfLastFailure = monitorStats?.timeOfLastFailure; + const timeSinceLastFailure = timeOfLastFailure > 0 ? Date.now() - timeOfLastFailure : 0; - const { time: lastCheckTime, units: lastCheckUnits } = getHumanReadableDuration( - monitor?.timeSinceLastCheck - ); + // Determine time since last check + const timeOfLastCheck = monitorStats?.lastCheckTimestamp; + const timeSinceLastCheck = Date.now() - timeOfLastCheck; + + const { time: streakTime, units: streakUnits } = + getHumanReadableDuration(timeSinceLastFailure); + + const { time: lastCheckTime, units: lastCheckUnits } = + getHumanReadableDuration(timeSinceLastCheck); return ( - + { heading="last response time" subHeading={ <> - {monitor?.latestResponseTime} + {monitorStats?.lastResponseTime} {"ms"} } @@ -64,4 +77,11 @@ const UptimeStatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => { ); }; +UptimeStatusBoxes.propTypes = { + shouldRender: PropTypes.bool, + monitor: PropTypes.object, + monitorStats: PropTypes.object, + certificateExpiry: PropTypes.string, +}; + export default UptimeStatusBoxes; diff --git a/src/Pages/Uptime/Details/index.jsx b/src/Pages/Uptime/Details/index.jsx index e404c214a..df5efbca4 100644 --- a/src/Pages/Uptime/Details/index.jsx +++ b/src/Pages/Uptime/Details/index.jsx @@ -16,7 +16,7 @@ import { useParams } from "react-router-dom"; import { useSelector } from "react-redux"; import { useTheme } from "@emotion/react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import useMonitorFetch from "./Hooks/useMonitorFetch"; +import useFetchUptimeMonitorDetails from "../../../Hooks/useFetchUptimeMonitorDetails"; import useCertificateFetch from "./Hooks/useCertificateFetch"; import useChecksFetch from "./Hooks/useChecksFetch"; import { useTranslation } from "react-i18next"; @@ -36,8 +36,7 @@ const UptimeDetails = () => { // Local state const [dateRange, setDateRange] = useState("recent"); - const [hoveredUptimeData, setHoveredUptimeData] = useState(null); - const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null); + const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(5); @@ -49,10 +48,13 @@ const UptimeDetails = () => { const isAdmin = useIsAdmin(); const { t } = useTranslation(); - const [monitor, monitorIsLoading, monitorNetworkError] = useMonitorFetch({ - monitorId, - dateRange, - }); + const [monitorData, monitorStats, monitorIsLoading, monitorNetworkError] = + useFetchUptimeMonitorDetails({ + monitorId, + dateRange, + }); + + const monitor = monitorData?.monitor; const [certificateExpiry, certificateIsLoading] = useCertificateFetch({ monitor, @@ -62,7 +64,6 @@ const UptimeDetails = () => { }); const monitorType = monitor?.type; - const [checks, checksCount, checksAreLoading, checksNetworkError] = useChecksFetch({ monitorId, monitorType, @@ -71,6 +72,8 @@ const UptimeDetails = () => { rowsPerPage, }); + console.log("render"); + // Handlers const handlePageChange = (_, newPage) => { setPage(newPage); @@ -119,38 +122,35 @@ const UptimeDetails = () => {