From a93edc11316d9cf98de5919db6e4c5b69d2c68dc Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 09:16:07 +0800 Subject: [PATCH 01/13] remove redux from action menu --- client/src/Components/ActionsMenu/index.jsx | 40 +++++++++------------ client/src/Hooks/useFetchSettings.js | 3 +- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/client/src/Components/ActionsMenu/index.jsx b/client/src/Components/ActionsMenu/index.jsx index 4d5ab7c01..67dd3356b 100644 --- a/client/src/Components/ActionsMenu/index.jsx +++ b/client/src/Components/ActionsMenu/index.jsx @@ -1,17 +1,22 @@ +// Components +import IconButton from "@mui/material/IconButton"; +import Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import Settings from "../../assets/icons/settings-bold.svg?react"; +import Dialog from "../../Components/Dialog"; + +// Utils 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 { IconButton, Menu, MenuItem } from "@mui/material"; import { deleteUptimeMonitor, pauseUptimeMonitor, } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; -import Settings from "../../assets/icons/settings-bold.svg?react"; import PropTypes from "prop-types"; -import Dialog from "../../Components/Dialog"; +import { usePauseMonitor, useDeleteMonitor } from "../../Hooks/monitorHooks"; const ActionsMenu = ({ monitor, @@ -23,37 +28,24 @@ const ActionsMenu = ({ const [anchorEl, setAnchorEl] = useState(null); const [actions, setActions] = useState({}); const [isOpen, setIsOpen] = useState(false); - const dispatch = useDispatch(); const theme = useTheme(); - const { isLoading } = useSelector((state) => state.uptimeMonitors); + const [pauseMonitor, isPausing, error] = usePauseMonitor(); + const [deleteMonitor, isDeleting] = useDeleteMonitor(); const handleRemove = async (event) => { event.preventDefault(); event.stopPropagation(); let monitor = { _id: actions.id }; - const action = await dispatch(deleteUptimeMonitor({ monitor })); - if (action.meta.requestStatus === "fulfilled") { - setIsOpen(false); // close modal - updateRowCallback(); - createToast({ body: "Monitor deleted successfully." }); - } else { - createToast({ body: "Failed to delete monitor." }); - } + await deleteMonitor({ monitor }); + updateRowCallback(); }; const handlePause = async () => { try { setIsLoading(true); - const action = await dispatch(pauseUptimeMonitor({ monitorId: monitor._id })); - if (pauseUptimeMonitor.fulfilled.match(action)) { - const state = action?.payload?.data.isActive === false ? "resumed" : "paused"; - createToast({ body: `Monitor ${state} successfully.` }); - pauseCallback(); - } else { - throw new Error(action?.error?.message ?? "Failed to pause monitor."); - } + await pauseMonitor({ monitorId: monitor._id }); + pauseCallback(); } catch (error) { - logger.error("Error pausing monitor:", monitor._id, error); createToast({ body: "Failed to pause monitor." }); } }; @@ -210,7 +202,7 @@ const ActionsMenu = ({ e.stopPropagation(); handleRemove(e); }} - isLoading={isLoading} + isLoading={isDeleting} modelTitle="modal-delete-monitor" modelDescription="delete-monitor-confirmation" /> diff --git a/client/src/Hooks/useFetchSettings.js b/client/src/Hooks/useFetchSettings.js index f7651e789..7d3ff28fc 100644 --- a/client/src/Hooks/useFetchSettings.js +++ b/client/src/Hooks/useFetchSettings.js @@ -33,12 +33,13 @@ const useSaveSettings = () => { const saveSettings = async (settings) => { setIsLoading(true); try { - await networkService.updateAppSettings({ settings }); + const settingsResponse = await networkService.updateAppSettings({ settings }); if (settings.checkTTL) { await networkService.updateChecksTTL({ ttl: settings.checkTTL, }); } + console.log({ settingsResponse }); createToast({ body: t("settingsSuccessSaved") }); } catch (error) { createToast({ body: t("settingsFailedToSave") }); From 9f204b36b07fb7c19de6ba77762f0cdec7cfe535 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 09:18:45 +0800 Subject: [PATCH 02/13] remove unused redux state from sidebar --- client/src/Components/Sidebar/index.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/src/Components/Sidebar/index.jsx b/client/src/Components/Sidebar/index.jsx index f487148f5..06a140c54 100644 --- a/client/src/Components/Sidebar/index.jsx +++ b/client/src/Components/Sidebar/index.jsx @@ -49,7 +49,6 @@ import { useDispatch, useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { clearAuthState } from "../../Features/Auth/authSlice"; import { toggleSidebar } from "../../Features/UI/uiSlice"; -import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; const getMenu = (t) => [ { name: t("menu.uptime"), path: "uptime", icon: }, @@ -197,7 +196,6 @@ function Sidebar() { const logout = async () => { // Clear auth state dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); navigate("/login"); }; From 8d512b47197d8f36fa594c551c1fb495b178834d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 09:27:29 +0800 Subject: [PATCH 03/13] remove redux --- client/src/Pages/Auth/NewPasswordConfirmed.jsx | 2 -- client/src/Utils/NetworkService.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/client/src/Pages/Auth/NewPasswordConfirmed.jsx b/client/src/Pages/Auth/NewPasswordConfirmed.jsx index db03f7de3..945a0d52d 100644 --- a/client/src/Pages/Auth/NewPasswordConfirmed.jsx +++ b/client/src/Pages/Auth/NewPasswordConfirmed.jsx @@ -3,7 +3,6 @@ import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router"; import { useDispatch } from "react-redux"; import { clearAuthState } from "../../Features/Auth/authSlice"; -import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; import Background from "../../assets/Images/background-grid.svg?react"; import ConfirmIcon from "../../assets/icons/check-outlined.svg?react"; import Logo from "../../assets/icons/checkmate-icon.svg?react"; @@ -19,7 +18,6 @@ const NewPasswordConfirmed = () => { const handleNavigate = () => { dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); navigate("/login"); }; diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index 6c38688a7..f25828c66 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -3,7 +3,6 @@ import i18next from "i18next"; const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL; const FALLBACK_BASE_URL = "http://localhost:5000/api/v1"; import { clearAuthState } from "../Features/Auth/authSlice"; -import { clearUptimeMonitorState } from "../Features/UptimeMonitors/uptimeMonitorsSlice"; class NetworkService { constructor(store, dispatch, navigate) { this.store = store; @@ -56,7 +55,6 @@ class NetworkService { if (error.response && error.response.status === 401) { dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); navigate("/login"); } else if (error.request && !error.response) { return Promise.reject(error); From fca39f191498f3ac37c51ca28b4149cc19ce8ae0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 09:51:26 +0800 Subject: [PATCH 04/13] remove redux from settings --- client/src/Hooks/monitorHooks.js | 56 +++++++++++++++++++++++ client/src/Hooks/useDeleteMonitorStats.js | 24 ---------- client/src/Pages/Settings/index.jsx | 50 ++++++++------------ 3 files changed, 74 insertions(+), 56 deletions(-) delete mode 100644 client/src/Hooks/useDeleteMonitorStats.js diff --git a/client/src/Hooks/monitorHooks.js b/client/src/Hooks/monitorHooks.js index 71f54de59..e5c405ac1 100644 --- a/client/src/Hooks/monitorHooks.js +++ b/client/src/Hooks/monitorHooks.js @@ -4,6 +4,7 @@ import { createToast } from "../Utils/toastUtils"; import { useTheme } from "@emotion/react"; import { useMonitorUtils } from "./useMonitorUtils"; import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; const useFetchMonitorsWithSummary = ({ teamId, types, monitorUpdateTrigger }) => { const [isLoading, setIsLoading] = useState(false); @@ -383,6 +384,58 @@ const usePauseMonitor = () => { return [pauseMonitor, isLoading, error]; }; +const useAddDemoMonitors = () => { + const [isLoading, setIsLoading] = useState(false); + const { t } = useTranslation(); + const addDemoMonitors = async () => { + try { + setIsLoading(true); + await networkService.addDemoMonitors(); + createToast({ body: t("settingsDemoMonitorsAdded") }); + } catch (error) { + createToast({ body: t("settingsFailedToAddDemoMonitors") }); + } finally { + setIsLoading(false); + } + }; + return [addDemoMonitors, isLoading]; +}; + +const useDeleteAllMonitors = () => { + const [isLoading, setIsLoading] = useState(false); + const { t } = useTranslation(); + const deleteAllMonitors = async () => { + try { + setIsLoading(true); + await networkService.deleteAllMonitors(); + createToast({ body: t("settingsMonitorsDeleted") }); + } catch (error) { + createToast({ body: t("settingsFailedToDeleteMonitors") }); + } finally { + setIsLoading(false); + } + }; + return [deleteAllMonitors, isLoading]; +}; + +const UseDeleteMonitorStats = () => { + const { t } = useTranslation(); + const [isLoading, setIsLoading] = useState(false); + const deleteMonitorStats = async ({ teamId }) => { + setIsLoading(true); + try { + const res = await networkService.deleteChecksByTeamId({ teamId }); + createToast({ body: t("settingsStatsCleared") }); + } catch (error) { + createToast({ body: t("settingsFailedToClearStats") }); + } finally { + setIsLoading(false); + } + }; + + return [deleteMonitorStats, isLoading]; +}; + export { useFetchMonitorsWithSummary, useFetchMonitorsWithChecks, @@ -395,4 +448,7 @@ export { useDeleteMonitor, useUpdateMonitor, usePauseMonitor, + useAddDemoMonitors, + useDeleteAllMonitors, + UseDeleteMonitorStats, }; diff --git a/client/src/Hooks/useDeleteMonitorStats.js b/client/src/Hooks/useDeleteMonitorStats.js deleted file mode 100644 index 64138929f..000000000 --- a/client/src/Hooks/useDeleteMonitorStats.js +++ /dev/null @@ -1,24 +0,0 @@ -import { useState } from "react"; -import { networkService } from "../main"; -import { createToast } from "../Utils/toastUtils"; -import { useTranslation } from "react-i18next"; - -const UseDeleteMonitorStats = () => { - const { t } = useTranslation(); - const [isLoading, setIsLoading] = useState(false); - const deleteMonitorStats = async ({ teamId }) => { - setIsLoading(true); - try { - const res = await networkService.deleteChecksByTeamId({ teamId }); - createToast({ body: t("settingsStatsCleared") }); - } catch (error) { - createToast({ body: t("settingsFailedToClearStats") }); - } finally { - setIsLoading(false); - } - }; - - return [deleteMonitorStats, isLoading]; -}; - -export { UseDeleteMonitorStats }; diff --git a/client/src/Pages/Settings/index.jsx b/client/src/Pages/Settings/index.jsx index b0e3c476b..d8bd79551 100644 --- a/client/src/Pages/Settings/index.jsx +++ b/client/src/Pages/Settings/index.jsx @@ -11,22 +11,20 @@ import SettingsEmail from "./SettingsEmail"; import Button from "@mui/material/Button"; // Utils import { settingsValidation } from "../../Validation/validation"; -import { createToast } from "../../Utils/toastUtils"; import { useState } from "react"; import { useTheme } from "@emotion/react"; import { useTranslation } from "react-i18next"; import { useSelector, useDispatch } from "react-redux"; import { setTimezone, setMode, setLanguage, setShowURL } from "../../Features/UI/uiSlice"; import SettingsStats from "./SettingsStats"; -import { - deleteMonitorChecksByTeamId, - addDemoMonitors, - deleteAllMonitors, -} from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; -import { useFetchSettings, useSaveSettings } from "../../Hooks/useFetchSettings"; -import { UseDeleteMonitorStats } from "../../Hooks/useDeleteMonitorStats"; -import { useIsAdmin } from "../../Hooks/useIsAdmin"; +import { useFetchSettings, useSaveSettings } from "../../Hooks/useFetchSettings"; +import { useIsAdmin } from "../../Hooks/useIsAdmin"; +import { + useAddDemoMonitors, + useDeleteAllMonitors, + UseDeleteMonitorStats, +} from "../../Hooks/monitorHooks"; // Constants const BREADCRUMBS = [{ name: `Settings`, path: "/settings" }]; @@ -43,15 +41,17 @@ const Settings = () => { const [isSettingsLoading, settingsError] = useFetchSettings({ setSettingsData, }); + const [addDemoMonitors, isAddingDemoMonitors] = useAddDemoMonitors(); const [isSaving, saveError, saveSettings] = useSaveSettings(); + const [deleteAllMonitors, isDeletingMonitors] = useDeleteAllMonitors(); const [deleteMonitorStats, isDeletingMonitorStats] = UseDeleteMonitorStats(); // Setup const isAdmin = useIsAdmin(); const theme = useTheme(); const HEADING_SX = { mt: theme.spacing(2), mb: theme.spacing(2) }; - const { t, i18n } = useTranslation(); + const { t } = useTranslation(); const dispatch = useDispatch(); // Handlers @@ -101,30 +101,12 @@ const Settings = () => { } if (name === "demo") { - try { - const action = await dispatch(addDemoMonitors()); - if (addDemoMonitors.fulfilled.match(action)) { - createToast({ body: t("settingsDemoMonitorsAdded") }); - } else { - createToast({ body: t("settingsFailedToAddDemoMonitors") }); - } - } catch (error) { - createToast({ body: t("settingsFailedToAddDemoMonitors") }); - } + await addDemoMonitors(); return; } if (name === "deleteMonitors") { - try { - const action = await dispatch(deleteAllMonitors()); - if (deleteAllMonitors.fulfilled.match(action)) { - createToast({ body: t("settingsMonitorsDeleted") }); - } else { - createToast({ body: t("settingsFailedToDeleteMonitors") }); - } - } catch (error) { - createToast({ body: t("settingsFailedToDeleteMonitors") }); - } + await deleteAllMonitors(); return; } @@ -185,7 +167,9 @@ const Settings = () => { isAdmin={isAdmin} HEADER_SX={HEADING_SX} handleChange={handleChange} - isLoading={isSettingsLoading || isSaving || isDeletingMonitorStats} + isLoading={ + isSettingsLoading || isSaving || isDeletingMonitorStats || isAddingDemoMonitors + } /> { }} >