diff --git a/client/src/App.jsx b/client/src/App.jsx index 7fb63ee61..b599890f1 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -10,6 +10,7 @@ import { logger } from "./Utils/Logger"; // Import the logger import { networkService } from "./main"; import { Routes } from "./Routes"; import WalletProvider from "./Components/WalletProvider"; +import AppLayout from "./Components/Layouts/AppLayout"; function App() { const mode = useSelector((state) => state.ui.mode); @@ -27,17 +28,10 @@ function App() { - { - return { - body: { - backgroundImage: `radial-gradient(circle, ${palette.gradient.color1}, ${palette.gradient.color2}, ${palette.gradient.color3}, ${palette.gradient.color4}, ${palette.gradient.color5})`, - color: palette.primary.contrastText, - }, - }; - }} - /> - + + + + diff --git a/client/src/Components/ActionsMenu/index.jsx b/client/src/Components/ActionsMenu/index.jsx index 4d5ab7c01..1b590a471 100644 --- a/client/src/Components/ActionsMenu/index.jsx +++ b/client/src/Components/ActionsMenu/index.jsx @@ -1,17 +1,18 @@ +// 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,38 +24,27 @@ 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." }); + } finally { + setIsLoading(false); } }; @@ -210,7 +200,7 @@ const ActionsMenu = ({ e.stopPropagation(); handleRemove(e); }} - isLoading={isLoading} + isLoading={isDeleting} modelTitle="modal-delete-monitor" modelDescription="delete-monitor-confirmation" /> diff --git a/client/src/Components/Dialog/index.jsx b/client/src/Components/Dialog/index.jsx index e07597df4..678eb8721 100644 --- a/client/src/Components/Dialog/index.jsx +++ b/client/src/Components/Dialog/index.jsx @@ -57,7 +57,7 @@ Dialog.propTypes = { onCancel: PropTypes.func.isRequired, confirmationButtonLabel: PropTypes.string.isRequired, onConfirm: PropTypes.func.isRequired, - isLoading: PropTypes.bool.isRequired, + isLoading: PropTypes.bool, }; export default Dialog; diff --git a/client/src/Components/Fallback/index.jsx b/client/src/Components/Fallback/index.jsx index b00ddbecc..47497ba02 100644 --- a/client/src/Components/Fallback/index.jsx +++ b/client/src/Components/Fallback/index.jsx @@ -11,7 +11,7 @@ import { useSelector } from "react-redux"; import Alert from "../Alert"; import { useTranslation } from "react-i18next"; import "./index.css"; -import { useFetchSettings } from "../../Hooks/useFetchSettings"; +import { useFetchSettings } from "../../Hooks/settingsHooks"; import { useState } from "react"; /** * Fallback component to display a fallback UI with a title, a list of checks, and a navigation button. diff --git a/client/src/Components/Inputs/Checkbox/index.jsx b/client/src/Components/Inputs/Checkbox/index.jsx index 58b83c7c7..1497c44fb 100644 --- a/client/src/Components/Inputs/Checkbox/index.jsx +++ b/client/src/Components/Inputs/Checkbox/index.jsx @@ -105,7 +105,7 @@ const Checkbox = ({ }; Checkbox.propTypes = { - id: PropTypes.string.isRequired, + id: PropTypes.string, name: PropTypes.string, label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, size: PropTypes.oneOf(["small", "medium", "large"]), diff --git a/client/src/Components/Inputs/Search/index.jsx b/client/src/Components/Inputs/Search/index.jsx index 86ce57779..e5654faeb 100644 --- a/client/src/Components/Inputs/Search/index.jsx +++ b/client/src/Components/Inputs/Search/index.jsx @@ -102,27 +102,7 @@ const Search = ({ ...(endAdornment && { endAdornment: endAdornment }), }, }} - sx={{ - "& fieldset": { - borderColor: theme.palette.primary.lowContrast, - borderRadius: theme.shape.borderRadius, - }, - "& .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset": - { - borderColor: theme.palette.primary.lowContrast, - }, - "& .MuiOutlinedInput-root": { - paddingY: 0, - }, - "& .MuiAutocomplete-tag": { - // CAIO_REVIEW - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.primary.lowContrast, - }, - "& .MuiChip-deleteIcon": { - color: theme.palette.primary.contrastText, // CAIO_REVIEW - }, - }} + sx={{}} /> {error && ( { + const theme = useTheme(); + + return ( + + {children} + + ); +}; + +AppLayout.propTypes = { + children: PropTypes.node, +}; + +export default AppLayout; diff --git a/client/src/Components/MonitorDetailsControlHeader/index.jsx b/client/src/Components/MonitorDetailsControlHeader/index.jsx index aa9ef91b9..8c9f845e9 100644 --- a/client/src/Components/MonitorDetailsControlHeader/index.jsx +++ b/client/src/Components/MonitorDetailsControlHeader/index.jsx @@ -11,10 +11,9 @@ import EmailIcon from "@mui/icons-material/Email"; import PropTypes from "prop-types"; import { useNavigate } from "react-router-dom"; import { useTheme } from "@mui/material/styles"; -import { usePauseMonitor } from "../../Hooks/useMonitorControls"; +import { usePauseMonitor } from "../../Hooks/monitorHooks"; import { useSendTestEmail } from "../../Hooks/useSendTestEmail"; import { useTranslation } from "react-i18next"; - /** * MonitorDetailsControlHeader component displays the control header for monitor details. * It includes status display, pause/resume button, and a configure button for admins. @@ -38,10 +37,7 @@ const MonitorDetailsControlHeader = ({ const navigate = useNavigate(); const theme = useTheme(); const { t } = useTranslation(); - const [pauseMonitor, isPausing, error] = usePauseMonitor({ - monitorId: monitor?._id, - triggerUpdate, - }); + const [pauseMonitor, isPausing, error] = usePauseMonitor(); const [isSending, emailError, sendTestEmail] = useSendTestEmail(); @@ -88,7 +84,10 @@ const MonitorDetailsControlHeader = ({ monitor?.isActive ? : } onClick={() => { - pauseMonitor(); + pauseMonitor({ + monitorId: monitor?._id, + triggerUpdate, + }); }} > {monitor?.isActive ? "Pause" : "Resume"} diff --git a/client/src/Components/MonitorDetailsControlHeader/status.jsx b/client/src/Components/MonitorDetailsControlHeader/status.jsx index 3d6e62c60..fdfb955f4 100644 --- a/client/src/Components/MonitorDetailsControlHeader/status.jsx +++ b/client/src/Components/MonitorDetailsControlHeader/status.jsx @@ -7,8 +7,7 @@ import Dot from "../../Components/Dot"; import { formatDurationRounded } from "../../Utils/timeUtils"; import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; -import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; - +import { useMonitorUtils } from "../../Hooks/useMonitorUtils"; /** * Status component displays the status information of a monitor. * It includes the monitor's name, URL, and check interval. @@ -23,7 +22,7 @@ import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; */ const Status = ({ monitor }) => { const theme = useTheme(); - const { statusColor, determineState } = useUtils(); + const { statusColor, determineState } = useMonitorUtils(); return ( diff --git a/client/src/Components/MonitorStatusHeader/index.jsx b/client/src/Components/MonitorStatusHeader/index.jsx index 7ea95410b..a81131152 100644 --- a/client/src/Components/MonitorStatusHeader/index.jsx +++ b/client/src/Components/MonitorStatusHeader/index.jsx @@ -2,7 +2,7 @@ import { Stack, Typography } from "@mui/material"; import PulseDot from "../Animated/PulseDot"; import Dot from "../Dot"; import { useTheme } from "@emotion/react"; -import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../Hooks/useMonitorUtils"; import { formatDurationRounded } from "../../Utils/timeUtils"; import ConfigButton from "./ConfigButton"; import SkeletonLayout from "./skeleton"; @@ -12,7 +12,7 @@ import { useTranslation } from "react-i18next"; const MonitorStatusHeader = ({ path, isLoading = false, isAdmin, monitor }) => { const theme = useTheme(); const { t } = useTranslation(); - const { statusColor, determineState } = useUtils(); + const { statusColor, determineState } = useMonitorUtils(); if (isLoading) { return ; } diff --git a/client/src/Components/Sidebar/index.jsx b/client/src/Components/Sidebar/index.jsx index 6be67a43d..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"); }; @@ -231,7 +229,6 @@ function Sidebar() { borderRight: `1px solid ${theme.palette.primary.lowContrast}`, borderColor: theme.palette.primary.lowContrast, borderRadius: 0, - backgroundColor: theme.palette.primary.main, "& :is(p, span, .MuiListSubheader-root)": { /* Text color for unselected menu items and menu headings diff --git a/client/src/Components/StarPrompt/index.jsx b/client/src/Components/StarPrompt/index.jsx index 8bf434085..64fce1d3f 100644 --- a/client/src/Components/StarPrompt/index.jsx +++ b/client/src/Components/StarPrompt/index.jsx @@ -33,7 +33,6 @@ const StarPrompt = ({ repoUrl = "https://github.com/bluewave-labs/checkmate" }) borderBottom: `1px solid ${theme.palette.primary.lowContrast}`, borderRadius: 0, gap: theme.spacing(1.5), - backgroundColor: theme.palette.primary.main, }} > { const theme = useTheme(); - const { statusToTheme } = useUtils(); + const { statusToTheme } = useMonitorUtils(); const themeColor = statusToTheme[status]; const statusBoxStyles = gradient @@ -136,8 +136,8 @@ const StatBox = ({ }; StatBox.propTypes = { - heading: PropTypes.string.isRequired, - subHeading: PropTypes.node.isRequired, + heading: PropTypes.string, + subHeading: PropTypes.node, gradient: PropTypes.bool, status: PropTypes.string, sx: PropTypes.object, diff --git a/client/src/Components/TabPanels/Account/ProfilePanel.jsx b/client/src/Components/TabPanels/Account/ProfilePanel.jsx index ad6d15b98..9b209bf3e 100644 --- a/client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -8,7 +8,6 @@ import ImageUpload from "../../Inputs/ImageUpload"; import { newOrChangedCredentials } from "../../../Validation/validation"; import { useDispatch, useSelector } from "react-redux"; import { clearAuthState, deleteUser, update } from "../../../Features/Auth/authSlice"; -import { clearUptimeMonitorState } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; import { createToast } from "../../../Utils/toastUtils"; import { logger } from "../../../Utils/Logger"; import { GenericDialog } from "../../Dialog/genericDialog"; @@ -162,7 +161,6 @@ const ProfilePanel = () => { const action = await dispatch(deleteUser()); if (action.payload.success) { dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); } else { if (action.payload) { // dispatch errors diff --git a/client/src/Pages/Auth/components/TextLink.jsx b/client/src/Components/TextLink/index.jsx similarity index 100% rename from client/src/Pages/Auth/components/TextLink.jsx rename to client/src/Components/TextLink/index.jsx diff --git a/client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js b/client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js deleted file mode 100644 index 62bdf4811..000000000 --- a/client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js +++ /dev/null @@ -1,389 +0,0 @@ -import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { networkService } from "../../main"; -const initialState = { - isLoading: false, - monitorsSummary: [], - success: null, - msg: null, -}; - -export const createInfrastructureMonitor = createAsyncThunk( - "infrastructureMonitors/createMonitor", - async (data, thunkApi) => { - try { - const { monitor } = data; - const res = await networkService.createMonitor({ monitor: monitor }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const checkInfrastructureEndpointResolution = createAsyncThunk( - "infrastructureMonitors/CheckEndpoint", - async (data, thunkApi) => { - try { - const { monitorURL } = data; - const res = await networkService.checkEndpointResolution({ - monitorURL: monitorURL, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const getInfrastructureMonitorById = createAsyncThunk( - "infrastructureMonitors/getMonitorById", - async (data, thunkApi) => { - try { - const { monitorId } = data; - const res = await networkService.getMonitorById({ monitorId: monitorId }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const getInfrastructureMonitorsByTeamId = createAsyncThunk( - "infrastructureMonitors/getMonitorsByTeamId", - async (_, thunkApi) => { - const user = thunkApi.getState().auth.user; - try { - const res = await networkService.getMonitorsAndSummaryByTeamId({ - teamId: user.teamId, - types: ["hardware"], - limit: 1, - rowsPerPage: 0, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const updateInfrastructureMonitor = createAsyncThunk( - "infrastructureMonitors/updateMonitor", - async ({ monitorId, monitor }, thunkApi) => { - try { - const updatedFields = { - name: monitor.name, - description: monitor.description, - interval: monitor.interval, - notifications: monitor.notifications, - thresholds: monitor.thresholds, - secret: monitor.secret, - }; - const res = await networkService.updateMonitor({ - monitorId, - monitor, - updatedFields, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const deleteInfrastructureMonitor = createAsyncThunk( - "infrastructureMonitors/deleteMonitor", - async (data, thunkApi) => { - try { - const { monitor } = data; - const res = await networkService.deleteMonitorById({ monitorId: monitor._id }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const pauseInfrastructureMonitor = createAsyncThunk( - "infrastructureMonitors/pauseMonitor", - async (data, thunkApi) => { - try { - const { monitorId } = data; - const res = await networkService.pauseMonitorById({ monitorId: monitorId }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const deleteInfrastructureMonitorChecksByTeamId = createAsyncThunk( - "infrastructureMonitors/deleteChecksByTeamId", - async (data, thunkApi) => { - try { - const { teamId } = data; - const res = await networkService.deleteChecksByTeamId({ teamId: teamId }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const deleteAllInfrastructureMonitors = createAsyncThunk( - "infrastructureMonitors/deleteAllMonitors", - async (data, thunkApi) => { - try { - const res = await networkService.deleteAllMonitors(); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -const infrastructureMonitorsSlice = createSlice({ - name: "infrastructureMonitors", - initialState, - reducers: { - clearInfrastructureMonitorState: (state) => { - state.isLoading = false; - state.monitorsSummary = []; - state.success = null; - state.msg = null; - }, - }, - extraReducers: (builder) => { - builder - // ***************************************************** - // Monitors by teamId - // ***************************************************** - - .addCase(getInfrastructureMonitorsByTeamId.pending, (state) => { - state.isLoading = true; - }) - .addCase(getInfrastructureMonitorsByTeamId.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.msg; - state.monitorsSummary = action.payload.data; - }) - .addCase(getInfrastructureMonitorsByTeamId.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Getting infrastructure monitors failed"; - }) - - // ***************************************************** - // Create Monitor - // ***************************************************** - .addCase(createInfrastructureMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(createInfrastructureMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(createInfrastructureMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to create infrastructure monitor"; - }) - // ***************************************************** - // Resolve Endpoint - // ***************************************************** - .addCase(checkInfrastructureEndpointResolution.pending, (state) => { - state.isLoading = true; - }) - .addCase(checkInfrastructureEndpointResolution.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(checkInfrastructureEndpointResolution.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to check endpoint resolution"; - }) - // ***************************************************** - // Get Monitor By Id - // ***************************************************** - .addCase(getInfrastructureMonitorById.pending, (state) => { - state.isLoading = true; - }) - .addCase(getInfrastructureMonitorById.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(getInfrastructureMonitorById.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to get infrastructure monitor"; - }) - // ***************************************************** - // update Monitor - // ***************************************************** - .addCase(updateInfrastructureMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(updateInfrastructureMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(updateInfrastructureMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to update infrastructure monitor"; - }) - - // ***************************************************** - // Delete Monitor - // ***************************************************** - .addCase(deleteInfrastructureMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteInfrastructureMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteInfrastructureMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete infrastructure monitor"; - }) - // ***************************************************** - // Delete Monitor checks by Team ID - // ***************************************************** - .addCase(deleteInfrastructureMonitorChecksByTeamId.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteInfrastructureMonitorChecksByTeamId.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteInfrastructureMonitorChecksByTeamId.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete monitor checks"; - }) - // ***************************************************** - // Pause Monitor - // ***************************************************** - .addCase(pauseInfrastructureMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(pauseInfrastructureMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(pauseInfrastructureMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to pause infrastructure monitor"; - }) - // ***************************************************** - // Delete all Monitors - // ***************************************************** - .addCase(deleteAllInfrastructureMonitors.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteAllInfrastructureMonitors.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteAllInfrastructureMonitors.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload ? action.payload.msg : "Failed to delete all monitors"; - }); - }, -}); - -export const { setInfrastructureMonitors, clearInfrastructureMonitorState } = - infrastructureMonitorsSlice.actions; - -export default infrastructureMonitorsSlice.reducer; diff --git a/client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js b/client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js deleted file mode 100644 index 3ed2da13f..000000000 --- a/client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js +++ /dev/null @@ -1,309 +0,0 @@ -import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { networkService } from "../../main"; -const initialState = { - isLoading: false, - monitorsSummary: [], - success: null, - msg: null, -}; - -export const createPageSpeed = createAsyncThunk( - "pageSpeedMonitors/createPageSpeed", - async (data, thunkApi) => { - try { - const { monitor } = data; - const res = await networkService.createMonitor({ monitor: monitor }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const checkEndpointResolution = createAsyncThunk( - "monitors/checkEndpoint", - async (data, thunkApi) => { - try { - const { monitorURL } = data; - const res = await networkService.checkEndpointResolution({ - monitorURL: monitorURL, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const getPagespeedMonitorById = createAsyncThunk( - "monitors/getMonitorById", - async (data, thunkApi) => { - try { - const { monitorId } = data; - const res = await networkService.getMonitorById({ monitorId: monitorId }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const getPageSpeedByTeamId = createAsyncThunk( - "pageSpeedMonitors/getPageSpeedByTeamId", - async (_, thunkApi) => { - const user = thunkApi.getState().auth.user; - try { - const res = await networkService.getMonitorsAndSummaryByTeamId({ - teamId: user.teamId, - types: ["pagespeed"], - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const updatePageSpeed = createAsyncThunk( - "pageSpeedMonitors/updatePageSpeed", - async (data, thunkApi) => { - try { - const { monitor } = data; - const updatedFields = { - name: monitor.name, - description: monitor.description, - interval: monitor.interval, - notifications: monitor.notifications, - }; - const res = await networkService.updateMonitor({ - monitorId: monitor._id, - updatedFields: updatedFields, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const deletePageSpeed = createAsyncThunk( - "pageSpeedMonitors/deletePageSpeed", - async (data, thunkApi) => { - try { - const { monitor } = data; - const res = await networkService.deleteMonitorById({ monitorId: monitor._id }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); -export const pausePageSpeed = createAsyncThunk( - "pageSpeedMonitors/pausePageSpeed", - async (data, thunkApi) => { - try { - const { monitorId } = data; - const res = await networkService.pauseMonitorById({ monitorId: monitorId }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -const pageSpeedMonitorSlice = createSlice({ - name: "pageSpeedMonitor", - initialState, - reducers: { - clearMonitorState: (state) => { - state.isLoading = false; - state.monitorsSummary = []; - state.success = null; - state.msg = null; - }, - }, - extraReducers: (builder) => { - builder - // ***************************************************** - // Monitors by teamId - // ***************************************************** - - .addCase(getPageSpeedByTeamId.pending, (state) => { - state.isLoading = true; - }) - .addCase(getPageSpeedByTeamId.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.msg; - state.monitorsSummary = action.payload.data; - }) - .addCase(getPageSpeedByTeamId.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Getting page speed monitors failed"; - }) - - // ***************************************************** - .addCase(getPagespeedMonitorById.pending, (state) => { - state.isLoading = true; - }) - .addCase(getPagespeedMonitorById.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(getPagespeedMonitorById.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to get pagespeed monitor"; - }) - - // ***************************************************** - // Create Monitor - // ***************************************************** - .addCase(createPageSpeed.pending, (state) => { - state.isLoading = true; - }) - .addCase(createPageSpeed.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(createPageSpeed.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to create page speed monitor"; - }) - // ***************************************************** - // Resolve Endpoint - // ***************************************************** - .addCase(checkEndpointResolution.pending, (state) => { - state.isLoading = true; - }) - .addCase(checkEndpointResolution.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(checkEndpointResolution.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to check endpoint resolution"; - }) - // ***************************************************** - // Update Monitor - // ***************************************************** - .addCase(updatePageSpeed.pending, (state) => { - state.isLoading = true; - }) - .addCase(updatePageSpeed.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(updatePageSpeed.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to update page speed monitor"; - }) - - // ***************************************************** - // Delete Monitor - // ***************************************************** - .addCase(deletePageSpeed.pending, (state) => { - state.isLoading = true; - }) - .addCase(deletePageSpeed.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deletePageSpeed.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete page speed monitor"; - }) - // ***************************************************** - // Pause Monitor - // ***************************************************** - .addCase(pausePageSpeed.pending, (state) => { - state.isLoading = true; - }) - .addCase(pausePageSpeed.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(pausePageSpeed.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to pause page speed monitor"; - }); - }, -}); - -export const { setMonitors, clearMonitorState } = pageSpeedMonitorSlice.actions; - -export default pageSpeedMonitorSlice.reducer; diff --git a/client/src/Features/Settings/settingsSlice.js b/client/src/Features/Settings/settingsSlice.js deleted file mode 100644 index d0725627a..000000000 --- a/client/src/Features/Settings/settingsSlice.js +++ /dev/null @@ -1,100 +0,0 @@ -import { networkService } from "../../main"; -import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; - -const initialState = { - isLoading: false, - apiBaseUrl: "", - logLevel: "debug", - pagespeedApiKey: "", -}; - -export const getAppSettings = createAsyncThunk( - "settings/getSettings", - async (data, thunkApi) => { - try { - const res = await networkService.getAppSettings(); - return res.data; - } catch (error) { - if (error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const updateAppSettings = createAsyncThunk( - "settings/updateSettings", - async ({ settings }, thunkApi) => { - try { - const parsedSettings = { - language: settings.language, - pagespeedApiKey: settings.pagespeedApiKey, - }; - const res = await networkService.updateAppSettings({ settings: parsedSettings }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -const handleGetSettingsFulfilled = (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - state.apiBaseUrl = action.payload.data.apiBaseUrl; - state.logLevel = action.payload.data.logLevel; - state.language = action.payload.data.language; - state.pagespeedApiKey = action.payload.data.pagespeedApiKey; -}; -const handleGetSettingsRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload ? action.payload.msg : "Failed to get settings."; -}; -const handleUpdateSettingsFulfilled = (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - state.apiBaseUrl = action.payload.data.apiBaseUrl; - state.logLevel = action.payload.data.logLevel; -}; -const handleUpdateSettingsRejected = (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload ? action.payload.msg : "Failed to update settings."; -}; - -const settingsSlice = createSlice({ - name: "settings", - initialState, - extraReducers: (builder) => { - builder - .addCase(getAppSettings.pending, (state) => { - state.isLoading = true; - }) - .addCase(getAppSettings.fulfilled, handleGetSettingsFulfilled) - .addCase(getAppSettings.rejected, handleGetSettingsRejected); - - builder - .addCase(updateAppSettings.pending, (state) => { - state.isLoading = true; - }) - .addCase(updateAppSettings.fulfilled, handleUpdateSettingsFulfilled) - .addCase(updateAppSettings.rejected, handleUpdateSettingsRejected); - }, -}); - -export default settingsSlice.reducer; diff --git a/client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js b/client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js deleted file mode 100644 index 74ad803d3..000000000 --- a/client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js +++ /dev/null @@ -1,380 +0,0 @@ -import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; -import { networkService } from "../../main"; -const initialState = { - isLoading: false, - monitorsSummary: [], - success: null, - msg: null, -}; - -export const createUptimeMonitor = createAsyncThunk( - "monitors/createMonitor", - async (data, thunkApi) => { - try { - const { monitor } = data; - const res = await networkService.createMonitor({ monitor: monitor }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const checkEndpointResolution = createAsyncThunk( - "monitors/checkEndpoint", - async (data, thunkApi) => { - try { - const { monitorURL } = data; - const res = await networkService.checkEndpointResolution({ - monitorURL: monitorURL, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const getUptimeMonitorById = createAsyncThunk( - "monitors/getMonitorById", - async (data, thunkApi) => { - try { - const { monitorId } = data; - const res = await networkService.getMonitorById({ monitorId: monitorId }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const updateUptimeMonitor = createAsyncThunk( - "monitors/updateMonitor", - async (data, thunkApi) => { - try { - const { monitor } = data; - const updatedFields = { - name: monitor.name, - description: monitor.description, - interval: monitor.interval, - notifications: monitor.notifications, - matchMethod: monitor.matchMethod, - expectedValue: monitor.expectedValue, - ignoreTlsErrors: monitor.ignoreTlsErrors, - jsonPath: monitor.jsonPath, - ...(monitor.type === "port" && { port: monitor.port }), - }; - const res = await networkService.updateMonitor({ - monitorId: monitor._id, - updatedFields, - }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const deleteUptimeMonitor = createAsyncThunk( - "monitors/deleteMonitor", - async (data, thunkApi) => { - try { - const { monitor } = data; - const res = await networkService.deleteMonitorById({ monitorId: monitor._id }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const pauseUptimeMonitor = createAsyncThunk( - "monitors/pauseMonitor", - async (data, thunkApi) => { - try { - const { monitorId } = data; - const res = await networkService.pauseMonitorById({ monitorId: monitorId }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const deleteMonitorChecksByTeamId = createAsyncThunk( - "monitors/deleteChecksByTeamId", - async (data, thunkApi) => { - try { - const { teamId } = data; - const res = await networkService.deleteChecksByTeamId({ teamId: teamId }); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); -export const addDemoMonitors = createAsyncThunk( - "monitors/addDemoMonitors", - async (data, thunkApi) => { - try { - const res = await networkService.addDemoMonitors(); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -export const deleteAllMonitors = createAsyncThunk( - "monitors/deleteAllMonitors", - async (data, thunkApi) => { - try { - const res = await networkService.deleteAllMonitors(); - return res.data; - } catch (error) { - if (error.response && error.response.data) { - return thunkApi.rejectWithValue(error.response.data); - } - const payload = { - status: false, - msg: error.message ? error.message : "Unknown error", - }; - return thunkApi.rejectWithValue(payload); - } - } -); - -const uptimeMonitorsSlice = createSlice({ - name: "uptimeMonitors", - initialState, - reducers: { - clearUptimeMonitorState: (state) => { - state.isLoading = false; - state.monitorsSummary = []; - state.success = null; - state.msg = null; - }, - }, - extraReducers: (builder) => { - builder - // ***************************************************** - // Create Monitor - // ***************************************************** - .addCase(createUptimeMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(createUptimeMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(createUptimeMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to create uptime monitor"; - }) - // ***************************************************** - // Resolve Endpoint - // ***************************************************** - .addCase(checkEndpointResolution.pending, (state) => { - state.isLoading = true; - }) - .addCase(checkEndpointResolution.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(checkEndpointResolution.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to check endpoint resolution"; - }) - // ***************************************************** - // Get Monitor By Id - // ***************************************************** - .addCase(getUptimeMonitorById.pending, (state) => { - state.isLoading = true; - }) - .addCase(getUptimeMonitorById.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(getUptimeMonitorById.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload ? action.payload.msg : "Failed to get uptime monitor"; - }) - // ***************************************************** - // update Monitor - // ***************************************************** - .addCase(updateUptimeMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(updateUptimeMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(updateUptimeMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to update uptime monitor"; - }) - - // ***************************************************** - // Delete Monitor - // ***************************************************** - .addCase(deleteUptimeMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteUptimeMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteUptimeMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete uptime monitor"; - }) - // ***************************************************** - // Delete Monitor checks by Team ID - // ***************************************************** - .addCase(deleteMonitorChecksByTeamId.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteMonitorChecksByTeamId.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteMonitorChecksByTeamId.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to delete monitor checks"; - }) - // ***************************************************** - // Pause Monitor - // ***************************************************** - .addCase(pauseUptimeMonitor.pending, (state) => { - state.isLoading = true; - }) - .addCase(pauseUptimeMonitor.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(pauseUptimeMonitor.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to pause uptime monitor"; - }) - // ***************************************************** - // Add Demo Monitors - // ***************************************************** - .addCase(addDemoMonitors.pending, (state) => { - state.isLoading = true; - }) - .addCase(addDemoMonitors.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(addDemoMonitors.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload - ? action.payload.msg - : "Failed to add demo uptime monitors"; - }) - // ***************************************************** - // Delete all Monitors - // ***************************************************** - .addCase(deleteAllMonitors.pending, (state) => { - state.isLoading = true; - }) - .addCase(deleteAllMonitors.fulfilled, (state, action) => { - state.isLoading = false; - state.success = action.payload.success; - state.msg = action.payload.msg; - }) - .addCase(deleteAllMonitors.rejected, (state, action) => { - state.isLoading = false; - state.success = false; - state.msg = action.payload ? action.payload.msg : "Failed to delete all monitors"; - }); - }, -}); - -export const { setUptimeMonitors, clearUptimeMonitorState } = uptimeMonitorsSlice.actions; - -export default uptimeMonitorsSlice.reducer; diff --git a/client/src/Hooks/checkHooks.js b/client/src/Hooks/checkHooks.js new file mode 100644 index 000000000..dac2be671 --- /dev/null +++ b/client/src/Hooks/checkHooks.js @@ -0,0 +1,85 @@ +import { useState, useEffect } from "react"; +import { networkService } from "../main"; +import { createToast } from "../Utils/toastUtils"; + +const useFetchChecks = ({ + teamId, + monitorId, + type, + status, + sortOrder, + limit, + dateRange, + filter, + page, + rowsPerPage, +}) => { + const [checks, setChecks] = useState(undefined); + const [checksCount, setChecksCount] = useState(undefined); + const [isLoading, setIsLoading] = useState(false); + const [networkError, setNetworkError] = useState(false); + + useEffect(() => { + const fetchChecks = async () => { + if (!type && !teamId) { + return; + } + + const method = monitorId + ? networkService.getChecksByMonitor + : networkService.getChecksByTeam; + + const config = monitorId + ? { + monitorId, + type, + status, + sortOrder, + limit, + dateRange, + filter, + page, + rowsPerPage, + } + : { + status, + teamId, + sortOrder, + limit, + dateRange, + filter, + page, + rowsPerPage, + }; + + try { + setIsLoading(true); + const res = await method(config); + setChecks(res.data.data.checks); + setChecksCount(res.data.data.checksCount); + } catch (error) { + setNetworkError(true); + createToast({ body: error.message }); + } finally { + setIsLoading(false); + } + }; + + fetchChecks(); + }, [ + monitorId, + teamId, + type, + status, + sortOrder, + limit, + dateRange, + filter, + page, + rowsPerPage, + ]); + + return [checks, checksCount, isLoading, networkError]; +}; + +export { useFetchChecks }; diff --git a/client/src/Hooks/monitorHooks.js b/client/src/Hooks/monitorHooks.js new file mode 100644 index 000000000..d88132dae --- /dev/null +++ b/client/src/Hooks/monitorHooks.js @@ -0,0 +1,471 @@ +import { useEffect, useState } from "react"; +import { networkService } from "../main"; +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 = ({ types, monitorUpdateTrigger }) => { + const [isLoading, setIsLoading] = useState(false); + const [monitors, setMonitors] = useState(undefined); + const [monitorsSummary, setMonitorsSummary] = useState(undefined); + const [networkError, setNetworkError] = useState(false); + + useEffect(() => { + const fetchMonitors = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorsWithSummaryByTeamId({ + types, + }); + const { monitors, summary } = res?.data?.data ?? {}; + setMonitors(monitors); + setMonitorsSummary(summary); + } catch (error) { + console.error(error); + setNetworkError(true); + createToast({ + body: error.message, + }); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [types, monitorUpdateTrigger]); + return [monitors, monitorsSummary, isLoading, networkError]; +}; + +const useFetchMonitorsWithChecks = ({ + types, + limit, + page, + rowsPerPage, + filter, + field, + order, + monitorUpdateTrigger, +}) => { + const [isLoading, setIsLoading] = useState(false); + const [count, setCount] = useState(undefined); + const [monitors, setMonitors] = useState(undefined); + const [networkError, setNetworkError] = useState(false); + + const theme = useTheme(); + const { getMonitorWithPercentage } = useMonitorUtils(); + useEffect(() => { + const fetchMonitors = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorsWithChecksByTeamId({ + limit, + types, + page, + rowsPerPage, + filter, + field, + order, + }); + const { count, monitors } = res?.data?.data ?? {}; + const mappedMonitors = monitors.map((monitor) => + getMonitorWithPercentage(monitor, theme) + ); + setMonitors(mappedMonitors); + setCount(count?.monitorsCount ?? 0); + } catch (error) { + console.error(error); + setNetworkError(true); + createToast({ + body: error.message, + }); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [ + field, + filter, + getMonitorWithPercentage, + limit, + order, + page, + rowsPerPage, + theme, + types, + monitorUpdateTrigger, + ]); + return [monitors, count, isLoading, networkError]; +}; + +const useFetchMonitorsByTeamId = ({ + types, + limit, + page, + rowsPerPage, + filter, + field, + order, + checkOrder, + normalize, + status, + updateTrigger, +}) => { + const [isLoading, setIsLoading] = useState(false); + const [monitors, setMonitors] = useState(undefined); + const [summary, setSummary] = useState(undefined); + const [networkError, setNetworkError] = useState(false); + + useEffect(() => { + const fetchMonitors = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorsByTeamId({ + limit, + types, + page, + rowsPerPage, + filter, + field, + order, + checkOrder, + status, + normalize, + }); + if (res?.data?.data?.filteredMonitors) { + setMonitors(res.data.data.filteredMonitors); + setSummary(res.data.data.summary); + } + } catch (error) { + setNetworkError(true); + createToast({ + body: error.message, + }); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [ + types, + limit, + page, + rowsPerPage, + filter, + field, + order, + updateTrigger, + checkOrder, + normalize, + status, + ]); + return [monitors, summary, isLoading, networkError]; +}; + +const useFetchStatsByMonitorId = ({ + monitorId, + sortOrder, + limit, + dateRange, + numToDisplay, + normalize, +}) => { + const [monitor, setMonitor] = useState(undefined); + const [audits, setAudits] = useState(undefined); + const [isLoading, setIsLoading] = useState(true); + const [networkError, setNetworkError] = useState(false); + useEffect(() => { + const fetchMonitor = async () => { + try { + setIsLoading(true); + const res = await networkService.getStatsByMonitorId({ + monitorId: monitorId, + sortOrder, + limit, + dateRange, + numToDisplay, + normalize, + }); + setMonitor(res?.data?.data ?? undefined); + setAudits(res?.data?.data?.checks?.[0]?.audits ?? undefined); + } catch (error) { + setNetworkError(true); + createToast({ body: error.message }); + } finally { + setIsLoading(false); + } + }; + fetchMonitor(); + }, [monitorId, dateRange, numToDisplay, normalize, sortOrder, limit]); + return [monitor, audits, isLoading, networkError]; +}; + +const useFetchMonitorById = ({ monitorId, setMonitor, updateTrigger }) => { + const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + const fetchMonitor = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorById({ monitorId: monitorId }); + setMonitor(res.data.data); + } catch (error) { + createToast({ body: error.message }); + } finally { + setIsLoading(false); + } + }; + fetchMonitor(); + }, [monitorId, setMonitor, updateTrigger]); + return [isLoading]; +}; + +const useFetchHardwareMonitorById = ({ monitorId, dateRange }) => { + const [isLoading, setIsLoading] = useState(true); + const [networkError, setNetworkError] = useState(false); + const [monitor, setMonitor] = useState(undefined); + + useEffect(() => { + const fetchMonitor = async () => { + try { + if (!monitorId) { + return { monitor: undefined, isLoading: false, networkError: undefined }; + } + const response = await networkService.getHardwareDetailsByMonitorId({ + monitorId: monitorId, + dateRange: dateRange, + }); + setMonitor(response.data.data); + } catch (error) { + setNetworkError(true); + } finally { + setIsLoading(false); + } + }; + fetchMonitor(); + }, [monitorId, dateRange]); + return [monitor, isLoading, networkError]; +}; + +const useFetchUptimeMonitorById = ({ monitorId, dateRange, trigger }) => { + const [networkError, setNetworkError] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [monitor, setMonitor] = useState(undefined); + const [monitorStats, setMonitorStats] = useState(undefined); + 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, trigger]); + return [monitor, monitorStats, isLoading, networkError]; +}; + +const useCreateMonitor = () => { + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + const createMonitor = async ({ monitor, redirect }) => { + try { + setIsLoading(true); + await networkService.createMonitor({ monitor }); + createToast({ body: "Monitor created successfully!" }); + if (redirect) { + navigate(redirect); + } + } catch (error) { + createToast({ body: "Failed to create monitor." }); + } finally { + setIsLoading(false); + } + }; + return [createMonitor, isLoading]; +}; + +const useDeleteMonitor = () => { + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + const deleteMonitor = async ({ monitor, redirect }) => { + try { + setIsLoading(true); + await networkService.deleteMonitorById({ monitorId: monitor._id }); + createToast({ body: "Monitor deleted successfully!" }); + if (redirect) { + navigate(redirect); + } + } catch (error) { + createToast({ body: "Failed to delete monitor." }); + } finally { + setIsLoading(false); + } + }; + return [deleteMonitor, isLoading]; +}; + +const useUpdateMonitor = () => { + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + const updateMonitor = async ({ monitor, redirect }) => { + try { + setIsLoading(true); + const updatedFields = { + name: monitor.name, + description: monitor.description, + interval: monitor.interval, + notifications: monitor.notifications, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + ignoreTlsErrors: monitor.ignoreTlsErrors, + jsonPath: monitor.jsonPath, + ...(monitor.type === "port" && { port: monitor.port }), + ...(monitor.type === "hardware" && { + thresholds: monitor.thresholds, + secret: monitor.secret, + }), + }; + await networkService.updateMonitor({ + monitorId: monitor._id, + updatedFields, + }); + + createToast({ body: "Monitor updated successfully!" }); + if (redirect) { + navigate(redirect); + } + } catch (error) { + createToast({ body: "Failed to update monitor." }); + } finally { + setIsLoading(false); + } + }; + return [updateMonitor, isLoading]; +}; + +const usePauseMonitor = () => { + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(undefined); + const pauseMonitor = async ({ monitorId, triggerUpdate }) => { + try { + setIsLoading(true); + const res = await networkService.pauseMonitorById({ monitorId }); + createToast({ + body: res.data.data.isActive + ? "Monitor resumed successfully" + : "Monitor paused successfully", + }); + triggerUpdate(); + } catch (error) { + setError(error); + } finally { + setIsLoading(false); + } + }; + + 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 () => { + setIsLoading(true); + try { + await networkService.deleteChecksByTeamId(); + createToast({ body: t("settingsStatsCleared") }); + } catch (error) { + createToast({ body: t("settingsFailedToClearStats") }); + } finally { + setIsLoading(false); + } + }; + + return [deleteMonitorStats, isLoading]; +}; + +const useCreateBulkMonitors = () => { + const [isLoading, setIsLoading] = useState(false); + + const createBulkMonitors = async (file, user) => { + setIsLoading(true); + + const formData = new FormData(); + formData.append("csvFile", file); + + try { + const response = await networkService.createBulkMonitors(formData); + return [true, response.data, null]; // [success, data, error] + } catch (err) { + const errorMessage = err?.response?.data?.msg || err.message; + return [false, null, errorMessage]; + } finally { + setIsLoading(false); + } + }; + + return [createBulkMonitors, isLoading]; +}; + +export { + useFetchMonitorsWithSummary, + useFetchMonitorsWithChecks, + useFetchMonitorsByTeamId, + useFetchStatsByMonitorId, + useFetchMonitorById, + useFetchUptimeMonitorById, + useFetchHardwareMonitorById, + useCreateMonitor, + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, + useAddDemoMonitors, + useDeleteAllMonitors, + useDeleteMonitorStats, + useCreateBulkMonitors, +}; diff --git a/client/src/Hooks/useFetchSettings.js b/client/src/Hooks/settingsHooks.js similarity index 91% rename from client/src/Hooks/useFetchSettings.js rename to client/src/Hooks/settingsHooks.js index f7651e789..7d3ff28fc 100644 --- a/client/src/Hooks/useFetchSettings.js +++ b/client/src/Hooks/settingsHooks.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") }); diff --git a/client/src/Hooks/useBulkMonitors.js b/client/src/Hooks/useBulkMonitors.js deleted file mode 100644 index a834f0140..000000000 --- a/client/src/Hooks/useBulkMonitors.js +++ /dev/null @@ -1,27 +0,0 @@ -import { useState } from "react"; -import { networkService } from "../main"; - -export const useBulkMonitors = () => { - const [isLoading, setIsLoading] = useState(false); - - const createBulkMonitors = async (file, user) => { - setIsLoading(true); - - const formData = new FormData(); - formData.append("csvFile", file); - formData.append("userId", user._id); - formData.append("teamId", user.teamId); - - try { - const response = await networkService.createBulkMonitors(formData); - return [true, response.data, null]; // [success, data, error] - } catch (err) { - const errorMessage = err?.response?.data?.msg || err.message; - return [false, null, errorMessage]; - } finally { - setIsLoading(false); - } - }; - - return [createBulkMonitors, isLoading]; -}; 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/Hooks/useFetchMonitorsWithChecks.js b/client/src/Hooks/useFetchMonitorsWithChecks.js deleted file mode 100644 index a6f0437b9..000000000 --- a/client/src/Hooks/useFetchMonitorsWithChecks.js +++ /dev/null @@ -1,72 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../main"; -import { createToast } from "../Utils/toastUtils"; -import { useTheme } from "@emotion/react"; -import { useMonitorUtils } from "./useMonitorUtils"; - -export const useFetchMonitorsWithChecks = ({ - teamId, - types, - limit, - page, - rowsPerPage, - filter, - field, - order, - monitorUpdateTrigger, -}) => { - const [isLoading, setIsLoading] = useState(false); - const [count, setCount] = useState(undefined); - const [monitors, setMonitors] = useState(undefined); - const [networkError, setNetworkError] = useState(false); - - const theme = useTheme(); - const { getMonitorWithPercentage } = useMonitorUtils(); - useEffect(() => { - const fetchMonitors = async () => { - try { - setIsLoading(true); - const res = await networkService.getMonitorsWithChecksByTeamId({ - teamId, - limit, - types, - page, - rowsPerPage, - filter, - field, - order, - }); - const { count, monitors } = res?.data?.data ?? {}; - const mappedMonitors = monitors.map((monitor) => - getMonitorWithPercentage(monitor, theme) - ); - setMonitors(mappedMonitors); - setCount(count?.monitorsCount ?? 0); - } catch (error) { - console.error(error); - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - fetchMonitors(); - }, [ - field, - filter, - getMonitorWithPercentage, - limit, - order, - page, - rowsPerPage, - teamId, - theme, - types, - monitorUpdateTrigger, - ]); - return [monitors, count, isLoading, networkError]; -}; - -export default useFetchMonitorsWithChecks; diff --git a/client/src/Hooks/useFetchMonitorsWithSummary.js b/client/src/Hooks/useFetchMonitorsWithSummary.js deleted file mode 100644 index cd21271d4..000000000 --- a/client/src/Hooks/useFetchMonitorsWithSummary.js +++ /dev/null @@ -1,37 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../main"; -import { createToast } from "../Utils/toastUtils"; - -export const useFetchMonitorsWithSummary = ({ teamId, types, monitorUpdateTrigger }) => { - const [isLoading, setIsLoading] = useState(false); - const [monitors, setMonitors] = useState(undefined); - const [monitorsSummary, setMonitorsSummary] = useState(undefined); - const [networkError, setNetworkError] = useState(false); - - useEffect(() => { - const fetchMonitors = async () => { - try { - setIsLoading(true); - const res = await networkService.getMonitorsWithSummaryByTeamId({ - teamId, - types, - }); - const { monitors, summary } = res?.data?.data ?? {}; - setMonitors(monitors); - setMonitorsSummary(summary); - } catch (error) { - console.error(error); - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - fetchMonitors(); - }, [teamId, types, monitorUpdateTrigger]); - return [monitors, monitorsSummary, isLoading, networkError]; -}; - -export default useFetchMonitorsWithSummary; diff --git a/client/src/Hooks/useFetchUptimeMonitorById.js b/client/src/Hooks/useFetchUptimeMonitorById.js deleted file mode 100644 index cb344569c..000000000 --- a/client/src/Hooks/useFetchUptimeMonitorById.js +++ /dev/null @@ -1,35 +0,0 @@ -import { useState, useEffect } from "react"; -import { useDispatch } from "react-redux"; -import { getUptimeMonitorById } from "../Features/UptimeMonitors/uptimeMonitorsSlice"; -import { useNavigate } from "react-router"; - -const useFetchUptimeMonitorById = (monitorId, updateTrigger) => { - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - const [monitor, setMonitor] = useState(null); - const navigate = useNavigate(); - const dispatch = useDispatch(); - useEffect(() => { - const fetchMonitor = async () => { - try { - setIsLoading(true); - const action = await dispatch(getUptimeMonitorById({ monitorId })); - - if (getUptimeMonitorById.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - } else if (getUptimeMonitorById.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - navigate("/not-found", { replace: true }); - } finally { - setIsLoading(false); - } - }; - fetchMonitor(); - }, [monitorId, dispatch, navigate, updateTrigger]); - return [monitor, isLoading, error]; -}; - -export { useFetchUptimeMonitorById }; diff --git a/client/src/Hooks/useFetchUptimeMonitorDetails.js b/client/src/Hooks/useFetchUptimeMonitorDetails.js deleted file mode 100644 index 3c0ac7ee6..000000000 --- a/client/src/Hooks/useFetchUptimeMonitorDetails.js +++ /dev/null @@ -1,35 +0,0 @@ -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, trigger }) => { - 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, trigger]); - return [monitor, monitorStats, isLoading, networkError]; -}; - -export default useFetchUptimeMonitorDetails; diff --git a/client/src/Hooks/useMonitorControls.js b/client/src/Hooks/useMonitorControls.js deleted file mode 100644 index b47603b58..000000000 --- a/client/src/Hooks/useMonitorControls.js +++ /dev/null @@ -1,28 +0,0 @@ -import { useState } from "react"; -import { networkService } from "../main"; -import { createToast } from "../Utils/toastUtils"; - -const usePauseMonitor = ({ monitorId, triggerUpdate }) => { - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(undefined); - const pauseMonitor = async () => { - try { - setIsLoading(true); - const res = await networkService.pauseMonitorById({ monitorId }); - createToast({ - body: res.data.data.isActive - ? "Monitor resumed successfully" - : "Monitor paused successfully", - }); - triggerUpdate(); - } catch (error) { - setError(error); - } finally { - setIsLoading(false); - } - }; - - return [pauseMonitor, isLoading, error]; -}; - -export { usePauseMonitor }; diff --git a/client/src/Hooks/useMonitorUtils.js b/client/src/Hooks/useMonitorUtils.js index 424112a81..417c6eadd 100644 --- a/client/src/Hooks/useMonitorUtils.js +++ b/client/src/Hooks/useMonitorUtils.js @@ -46,7 +46,27 @@ const useMonitorUtils = () => { pending: theme.palette.warning.lowContrast, }; - return { getMonitorWithPercentage, determineState, statusColor }; + const statusToTheme = { + up: "success", + down: "error", + paused: "warning", + pending: "secondary", + "cannot resolve": "tertiary", + }; + + const pagespeedStatusMsg = { + up: "Live (collecting data)", + down: "Inactive", + paused: "Paused", + }; + + return { + getMonitorWithPercentage, + determineState, + statusColor, + statusToTheme, + pagespeedStatusMsg, + }; }; export { useMonitorUtils }; diff --git a/client/src/Hooks/useNotifications.js b/client/src/Hooks/useNotifications.js index 54e3d1bd9..f19b76dde 100644 --- a/client/src/Hooks/useNotifications.js +++ b/client/src/Hooks/useNotifications.js @@ -2,7 +2,6 @@ import { useState, useEffect, useCallback } from "react"; import { createToast } from "../Utils/toastUtils"; import { networkService } from "../main"; import { useNavigate } from "react-router-dom"; -import { useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { NOTIFICATION_TYPES } from "../Pages/Notifications/utils"; @@ -37,15 +36,12 @@ const useGetNotificationsByTeamId = (updateTrigger) => { const [notifications, setNotifications] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); - const { user } = useSelector((state) => state.auth); const { t } = useTranslation(); const getNotifications = useCallback(async () => { try { setIsLoading(true); - const response = await networkService.getNotificationsByTeamId({ - teamId: user.teamId, - }); + const response = await networkService.getNotificationsByTeamId(); setNotifications(response?.data?.data ?? []); } catch (error) { setError(error); @@ -55,7 +51,7 @@ const useGetNotificationsByTeamId = (updateTrigger) => { } finally { setIsLoading(false); } - }, [user.teamId]); + }, [t]); useEffect(() => { getNotifications(); diff --git a/client/src/Pages/Auth/Login/index.jsx b/client/src/Pages/Auth/Login/index.jsx index a969706e7..098fbbf08 100644 --- a/client/src/Pages/Auth/Login/index.jsx +++ b/client/src/Pages/Auth/Login/index.jsx @@ -5,7 +5,7 @@ import Button from "@mui/material/Button"; import TextInput from "../../../Components/Inputs/TextInput"; import { PasswordEndAdornment } from "../../../Components/Inputs/TextInput/Adornments"; import { loginCredentials } from "../../../Validation/validation"; -import TextLink from "../components/TextLink"; +import TextLink from "../../../Components/TextLink"; import Typography from "@mui/material/Typography"; // Utils 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/Pages/Incidents/Components/IncidentTable/index.jsx b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx index cc2d212f0..cf6fbb49d 100644 --- a/client/src/Pages/Incidents/Components/IncidentTable/index.jsx +++ b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx @@ -10,10 +10,10 @@ 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 { useState, useEffect } from "react"; import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; +import { useFetchChecks } from "../../../../Hooks/checkHooks"; const IncidentTable = ({ shouldRender, @@ -26,17 +26,25 @@ const IncidentTable = ({ const uiTimezone = useSelector((state) => state.ui.timezone); //Local state + const [teamId, setTeamId] = useState(undefined); + const [monitorId, setMonitorId] = useState(undefined); + const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); const selectedMonitorDetails = monitors?.[selectedMonitor]; const selectedMonitorType = selectedMonitorDetails?.type; - const { isLoading, networkError, checks, checksCount } = useChecksFetch({ - selectedMonitor, - selectedMonitorType, - filter, + + const [checks, checksCount, isLoading, networkError] = useFetchChecks({ + status: false, + monitorId, + teamId, + type: selectedMonitorType, + sortOrder: "desc", + limit: null, dateRange, - page, - rowsPerPage, + filter: filter, + page: page, + rowsPerPage: rowsPerPage, }); const { t } = useTranslation(); @@ -50,6 +58,16 @@ const IncidentTable = ({ setRowsPerPage(event.target.value); }; + useEffect(() => { + if (selectedMonitor === "0") { + setTeamId("placeholder"); // TODO this isn't needed any longer, fix hook + setMonitorId(undefined); + } else { + setMonitorId(selectedMonitor); + setTeamId(undefined); + } + }, [selectedMonitor]); + const headers = [ { id: "monitorName", diff --git a/client/src/Pages/Incidents/Hooks/useChecksFetch.jsx b/client/src/Pages/Incidents/Hooks/useChecksFetch.jsx deleted file mode 100644 index a2f2c8211..000000000 --- a/client/src/Pages/Incidents/Hooks/useChecksFetch.jsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useState, useEffect } from "react"; -import { networkService } from "../../../main"; -import { createToast } from "../../../Utils/toastUtils"; -import { useSelector } from "react-redux"; -const useChecksFetch = ({ - selectedMonitor, - selectedMonitorType, - filter, - dateRange, - page, - rowsPerPage, -}) => { - //Redux - const { user } = useSelector((state) => state.auth); - - //Local - const [isLoading, setIsLoading] = useState(true); - 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({ - status: false, - teamId: user.teamId, - sortOrder: "desc", - limit: null, - dateRange, - filter: filter, - page: page, - rowsPerPage: rowsPerPage, - }); - } else { - res = await networkService.getChecksByMonitor({ - status: false, - monitorId: selectedMonitor, - type: selectedMonitorType, - 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(); - }, [user, dateRange, page, rowsPerPage, filter, selectedMonitor, selectedMonitorType]); - 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 deleted file mode 100644 index 46d187aaf..000000000 --- a/client/src/Pages/Incidents/Hooks/useMonitorsFetch.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import { useState, useEffect } from "react"; -import { networkService } from "../../../main"; -import { createToast } from "../../../Utils/toastUtils"; -const useMonitorsFetch = ({ teamId }) => { - //Local state - const [isLoading, setIsLoading] = useState(true); - const [networkError, setNetworkError] = useState(false); - - const [monitors, setMonitors] = useState(undefined); - - useEffect(() => { - const fetchMonitors = async () => { - try { - setIsLoading(true); - const res = await networkService.getMonitorsByTeamId({ - 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?.filteredMonitors?.length > 0) { - const monitorLookup = res.data.data.filteredMonitors.reduce((acc, monitor) => { - acc[monitor._id] = { - _id: monitor._id, - name: monitor.name, - type: monitor.type, - }; - return acc; - }, {}); - setMonitors(monitorLookup); - } - } catch (error) { - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - - fetchMonitors(); - }, [teamId]); - return { isLoading, monitors, networkError }; -}; - -export { useMonitorsFetch }; diff --git a/client/src/Pages/Incidents/index.jsx b/client/src/Pages/Incidents/index.jsx index 16a62a621..011961503 100644 --- a/client/src/Pages/Incidents/index.jsx +++ b/client/src/Pages/Incidents/index.jsx @@ -1,22 +1,20 @@ // Components import { Stack } from "@mui/material"; import Breadcrumbs from "../../Components/Breadcrumbs"; +import GenericFallback from "../../Components/GenericFallback"; +import IncidentTable from "./Components/IncidentTable"; +import OptionsHeader from "./Components/OptionsHeader"; //Utils import { useTheme } from "@emotion/react"; -import { useMonitorsFetch } from "./Hooks/useMonitorsFetch"; -import { useSelector } from "react-redux"; -import OptionsHeader from "./Components/OptionsHeader"; -import { useState } from "react"; -import IncidentTable from "./Components/IncidentTable"; -import GenericFallback from "../../Components/GenericFallback"; +import { useFetchMonitorsByTeamId } from "../../Hooks/monitorHooks"; +import { useState, useEffect } from "react"; import NetworkError from "../../Components/GenericFallback/NetworkError"; import { useTranslation } from "react-i18next"; -//Constants +//Constants const Incidents = () => { // Redux state - const { user } = useSelector((state) => state.auth); const { t } = useTranslation(); const BREADCRUMBS = [ @@ -27,12 +25,23 @@ const Incidents = () => { const [selectedMonitor, setSelectedMonitor] = useState("0"); const [filter, setFilter] = useState(undefined); const [dateRange, setDateRange] = useState(undefined); + const [monitorLookup, setMonitorLookup] = useState(undefined); + //Utils const theme = useTheme(); + const [monitors, , isLoading, networkError] = useFetchMonitorsByTeamId({}); - const { monitors, isLoading, networkError } = useMonitorsFetch({ - teamId: user.teamId, - }); + useEffect(() => { + const monitorLookup = monitors?.reduce((acc, monitor) => { + acc[monitor._id] = { + _id: monitor._id, + name: monitor.name, + type: monitor.type, + }; + return acc; + }, {}); + setMonitorLookup(monitorLookup); + }, [monitors]); if (networkError) { return ( @@ -47,7 +56,7 @@ const Incidents = () => { { /> { const CreateInfrastructureMonitor = () => { const theme = useTheme(); const { user } = useSelector((state) => state.auth); - const monitorState = useSelector((state) => state.infrastructureMonitor); - const dispatch = useDispatch(); - const navigate = useNavigate(); const { monitorId } = useParams(); const { t } = useTranslation(); @@ -65,9 +59,11 @@ const CreateInfrastructureMonitor = () => { const isCreate = typeof monitorId === "undefined"; // Fetch monitor details if editing - const { monitor, isLoading, networkError } = useHardwareMonitorsFetch({ monitorId }); + const [monitor, isLoading, networkError] = useFetchHardwareMonitorById({ monitorId }); const [notifications, notificationsAreLoading, notificationsError] = useGetNotificationsByTeamId(); + const [updateMonitor, isUpdating] = useUpdateMonitor(); + const [createMonitor, isCreating] = useCreateMonitor(); // State const [errors, setErrors] = useState({}); @@ -187,29 +183,18 @@ const CreateInfrastructureMonitor = () => { }; form = { + ...(isCreate ? {} : { _id: monitorId }), ...rest, description: form.name, - teamId: user.teamId, - userId: user._id, type: "hardware", notifications: infrastructureMonitor.notifications, thresholds, }; // Handle create or update - const action = isCreate - ? await dispatch(createInfrastructureMonitor({ monitor: form })) - : await dispatch(updateInfrastructureMonitor({ monitorId, monitor: form })); - if (action.meta.requestStatus === "fulfilled") { - createToast({ - body: isCreate - ? t("infrastructureMonitorCreated") - : t("infrastructureMonitorUpdated"), - }); - navigate("/infrastructure"); - } else { - createToast({ body: "Failed to save monitor." }); - } + isCreate + ? await createMonitor({ monitor: form, redirect: "/infrastructure" }) + : await updateMonitor({ monitor: form, redirect: "/infrastructure" }); }; const onChange = (event) => { @@ -448,7 +433,7 @@ const CreateInfrastructureMonitor = () => { type="submit" variant="contained" color="accent" - loading={monitorState?.isLoading} + loading={isLoading || isUpdating || isCreating || notificationsAreLoading} > {t(isCreate ? "infrastructureCreateMonitor" : "infrastructureEditMonitor")} diff --git a/client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx b/client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx index 66483d448..6c11e7c82 100644 --- a/client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx +++ b/client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx @@ -4,14 +4,14 @@ import StatusBoxes from "../../../../../Components/StatusBoxes"; import StatBox from "../../../../../Components/StatBox"; //Utils -import useUtils from "../../../../../Pages/Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; import { useHardwareUtils } from "../../Hooks/useHardwareUtils"; import { useTranslation } from "react-i18next"; const InfraStatBoxes = ({ shouldRender, monitor }) => { // Utils const { formatBytes } = useHardwareUtils(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); const { t } = useTranslation(); const { stats } = monitor ?? {}; diff --git a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx b/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx deleted file mode 100644 index c0b61ef62..000000000 --- a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; - -const useHardwareMonitorsFetch = ({ monitorId, dateRange }) => { - // Abort early if creating monitor - - const [isLoading, setIsLoading] = useState(true); - const [networkError, setNetworkError] = useState(false); - const [monitor, setMonitor] = useState(undefined); - - useEffect(() => { - const fetchData = async () => { - try { - if (!monitorId) { - return { monitor: undefined, isLoading: false, networkError: undefined }; - } - const response = await networkService.getHardwareDetailsByMonitorId({ - monitorId: monitorId, - dateRange: dateRange, - }); - setMonitor(response.data.data); - } catch (error) { - setNetworkError(true); - } finally { - setIsLoading(false); - } - }; - fetchData(); - }, [monitorId, dateRange]); - - return { - isLoading, - networkError, - monitor, - }; -}; - -export { useHardwareMonitorsFetch }; diff --git a/client/src/Pages/Infrastructure/Details/index.jsx b/client/src/Pages/Infrastructure/Details/index.jsx index fefb7d2aa..842e0f6d9 100644 --- a/client/src/Pages/Infrastructure/Details/index.jsx +++ b/client/src/Pages/Infrastructure/Details/index.jsx @@ -11,7 +11,7 @@ import GenericFallback from "../../../Components/GenericFallback"; // Utils import { useTheme } from "@emotion/react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import { useHardwareMonitorsFetch } from "./Hooks/useHardwareMonitorsFetch"; +import { useFetchHardwareMonitorById } from "../../../Hooks/monitorHooks"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useParams } from "react-router-dom"; @@ -33,7 +33,7 @@ const InfrastructureDetails = () => { const { t } = useTranslation(); const isAdmin = useIsAdmin(); - const { isLoading, networkError, monitor } = useHardwareMonitorsFetch({ + const [monitor, isLoading, networkError] = useFetchHardwareMonitorById({ monitorId, dateRange, }); diff --git a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx index 9ab128c5e..639753638 100644 --- a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx +++ b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx @@ -12,7 +12,7 @@ import CustomGauge from "../../../../../Components/Charts/CustomGauge"; // Utils import { useTheme } from "@emotion/react"; -import useUtils from "../../../../Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; import { useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; @@ -27,7 +27,7 @@ const MonitorsTable = ({ // Utils const theme = useTheme(); const { t } = useTranslation(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); const navigate = useNavigate(); // Handlers @@ -94,6 +94,7 @@ const MonitorsTable = ({ monitor={row} isAdmin={isAdmin} updateCallback={handleActionMenuDelete} + isLoading={isLoading} /> ), }, @@ -151,7 +152,7 @@ const MonitorsTable = ({ }; MonitorsTable.propTypes = { - shouldRender: PropTypes.bool, + isLoading: PropTypes.bool, monitors: PropTypes.array, isAdmin: PropTypes.bool, handleActionMenuDelete: PropTypes.func, diff --git a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTableMenu/index.jsx b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTableMenu/index.jsx index bce3020a6..15193da3e 100644 --- a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTableMenu/index.jsx +++ b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTableMenu/index.jsx @@ -1,7 +1,6 @@ /* TODO I basically copied and pasted this component from the actionsMenu. Check how we can make it reusable */ import { useRef, useState } from "react"; -import { useSelector } from "react-redux"; import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router-dom"; import { createToast } from "../../../../../Utils/toastUtils"; @@ -30,7 +29,6 @@ const InfrastructureMenu = ({ monitor, isAdmin, updateCallback }) => { const [isOpen, setIsOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false); const theme = useTheme(); - const { isLoading } = useSelector((state) => state.uptimeMonitors); const openMenu = (e) => { e.stopPropagation(); @@ -117,7 +115,6 @@ const InfrastructureMenu = ({ monitor, isAdmin, updateCallback }) => { onCancel={cancelRemove} confirmationButtonLabel="Delete" onConfirm={handleRemove} - isLoading={isLoading} modelTitle="modal-delete-monitor" modelDescription="delete-monitor-confirmation" /> diff --git a/client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx b/client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx deleted file mode 100644 index 883205c77..000000000 --- a/client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useState, useEffect } from "react"; -import { useSelector } from "react-redux"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; - -const useMonitorFetch = ({ page, field, filter, rowsPerPage, updateTrigger }) => { - // Redux state - const { user } = useSelector((state) => state.auth); - - // Local state - const [isLoading, setIsLoading] = useState(true); - const [networkError, setNetworkError] = useState(false); - const [monitors, setMonitors] = useState(undefined); - const [summary, setSummary] = useState(undefined); - - useEffect(() => { - const fetchMonitors = async () => { - try { - const response = await networkService.getMonitorsByTeamId({ - teamId: user.teamId, - limit: 1, - field: field, - filter: filter, - types: ["hardware"], - page: page, - rowsPerPage: rowsPerPage, - }); - setMonitors(response?.data?.data?.filteredMonitors ?? []); - setSummary(response?.data?.data?.summary ?? {}); - } catch (error) { - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - - fetchMonitors(); - }, [page, field, filter, rowsPerPage, user.teamId, updateTrigger]); - - return { monitors, summary, isLoading, networkError }; -}; - -export { useMonitorFetch }; diff --git a/client/src/Pages/Infrastructure/Monitors/index.jsx b/client/src/Pages/Infrastructure/Monitors/index.jsx index 445ba8105..d944df3ce 100644 --- a/client/src/Pages/Infrastructure/Monitors/index.jsx +++ b/client/src/Pages/Infrastructure/Monitors/index.jsx @@ -11,15 +11,18 @@ import Filter from "./Components/Filters"; import SearchComponent from "../../Uptime/Monitors/Components/SearchComponent"; // Utils import { useTheme } from "@emotion/react"; -import { useMonitorFetch } from "./Hooks/useMonitorFetch"; import { useState } from "react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; import { useTranslation } from "react-i18next"; +import { useFetchMonitorsByTeamId } from "../../../Hooks/monitorHooks"; // Constants +const TYPES = ["hardware"]; const BREADCRUMBS = [{ name: `infrastructure`, path: "/infrastructure" }]; const InfrastructureMonitors = () => { // Redux state + + // Local state const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(5); const [updateTrigger, setUpdateTrigger] = useState(false); @@ -53,7 +56,9 @@ const InfrastructureMonitors = () => { const field = toFilterStatus !== undefined ? "status" : undefined; - const { monitors, summary, isLoading, networkError } = useMonitorFetch({ + const [monitors, summary, isLoading, networkError] = useFetchMonitorsByTeamId({ + limit: 1, + types: TYPES, page, field: field, filter: toFilterStatus ?? search, @@ -119,7 +124,7 @@ const InfrastructureMonitors = () => { { setIsLoading(true); try { const response = await networkService.getMonitorsByTeamId({ - teamId: user.teamId, limit: null, types: ["http", "ping", "pagespeed", "port"], }); diff --git a/client/src/Pages/Notifications/create/index.jsx b/client/src/Pages/Notifications/create/index.jsx index 19335c790..3c17c113c 100644 --- a/client/src/Pages/Notifications/create/index.jsx +++ b/client/src/Pages/Notifications/create/index.jsx @@ -48,8 +48,6 @@ const CreateNotifications = () => { // local state const [notification, setNotification] = useState({ - userId: user._id, - teamId: user.teamId, notificationName: "", address: "", type: NOTIFICATION_TYPES[0]._id, diff --git a/client/src/Pages/Notifications/index.jsx b/client/src/Pages/Notifications/index.jsx index 9ad5f1578..ce5cbb547 100644 --- a/client/src/Pages/Notifications/index.jsx +++ b/client/src/Pages/Notifications/index.jsx @@ -17,7 +17,6 @@ import { useDeleteNotification, } from "../../Hooks/useNotifications"; import { useTranslation } from "react-i18next"; -import { useParams } from "react-router-dom"; const Notifications = () => { const navigate = useNavigate(); diff --git a/client/src/Pages/PageSpeed/Configure/index.jsx b/client/src/Pages/PageSpeed/Configure/index.jsx index 487106247..71e1e111e 100644 --- a/client/src/Pages/PageSpeed/Configure/index.jsx +++ b/client/src/Pages/PageSpeed/Configure/index.jsx @@ -12,45 +12,43 @@ import NotificationsConfig from "../../../Components/NotificationConfig"; import Dialog from "../../../Components/Dialog"; // Utils -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useTheme } from "@emotion/react"; -import { useDispatch, useSelector } from "react-redux"; -import { useNavigate, useParams } from "react-router"; -import { - deletePageSpeed, - getPagespeedMonitorById, - getPageSpeedByTeamId, - updatePageSpeed, - pausePageSpeed, -} from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice"; +import { useParams } from "react-router"; import { monitorValidation } from "../../../Validation/validation"; -import { createToast } from "../../../Utils/toastUtils"; import { useTranslation } from "react-i18next"; -import useUtils from "../../Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; - +import { + useFetchMonitorById, + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, +} from "../../../Hooks/monitorHooks"; const PageSpeedConfigure = () => { // Redux state - const { isLoading } = useSelector((state) => state.pageSpeedMonitors); // Local state const [monitor, setMonitor] = useState({}); const [errors, setErrors] = useState({}); - const [buttonLoading, setButtonLoading] = useState(false); const [isOpen, setIsOpen] = useState(false); + const [updateTrigger, setUpdateTrigger] = useState(false); // Utils const theme = useTheme(); const { t } = useTranslation(); - const navigate = useNavigate(); - const dispatch = useDispatch(); const MS_PER_MINUTE = 60000; const { monitorId } = useParams(); - const { statusColor, pagespeedStatusMsg, determineState } = useUtils(); + const { statusColor, pagespeedStatusMsg, determineState } = useMonitorUtils(); const [notifications, notificationsAreLoading, notificationsError] = useGetNotificationsByTeamId(); + const [isLoading] = useFetchMonitorById({ monitorId, setMonitor, updateTrigger }); + const [deleteMonitor, isDeleting] = useDeleteMonitor(); + const [updateMonitor, isUpdating] = useUpdateMonitor(); + const [pauseMonitor, isPausing] = usePauseMonitor(); + const frequencies = [ { _id: 3, name: "3 minutes" }, { _id: 5, name: "5 minutes" }, @@ -61,24 +59,10 @@ const PageSpeedConfigure = () => { { _id: 10080, name: "1 week" }, ]; - useEffect(() => { - const fetchMonitor = async () => { - try { - const action = await dispatch(getPagespeedMonitorById({ monitorId })); - - if (getPagespeedMonitorById.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - } else if (getPagespeedMonitorById.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - logger.error("Error fetching monitor of id: " + monitorId); - navigate("/not-found", { replace: true }); - } - }; - fetchMonitor(); - }, [dispatch, monitorId, navigate]); + // Handlers + const triggerUpdate = () => { + setUpdateTrigger(!updateTrigger); + }; const onChange = (event) => { let { value, name } = event.target; @@ -106,42 +90,17 @@ const PageSpeedConfigure = () => { }; const handlePause = async () => { - try { - const action = await dispatch(pausePageSpeed({ monitorId })); - if (pausePageSpeed.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - const state = action?.payload?.data.isActive === false ? "paused" : "resumed"; - createToast({ body: `Monitor ${state} successfully.` }); - } else if (pausePageSpeed.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - createToast({ body: "Failed to pause monitor" }); - } + await pauseMonitor({ monitorId, triggerUpdate }); }; const onSubmit = async (event) => { event.preventDefault(); - const action = await dispatch(updatePageSpeed({ monitor: monitor })); - if (action.meta.requestStatus === "fulfilled") { - createToast({ body: "Monitor updated successfully!" }); - dispatch(getPageSpeedByTeamId()); - } else { - createToast({ body: "Failed to update monitor." }); - } + await updateMonitor({ monitor, redirect: "/pagespeed" }); }; const handleRemove = async (event) => { event.preventDefault(); - setButtonLoading(true); - const action = await dispatch(deletePageSpeed({ monitor })); - if (action.meta.requestStatus === "fulfilled") { - navigate("/pagespeed"); - } else { - createToast({ body: "Failed to delete monitor." }); - } - setButtonLoading(false); + await deleteMonitor({ monitor, redirect: "/pagespeed" }); }; return ( @@ -363,7 +322,7 @@ const PageSpeedConfigure = () => { mt="auto" > diff --git a/client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx b/client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx deleted file mode 100644 index 18ffd8782..000000000 --- a/client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; -import { useNavigate } from "react-router-dom"; -const useMonitorFetch = ({ monitorId }) => { - const navigate = useNavigate(); - - const [monitor, setMonitor] = useState(undefined); - const [audits, setAudits] = useState(undefined); - const [isLoading, setIsLoading] = useState(true); - const [networkError, setNetworkError] = useState(false); - useEffect(() => { - const fetchMonitor = async () => { - try { - const res = await networkService.getStatsByMonitorId({ - monitorId: monitorId, - sortOrder: "desc", - limit: 50, - dateRange: "day", - numToDisplay: null, - normalize: null, - }); - setMonitor(res?.data?.data ?? undefined); - setAudits(res?.data?.data?.checks?.[0]?.audits ?? undefined); - } catch (error) { - setNetworkError(true); - createToast({ body: error.message }); - } finally { - setIsLoading(false); - } - }; - - fetchMonitor(); - }, [monitorId, navigate]); - - return { monitor, audits, isLoading }; -}; - -export { useMonitorFetch }; diff --git a/client/src/Pages/PageSpeed/Details/index.jsx b/client/src/Pages/PageSpeed/Details/index.jsx index 5724f12a1..e8afcf8c8 100644 --- a/client/src/Pages/PageSpeed/Details/index.jsx +++ b/client/src/Pages/PageSpeed/Details/index.jsx @@ -11,8 +11,7 @@ import GenericFallback from "../../../Components/GenericFallback"; import { useTheme } from "@emotion/react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; import { useParams } from "react-router-dom"; -import { useSelector } from "react-redux"; -import { useMonitorFetch } from "./Hooks/useMonitorFetch"; +import { useFetchStatsByMonitorId } from "../../../Hooks/monitorHooks"; import { useState } from "react"; import { useTranslation } from "react-i18next"; // Constants @@ -28,8 +27,13 @@ const PageSpeedDetails = () => { const isAdmin = useIsAdmin(); const { monitorId } = useParams(); - const { monitor, audits, isLoading, networkError } = useMonitorFetch({ + const [monitor, audits, isLoading, networkError] = useFetchStatsByMonitorId({ monitorId, + sortOrder: "desc", + limit: 50, + dateRange: "day", + numToDisplay: null, + normalize: null, }); const [metrics, setMetrics] = useState({ diff --git a/client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx b/client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx index 8c83f102a..2220a4b16 100644 --- a/client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx +++ b/client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx @@ -8,7 +8,7 @@ import { useTheme } from "@emotion/react"; import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts"; import { useSelector } from "react-redux"; import { formatDateWithTz, formatDurationSplit } from "../../../../../Utils/timeUtils"; -import useUtils from "../../../../Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import IconBox from "../../../../../Components/IconBox"; @@ -118,7 +118,7 @@ const processData = (data) => { const PagespeedAreaChart = ({ data, status }) => { const theme = useTheme(); const [isHovered, setIsHovered] = useState(false); - const { statusToTheme } = useUtils(); + const { statusToTheme } = useMonitorUtils(); const themeColor = statusToTheme[status]; @@ -206,7 +206,7 @@ PagespeedAreaChart.propTypes = { * @returns {JSX.Element} - The rendered card. */ const Card = ({ monitor }) => { - const { determineState, pagespeedStatusMsg } = useUtils(); + const { determineState, pagespeedStatusMsg } = useMonitorUtils(); const theme = useTheme(); const { t } = useTranslation(); const navigate = useNavigate(); @@ -275,7 +275,7 @@ const Card = ({ monitor }) => { sx={{ gridColumnStart: 1, gridColumnEnd: 4 }} > diff --git a/client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx b/client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx deleted file mode 100644 index d2f0f4842..000000000 --- a/client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; - -const useMonitorsFetch = ({ teamId }) => { - const [isLoading, setIsLoading] = useState(true); - const [monitors, setMonitors] = useState([]); - const [summary, setSummary] = useState({}); - const [networkError, setNetworkError] = useState(false); - - useEffect(() => { - const fetchMonitors = async () => { - try { - setIsLoading(true); - const res = await networkService.getMonitorsByTeamId({ - teamId: teamId, - limit: 10, - types: ["pagespeed"], - page: null, - rowsPerPage: null, - filter: null, - field: null, - order: null, - }); - if (res?.data?.data?.filteredMonitors) { - setMonitors(res.data.data.filteredMonitors); - setSummary(res.data.data.summary); - } - } catch (error) { - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - - fetchMonitors(); - }, [teamId]); - 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 808541cee..65e43f2ec 100644 --- a/client/src/Pages/PageSpeed/Monitors/index.jsx +++ b/client/src/Pages/PageSpeed/Monitors/index.jsx @@ -5,26 +5,29 @@ 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"; // Utils import { useTheme } from "@emotion/react"; -import { useSelector } from "react-redux"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import useMonitorsFetch from "./Hooks/useMonitorsFetch"; -import GenericFallback from "../../../Components/GenericFallback"; import { useTranslation } from "react-i18next"; +import { useFetchMonitorsByTeamId } from "../../../Hooks/monitorHooks"; // Constants const BREADCRUMBS = [{ name: `pagespeed`, path: "/pagespeed" }]; - +const TYPES = ["pagespeed"]; const PageSpeed = () => { const theme = useTheme(); const { t } = useTranslation(); const isAdmin = useIsAdmin(); - const { user } = useSelector((state) => state.auth); - const { pagespeedApiKey } = useSelector((state) => state.settings); - const { isLoading, monitors, summary, networkError } = useMonitorsFetch({ - teamId: user.teamId, + const [monitors, monitorsSummary, isLoading, networkError] = useFetchMonitorsByTeamId({ + limit: 10, + types: TYPES, + page: null, + rowsPerPage: null, + filter: null, + field: null, + order: null, }); if (networkError === true) { @@ -53,7 +56,7 @@ const PageSpeed = () => { ]} link="/pagespeed/create" isAdmin={isAdmin} - showPageSpeedWarning={isAdmin && !pagespeedApiKey} + // showPageSpeedWarning={isAdmin && !pagespeedApiKey} /> ); } @@ -68,7 +71,7 @@ const PageSpeed = () => { /> { const [isSettingsLoading, settingsError] = useFetchSettings({ setSettingsData, }); + const [addDemoMonitors, isAddingDemoMonitors] = useAddDemoMonitors(); const [isSaving, saveError, saveSettings] = useSaveSettings(); - const [deleteMonitorStats, isDeletingMonitorStats] = UseDeleteMonitorStats(); + 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 @@ -96,35 +96,17 @@ const Settings = () => { } if (name === "deleteStats") { - await deleteMonitorStats({ teamId: user.teamId }); + await deleteMonitorStats(); return; } 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 + } /> { }} > diff --git a/client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx b/client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx index ddc7e223b..0e362e187 100644 --- a/client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx +++ b/client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx @@ -5,7 +5,7 @@ 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"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; const UptimeStatusBoxes = ({ isLoading = false, @@ -14,7 +14,7 @@ const UptimeStatusBoxes = ({ certificateExpiry, }) => { const theme = useTheme(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); // Determine time since last failure const timeOfLastFailure = monitorStats?.timeOfLastFailure; diff --git a/client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx b/client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx deleted file mode 100644 index 586cc8359..000000000 --- a/client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useState } from "react"; -import { useEffect } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; -export const useChecksFetch = ({ - monitorId, - monitorType, - dateRange, - page, - rowsPerPage, -}) => { - const [checks, setChecks] = useState(undefined); - const [checksCount, setChecksCount] = useState(undefined); - const [isLoading, setIsLoading] = useState(false); - const [networkError, setNetworkError] = useState(false); - - useEffect(() => { - if (!monitorType) { - return; - } - - const fetchChecks = async () => { - try { - setIsLoading(true); - const res = await networkService.getChecksByMonitor({ - monitorId: monitorId, - type: monitorType, - sortOrder: "desc", - limit: null, - dateRange: dateRange, - filter: null, - page: page, - rowsPerPage: rowsPerPage, - }); - setChecks(res.data.data.checks); - setChecksCount(res.data.data.checksCount); - } catch (error) { - setNetworkError(true); - createToast({ body: error.message }); - } finally { - setIsLoading(false); - } - }; - fetchChecks(); - }, [monitorId, monitorType, dateRange, page, rowsPerPage]); - - return [checks, checksCount, isLoading, networkError]; -}; - -export default useChecksFetch; diff --git a/client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx b/client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx deleted file mode 100644 index 9491e7cc3..000000000 --- a/client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; -import { useNavigate } from "react-router-dom"; -import { createToast } from "../../../../Utils/toastUtils"; - -export const useMonitorFetch = ({ monitorId, dateRange }) => { - const [networkError, setNetworkError] = useState(false); - const [isLoading, setIsLoading] = useState(true); - const [monitor, setMonitor] = useState(undefined); - const navigate = useNavigate(); - - useEffect(() => { - const fetchMonitors = async () => { - try { - const res = await networkService.getUptimeDetailsById({ - monitorId: monitorId, - dateRange: dateRange, - normalize: true, - }); - setMonitor(res?.data?.data ?? {}); - } catch (error) { - setNetworkError(true); - createToast({ body: error.message }); - } finally { - setIsLoading(false); - } - }; - fetchMonitors(); - }, [dateRange, monitorId, navigate]); - return [monitor, isLoading, networkError]; -}; - -export default useMonitorFetch; diff --git a/client/src/Pages/Uptime/Details/index.jsx b/client/src/Pages/Uptime/Details/index.jsx index 570b687d5..79762e0c3 100644 --- a/client/src/Pages/Uptime/Details/index.jsx +++ b/client/src/Pages/Uptime/Details/index.jsx @@ -1,15 +1,14 @@ // Components import Breadcrumbs from "../../../Components/Breadcrumbs"; import MonitorDetailsControlHeader from "../../../Components/MonitorDetailsControlHeader"; -import MonitorStatusHeader from "../../../Components/MonitorStatusHeader"; import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader"; import ChartBoxes from "./Components/ChartBoxes"; import ResponseTimeChart from "./Components/Charts/ResponseTimeChart"; import ResponseTable from "./Components/ResponseTable"; import UptimeStatusBoxes from "./Components/UptimeStatusBoxes"; import GenericFallback from "../../../Components/GenericFallback"; -// MUI Components -import { Stack, Typography } from "@mui/material"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; // Utils import { useState } from "react"; @@ -17,9 +16,9 @@ import { useParams } from "react-router-dom"; import { useSelector } from "react-redux"; import { useTheme } from "@emotion/react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import useFetchUptimeMonitorDetails from "../../../Hooks/useFetchUptimeMonitorDetails"; +import { useFetchUptimeMonitorById } from "../../../Hooks/monitorHooks"; import useCertificateFetch from "./Hooks/useCertificateFetch"; -import useChecksFetch from "./Hooks/useChecksFetch"; +import { useFetchChecks } from "../../../Hooks/checkHooks"; import { useTranslation } from "react-i18next"; // Constants @@ -50,7 +49,7 @@ const UptimeDetails = () => { const { t } = useTranslation(); const [monitorData, monitorStats, monitorIsLoading, monitorNetworkError] = - useFetchUptimeMonitorDetails({ + useFetchUptimeMonitorById({ monitorId, dateRange, trigger, @@ -66,10 +65,14 @@ const UptimeDetails = () => { }); const monitorType = monitor?.type; - const [checks, checksCount, checksAreLoading, checksNetworkError] = useChecksFetch({ + + const [checks, checksCount, checksAreLoading, checksNetworkError] = useFetchChecks({ monitorId, - monitorType, + type: monitorType, + sortOrder: "desc", + limit: null, dateRange, + filter: null, page, rowsPerPage, }); diff --git a/client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx b/client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx index ba4a139e3..63a40f85a 100644 --- a/client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx +++ b/client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx @@ -13,7 +13,7 @@ import TableSkeleton from "../../../../../Components/Table/skeleton"; // Utils import { useTheme } from "@emotion/react"; -import useUtils from "../../Hooks/useUtils"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; import { useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; @@ -58,7 +58,7 @@ const UptimeDataTable = ({ }) => { // Utils const navigate = useNavigate(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); const theme = useTheme(); const { t } = useTranslation(); diff --git a/client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx b/client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx deleted file mode 100644 index 1f1d15212..000000000 --- a/client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; -import { useTheme } from "@emotion/react"; -import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils"; - -export const useMonitorFetch = ({ - teamId, - limit, - page, - rowsPerPage, - filter, - field, - order, - triggerUpdate, -}) => { - const [monitorsAreLoading, setMonitorsAreLoading] = useState(false); - const [monitors, setMonitors] = useState(undefined); - const [filteredMonitors, setFilteredMonitors] = useState(undefined); - const [monitorsSummary, setMonitorsSummary] = useState(undefined); - const [networkError, setNetworkError] = useState(false); - - const theme = useTheme(); - const { getMonitorWithPercentage } = useMonitorUtils(); - useEffect(() => { - const fetchMonitors = async () => { - try { - setMonitorsAreLoading(true); - const res = await networkService.getMonitorsByTeamId({ - teamId, - limit, - types: ["http", "ping", "docker", "port"], - page, - rowsPerPage, - filter, - field, - order, - }); - const { monitors, filteredMonitors, summary } = res.data.data; - const mappedMonitors = filteredMonitors.map((monitor) => - getMonitorWithPercentage(monitor, theme) - ); - setMonitors(monitors); - setFilteredMonitors(mappedMonitors); - setMonitorsSummary(summary); - } catch (error) { - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setMonitorsAreLoading(false); - } - }; - fetchMonitors(); - }, [ - teamId, - limit, - field, - filter, - order, - page, - rowsPerPage, - theme, - triggerUpdate, - getMonitorWithPercentage, - ]); - return { - monitors, - filteredMonitors, - monitorsSummary, - monitorsAreLoading, - networkError, - }; -}; - -export default useMonitorFetch; diff --git a/client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx b/client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx deleted file mode 100644 index ff3288cdf..000000000 --- a/client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx +++ /dev/null @@ -1,110 +0,0 @@ -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"; - }; - - /* TODO Refactor: from here on shouldn't live in a custom hook, but on theme, or constants */ - const theme = useTheme(); - - const statusColor = { - up: theme.palette.success.lowContrast, - down: theme.palette.error.lowContrast, - paused: theme.palette.warning.lowContrast, - pending: theme.palette.warning.lowContrast, - }; - - const statusMsg = { - up: "Your site is up.", - down: "Your site is down.", - paused: "Pending...", - }; - - const pagespeedStatusMsg = { - up: "Live (collecting data)", - down: "Inactive", - paused: "Paused", - }; - - /* - TODO - This is used on - 1) Details > Gradient card */ - /* These are rediections. We should do something that maps up to success, down to error, and get the theme by that - See Client\src\Components\Label\index.jsx - */ - - const statusToTheme = { - up: "success", - down: "error", - paused: "warning", - pending: "secondary", - "cannot resolve": "tertiary", - }; - - const getStatusStyles = (status) => { - const themeColor = statusToTheme[status]; - - return { - backgroundColor: theme.palette[themeColor].lowContrast, - background: `linear-gradient(340deg, ${theme.palette[themeColor].main} -60%, ${theme.palette[themeColor].lowContrast} 35%)`, - borderColor: theme.palette[themeColor].lowContrast, - "& h2": { - color: theme.palette[themeColor].contrastText, - textTransform: "uppercase", - }, - "& p": { - color: theme.palette[themeColor].contrastText, - }, - }; - }; - - const statusStyles = { - up: { - backgroundColor: theme.palette.success.lowContrast, - background: `linear-gradient(340deg, ${theme.palette.tertiary.main} -60%, ${theme.palette.success.lowContrast} 35%)`, // CAIO_REVIEW - borderColor: theme.palette.success.contrastText, - // "& h2": { color: theme.palette.success.contrastText }, // CAIO_REVIEW - }, - down: { - backgroundColor: theme.palette.error.lowContrast, - background: `linear-gradient(340deg, ${theme.palette.tertiary.main} -60%, ${theme.palette.error.lowContrast} 35%)`, // CAIO_REVIEW - borderColor: theme.palette.error.contrastText, - "& h2": { color: theme.palette.error.contrastText }, // CAIO_REVIEW - "& .MuiTypography-root": { color: theme.palette.error.contrastText }, // CAIO_REVIEW - }, - paused: { - backgroundColor: theme.palette.warning.lowContrast, - background: `linear-gradient(340deg, ${theme.palette.tertiary.main} -60%, ${theme.palette.warning.lowContrast} 35%)`, // CAIO_REVIEW - borderColor: theme.palette.warning.contrastText, - "& h2": { color: theme.palette.warning.contrastText }, // CAIO_REVIEW - "& .MuiTypography-root": { color: theme.palette.warning.contrastText }, // CAIO_REVIEW - }, - pending: { - backgroundColor: theme.palette.warning.lowContrast, - background: `linear-gradient(340deg, ${theme.palette.tertiary.main} -60%, ${theme.palette.warning.lowContrast} 35%)`, // CAIO_REVIEW - borderColor: theme.palette.warning.contrastText, - "& h2": { color: theme.palette.warning.contrastText }, // CAIO_REVIEW - }, - }; - - /* These are rediections. We should do something that maps up to success, down to error, and get the theme by that - - */ - - return { - determineState, - statusColor, - statusMsg, - pagespeedStatusMsg, - statusStyles, - statusToTheme, - getStatusStyles, - }; -}; - -export default useUtils; diff --git a/client/src/Pages/Uptime/Monitors/index.jsx b/client/src/Pages/Uptime/Monitors/index.jsx index 793731190..030ce922c 100644 --- a/client/src/Pages/Uptime/Monitors/index.jsx +++ b/client/src/Pages/Uptime/Monitors/index.jsx @@ -28,9 +28,12 @@ import { useNavigate } from "react-router-dom"; import { useSelector, useDispatch } from "react-redux"; import { setRowsPerPage } from "../../../Features/UI/uiSlice"; import PropTypes from "prop-types"; -import useFetchMonitorsWithSummary from "../../../Hooks/useFetchMonitorsWithSummary"; -import useFetchMonitorsWithChecks from "../../../Hooks/useFetchMonitorsWithChecks"; +import { + useFetchMonitorsWithSummary, + useFetchMonitorsWithChecks, +} from "../../../Hooks/monitorHooks"; import { useTranslation } from "react-i18next"; + const TYPES = ["http", "ping", "docker", "port"]; const CreateMonitorButton = ({ shouldRender }) => { // Utils @@ -61,7 +64,6 @@ CreateMonitorButton.propTypes = { const UptimeMonitors = () => { // Redux state - const { user } = useSelector((state) => state.auth); const rowsPerPage = useSelector((state) => state.ui.monitors.rowsPerPage); // Local state @@ -78,7 +80,6 @@ const UptimeMonitors = () => { // Utils const theme = useTheme(); - const navigate = useNavigate(); const isAdmin = useIsAdmin(); const dispatch = useDispatch(); const { t } = useTranslation(); @@ -104,11 +105,8 @@ const UptimeMonitors = () => { setMonitorUpdateTrigger((prev) => !prev); }, []); - const teamId = user.teamId; - const [monitors, monitorsSummary, monitorsWithSummaryIsLoading, networkError] = useFetchMonitorsWithSummary({ - teamId, types: TYPES, monitorUpdateTrigger, }); @@ -138,7 +136,6 @@ const UptimeMonitors = () => { monitorsWithChecksIsLoading, monitorsWithChecksNetworkError, ] = useFetchMonitorsWithChecks({ - teamId, types: effectiveTypes, limit: 25, page: page, diff --git a/client/src/Utils/Logger.js b/client/src/Utils/Logger.js index b22cc0a22..f132ad408 100644 --- a/client/src/Utils/Logger.js +++ b/client/src/Utils/Logger.js @@ -6,8 +6,7 @@ class Logger { constructor() { let logLevel = LOG_LEVEL; this.unsubscribe = store.subscribe(() => { - const state = store.getState(); - logLevel = state.settings.logLevel || "debug"; + logLevel = "debug"; this.updateLogLevel(logLevel); }); } diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index 3d124a3e6..ecde44a9e 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); @@ -137,36 +135,6 @@ class NetworkService { }); } - /** - * - * ************************************ - * Gets monitors and summary of stats by TeamID - * ************************************ - * - * @async - * @param {Object} config - The configuration object. - * @param {string} config.teamId - Team ID - * @param {Array} config.types - Array of monitor types - * @returns {Promise} The response from the axios POST request. - */ - async getMonitorsSummaryByTeamId(config) { - const params = new URLSearchParams(); - - if (config.types) { - config.types.forEach((type) => { - params.append("type", type); - }); - } - return this.axiosInstance.get( - `/monitors/team/summary/${config.teamId}?${params.toString()}`, - { - headers: { - "Content-Type": "application/json", - }, - } - ); - } - /** * ************************************ * Get all uptime monitors for a Team @@ -186,7 +154,7 @@ class NetworkService { */ async getMonitorsByTeamId(config) { - const { teamId, limit, types, page, rowsPerPage, filter, field, order } = config; + const { limit, types, page, rowsPerPage, filter, field, order } = config; const params = new URLSearchParams(); if (limit) params.append("limit", limit); @@ -201,7 +169,7 @@ class NetworkService { if (field) params.append("field", field); if (order) params.append("order", order); - return this.axiosInstance.get(`/monitors/team/${teamId}?${params.toString()}`, { + return this.axiosInstance.get(`/monitors/team?${params.toString()}`, { headers: { "Content-Type": "application/json", }, @@ -304,7 +272,7 @@ class NetworkService { * @returns {Promise} The response from the axios DELETE request. */ async deleteChecksByTeamId(config) { - return this.axiosInstance.delete(`/checks/team/${config.teamId}`, { + return this.axiosInstance.delete(`/checks/team`, { headers: { "Content-Type": "application/json", }, @@ -587,7 +555,7 @@ class NetworkService { * */ - async getChecksByMonitor(config) { + getChecksByMonitor = async (config) => { const params = new URLSearchParams(); if (config.type) params.append("type", config.type); if (config.sortOrder) params.append("sortOrder", config.sortOrder); @@ -599,7 +567,7 @@ class NetworkService { if (config.status !== undefined) params.append("status", config.status); return this.axiosInstance.get(`/checks/${config.monitorId}?${params.toString()}`); - } + }; /** * ************************************ @@ -618,7 +586,7 @@ class NetworkService { * @returns {Promise} The response from the axios GET request. * */ - async getChecksByTeam(config) { + getChecksByTeam = async (config) => { const params = new URLSearchParams(); if (config.sortOrder) params.append("sortOrder", config.sortOrder); if (config.limit) params.append("limit", config.limit); @@ -627,8 +595,8 @@ class NetworkService { if (config.page) params.append("page", config.page); if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); if (config.status !== undefined) params.append("status", config.status); - return this.axiosInstance.get(`/checks/team/${config.teamId}?${params.toString()}`); - } + return this.axiosInstance.get(`/checks/team?${params.toString()}`); + }; /** * ************************************ @@ -870,8 +838,7 @@ class NetworkService { } async getStatusPagesByTeamId(config) { - const { teamId } = config; - return this.axiosInstance.get(`/status-page/team/${teamId}`, { + return this.axiosInstance.get(`/status-page/team`, { headers: { "Content-Type": "application/json", }, @@ -879,11 +846,9 @@ class NetworkService { } async createStatusPage(config) { - const { user, form, isCreate } = config; + const { form, isCreate } = config; const fd = new FormData(); - fd.append("teamId", user.teamId); - fd.append("userId", user._id); fd.append("type", form.type); form.isPublished !== undefined && fd.append("isPublished", form.isPublished); form.companyName && fd.append("companyName", form.companyName); @@ -951,7 +916,7 @@ class NetworkService { // Fetch monitors with summary by TeamID // ************************************ async getMonitorsWithSummaryByTeamId(config) { - const { teamId, types } = config; + const { types } = config; const params = new URLSearchParams(); if (types) { @@ -960,21 +925,18 @@ class NetworkService { }); } - return this.axiosInstance.get( - `/monitors/summary/team/${teamId}?${params.toString()}`, - { - headers: { - "Content-Type": "application/json", - }, - } - ); + return this.axiosInstance.get(`/monitors/summary/team?${params.toString()}`, { + headers: { + "Content-Type": "application/json", + }, + }); } // ************************************ // Fetch monitors with checks by TeamID // ************************************ async getMonitorsWithChecksByTeamId(config) { - const { teamId, limit, types, page, rowsPerPage, filter, field, order } = config; + const { limit, types, page, rowsPerPage, filter, field, order } = config; const params = new URLSearchParams(); if (limit) params.append("limit", limit); @@ -989,14 +951,11 @@ class NetworkService { if (field) params.append("field", field); if (order) params.append("order", order); - return this.axiosInstance.get( - `/monitors/team/${teamId}/with-checks?${params.toString()}`, - { - headers: { - "Content-Type": "application/json", - }, - } - ); + return this.axiosInstance.get(`/monitors/team/with-checks?${params.toString()}`, { + headers: { + "Content-Type": "application/json", + }, + }); } // ************************************ @@ -1034,8 +993,7 @@ class NetworkService { } async getNotificationsByTeamId(config) { - const { teamId } = config; - return this.axiosInstance.get(`/notifications/team/${teamId}`); + return this.axiosInstance.get(`/notifications/team`); } async deleteNotificationById(config) { diff --git a/client/src/Utils/Theme/constants.js b/client/src/Utils/Theme/constants.js index 920a55f69..48dcdfd85 100644 --- a/client/src/Utils/Theme/constants.js +++ b/client/src/Utils/Theme/constants.js @@ -85,6 +85,7 @@ const semanticColors = { const newColors = { offWhite: "#FEFEFE", offBlack: "#131315", + gray10: "#F4F4FF", gray100: "#F3F3F3", gray200: "#EFEFEF", gray500: "#A2A3A3", @@ -175,6 +176,12 @@ const newSemanticColors = { dark: newColors.blueGray600, }, }, + primaryBackground: { + main: { + light: newColors.gray10, + dark: "#000000", + }, + }, secondary: { main: { light: newColors.gray200, diff --git a/client/src/Utils/Theme/globalTheme.js b/client/src/Utils/Theme/globalTheme.js index 37526ed73..58d9943be 100644 --- a/client/src/Utils/Theme/globalTheme.js +++ b/client/src/Utils/Theme/globalTheme.js @@ -380,6 +380,33 @@ const baseTheme = (palette) => ({ }, }, }, + + MuiAutocomplete: { + styleOverrides: { + root: ({ theme }) => ({ + "& .MuiOutlinedInput-root": { + paddingTop: 0, + paddingBottom: 0, + }, + "& fieldset": { + borderColor: theme.palette.primary.lowContrast, + borderRadius: theme.shape.borderRadius, + }, + "& .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset": + { + borderColor: theme.palette.primary.lowContrast, + }, + + "& .MuiAutocomplete-tag": { + color: theme.palette.primary.contrastText, + backgroundColor: theme.palette.primary.lowContrast, + }, + "& .MuiChip-deleteIcon": { + color: theme.palette.primary.contrastText, // CAIO_REVIEW + }, + }), + }, + }, MuiTab: { styleOverrides: { root: ({ theme }) => ({ diff --git a/client/src/assets/Images/background.svg b/client/src/assets/Images/background.svg new file mode 100644 index 000000000..728597e95 --- /dev/null +++ b/client/src/assets/Images/background.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/src/locales/en.json b/client/src/locales/en.json index 655f427bf..bc6c8bbb9 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -525,6 +525,7 @@ "notifyEmails": "Also notify via email to multiple addresses (coming soon)", "seperateEmails": "You can separate multiple emails with a comma", "checkFrequency": "Check frequency", + "advancedMatching": "Advanced matching", "matchMethod": "Match Method", "expectedValue": "Expected value", "deleteDialogTitle": "Do you really want to delete this monitor?", diff --git a/client/src/store.js b/client/src/store.js index 92ddfad00..69aa3d50b 100644 --- a/client/src/store.js +++ b/client/src/store.js @@ -1,11 +1,7 @@ import { configureStore, combineReducers } from "@reduxjs/toolkit"; -import uptimeMonitorsReducer from "./Features/UptimeMonitors/uptimeMonitorsSlice"; -import infrastructureMonitorsReducer from "./Features/InfrastructureMonitors/infrastructureMonitorsSlice"; -import pageSpeedMonitorReducer from "./Features/PageSpeedMonitor/pageSpeedMonitorSlice"; import authReducer from "./Features/Auth/authSlice"; import uiReducer from "./Features/UI/uiSlice"; -import settingsReducer from "./Features/Settings/settingsSlice"; import storage from "redux-persist/lib/storage"; import { persistReducer, persistStore, createTransform } from "redux-persist"; @@ -23,17 +19,13 @@ const authTransform = createTransform( const persistConfig = { key: "root", storage, - whitelist: ["auth", "monitors", "pageSpeed", "ui", "settings"], + whitelist: ["auth", "ui"], transforms: [authTransform], }; const rootReducer = combineReducers({ - uptimeMonitors: uptimeMonitorsReducer, - infrastructureMonitors: infrastructureMonitorsReducer, auth: authReducer, - pageSpeedMonitors: pageSpeedMonitorReducer, ui: uiReducer, - settings: settingsReducer, }); const persistedReducer = persistReducer(persistConfig, rootReducer); diff --git a/server/controllers/checkController.js b/server/controllers/checkController.js index 2addd7359..387839beb 100755 --- a/server/controllers/checkController.js +++ b/server/controllers/checkController.js @@ -54,7 +54,18 @@ class CheckController { } try { - const result = await this.db.getChecksByMonitor(req); + const { monitorId } = req.params; + let { type, sortOrder, dateRange, filter, page, rowsPerPage, status } = req.query; + const result = await this.db.getChecksByMonitor({ + monitorId, + type, + sortOrder, + dateRange, + filter, + page, + rowsPerPage, + status, + }); return res.success({ msg: this.stringService.checkGet, @@ -74,8 +85,17 @@ class CheckController { return; } try { - const checkData = await this.db.getChecksByTeam(req); + let { sortOrder, dateRange, filter, page, rowsPerPage } = req.query; + const { teamId } = req.user; + const checkData = await this.db.getChecksByTeam({ + sortOrder, + dateRange, + filter, + page, + rowsPerPage, + teamId, + }); return res.success({ msg: this.stringService.checkGet, data: checkData, @@ -114,7 +134,8 @@ class CheckController { } try { - const deletedCount = await this.db.deleteChecksByTeamId(req.params.teamId); + const { teamId } = req.user; + const deletedCount = await this.db.deleteChecksByTeamId(teamId); return res.success({ msg: this.stringService.checkDelete, @@ -137,9 +158,7 @@ class CheckController { try { // Get user's teamId - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); + const { teamId } = req.user; const ttl = parseInt(req.body.ttl, 10) * SECONDS_PER_DAY; await this.db.updateChecksTTL(teamId, ttl); diff --git a/server/controllers/maintenanceWindowController.js b/server/controllers/maintenanceWindowController.js index a11f957ed..20077cc65 100755 --- a/server/controllers/maintenanceWindowController.js +++ b/server/controllers/maintenanceWindowController.js @@ -7,8 +7,6 @@ import { getMaintenanceWindowsByTeamIdQueryValidation, deleteMaintenanceWindowByIdParamValidation, } from "../validation/joi.js"; -import jwt from "jsonwebtoken"; -import { getTokenFromHeaders } from "../utils/utils.js"; import { handleValidationError, handleError } from "./controllerUtils.js"; const SERVICE_NAME = "maintenanceWindowController"; @@ -28,9 +26,7 @@ class MaintenanceWindowController { return; } try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); + const { teamId } = req.user; const monitorIds = req.body.monitors; const dbTransactions = monitorIds.map((monitorId) => { return this.db.createMaintenanceWindow({ @@ -81,9 +77,7 @@ class MaintenanceWindowController { } try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); + const { teamId } = req.user; const maintenanceWindows = await this.db.getMaintenanceWindowsByTeamId( teamId, req.query diff --git a/server/controllers/monitorController.js b/server/controllers/monitorController.js index 00b525125..228eabbb0 100755 --- a/server/controllers/monitorController.js +++ b/server/controllers/monitorController.js @@ -78,7 +78,14 @@ class MonitorController { getUptimeDetailsById = async (req, res, next) => { try { - const data = await this.db.getUptimeDetailsById(req); + const { monitorId } = req.params; + const { dateRange, normalize } = req.query; + + const data = await this.db.getUptimeDetailsById({ + monitorId, + dateRange, + normalize, + }); return res.success({ msg: this.stringService.monitorGetByIdSuccess, data, @@ -107,7 +114,17 @@ class MonitorController { } try { - const monitorStats = await this.db.getMonitorStatsById(req); + let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query; + const { monitorId } = req.params; + + const monitorStats = await this.db.getMonitorStatsById({ + monitorId, + limit, + sortOrder, + dateRange, + numToDisplay, + normalize, + }); return res.success({ msg: this.stringService.monitorStatsById, data: monitorStats, @@ -135,7 +152,9 @@ class MonitorController { return; } try { - const monitor = await this.db.getHardwareDetailsById(req); + const { monitorId } = req.params; + const { dateRange } = req.query; + const monitor = await this.db.getHardwareDetailsById({ monitorId, dateRange }); return res.success({ msg: this.stringService.monitorGetByIdSuccess, data: monitor, @@ -219,7 +238,12 @@ class MonitorController { } try { - const monitor = await this.db.createMonitor(req, res); + const { _id, teamId } = req.user; + const monitor = await this.db.createMonitor({ + body: req.body, + teamId, + userId: _id, + }); // Add monitor to job queue this.jobQueue.addJob(monitor._id, monitor); @@ -261,10 +285,10 @@ class MonitorController { throw new Error("File is empty"); } - const { userId, teamId } = req.body; + const { _id, teamId } = req.user; - if (!userId || !teamId) { - throw new Error("Missing userId or teamId in form data"); + if (!_id || !teamId) { + throw new Error("Missing userId or teamId"); } // Get file buffer from memory and convert to string @@ -299,7 +323,7 @@ class MonitorController { } const enrichedData = data.map((monitor) => ({ - userId, + userId: _id, teamId, ...monitor, description: monitor.description || monitor.name || monitor.url, @@ -388,7 +412,8 @@ class MonitorController { } try { - const monitor = await this.db.deleteMonitor(req, res, next); + const monitorId = req.params.monitorId; + const monitor = await this.db.deleteMonitor({ monitorId }); await this.jobQueue.deleteJob(monitor); await this.db.deleteStatusPagesByMonitorId(monitor._id); return res.success({ msg: this.stringService.monitorDelete }); @@ -410,9 +435,7 @@ class MonitorController { */ deleteAllMonitors = async (req, res, next) => { try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); + const { teamId } = req.user; const { monitors, deletedCount } = await this.db.deleteAllMonitors(teamId); await Promise.all( monitors.map(async (monitor) => { @@ -494,18 +517,8 @@ class MonitorController { } try { - const monitor = await Monitor.findOneAndUpdate( - { _id: req.params.monitorId }, - [ - { - $set: { - isActive: { $not: "$isActive" }, - status: "$$REMOVE", - }, - }, - ], - { new: true } - ); + const monitorId = req.params.monitorId; + const monitor = await this.db.pauseMonitor({ monitorId }); monitor.isActive === true ? await this.jobQueue.resumeJob(monitor._id, monitor) : await this.jobQueue.pauseJob(monitor); @@ -534,9 +547,7 @@ class MonitorController { */ addDemoMonitors = async (req, res, next) => { try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { _id, teamId } = jwt.verify(token, jwtSecret); + const { _id, teamId } = req.user; const demoMonitors = await this.db.addDemoMonitors(_id, teamId); await Promise.all( demoMonitors.map((monitor) => this.jobQueue.addJob(monitor._id, monitor)) @@ -603,7 +614,19 @@ class MonitorController { } try { - const monitors = await this.db.getMonitorsByTeamId(req); + let { limit, type, page, rowsPerPage, filter, field, order } = req.query; + const teamId = req.user.teamId; + + const monitors = await this.db.getMonitorsByTeamId({ + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + teamId, + }); return res.success({ msg: this.stringService.monitorGetByTeamId, data: monitors, @@ -622,7 +645,15 @@ class MonitorController { } try { - const result = await this.db.getMonitorsAndSummaryByTeamId(req); + const { explain } = req; + const { type } = req.query; + const { teamId } = req.user; + + const result = await this.db.getMonitorsAndSummaryByTeamId({ + type, + explain, + teamId, + }); return res.success({ msg: "OK", // TODO data: result, @@ -641,7 +672,21 @@ class MonitorController { } try { - const result = await this.db.getMonitorsWithChecksByTeamId(req); + const { explain } = req; + let { limit, type, page, rowsPerPage, filter, field, order } = req.query; + const { teamId } = req.user; + + const result = await this.db.getMonitorsWithChecksByTeamId({ + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + teamId, + explain, + }); return res.success({ msg: "OK", // TODO data: result, @@ -653,11 +698,7 @@ class MonitorController { seedDb = async (req, res, next) => { try { - const { type } = req.body; - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { _id, teamId } = jwt.verify(token, jwtSecret); - + const { _id, teamId } = req.user; await seedDb(_id, teamId); res.success({ msg: "Database seeded" }); } catch (error) { diff --git a/server/controllers/notificationController.js b/server/controllers/notificationController.js index 4efcf4988..a9c399901 100755 --- a/server/controllers/notificationController.js +++ b/server/controllers/notificationController.js @@ -140,7 +140,7 @@ class NotificationController { getNotificationsByTeamId = async (req, res, next) => { try { - const notifications = await this.db.getNotificationsByTeamId(req.params.teamId); + const notifications = await this.db.getNotificationsByTeamId(req.user.teamId); return res.success({ msg: "Notifications fetched successfully", data: notifications, diff --git a/server/controllers/statusPageController.js b/server/controllers/statusPageController.js index eb3e69484..577cf4997 100755 --- a/server/controllers/statusPageController.js +++ b/server/controllers/statusPageController.js @@ -24,7 +24,13 @@ class StatusPageController { } try { - const statusPage = await this.db.createStatusPage(req.body, req.file); + const { _id, teamId } = req.user; + const statusPage = await this.db.createStatusPage({ + statusPageData: req.body, + image: req.file, + userId: _id, + teamId, + }); return res.success({ msg: this.stringService.statusPageCreate, data: statusPage, @@ -93,7 +99,7 @@ class StatusPageController { getStatusPagesByTeamId = async (req, res, next) => { try { - const teamId = req.params.teamId; + const teamId = req.user.teamId; const statusPages = await this.db.getStatusPagesByTeamId(teamId); return res.success({ diff --git a/server/db/models/Monitor.js b/server/db/models/Monitor.js index 26a79b815..7bb38f520 100755 --- a/server/db/models/Monitor.js +++ b/server/db/models/Monitor.js @@ -3,6 +3,7 @@ import HardwareCheck from "./HardwareCheck.js"; import PageSpeedCheck from "./PageSpeedCheck.js"; import Check from "./Check.js"; import MonitorStats from "./MonitorStats.js"; +import StatusPage from "./StatusPage.js"; const MonitorSchema = mongoose.Schema( { @@ -105,6 +106,9 @@ MonitorSchema.pre("findOneAndDelete", async function (next) { await Check.deleteMany({ monitorId: doc._id }); } + // Deal with status pages + await StatusPage.updateMany({ monitors: doc._id }, { $pull: { monitors: doc._id } }); + await MonitorStats.deleteMany({ monitorId: doc._id.toString() }); next(); } catch (error) { diff --git a/server/db/mongo/modules/checkModule.js b/server/db/mongo/modules/checkModule.js index 8e71dd6eb..f409fd3d2 100755 --- a/server/db/mongo/modules/checkModule.js +++ b/server/db/mongo/modules/checkModule.js @@ -57,10 +57,17 @@ const createChecks = async (checks) => { * @returns {Promise>} * @throws {Error} */ -const getChecksByMonitor = async (req) => { +const getChecksByMonitor = async ({ + monitorId, + type, + sortOrder, + dateRange, + filter, + page, + rowsPerPage, + status, +}) => { try { - const { monitorId } = req.params; - let { type, sortOrder, dateRange, filter, page, rowsPerPage, status } = req.query; status = typeof status !== "undefined" ? false : undefined; page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); @@ -142,12 +149,17 @@ const getChecksByMonitor = async (req) => { } }; -const getChecksByTeam = async (req) => { +const getChecksByTeam = async ({ + sortOrder, + dateRange, + filter, + page, + rowsPerPage, + teamId, +}) => { try { - let { sortOrder, dateRange, filter, page, rowsPerPage } = req.query; page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); - const { teamId } = req.params; const matchStage = { teamId: ObjectId.createFromHexString(teamId), status: false, diff --git a/server/db/mongo/modules/diagnosticModule.js b/server/db/mongo/modules/diagnosticModule.js index af6d83764..7a51ad193 100755 --- a/server/db/mongo/modules/diagnosticModule.js +++ b/server/db/mongo/modules/diagnosticModule.js @@ -6,10 +6,7 @@ import { buildMonitorSummaryByTeamIdPipeline, buildMonitorsByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline, - buildDePINDetailsByDateRange, - buildDePINLatestChecks, } from "./monitorModuleQueries.js"; -import { getDateRange } from "./monitorModule.js"; const getMonitorsByTeamIdExecutionStats = async (req) => { try { diff --git a/server/db/mongo/modules/monitorModule.js b/server/db/mongo/modules/monitorModule.js index 3d9adab39..32ed8b146 100755 --- a/server/db/mongo/modules/monitorModule.js +++ b/server/db/mongo/modules/monitorModule.js @@ -3,7 +3,6 @@ import MonitorStats from "../../models/MonitorStats.js"; import Check from "../../models/Check.js"; import PageSpeedCheck from "../../models/PageSpeedCheck.js"; import HardwareCheck from "../../models/HardwareCheck.js"; -import Notification from "../../models/Notification.js"; import { NormalizeData, NormalizeDataUptimeDetails } from "../../../utils/dataUtils.js"; import ServiceRegistry from "../../../service/serviceRegistry.js"; import StringService from "../../../service/stringService.js"; @@ -15,14 +14,11 @@ import { ObjectId } from "mongodb"; import { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline, - buildMonitorStatsPipeline, buildMonitorSummaryByTeamIdPipeline, buildMonitorsByTeamIdPipeline, buildMonitorsAndSummaryByTeamIdPipeline, buildMonitorsWithChecksByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline, - buildDePINDetailsByDateRange, - buildDePINLatestChecks, } from "./monitorModuleQueries.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -335,11 +331,9 @@ const calculateGroupStats = (group) => { * @returns {Promise} * @throws {Error} */ -const getUptimeDetailsById = async (req) => { +const getUptimeDetailsById = async ({ monitorId, dateRange, normalize }) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { - const { monitorId } = req.params; - const { dateRange, normalize } = req.query; const dates = getDateRange(dateRange); const formatLookup = { recent: "%Y-%m-%dT%H:%M:00Z", @@ -392,11 +386,16 @@ const getUptimeDetailsById = async (req) => { * @returns {Promise} * @throws {Error} */ -const getMonitorStatsById = async (req) => { +const getMonitorStatsById = async ({ + monitorId, + limit, + sortOrder, + dateRange, + numToDisplay, + normalize, +}) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { - const { monitorId } = req.params; - // Get monitor, if we can't find it, abort with error const monitor = await Monitor.findById(monitorId); if (monitor === null || monitor === undefined) { @@ -404,7 +403,6 @@ const getMonitorStatsById = async (req) => { } // Get query params - let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query; const sort = sortOrder === "asc" ? 1 : -1; // Get Checks for monitor in date range requested @@ -454,10 +452,8 @@ const getMonitorStatsById = async (req) => { } }; -const getHardwareDetailsById = async (req) => { +const getHardwareDetailsById = async ({ monitorId, dateRange }) => { try { - const { monitorId } = req.params; - const { dateRange } = req.query; const monitor = await Monitor.findById(monitorId); const dates = getDateRange(dateRange); const formatLookup = { @@ -509,8 +505,16 @@ const getMonitorById = async (monitorId) => { } }; -const getMonitorsByTeamId = async (req) => { - let { limit, type, page, rowsPerPage, filter, field, order } = req.query; +const getMonitorsByTeamId = async ({ + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + teamId, +}) => { limit = parseInt(limit); page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); @@ -519,7 +523,7 @@ const getMonitorsByTeamId = async (req) => { order = "asc"; } // Build match stage - const matchStage = { teamId: ObjectId.createFromHexString(req.params.teamId) }; + const matchStage = { teamId: ObjectId.createFromHexString(teamId) }; if (type !== undefined) { matchStage.type = Array.isArray(type) ? { $in: type } : type; } @@ -557,16 +561,15 @@ const getMonitorsByTeamId = async (req) => { return { summary, monitors, filteredMonitors: normalizedFilteredMonitors }; }; -const getMonitorsAndSummaryByTeamId = async (req) => { +const getMonitorsAndSummaryByTeamId = async ({ type, explain, teamId }) => { try { - const { type } = req.query; - const teamId = ObjectId.createFromHexString(req.params.teamId); - const matchStage = { teamId }; + const parsedTeamId = ObjectId.createFromHexString(teamId); + const matchStage = { teamId: parsedTeamId }; if (type !== undefined) { matchStage.type = Array.isArray(type) ? { $in: type } : type; } - if (req.explain === true) { + if (explain === true) { return Monitor.aggregate( buildMonitorsAndSummaryByTeamIdPipeline({ matchStage }) ).explain("executionStats"); @@ -584,9 +587,18 @@ const getMonitorsAndSummaryByTeamId = async (req) => { } }; -const getMonitorsWithChecksByTeamId = async (req) => { +const getMonitorsWithChecksByTeamId = async ({ + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + teamId, + explain, +}) => { try { - let { limit, type, page, rowsPerPage, filter, field, order } = req.query; limit = parseInt(limit); page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); @@ -594,14 +606,14 @@ const getMonitorsWithChecksByTeamId = async (req) => { field = "name"; order = "asc"; } - const teamId = ObjectId.createFromHexString(req.params.teamId); + const parsedTeamId = ObjectId.createFromHexString(teamId); // Build match stage - const matchStage = { teamId }; + const matchStage = { teamId: parsedTeamId }; if (type !== undefined) { matchStage.type = Array.isArray(type) ? { $in: type } : type; } - if (req.explain === true) { + if (explain === true) { return Monitor.aggregate( buildMonitorsWithChecksByTeamIdPipeline({ matchStage, @@ -653,9 +665,9 @@ const getMonitorsWithChecksByTeamId = async (req) => { * @returns {Promise} * @throws {Error} */ -const createMonitor = async (req, res) => { +const createMonitor = async ({ body, teamId, userId }) => { try { - const monitor = new Monitor({ ...req.body }); + const monitor = new Monitor({ ...body, teamId, userId }); const saved = await monitor.save(); return saved; } catch (error) { @@ -694,15 +706,15 @@ const createBulkMonitors = async (req) => { * @returns {Promise} * @throws {Error} */ -const deleteMonitor = async (req, res) => { +const deleteMonitor = async ({ monitorId }) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); - - const monitorId = req.params.monitorId; try { const monitor = await Monitor.findByIdAndDelete(monitorId); + if (!monitor) { throw new Error(stringService.getDbFindMonitorById(monitorId)); } + return monitor; } catch (error) { error.service = SERVICE_NAME; @@ -788,6 +800,29 @@ const addDemoMonitors = async (userId, teamId) => { } }; +const pauseMonitor = async ({ monitorId }) => { + try { + const monitor = await Monitor.findOneAndUpdate( + { _id: monitorId }, + [ + { + $set: { + isActive: { $not: "$isActive" }, + status: "$$REMOVE", + }, + }, + ], + { new: true } + ); + + return monitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "pauseMonitor"; + throw error; + } +}; + export { getAllMonitors, getAllMonitorsWithUptimeStats, @@ -805,6 +840,7 @@ export { editMonitor, addDemoMonitors, getHardwareDetailsById, + pauseMonitor, }; // Helper functions diff --git a/server/db/mongo/modules/monitorModuleQueries.js b/server/db/mongo/modules/monitorModuleQueries.js index f17eff2c2..35ddb8478 100755 --- a/server/db/mongo/modules/monitorModuleQueries.js +++ b/server/db/mongo/modules/monitorModuleQueries.js @@ -902,102 +902,6 @@ const buildGetMonitorsByTeamIdPipeline = (req) => { ]; }; -const buildDePINDetailsByDateRange = (monitor, dates, dateString) => { - return [ - { - $match: { - monitorId: monitor._id, - updatedAt: { $gte: dates.start, $lte: dates.end }, - }, - }, - { - $project: { - _id: 0, - city: 1, - updatedAt: 1, - "location.lat": 1, - "location.lng": 1, - responseTime: 1, - }, - }, - { - $facet: { - groupedMapChecks: [ - { - $group: { - _id: { - city: "$city", - lat: "$location.lat", - lng: "$location.lng", - }, - - avgResponseTime: { - $avg: "$responseTime", - }, - }, - }, - ], - groupedChecks: [ - { - $group: { - _id: { - date: { - $dateToString: { - format: dateString, - date: "$updatedAt", - }, - }, - }, - avgResponseTime: { - $avg: "$responseTime", - }, - }, - }, - { - $sort: { - "_id.date": 1, - }, - }, - ], - }, - }, - { - $project: { - groupedMapChecks: "$groupedMapChecks", - groupedChecks: "$groupedChecks", - }, - }, - ]; -}; - -const buildDePINLatestChecks = (monitor) => { - return [ - { - $match: { - monitorId: monitor._id, - }, - }, - { - $sort: { updatedAt: -1 }, - }, - { - $limit: 5, - }, - { - $project: { - responseTime: 1, - city: 1, - countryCode: 1, - uptBurnt: { - $toString: { - $ifNull: ["$uptBurnt", 0], - }, - }, - }, - }, - ]; -}; - export { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline, @@ -1008,6 +912,4 @@ export { buildMonitorsAndSummaryByTeamIdPipeline, buildMonitorsWithChecksByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline, - buildDePINDetailsByDateRange, - buildDePINLatestChecks, }; diff --git a/server/db/mongo/modules/statusPageModule.js b/server/db/mongo/modules/statusPageModule.js index cd985588a..d6331fcf8 100755 --- a/server/db/mongo/modules/statusPageModule.js +++ b/server/db/mongo/modules/statusPageModule.js @@ -1,16 +1,19 @@ import StatusPage from "../../models/StatusPage.js"; -import Monitor from "../../models/Monitor.js"; import { NormalizeData } from "../../../utils/dataUtils.js"; import ServiceRegistry from "../../../service/serviceRegistry.js"; import StringService from "../../../service/stringService.js"; const SERVICE_NAME = "statusPageModule"; -const createStatusPage = async (statusPageData, image) => { +const createStatusPage = async ({ statusPageData, image, userId, teamId }) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { - const statusPage = new StatusPage({ ...statusPageData }); + const statusPage = new StatusPage({ + ...statusPageData, + userId, + teamId, + }); if (image) { statusPage.logo = { data: image.buffer, @@ -103,7 +106,10 @@ const getStatusPage = async (url) => { }, }, { - $unwind: "$monitors", + $unwind: { + path: "$monitors", + preserveNullAndEmptyArrays: true, + }, }, { $lookup: { @@ -150,9 +156,17 @@ const getStatusPage = async (url) => { url: 1, }, monitors: { - $sortArray: { - input: "$monitors", - sortBy: { orderIndex: 1 }, + $filter: { + input: { + $sortArray: { + input: "$monitors", + sortBy: { orderIndex: 1 }, + }, + }, + as: "monitor", + cond: { + $ne: ["$$monitor.orderIndex", -1], + }, }, }, }, diff --git a/server/routes/checkRoute.js b/server/routes/checkRoute.js index bab45e4d4..aedca991a 100755 --- a/server/routes/checkRoute.js +++ b/server/routes/checkRoute.js @@ -11,7 +11,15 @@ class CheckRoutes { } initRoutes() { + this.router.get("/team", this.checkController.getChecksByTeam); + this.router.delete( + "/team", + isAllowed(["admin", "superadmin"]), + this.checkController.deleteChecksByTeamId + ); + this.router.get("/:monitorId", this.checkController.getChecksByMonitor); + this.router.post( "/:monitorId", verifyOwnership(Monitor, "monitorId"), @@ -23,14 +31,6 @@ class CheckRoutes { this.checkController.deleteChecks ); - this.router.get("/team/:teamId", this.checkController.getChecksByTeam); - - this.router.delete( - "/team/:teamId", - isAllowed(["admin", "superadmin"]), - this.checkController.deleteChecksByTeamId - ); - this.router.put( "/team/ttl", isAllowed(["admin", "superadmin"]), diff --git a/server/routes/monitorRoute.js b/server/routes/monitorRoute.js index 6d1db90ad..0dabab407 100755 --- a/server/routes/monitorRoute.js +++ b/server/routes/monitorRoute.js @@ -35,17 +35,17 @@ class MonitorRoutes { fetchMonitorCertificate ); }); + this.router.get("/team", this.monitorController.getMonitorsByTeamId); + this.router.get("/:monitorId", this.monitorController.getMonitorById); - this.router.get("/team/:teamId", this.monitorController.getMonitorsByTeamId); - this.router.get( - "/summary/team/:teamId", + "/summary/team", this.monitorController.getMonitorsAndSummaryByTeamId ); this.router.get( - "/team/:teamId/with-checks", + "/team/with-checks", this.monitorController.getMonitorsWithChecksByTeamId ); diff --git a/server/routes/notificationRoute.js b/server/routes/notificationRoute.js index 064cd4b5d..ace8bdae1 100755 --- a/server/routes/notificationRoute.js +++ b/server/routes/notificationRoute.js @@ -17,10 +17,7 @@ class NotificationRoutes { this.router.post("/", this.notificationController.createNotification); - this.router.get( - "/team/:teamId", - this.notificationController.getNotificationsByTeamId - ); + this.router.get("/team", this.notificationController.getNotificationsByTeamId); this.router.delete("/:id", this.notificationController.deleteNotification); diff --git a/server/routes/statusPageRoute.js b/server/routes/statusPageRoute.js index 4e6a4d248..98b3a3433 100755 --- a/server/routes/statusPageRoute.js +++ b/server/routes/statusPageRoute.js @@ -12,11 +12,7 @@ class StatusPageRoutes { initRoutes() { this.router.get("/", this.statusPageController.getStatusPage); - this.router.get( - "/team/:teamId", - verifyJWT, - this.statusPageController.getStatusPagesByTeamId - ); + this.router.get("/team", verifyJWT, this.statusPageController.getStatusPagesByTeamId); this.router.get("/:url", this.statusPageController.getStatusPageByUrl); this.router.post( diff --git a/server/validation/joi.js b/server/validation/joi.js index 271cd2d21..622cb9148 100755 --- a/server/validation/joi.js +++ b/server/validation/joi.js @@ -126,9 +126,7 @@ const getMonitorByIdQueryValidation = joi.object({ normalize: joi.boolean(), }); -const getMonitorsByTeamIdParamValidation = joi.object({ - teamId: joi.string().required(), -}); +const getMonitorsByTeamIdParamValidation = joi.object({}); const getMonitorsByTeamIdQueryValidation = joi.object({ limit: joi.number(), @@ -167,8 +165,6 @@ const getCertificateParamValidation = joi.object({ const createMonitorBodyValidation = joi.object({ _id: joi.string(), - userId: joi.string().required(), - teamId: joi.string().required(), name: joi.string().required(), description: joi.string().required(), type: joi.string().required(), @@ -190,7 +186,12 @@ const createMonitorBodyValidation = joi.object({ matchMethod: joi.string(), }); -const createMonitorsBodyValidation = joi.array().items(createMonitorBodyValidation); +const createMonitorsBodyValidation = joi.array().items( + createMonitorBodyValidation.keys({ + userId: joi.string().required(), + teamId: joi.string().required(), + }) +); const editMonitorBodyValidation = joi.object({ name: joi.string(), @@ -303,9 +304,7 @@ const getChecksQueryValidation = joi.object({ status: joi.boolean(), }); -const getTeamChecksParamValidation = joi.object({ - teamId: joi.string().required(), -}); +const getTeamChecksParamValidation = joi.object({}); const getTeamChecksQueryValidation = joi.object({ sortOrder: joi.string().valid("asc", "desc"), @@ -321,9 +320,7 @@ const deleteChecksParamValidation = joi.object({ monitorId: joi.string().required(), }); -const deleteChecksByTeamIdParamValidation = joi.object({ - teamId: joi.string().required(), -}); +const deleteChecksByTeamIdParamValidation = joi.object({}); const updateChecksTTLBodyValidation = joi.object({ ttl: joi.number().required(), @@ -430,8 +427,6 @@ const getStatusPageQueryValidation = joi.object({ }); const createStatusPageBodyValidation = joi.object({ - userId: joi.string().required(), - teamId: joi.string().required(), type: joi.string().valid("uptime").required(), companyName: joi.string().required(), url: joi