From 2eeb8b6e4edcf5603b97bdd1915d798e4138e004 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 15:04:48 -0800 Subject: [PATCH 1/5] Changed NetworkErrorFallback to a generic callback --- .../GenericFallback/NetworkError.jsx | 20 +++++++++++++++++++ .../index.jsx | 17 +++++----------- 2 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 Client/src/Components/GenericFallback/NetworkError.jsx rename Client/src/Components/{NetworkErrorFallback => GenericFallback}/index.jsx (81%) diff --git a/Client/src/Components/GenericFallback/NetworkError.jsx b/Client/src/Components/GenericFallback/NetworkError.jsx new file mode 100644 index 000000000..b26037a57 --- /dev/null +++ b/Client/src/Components/GenericFallback/NetworkError.jsx @@ -0,0 +1,20 @@ +import { Typography } from "@mui/material"; +import { useTheme } from "@emotion/react"; + +const NetworkError = () => { + const theme = useTheme(); + return ( + <> + + Network error + + Please check your connection + + ); +}; + +export default NetworkError; diff --git a/Client/src/Components/NetworkErrorFallback/index.jsx b/Client/src/Components/GenericFallback/index.jsx similarity index 81% rename from Client/src/Components/NetworkErrorFallback/index.jsx rename to Client/src/Components/GenericFallback/index.jsx index 1039b256b..af9ed2ea4 100644 --- a/Client/src/Components/NetworkErrorFallback/index.jsx +++ b/Client/src/Components/GenericFallback/index.jsx @@ -1,5 +1,5 @@ import { useTheme } from "@emotion/react"; -import { Box, Stack, Typography } from "@mui/material"; +import { Box, Stack } 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"; @@ -11,13 +11,13 @@ import { useSelector } from "react-redux"; * @returns {JSX.Element} The rendered fallback UI. */ -const NetworkErrorFallback = () => { +const GenericFallback = ({ children }) => { const theme = useTheme(); const mode = useSelector((state) => state.ui.mode); return ( { maxWidth={"300px"} zIndex={1} > - - Network error - - Please check your connection + {children} ); }; -export default NetworkErrorFallback; +export default GenericFallback; From 6c3b026199b93b1092cbb0f007b252f7f4884f00 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 15:05:45 -0800 Subject: [PATCH 2/5] Add a skeleton for table component --- Client/src/Components/Table/skeleton.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Client/src/Components/Table/skeleton.jsx diff --git a/Client/src/Components/Table/skeleton.jsx b/Client/src/Components/Table/skeleton.jsx new file mode 100644 index 000000000..062d39156 --- /dev/null +++ b/Client/src/Components/Table/skeleton.jsx @@ -0,0 +1,14 @@ +import { Skeleton } from "@mui/material"; + +const TableSkeleton = () => { + return ( + + ); +}; + +export default TableSkeleton; From 3c93c6ffc3d49cba1f501e8f2eec9666ae3eef32 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 15:06:02 -0800 Subject: [PATCH 3/5] Add network operation hooks --- .../Pages/Incidents/Hooks/useChecksFetch.jsx | 60 +++++++++++++++++++ .../Incidents/Hooks/useMonitorsFetch.jsx | 51 ++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 Client/src/Pages/Incidents/Hooks/useChecksFetch.jsx create mode 100644 Client/src/Pages/Incidents/Hooks/useMonitorsFetch.jsx diff --git a/Client/src/Pages/Incidents/Hooks/useChecksFetch.jsx b/Client/src/Pages/Incidents/Hooks/useChecksFetch.jsx new file mode 100644 index 000000000..57444ad05 --- /dev/null +++ b/Client/src/Pages/Incidents/Hooks/useChecksFetch.jsx @@ -0,0 +1,60 @@ +import { useState, useEffect } from "react"; +import { networkService } from "../../../main"; +import { createToast } from "../../../Utils/toastUtils"; +import { useSelector } from "react-redux"; +const useChecksFetch = ({ selectedMonitor, filter, dateRange, page, rowsPerPage }) => { + //Redux + const { authToken, user } = useSelector((state) => state.auth); + + //Local + const [isLoading, setIsLoading] = useState(false); + const [networkError, setNetworkError] = useState(false); + const [checks, setChecks] = useState(undefined); + const [checksCount, setChecksCount] = useState(undefined); + + useEffect(() => { + const fetchChecks = async () => { + try { + setIsLoading(true); + let res; + + if (selectedMonitor === "0") { + res = await networkService.getChecksByTeam({ + authToken: authToken, + status: false, + teamId: user.teamId, + sortOrder: "desc", + limit: null, + dateRange, + filter: filter, + page: page, + rowsPerPage: rowsPerPage, + }); + } else { + res = await networkService.getChecksByMonitor({ + authToken: authToken, + status: false, + monitorId: selectedMonitor, + sortOrder: "desc", + limit: null, + dateRange, + filter: filter, + page, + rowsPerPage, + }); + } + setChecks(res.data.data.checks); + setChecksCount(res.data.data.checksCount); + } catch (error) { + setNetworkError(true); + createToast({ body: error.message }); + } finally { + setIsLoading(false); + } + }; + fetchChecks(); + }, [authToken, user, dateRange, page, rowsPerPage, filter, selectedMonitor]); + return { isLoading, networkError, checks, checksCount }; +}; + +export default useChecksFetch; diff --git a/Client/src/Pages/Incidents/Hooks/useMonitorsFetch.jsx b/Client/src/Pages/Incidents/Hooks/useMonitorsFetch.jsx new file mode 100644 index 000000000..0ffd18571 --- /dev/null +++ b/Client/src/Pages/Incidents/Hooks/useMonitorsFetch.jsx @@ -0,0 +1,51 @@ +import { useState, useEffect } from "react"; +import { networkService } from "../../../main"; +import { createToast } from "../../../Utils/toastUtils"; +const useMonitorsFetch = ({ authToken, teamId }) => { + //Local state + const [isLoading, setIsLoading] = useState(false); + const [networkError, setNetworkError] = useState(false); + + const [monitors, setMonitors] = useState(undefined); + + useEffect(() => { + const fetchMonitors = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorsByTeamId({ + authToken, + teamId, + limit: null, + types: null, + status: null, + checkOrder: null, + normalize: null, + page: null, + rowsPerPage: null, + filter: null, + field: null, + order: null, + }); + if (res?.data?.data?.monitors?.length > 0) { + const monitorLookup = res.data.data.monitors.reduce((acc, monitor) => { + acc[monitor._id] = monitor; + return acc; + }, {}); + setMonitors(monitorLookup); + } + } catch (error) { + setNetworkError(true); + createToast({ + body: error.message, + }); + } finally { + setIsLoading(false); + } + }; + + fetchMonitors(); + }, [authToken, teamId]); + return { isLoading, monitors, networkError }; +}; + +export { useMonitorsFetch }; From c1144a4432feed0b989a3a6cde9a938fa817bbb5 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 15:06:30 -0800 Subject: [PATCH 4/5] update proptypes --- Client/src/Components/Table/TablePagination/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Components/Table/TablePagination/index.jsx b/Client/src/Components/Table/TablePagination/index.jsx index 014e2405f..8427f4270 100644 --- a/Client/src/Components/Table/TablePagination/index.jsx +++ b/Client/src/Components/Table/TablePagination/index.jsx @@ -8,7 +8,7 @@ Pagination.propTypes = { paginationLabel: PropTypes.string, // Label for the pagination. 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. + rowsPerPage: PropTypes.number, // 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. }; From 02d93fdcd494f1fad0b37fc07e725ab1c7023358 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 29 Jan 2025 15:06:41 -0800 Subject: [PATCH 5/5] Refactor incidents --- .../Components/IncidentTable/index.jsx | 126 +++++++++ .../Components/OptionsHeader/index.jsx | 162 ++++++++++++ .../Components/OptionsHeader/skeleton.jsx | 11 + .../Incidents/IncidentTable/Empty/Empty.jsx | 32 --- .../IncidentTable/Skeleton/Skeleton.jsx | 21 -- .../Pages/Incidents/IncidentTable/index.jsx | 187 ------------- Client/src/Pages/Incidents/index.jsx | 248 ++++-------------- Client/src/Pages/Incidents/skeleton.jsx | 50 ---- Client/src/Pages/PageSpeed/Monitors/index.jsx | 17 +- .../Components/UptimeDataTable/index.jsx | 4 +- .../Components/UptimeDataTable/skeleton.jsx | 14 - Client/src/Pages/Uptime/Monitors/index.jsx | 21 +- Client/src/Routes/index.jsx | 2 +- Client/src/Utils/NetworkService.js | 1 - 14 files changed, 383 insertions(+), 513 deletions(-) create mode 100644 Client/src/Pages/Incidents/Components/IncidentTable/index.jsx create mode 100644 Client/src/Pages/Incidents/Components/OptionsHeader/index.jsx create mode 100644 Client/src/Pages/Incidents/Components/OptionsHeader/skeleton.jsx delete mode 100644 Client/src/Pages/Incidents/IncidentTable/Empty/Empty.jsx delete mode 100644 Client/src/Pages/Incidents/IncidentTable/Skeleton/Skeleton.jsx delete mode 100644 Client/src/Pages/Incidents/IncidentTable/index.jsx delete mode 100644 Client/src/Pages/Incidents/skeleton.jsx delete mode 100644 Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/skeleton.jsx diff --git a/Client/src/Pages/Incidents/Components/IncidentTable/index.jsx b/Client/src/Pages/Incidents/Components/IncidentTable/index.jsx new file mode 100644 index 000000000..ba7a03e05 --- /dev/null +++ b/Client/src/Pages/Incidents/Components/IncidentTable/index.jsx @@ -0,0 +1,126 @@ +//Components +import Table from "../../../../Components/Table"; +import TableSkeleton from "../../../../Components/Table/skeleton"; +import Pagination from "../../../../Components/Table/TablePagination"; +import { StatusLabel } from "../../../../Components/Label"; +import { HttpStatusLabel } from "../../../../Components/HttpStatusLabel"; +import GenericFallback from "../../../../Components/GenericFallback"; +import NetworkError from "../../../../Components/GenericFallback/NetworkError"; + +//Utils +import { formatDateWithTz } from "../../../../Utils/timeUtils"; +import { useSelector } from "react-redux"; +import { useState } from "react"; +import useChecksFetch from "../../Hooks/useChecksFetch"; +import PropTypes from "prop-types"; + +const IncidentTable = ({ + shouldRender, + monitors, + selectedMonitor, + filter, + dateRange, +}) => { + //Redux state + const uiTimezone = useSelector((state) => state.ui.timezone); + + //Local state + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const { isLoading, networkError, checks, checksCount } = useChecksFetch({ + selectedMonitor, + filter, + dateRange, + page, + rowsPerPage, + }); + + //Handlers + const handleChangePage = (_, newPage) => { + setPage(newPage); + }; + + const handleChangeRowsPerPage = (event) => { + setRowsPerPage(event.target.value); + }; + + const headers = [ + { + id: "monitorName", + content: "Monitor Name", + render: (row) => monitors[row.monitorId]?.name ?? "N/A", + }, + { + id: "status", + content: "Status", + render: (row) => { + const status = row.status === true ? "up" : "down"; + return ( + + ); + }, + }, + { + id: "dateTime", + content: "Date & Time", + render: (row) => { + const formattedDate = formatDateWithTz( + row.createdAt, + "YYYY-MM-DD HH:mm:ss A", + uiTimezone + ); + return formattedDate; + }, + }, + { + id: "statusCode", + content: "Status Code", + render: (row) => , + }, + { id: "message", content: "Message", render: (row) => row.message }, + ]; + + if (!shouldRender || isLoading) return ; + + if (networkError) { + return ( + + + + ); + } + + if (!isLoading && typeof checksCount === "undefined") { + return No incidents recorded; + } + + return ( + <> + + + + ); +}; + +IncidentTable.propTypes = { + shouldRender: PropTypes.bool, + monitors: PropTypes.object, + selectedMonitor: PropTypes.string, + filter: PropTypes.string, + dateRange: PropTypes.string, +}; +export default IncidentTable; diff --git a/Client/src/Pages/Incidents/Components/OptionsHeader/index.jsx b/Client/src/Pages/Incidents/Components/OptionsHeader/index.jsx new file mode 100644 index 000000000..967acfed5 --- /dev/null +++ b/Client/src/Pages/Incidents/Components/OptionsHeader/index.jsx @@ -0,0 +1,162 @@ +// Components +import { Stack, Typography, Button, ButtonGroup } from "@mui/material"; +import Select from "../../../../Components/Inputs/Select"; +import PropTypes from "prop-types"; + +//Utils +import { useTheme } from "@emotion/react"; +import SkeletonLayout from "./skeleton"; + +const OptionsHeader = ({ + shouldRender, + selectedMonitor = 0, + setSelectedMonitor, + monitors, + filter = "all", + setFilter, + dateRange = "hour", + setDateRange, +}) => { + const theme = useTheme(); + const monitorNames = typeof monitors !== "undefined" ? Object.values(monitors) : []; + + if (!shouldRender) return ; + + return ( + + + + Incidents for + + - - - - Filter by: - - - - - - - - - - Show: - - - - - - - - - - - - )} + + + + ); }; diff --git a/Client/src/Pages/Incidents/skeleton.jsx b/Client/src/Pages/Incidents/skeleton.jsx deleted file mode 100644 index 51848a108..000000000 --- a/Client/src/Pages/Incidents/skeleton.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Stack, Skeleton } from "@mui/material"; -import { useTheme } from "@emotion/react"; - -/** - * Renders a skeleton layout. - * - * @returns {JSX.Element} - */ -const SkeletonLayout = () => { - const theme = useTheme(); - - return ( - <> - - - - - - - - - ); -}; - -export default SkeletonLayout; diff --git a/Client/src/Pages/PageSpeed/Monitors/index.jsx b/Client/src/Pages/PageSpeed/Monitors/index.jsx index a0c24d48e..470a33f18 100644 --- a/Client/src/Pages/PageSpeed/Monitors/index.jsx +++ b/Client/src/Pages/PageSpeed/Monitors/index.jsx @@ -1,6 +1,6 @@ // Components import Breadcrumbs from "../../../Components/Breadcrumbs"; -import { Stack } from "@mui/material"; +import { Stack, Typography } from "@mui/material"; import CreateMonitorHeader from "../../../Components/CreateMonitorHeader"; import MonitorCountHeader from "../../../Components/MonitorCountHeader"; import MonitorGrid from "./Components/MonitorGrid"; @@ -11,7 +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"; +import GenericFallback from "../../../Components/GenericFallback"; // Constants const BREADCRUMBS = [{ name: `pagespeed`, path: "/pagespeed" }]; @@ -27,7 +27,18 @@ const PageSpeed = () => { }); if (networkError === true) { - return ; + return ( + + + Network error + + Please check your connection + + ); } if (!isLoading && monitors?.length === 0) { diff --git a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx index 8a9fdcbe9..2824f80d6 100644 --- a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx +++ b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx @@ -9,7 +9,7 @@ import BarChart from "../../../../../Components/Charts/BarChart"; import ActionsMenu from "../ActionsMenu"; import LoadingSpinner from "../LoadingSpinner"; -import UptimeDataTableSkeleton from "./skeleton"; +import TableSkeleton from "../../../../../Components/Table/skeleton"; // Utils import { useTheme } from "@emotion/react"; @@ -175,7 +175,7 @@ const UptimeDataTable = ({ ]; if (monitorsAreLoading) { - return ; + return ; } return ( diff --git a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/skeleton.jsx b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/skeleton.jsx deleted file mode 100644 index e3c4fa2e2..000000000 --- a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/skeleton.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Skeleton } from "@mui/material"; - -const UptimeDataTableSkeleton = () => { - return ( - - ); -}; - -export default UptimeDataTableSkeleton; diff --git a/Client/src/Pages/Uptime/Monitors/index.jsx b/Client/src/Pages/Uptime/Monitors/index.jsx index 1356d9e7c..31dcc93be 100644 --- a/Client/src/Pages/Uptime/Monitors/index.jsx +++ b/Client/src/Pages/Uptime/Monitors/index.jsx @@ -6,13 +6,13 @@ 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 GenericFallback from "../../../Components/GenericFallback"; import SearchComponent from "./Components/SearchComponent"; import MonitorCountHeader from "../../../Components/MonitorCountHeader"; // MUI Components -import { Stack, Box, Button } from "@mui/material"; +import { Stack, Box, Button, Typography } from "@mui/material"; // Utils import { useState, useCallback } from "react"; @@ -109,8 +109,21 @@ const UptimeMonitors = () => { }); const totalMonitors = monitorsSummary?.totalMonitors ?? 0; - if (networkError) return ; - + if (networkError) { + return ( + + {" "} + + Network error + + Please check your connection + + ); + } if (!isLoading && !monitorsAreLoading && totalMonitors === 0) { return (