From a90ed64a6f8d5ab4f6b07297da5344467de2d416 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:37:49 -0800 Subject: [PATCH 01/14] fix monitorStatusHeader name, safe property access --- Client/src/Components/MonitorStatusHeader/index.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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; From cadb9bb9510eb5b172d5f4e15315ede2b8a67e96 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:38:16 -0800 Subject: [PATCH 02/14] provide default values for pagination --- Client/src/Components/Table/TablePagination/index.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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, }) { From 65c909e1664921c37c36f09f0be9a424263b989c Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:38:34 -0800 Subject: [PATCH 03/14] provide default props for Table --- Client/src/Components/Table/index.jsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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, }), From 6788b5672e3cd05924503711fb3af408b429aae1 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:39:17 -0800 Subject: [PATCH 04/14] add ref to stop initial search --- .../Uptime/Monitors/Components/SearchComponent/index.jsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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]); From ef28f8c38b208724b1824f5cd93fa451f56eebe8 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:39:47 -0800 Subject: [PATCH 05/14] simplify UptimeDataTable --- .../Components/UptimeDataTable/index.jsx | 121 ++++++------------ .../Components/UptimeDataTable/skeleton.jsx | 9 +- 2 files changed, 41 insertions(+), 89 deletions(-) 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; From 12711e87ee558c15f4e1f500887c4aaaf3fa4ef4 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:40:27 -0800 Subject: [PATCH 06/14] set initial state explicitly to for consistency --- Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx | 6 +++--- Client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx b/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx index d4b7e5ba7..12fcd9923 100644 --- a/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx +++ b/Client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx @@ -43,9 +43,9 @@ 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 theme = useTheme(); 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"; From a9851787e8bcd344882a851dd59f3474958b59a6 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:41:06 -0800 Subject: [PATCH 07/14] Use MonitorCountHeader, initialze state to undefined' --- Client/src/Pages/Uptime/Monitors/index.jsx | 32 ++++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/Client/src/Pages/Uptime/Monitors/index.jsx b/Client/src/Pages/Uptime/Monitors/index.jsx index d60b5c495..f8d78ad52 100644 --- a/Client/src/Pages/Uptime/Monitors/index.jsx +++ b/Client/src/Pages/Uptime/Monitors/index.jsx @@ -6,6 +6,10 @@ import UptimeDataTable from "./Components/UptimeDataTable"; import Pagination from "../../../Components/Table/TablePagination"; import CreateMonitorHeader from "../../../Components/CreateMonitorHeader"; import Fallback from "../../../Components/Fallback"; +import SearchComponent from "./Components/SearchComponent"; + +import MonitorCountHeader from "../../../Components/MonitorCountHeader"; + // MUI Components import { Stack, Box, Button } from "@mui/material"; @@ -53,9 +57,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,21 +89,19 @@ const UptimeMonitors = () => { }, []); const teamId = user.teamId; - const { monitorsAreLoading, monitors, filteredMonitors, monitorsSummary } = useMonitorsFetch({ authToken, teamId, limit: 25, - page, + page: page, rowsPerPage: rowsPerPage, filter: search, - field: sort.field, - order: sort.order, + field: sort?.field, + order: sort?.order, triggerUpdate: monitorUpdateTrigger, }); const totalMonitors = monitorsSummary?.totalMonitors ?? 0; - if (!isLoading && !monitorsAreLoading && monitors?.length === 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} /> From cfb5c7ae5d0ae77e2d5d7d503f019082761022a1 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:41:32 -0800 Subject: [PATCH 08/14] Update hooks to use undefined for initial state --- Client/src/Pages/Uptime/Details/Hooks/useCertificateFetch.jsx | 2 +- Client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx | 4 ++-- Client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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(() => { From 4c3b348642d8b11dbf3366ec12dd4512bb7594e9 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:42:02 -0800 Subject: [PATCH 09/14] safe param access --- .../Pages/Uptime/Details/Components/ChartBoxes/index.jsx | 4 ++-- .../Pages/Uptime/Details/Components/Charts/DownBarChart.jsx | 2 +- .../Uptime/Details/Components/Charts/ResponseTimeChart.jsx | 2 +- .../Pages/Uptime/Details/Components/ResponseTable/index.jsx | 6 +++--- .../Uptime/Details/Components/UptimeStatusBoxes/index.jsx | 1 + 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx b/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx index 5565529fb..aa40442a9 100644 --- a/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx +++ b/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx @@ -123,7 +123,7 @@ const ChartBoxes = ({ icon={} 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 ); From 80a16ef61a255fd4e1cb90e959261df2f0aac024 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:42:40 -0800 Subject: [PATCH 10/14] safe param access --- .../Details/Components/Charts/PieChart.jsx | 4 +- .../Components/Charts/PieChartLegend.jsx | 130 +++++++++--------- .../Components/PageSpeedAreaChart/index.jsx | 4 - .../Components/PerformanceReport/index.jsx | 6 +- Client/src/Pages/PageSpeed/Details/index.jsx | 5 +- 5 files changed, 69 insertions(+), 80 deletions(-) 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, From 0feb2053a113d46cef0e64ea2d32acfb9c03105a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 11:42:59 -0800 Subject: [PATCH 11/14] remove null return --- .../Pages/PageSpeed/Monitors/Components/MonitorGrid/index.jsx | 2 -- 1 file changed, 2 deletions(-) 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 ( Date: Wed, 29 Jan 2025 12:31:19 -0800 Subject: [PATCH 12/14] move fallback css to fallback css file --- .../src/Components/Layouts/HomeLayout/index.css | 17 ----------------- 1 file changed, 17 deletions(-) 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; -} From d2d2e87184b3836a1c96b45113230cb1644c4aac Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 12:31:43 -0800 Subject: [PATCH 13/14] Add a network fallback component --- Client/src/Components/Fallback/index.css | 16 ++++ .../Components/NetworkErrorFallback/index.jsx | 85 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 Client/src/Components/NetworkErrorFallback/index.jsx 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/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; From 4559215316c07d55f4ebfb19d5e7a2e913abce14 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 12:32:09 -0800 Subject: [PATCH 14/14] add network error state to hooks --- .../Monitors/Hooks/useMonitorsFetch.jsx | 4 ++- Client/src/Pages/PageSpeed/Monitors/index.jsx | 7 +++- .../Monitors/Hooks/useMonitorsFetch.jsx | 10 +++++- Client/src/Pages/Uptime/Monitors/index.jsx | 35 ++++++++++++------- Client/src/Utils/NetworkService.js | 1 - 5 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx b/Client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx index 06d423acc..72bfbfc35 100644 --- a/Client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx +++ b/Client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx @@ -6,6 +6,7 @@ const useMonitorsFetch = ({ authToken, teamId }) => { 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 ( { }, []); const teamId = user.teamId; - const { monitorsAreLoading, monitors, filteredMonitors, monitorsSummary } = - useMonitorsFetch({ - authToken, - teamId, - limit: 25, - page: 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 (