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/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/StatBox/index.jsx b/client/src/Components/StatBox/index.jsx index 673736ea9..93fa46c2b 100644 --- a/client/src/Components/StatBox/index.jsx +++ b/client/src/Components/StatBox/index.jsx @@ -2,7 +2,7 @@ import { Stack, Typography } from "@mui/material"; import Image from "../Image"; import { useTheme } from "@mui/material/styles"; import PropTypes from "prop-types"; -import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../Hooks/useMonitorUtils"; /** * StatBox Component @@ -41,7 +41,7 @@ const StatBox = ({ sx, }) => { const theme = useTheme(); - const { statusToTheme } = useUtils(); + const { statusToTheme } = useMonitorUtils(); const themeColor = statusToTheme[status]; const statusBoxStyles = gradient 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..71f54de59 --- /dev/null +++ b/client/src/Hooks/monitorHooks.js @@ -0,0 +1,398 @@ +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"; + +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]; +}; + +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]; +}; + +const useFetchMonitorsByTeamId = ({ + teamId, + 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({ + teamId, + 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(); + }, [ + teamId, + 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]; +}; + +export { + useFetchMonitorsWithSummary, + useFetchMonitorsWithChecks, + useFetchMonitorsByTeamId, + useFetchStatsByMonitorId, + useFetchMonitorById, + useFetchUptimeMonitorById, + useFetchHardwareMonitorById, + useCreateMonitor, + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, +}; 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/Pages/Incidents/Components/IncidentTable/index.jsx b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx index cc2d212f0..bc4872735 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, @@ -24,19 +24,28 @@ const IncidentTable = ({ }) => { //Redux state const uiTimezone = useSelector((state) => state.ui.timezone); + const { user } = useSelector((state) => state.auth); //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 +59,16 @@ const IncidentTable = ({ setRowsPerPage(event.target.value); }; + useEffect(() => { + if (selectedMonitor === "0") { + setTeamId(user.teamId); + setMonitorId(undefined); + } else { + setMonitorId(selectedMonitor); + setTeamId(undefined); + } + }, [selectedMonitor, user.teamId]); + 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..d01543f8a 100644 --- a/client/src/Pages/Incidents/index.jsx +++ b/client/src/Pages/Incidents/index.jsx @@ -1,19 +1,19 @@ // 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 { useFetchMonitorsByTeamId } from "../../Hooks/monitorHooks"; 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 { 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); @@ -27,13 +27,26 @@ 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 } = useMonitorsFetch({ + const [monitors, , isLoading, networkError] = useFetchMonitorsByTeamId({ 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 +60,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,6 +183,7 @@ const CreateInfrastructureMonitor = () => { }; form = { + ...(isCreate ? {} : { _id: monitorId }), ...rest, description: form.name, teamId: user.teamId, @@ -197,19 +194,9 @@ const CreateInfrastructureMonitor = () => { }; // 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 +435,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 0ec435c60..761ecaa0e 100644 --- a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx +++ b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx @@ -10,7 +10,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"; @@ -19,7 +19,7 @@ const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete // Utils const theme = useTheme(); const { t } = useTranslation(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); const navigate = useNavigate(); // Handlers 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 bfba843c0..6d49f64a7 100644 --- a/client/src/Pages/Infrastructure/Monitors/index.jsx +++ b/client/src/Pages/Infrastructure/Monitors/index.jsx @@ -10,15 +10,20 @@ import Fallback from "../../../Components/Fallback"; import Filter from "./Components/Filters"; // Utils import { useTheme } from "@emotion/react"; -import { useMonitorFetch } from "./Hooks/useMonitorFetch"; import { useState } from "react"; +import { useSelector } from "react-redux"; 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 + const { user } = useSelector((state) => state.auth); + + // Local state const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(5); const [updateTrigger, setUpdateTrigger] = useState(false); @@ -50,7 +55,10 @@ const InfrastructureMonitors = () => { const field = toFilterStatus !== undefined ? "status" : undefined; - const { monitors, summary, isLoading, networkError } = useMonitorFetch({ + const [monitors, summary, isLoading, networkError] = useFetchMonitorsByTeamId({ + teamId: user.teamId, + limit: 1, + types: TYPES, page, field: field, filter: toFilterStatus, 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..319b0da21 100644 --- a/client/src/Pages/PageSpeed/Monitors/index.jsx +++ b/client/src/Pages/PageSpeed/Monitors/index.jsx @@ -5,17 +5,17 @@ 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(); @@ -23,8 +23,15 @@ const PageSpeed = () => { const { user } = useSelector((state) => state.auth); const { pagespeedApiKey } = useSelector((state) => state.settings); - const { isLoading, monitors, summary, networkError } = useMonitorsFetch({ + const [monitors, monitorsSummary, isLoading, networkError] = useFetchMonitorsByTeamId({ teamId: user.teamId, + limit: 10, + types: TYPES, + page: null, + rowsPerPage: null, + filter: null, + field: null, + order: null, }); if (networkError === true) { @@ -68,7 +75,7 @@ const PageSpeed = () => { /> { const theme = useTheme(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); const { showURL } = useSelector((state) => state.ui); diff --git a/client/src/Pages/Uptime/Configure/index.jsx b/client/src/Pages/Uptime/Configure/index.jsx index 8718a2209..5a380f8e5 100644 --- a/client/src/Pages/Uptime/Configure/index.jsx +++ b/client/src/Pages/Uptime/Configure/index.jsx @@ -1,37 +1,37 @@ -import { useNavigate, useParams } from "react-router"; -import { useTheme } from "@emotion/react"; -import { useDispatch, useSelector } from "react-redux"; -import { useState, useEffect } from "react"; -import { - Box, - Stack, - Tooltip, - Typography, - Button, - FormControlLabel, - Switch, -} from "@mui/material"; -import { monitorValidation } from "../../../Validation/validation"; -import { createToast } from "../../../Utils/toastUtils"; -import { useTranslation } from "react-i18next"; +// Components +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; +import Tooltip from "@mui/material/Tooltip"; +import Typography from "@mui/material/Typography"; +import Button from "@mui/material/Button"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Switch from "@mui/material/Switch"; import ConfigBox from "../../../Components/ConfigBox"; -import { - updateUptimeMonitor, - deleteUptimeMonitor, -} from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import Breadcrumbs from "../../../Components/Breadcrumbs"; import TextInput from "../../../Components/Inputs/TextInput"; import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments"; import Select from "../../../Components/Inputs/Select"; -import Breadcrumbs from "../../../Components/Breadcrumbs"; -import PulseDot from "../../../Components/Animated/PulseDot"; -import "./index.css"; import Dialog from "../../../Components/Dialog"; -import { usePauseMonitor } from "../../../Hooks/useMonitorControls"; +import PulseDot from "../../../Components/Animated/PulseDot"; +import Checkbox from "../../../Components/Inputs/Checkbox"; + +// Utils +import { useParams } from "react-router"; +import { useTheme } from "@emotion/react"; +import { useState } from "react"; +import { monitorValidation } from "../../../Validation/validation"; +import { createToast } from "../../../Utils/toastUtils"; +import { useTranslation } from "react-i18next"; import PauseOutlinedIcon from "@mui/icons-material/PauseOutlined"; import PlayArrowOutlinedIcon from "@mui/icons-material/PlayArrowOutlined"; import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; -import { useFetchUptimeMonitorById } from "../../../Hooks/useFetchUptimeMonitorById"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; +import { + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, + useFetchMonitorById, +} from "../../../Hooks/monitorHooks"; import NotificationsConfig from "../../../Components/NotificationConfig"; /** @@ -76,18 +76,19 @@ const Configure = () => { }; // Network - const [monitor, isLoading, error] = useFetchUptimeMonitorById(monitorId, updateTrigger); const [notifications, notificationsAreLoading, notificationsError] = useGetNotificationsByTeamId(); - const [pauseMonitor, isPausing, pauseError] = usePauseMonitor({ - monitorId: monitor?._id, - triggerUpdate, + const [pauseMonitor, isPausing, pauseError] = usePauseMonitor({}); + const [deleteMonitor, isDeleting] = useDeleteMonitor(); + const [updateMonitor, isUpdating] = useUpdateMonitor(); + const [isLoading] = useFetchMonitorById({ + monitorId, + setMonitor: setForm, + updateTrigger, }); const MS_PER_MINUTE = 60000; - const navigate = useNavigate(); const theme = useTheme(); - const dispatch = useDispatch(); const matchMethodOptions = [ { _id: "equal", name: "Equal" }, @@ -111,7 +112,7 @@ const Configure = () => { // Handlers const handlePause = async () => { - const res = await pauseMonitor(); + const res = await pauseMonitor({ monitorId: form?._id, triggerUpdate }); if (typeof res !== "undefined") { triggerUpdate(); } @@ -119,12 +120,7 @@ const Configure = () => { const handleRemove = async (event) => { event.preventDefault(); - const action = await dispatch(deleteUptimeMonitor({ monitor })); - if (action.meta.requestStatus === "fulfilled") { - navigate("/uptime"); - } else { - createToast({ body: "Failed to delete monitor." }); - } + await deleteMonitor({ monitor: form, redirect: "/uptime" }); }; const onChange = (event) => { @@ -134,6 +130,17 @@ const Configure = () => { value = checked; } + if (name === "useAdvancedMatching") { + setForm((prevForm) => { + return { + ...prevForm, + matchMethod: "equal", + }; + }); + setUseAdvancedMatching(!useAdvancedMatching); + return; + } + if (name === "interval") { value = value * MS_PER_MINUTE; } @@ -146,7 +153,6 @@ const Configure = () => { setErrors((prev) => { const updatedErrors = { ...prev }; - if (validation.error) updatedErrors[name] = validation.error.details[0].message; else delete updatedErrors[name]; return updatedErrors; @@ -183,7 +189,7 @@ const Configure = () => { if (validation.error) { const newErrors = {}; - error.details.forEach((err) => { + validation.error.details.forEach((err) => { newErrors[err.path[0]] = err.message; }); setErrors(newErrors); @@ -192,27 +198,12 @@ const Configure = () => { } toSubmit.notifications = form.notifications; - const action = await dispatch(updateUptimeMonitor({ monitor: toSubmit })); - if (action.meta.requestStatus === "fulfilled") { - createToast({ body: "Monitor updated successfully!" }); - } else { - createToast({ body: "Failed to update monitor." }); - } + console.log(JSON.stringify(toSubmit, null, 2)); + // await updateMonitor({ monitor: toSubmit, redirect: "/uptime" }); }; - // Effects - useEffect(() => { - if (monitor?.matchMethod) { - setUseAdvancedMatching(true); - } - - setForm({ - ...monitor, - }); - }, [monitor, notifications]); - // Parse the URL - const parsedUrl = parseUrl(monitor?.url); + const parsedUrl = parseUrl(form?.url); const protocol = parsedUrl?.protocol?.replace(":", "") || ""; const { determineState, statusColor } = useMonitorUtils(); @@ -434,7 +425,13 @@ const Configure = () => { onChange={onChange} items={frequencies} /> - {form.type === "http" && ( + + {form.type === "http" && useAdvancedMatching && ( <>