From 8e13855b66bbdc9722a0ab574469ee6569cfa952 Mon Sep 17 00:00:00 2001 From: karenvicent Date: Fri, 5 Sep 2025 15:53:22 -0400 Subject: [PATCH 1/5] feat create pagestate wrapper --- .../Charts/StatusPageBarChart/index.jsx | 2 +- .../src/Components/PageStateWrapper/index.jsx | 66 ++++++++ .../Pages/Infrastructure/Monitors/index.jsx | 120 ++++++------- client/src/Pages/Maintenance/index.jsx | 106 +++++------- client/src/Pages/Notifications/index.jsx | 84 +++++---- client/src/Pages/PageSpeed/Monitors/index.jsx | 89 ++++------ .../Pages/StatusPage/StatusPages/index.jsx | 67 +++----- client/src/Pages/Uptime/Monitors/index.jsx | 160 ++++++++---------- 8 files changed, 328 insertions(+), 366 deletions(-) create mode 100644 client/src/Components/PageStateWrapper/index.jsx diff --git a/client/src/Components/Charts/StatusPageBarChart/index.jsx b/client/src/Components/Charts/StatusPageBarChart/index.jsx index b86f19c77..e8a34806d 100644 --- a/client/src/Components/Charts/StatusPageBarChart/index.jsx +++ b/client/src/Components/Charts/StatusPageBarChart/index.jsx @@ -22,7 +22,7 @@ const Bar = ({ width, height, backgroundColor, borderRadius, children }) => { return ( { + const theme = useTheme(); + const { t } = useTranslation(); + const isAdmin = useIsAdmin(); + const hasInitialized = !isLoading && items !== undefined; + const showEmpty = items == undefined || items.length === 0; + + if (networkError) { + return ( + + + {t("common.toasts.networkError")} + + {t("common.toasts.checkConnection")} + + ); + } + if (!hasInitialized) { + return null; + } + if (showEmpty) { + return ( + + {fallbackChildren} + + ); + } + return <>{children}; +}; + +PageStateWrapper.propTypes = { + networkError: PropTypes.any, + isLoading: PropTypes.bool, + items: PropTypes.oneOfType([PropTypes.array, PropTypes.oneOf([null])]), + type: PropTypes.string, + fallbackLink: PropTypes.string, + fallbackChildren: PropTypes.node, + children: PropTypes.node, +}; + +export default PageStateWrapper; diff --git a/client/src/Pages/Infrastructure/Monitors/index.jsx b/client/src/Pages/Infrastructure/Monitors/index.jsx index f7ca892c2..bfd2270fe 100644 --- a/client/src/Pages/Infrastructure/Monitors/index.jsx +++ b/client/src/Pages/Infrastructure/Monitors/index.jsx @@ -1,12 +1,11 @@ // Components -import { Stack, Typography } from "@mui/material"; +import { Stack } from "@mui/material"; import Breadcrumbs from "../../../Components/Breadcrumbs"; import MonitorCountHeader from "../../../Components/MonitorCountHeader"; import MonitorCreateHeader from "../../../Components/MonitorCreateHeader"; import MonitorsTable from "./Components/MonitorsTable"; import Pagination from "../../..//Components/Table/TablePagination"; -import GenericFallback from "../../../Components/GenericFallback"; -import Fallback from "../../../Components/Fallback"; +import PageStateWrapper from "../../../Components/PageStateWrapper"; import Filter from "./Components/Filters"; import SearchComponent from "../../Uptime/Monitors/Components/SearchComponent"; // Utils @@ -81,75 +80,58 @@ const InfrastructureMonitors = () => { updateTrigger, }); - if (networkError === true) { - return ( - - - {t("common.toasts.networkError")} - - {t("common.toasts.checkConnection")} - - ); - } - - if (!isLoading && typeof summary?.totalMonitors === "undefined") { - return ( - - ); - } - return ( - - - + - - - - - + items={monitors} + type="infrastructureMonitor" + fallbackLink="/infrastructure/create" + > + + + + + + + + - - - + + + + + ); }; diff --git a/client/src/Pages/Maintenance/index.jsx b/client/src/Pages/Maintenance/index.jsx index 9f9528a67..b53706216 100644 --- a/client/src/Pages/Maintenance/index.jsx +++ b/client/src/Pages/Maintenance/index.jsx @@ -1,6 +1,5 @@ import { Stack, Button } from "@mui/material"; import { useTheme } from "@emotion/react"; -import Fallback from "../../Components/Fallback"; import { useState, useEffect } from "react"; import "./index.css"; import MaintenanceTable from "./MaintenanceTable"; @@ -8,24 +7,21 @@ import { useSelector } from "react-redux"; import { networkService } from "../../main"; import Breadcrumbs from "../../Components/Breadcrumbs"; import { useNavigate } from "react-router-dom"; -import { useIsAdmin } from "../../Hooks/useIsAdmin"; import { useTranslation } from "react-i18next"; -import { Typography } from "@mui/material"; -import GenericFallback from "../../Components/GenericFallback"; +import PageStateWrapper from "../../Components/PageStateWrapper"; const Maintenance = () => { const theme = useTheme(); const { t } = useTranslation(); const navigate = useNavigate(); const rowsPerPage = useSelector((state) => state?.ui?.maintenance?.rowsPerPage ?? 5); - const isAdmin = useIsAdmin(); const [maintenanceWindows, setMaintenanceWindows] = useState([]); const [maintenanceWindowCount, setMaintenanceWindowCount] = useState(0); const [page, setPage] = useState(0); const [sort, setSort] = useState({}); const [updateTrigger, setUpdateTrigger] = useState(false); const [networkError, setNetworkError] = useState(false); - const [isDataFetched, setIsDataFetched] = useState(false); + const [isLoading, setIsLoading] = useState(false); const handleActionMenuDelete = () => { setUpdateTrigger((prev) => !prev); @@ -34,6 +30,7 @@ const Maintenance = () => { useEffect(() => { const fetchMaintenanceWindows = async () => { try { + setIsLoading(true); const response = await networkService.getMaintenanceWindowsByTeamId({ page: page, rowsPerPage: rowsPerPage, @@ -44,70 +41,53 @@ const Maintenance = () => { } catch (error) { setNetworkError(true); } finally { - setIsDataFetched(true); + setIsLoading(false); } }; fetchMaintenanceWindows(); }, [page, rowsPerPage, updateTrigger]); - if (networkError) { - return ( - - - {t("common.toasts.networkError")} - - {t("common.toasts.checkConnection")} - - ); - } - // Only show the fallback if we've fetched data and there are no maintenance windows - if (isDataFetched && maintenanceWindows.length === 0) { - return ( - - ); - } - return ( - - + - - - - - + + + + + + + + + ); }; diff --git a/client/src/Pages/Notifications/index.jsx b/client/src/Pages/Notifications/index.jsx index f9fc2e674..910fbf1fc 100644 --- a/client/src/Pages/Notifications/index.jsx +++ b/client/src/Pages/Notifications/index.jsx @@ -4,11 +4,10 @@ import Typography from "@mui/material/Typography"; import Breadcrumbs from "../../Components/Breadcrumbs"; import Button from "@mui/material/Button"; import DataTable from "../../Components/Table"; -import Fallback from "../../Components/Fallback"; import ActionMenu from "./components/ActionMenu"; +import PageStateWrapper from "../../Components/PageStateWrapper"; // Utils -import { useIsAdmin } from "../../Hooks/useIsAdmin"; import { useState } from "react"; import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router-dom"; @@ -23,7 +22,6 @@ const Notifications = () => { const theme = useTheme(); const BREADCRUMBS = [{ name: "notifications", path: "/notifications" }]; const [updateTrigger, setUpdateTrigger] = useState(false); - const isAdmin = useIsAdmin(); const [notifications, isLoading, error] = useGetNotificationsByTeamId(updateTrigger); const [deleteNotification, isDeleting, deleteError] = useDeleteNotification(); const { t } = useTranslation(); @@ -76,49 +74,47 @@ const Notifications = () => { }, ]; - if (notifications?.length === 0) { - return ( - - ); - } - return ( - - - + - - - {t("notifications.createTitle")} - navigate(`/notifications/${row._id}`), - rowSX: { - cursor: "pointer", - "&:hover td": { - backgroundColor: theme.palette.tertiary.main, - transition: "background-color .3s ease", - }, - }, - }} - headers={headers} - data={notifications} - /> - + + + + + + {t("notifications.createTitle")} + navigate(`/notifications/${row._id}`), + rowSX: { + cursor: "pointer", + "&:hover td": { + backgroundColor: theme.palette.tertiary.main, + transition: "background-color .3s ease", + }, + }, + }} + headers={headers} + data={notifications} + /> + + + ); }; diff --git a/client/src/Pages/PageSpeed/Monitors/index.jsx b/client/src/Pages/PageSpeed/Monitors/index.jsx index dff28e9cb..b5b13ba6e 100644 --- a/client/src/Pages/PageSpeed/Monitors/index.jsx +++ b/client/src/Pages/PageSpeed/Monitors/index.jsx @@ -1,18 +1,16 @@ // Components import { useState } from "react"; import Breadcrumbs from "../../../Components/Breadcrumbs"; -import { Stack, Typography } from "@mui/material"; +import { Stack } from "@mui/material"; import CreateMonitorHeader from "../../../Components/MonitorCreateHeader"; import MonitorCountHeader from "../../../Components/MonitorCountHeader"; import MonitorGrid from "./Components/MonitorGrid"; -import Fallback from "../../../Components/Fallback"; -import GenericFallback from "../../../Components/GenericFallback"; +import PageStateWrapper from "../../../Components/PageStateWrapper"; import FallbackPageSpeedWarning from "../../../Components/Fallback/FallbackPageSpeedWarning"; // Utils import { useTheme } from "@emotion/react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import { useTranslation } from "react-i18next"; import { useFetchMonitorsByTeamId } from "../../../Hooks/monitorHooks"; import { useFetchSettings } from "../../../Hooks/settingsHooks"; // Constants @@ -20,7 +18,6 @@ const BREADCRUMBS = [{ name: `pagespeed`, path: "/pagespeed" }]; const TYPES = ["pagespeed"]; const PageSpeed = () => { const theme = useTheme(); - const { t } = useTranslation(); const isAdmin = useIsAdmin(); const [monitors, monitorsSummary, isLoading, networkError] = useFetchMonitorsByTeamId({ @@ -40,56 +37,42 @@ const PageSpeed = () => { setIsEmailPasswordSet: () => {}, }); - if (networkError === true) { - return ( - - - {t("common.toasts.networkError")} - - {t("common.toasts.checkConnection")} - - ); - } - - if (!isLoading && monitors?.length === 0) { - return ( - - {isAdmin && settingsData && !settingsData.pagespeedApiKey && ( - - )} - - ); - } - return ( - - - + - - - + items={monitors} + type="pageSpeed" + fallbackLink="/pagespeed/create" + fallbackChildren={ + isAdmin && + settingsData && + !settingsData.pagespeedApiKey && ( + + ) + } + > + + + + + + + + ); }; diff --git a/client/src/Pages/StatusPage/StatusPages/index.jsx b/client/src/Pages/StatusPage/StatusPages/index.jsx index 7057eec0c..fa26c2b81 100644 --- a/client/src/Pages/StatusPage/StatusPages/index.jsx +++ b/client/src/Pages/StatusPage/StatusPages/index.jsx @@ -1,14 +1,11 @@ // Components -import { Stack, Typography } from "@mui/material"; +import { Stack } from "@mui/material"; import Breadcrumbs from "../../../Components/Breadcrumbs"; -import Fallback from "../../../Components/Fallback"; import MonitorCreateHeader from "../../../Components/MonitorCreateHeader"; -import GenericFallback from "../../../Components/GenericFallback"; import StatusPagesTable from "./Components/StatusPagesTable"; -import SkeletonLayout from "../../../Components/Skeletons/FullPage"; +import PageStateWrapper from "../../../Components/PageStateWrapper"; // Utils import { useTheme } from "@emotion/react"; -import { useTranslation } from "react-i18next"; import { useStatusPagesFetch } from "./Hooks/useStatusPagesFetch"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; const BREADCRUMBS = [{ name: `Status Pages`, path: "" }]; @@ -16,52 +13,30 @@ const BREADCRUMBS = [{ name: `Status Pages`, path: "" }]; const StatusPages = () => { // Utils const theme = useTheme(); - const { t } = useTranslation(); const isAdmin = useIsAdmin(); const [isLoading, networkError, statusPages] = useStatusPagesFetch(); - if (isLoading) { - return ; - } - - if (networkError === true) { - return ( - - - {t("common.toasts.networkError")} - - {t("common.toasts.checkConnection")} - - ); - } - - if (!isLoading && typeof statusPages !== "undefined" && statusPages.length === 0) { - return ( - - ); - } - return ( - - - + - - + items={statusPages} + type="statusPage" + fallbackLink="/status/uptime/create" + > + + + + + + + ); }; diff --git a/client/src/Pages/Uptime/Monitors/index.jsx b/client/src/Pages/Uptime/Monitors/index.jsx index 607092785..7f28c1cd7 100644 --- a/client/src/Pages/Uptime/Monitors/index.jsx +++ b/client/src/Pages/Uptime/Monitors/index.jsx @@ -10,16 +10,14 @@ import StatusBoxes from "./Components/StatusBoxes"; import UptimeDataTable from "./Components/UptimeDataTable"; import Pagination from "../../../Components/Table/TablePagination"; import CreateMonitorHeader from "../../../Components/MonitorCreateHeader"; -import Fallback from "../../../Components/Fallback"; -import GenericFallback from "../../../Components/GenericFallback"; import SearchComponent from "./Components/SearchComponent"; import Filter from "./Components/Filter"; +import PageStateWrapper from "../../../Components/PageStateWrapper"; import MonitorCountHeader from "../../../Components/MonitorCountHeader"; // MUI Components -import { Stack, Box, Button, Typography } from "@mui/material"; - +import { Stack, Box, Button } from "@mui/material"; // Utils import { useState, useCallback, useEffect } from "react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; @@ -153,93 +151,75 @@ const UptimeMonitors = () => { }, [isSearching]); const isLoading = monitorsWithSummaryIsLoading || monitorsWithChecksIsLoading; - if (networkError) { - return ( - - - {t("common.toasts.networkError")} - - {t("common.toasts.checkConnection")} - - ); - } - if ( - !isLoading && - (monitorsSummary?.totalMonitors === 0 || - typeof monitorsSummary?.totalMonitors === "undefined") - ) { - return ( - - ); - } - return ( - - - - - - - - - - - - - + return ( + <> + + + + + + + + + + + + + + + + + ); }; From 80d121e87a8570619fb9d15029f4aeb6c9a8969b Mon Sep 17 00:00:00 2001 From: karenvicent Date: Mon, 8 Sep 2025 10:49:42 -0400 Subject: [PATCH 2/5] add llama suggestions --- client/src/Components/PageStateWrapper/index.jsx | 6 +++--- client/src/Hooks/useNotifications.js | 2 +- client/src/Pages/Maintenance/index.jsx | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/client/src/Components/PageStateWrapper/index.jsx b/client/src/Components/PageStateWrapper/index.jsx index c6350cef8..f971b2c9c 100644 --- a/client/src/Components/PageStateWrapper/index.jsx +++ b/client/src/Components/PageStateWrapper/index.jsx @@ -17,9 +17,9 @@ const PageStateWrapper = ({ const theme = useTheme(); const { t } = useTranslation(); const isAdmin = useIsAdmin(); - const hasInitialized = !isLoading && items !== undefined; - const showEmpty = items == undefined || items.length === 0; - + const isEmpty = items === null || (Array.isArray(items) && items.length === 0); + const hasInitialized = items !== undefined && !(isLoading && isEmpty); + const showEmpty = !isLoading && isEmpty; if (networkError) { return ( diff --git a/client/src/Hooks/useNotifications.js b/client/src/Hooks/useNotifications.js index 7feebd44c..539725958 100644 --- a/client/src/Hooks/useNotifications.js +++ b/client/src/Hooks/useNotifications.js @@ -34,7 +34,7 @@ const useCreateNotification = () => { const useGetNotificationsByTeamId = (updateTrigger) => { const [notifications, setNotifications] = useState([]); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const { t } = useTranslation(); diff --git a/client/src/Pages/Maintenance/index.jsx b/client/src/Pages/Maintenance/index.jsx index b53706216..09fb4c2c4 100644 --- a/client/src/Pages/Maintenance/index.jsx +++ b/client/src/Pages/Maintenance/index.jsx @@ -15,13 +15,13 @@ const Maintenance = () => { const { t } = useTranslation(); const navigate = useNavigate(); const rowsPerPage = useSelector((state) => state?.ui?.maintenance?.rowsPerPage ?? 5); - const [maintenanceWindows, setMaintenanceWindows] = useState([]); + const [maintenanceWindows, setMaintenanceWindows] = useState(undefined); const [maintenanceWindowCount, setMaintenanceWindowCount] = useState(0); const [page, setPage] = useState(0); const [sort, setSort] = useState({}); const [updateTrigger, setUpdateTrigger] = useState(false); const [networkError, setNetworkError] = useState(false); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true) const handleActionMenuDelete = () => { setUpdateTrigger((prev) => !prev); @@ -30,14 +30,15 @@ const Maintenance = () => { useEffect(() => { const fetchMaintenanceWindows = async () => { try { + setNetworkError(false); setIsLoading(true); const response = await networkService.getMaintenanceWindowsByTeamId({ page: page, rowsPerPage: rowsPerPage, }); const { maintenanceWindows, maintenanceWindowCount } = response.data.data; - setMaintenanceWindows(maintenanceWindows); - setMaintenanceWindowCount(maintenanceWindowCount); + setMaintenanceWindows(maintenanceWindows ?? []); + setMaintenanceWindowCount(maintenanceWindowCount ?? 0); } catch (error) { setNetworkError(true); } finally { From ba954a1d6eb19c02738e9d885944aa246aabff0e Mon Sep 17 00:00:00 2001 From: karenvicent Date: Mon, 8 Sep 2025 11:15:14 -0400 Subject: [PATCH 3/5] run format --- client/src/Pages/Maintenance/index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/Pages/Maintenance/index.jsx b/client/src/Pages/Maintenance/index.jsx index 09fb4c2c4..ee142f524 100644 --- a/client/src/Pages/Maintenance/index.jsx +++ b/client/src/Pages/Maintenance/index.jsx @@ -21,7 +21,7 @@ const Maintenance = () => { const [sort, setSort] = useState({}); const [updateTrigger, setUpdateTrigger] = useState(false); const [networkError, setNetworkError] = useState(false); - const [isLoading, setIsLoading] = useState(true) + const [isLoading, setIsLoading] = useState(true); const handleActionMenuDelete = () => { setUpdateTrigger((prev) => !prev); @@ -37,8 +37,8 @@ const Maintenance = () => { rowsPerPage: rowsPerPage, }); const { maintenanceWindows, maintenanceWindowCount } = response.data.data; - setMaintenanceWindows(maintenanceWindows ?? []); - setMaintenanceWindowCount(maintenanceWindowCount ?? 0); + setMaintenanceWindows(maintenanceWindows ?? []); + setMaintenanceWindowCount(maintenanceWindowCount ?? 0); } catch (error) { setNetworkError(true); } finally { From 5bad362793defb40d8f05be7bc04bf419ae8a591 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 8 Sep 2025 20:27:20 -0700 Subject: [PATCH 4/5] fix server build order --- docker/dist-mono/docker-compose.yaml | 1 + docker/dist-mono/server.Dockerfile | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docker/dist-mono/docker-compose.yaml b/docker/dist-mono/docker-compose.yaml index 0e0b49445..9553757b9 100755 --- a/docker/dist-mono/docker-compose.yaml +++ b/docker/dist-mono/docker-compose.yaml @@ -1,6 +1,7 @@ services: server: image: ghcr.io/bluewave-labs/checkmate-backend-mono:latest + pull_policy: always restart: always ports: - "52345:52345" diff --git a/docker/dist-mono/server.Dockerfile b/docker/dist-mono/server.Dockerfile index 7304d9995..405f9e6ac 100644 --- a/docker/dist-mono/server.Dockerfile +++ b/docker/dist-mono/server.Dockerfile @@ -1,10 +1,5 @@ FROM node:20-slim AS frontend-build -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* - WORKDIR /app/client COPY client/package*.json ./ @@ -15,6 +10,11 @@ RUN npm run build FROM node:20-slim AS app +# Install ping +RUN apt-get update \ + && apt-get install -y iputils-ping \ + && rm -rf /var/lib/apt/lists/* + WORKDIR /app/server COPY server ./ From a015cc67060501751cac851c280d632b56d16e03 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 8 Sep 2025 20:29:38 -0700 Subject: [PATCH 5/5] fix dist-arm, bump version --- client/vite.config.js | 4 ++-- docker/dist-arm/server.Dockerfile | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/vite.config.js b/client/vite.config.js index a90b67462..a6b7152f1 100644 --- a/client/vite.config.js +++ b/client/vite.config.js @@ -5,12 +5,12 @@ import { execSync } from "child_process"; export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ""); - let version = "3.1.7"; + let version = "3.1.8"; return { base: "/", plugins: [svgr(), react()], - optimizeDeps: { + optimizeDeps: { Maybe this will be the straw th include: ["@mui/material/Tooltip", "@emotion/styled"], }, define: { diff --git a/docker/dist-arm/server.Dockerfile b/docker/dist-arm/server.Dockerfile index 3a4aa755d..e47db9a74 100644 --- a/docker/dist-arm/server.Dockerfile +++ b/docker/dist-arm/server.Dockerfile @@ -3,10 +3,7 @@ # --------------------- FROM node:24-slim AS frontend-build -# Install ping -RUN apt-get update \ - && apt-get install -y iputils-ping \ - && rm -rf /var/lib/apt/lists/* + WORKDIR /app/client @@ -25,6 +22,11 @@ RUN npm run build # --------------------- FROM node:24-slim AS backend +# Install ping +RUN apt-get update \ + && apt-get install -y iputils-ping \ + && rm -rf /var/lib/apt/lists/* + WORKDIR /app/server COPY server/package.json ./