diff --git a/client/src/Components/MonitorDetailsControlHeader/index.jsx b/client/src/Components/MonitorDetailsControlHeader/index.jsx
new file mode 100644
index 000000000..c4dcf1ffc
--- /dev/null
+++ b/client/src/Components/MonitorDetailsControlHeader/index.jsx
@@ -0,0 +1,94 @@
+import Stack from "@mui/material/Stack";
+import Status from "./status";
+import Skeleton from "./skeleton";
+import Button from "@mui/material/Button";
+import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined";
+import PauseOutlinedIcon from "@mui/icons-material/PauseOutlined";
+import PlayArrowOutlinedIcon from "@mui/icons-material/PlayArrowOutlined";
+
+// Utils
+import PropTypes from "prop-types";
+import { useNavigate } from "react-router-dom";
+import { useTheme } from "@mui/material/styles";
+import { usePauseMonitor } from "../../Hooks/useMonitorControls";
+
+/**
+ * MonitorDetailsControlHeader component displays the control header for monitor details.
+ * It includes status display, pause/resume button, and a configure button for admins.
+ *
+ * @component
+ * @param {Object} props - Component props
+ * @param {string} props.path - The base path for navigation
+ * @param {boolean} [props.isLoading=false] - Flag indicating if the data is loading
+ * @param {boolean} [props.isAdmin=false] - Flag indicating if the user is an admin
+ * @param {Object} props.monitor - The monitor object containing details
+ * @param {Function} props.triggerUpdate - Function to trigger an update
+ * @returns {JSX.Element} The rendered component
+ */
+const MonitorDetailsControlHeader = ({
+ path,
+ isLoading = false,
+ isAdmin = false,
+ monitor,
+ triggerUpdate,
+}) => {
+ const navigate = useNavigate();
+ const theme = useTheme();
+ const [pauseMonitor, isPausing, error] = usePauseMonitor({
+ monitorId: monitor?._id,
+ triggerUpdate,
+ });
+
+ if (isLoading) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ :
+ }
+ onClick={() => {
+ pauseMonitor();
+ }}
+ >
+ {monitor?.isActive ? "Pause" : "Resume"}
+
+
+ {isAdmin && (
+ }
+ onClick={() => navigate(`/${path}/configure/${monitor._id}`)}
+ >
+ Configure
+
+ )}
+
+
+ );
+};
+
+MonitorDetailsControlHeader.propTypes = {
+ path: PropTypes.string,
+ isLoading: PropTypes.bool,
+ isAdmin: PropTypes.bool,
+ monitor: PropTypes.object,
+ triggerUpdate: PropTypes.func,
+};
+
+export default MonitorDetailsControlHeader;
diff --git a/client/src/Components/MonitorDetailsControlHeader/skeleton.jsx b/client/src/Components/MonitorDetailsControlHeader/skeleton.jsx
new file mode 100644
index 000000000..64dc0547f
--- /dev/null
+++ b/client/src/Components/MonitorDetailsControlHeader/skeleton.jsx
@@ -0,0 +1,23 @@
+import { Stack, Skeleton } from "@mui/material";
+
+const SkeletonLayout = () => {
+ return (
+
+
+
+
+ );
+};
+
+export default SkeletonLayout;
diff --git a/client/src/Components/MonitorDetailsControlHeader/status.jsx b/client/src/Components/MonitorDetailsControlHeader/status.jsx
new file mode 100644
index 000000000..9e01dd502
--- /dev/null
+++ b/client/src/Components/MonitorDetailsControlHeader/status.jsx
@@ -0,0 +1,56 @@
+// Components
+import Stack from "@mui/material/Stack";
+import PulseDot from "../../Components/Animated/PulseDot";
+import Typography from "@mui/material/Typography";
+import Dot from "../../Components/Dot";
+// Utils
+import { formatDurationRounded } from "../../Utils/timeUtils";
+import PropTypes from "prop-types";
+import { useTheme } from "@emotion/react";
+import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils";
+
+/**
+ * Status component displays the status information of a monitor.
+ * It includes the monitor's name, URL, and check interval.
+ *
+ * @component
+ * @param {Object} props - Component props
+ * @param {Object} props.monitor - The monitor object containing details
+ * @param {string} props.monitor.name - The name of the monitor
+ * @param {string} props.monitor.url - The URL of the monitor
+ * @param {number} props.monitor.interval - The interval at which the monitor checks
+ * @returns {JSX.Element} The rendered component
+ */
+const Status = ({ monitor }) => {
+ const theme = useTheme();
+ const { statusColor, determineState } = useUtils();
+
+ return (
+
+ {monitor?.name}
+
+
+
+ {monitor?.url?.replace(/^https?:\/\//, "") || "..."}
+
+
+
+ Checking every {formatDurationRounded(monitor?.interval)}.
+
+
+
+ );
+};
+
+Status.propTypes = {
+ monitor: PropTypes.object,
+};
+
+export default Status;
diff --git a/client/src/Hooks/useFetchUptimeMonitorDetails.js b/client/src/Hooks/useFetchUptimeMonitorDetails.js
index c8cb81e65..3c0ac7ee6 100644
--- a/client/src/Hooks/useFetchUptimeMonitorDetails.js
+++ b/client/src/Hooks/useFetchUptimeMonitorDetails.js
@@ -3,13 +3,12 @@ import { networkService } from "../main";
import { useNavigate } from "react-router-dom";
import { createToast } from "../Utils/toastUtils";
-export const useFetchUptimeMonitorDetails = ({ monitorId, dateRange }) => {
+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 {
@@ -29,7 +28,7 @@ export const useFetchUptimeMonitorDetails = ({ monitorId, dateRange }) => {
}
};
fetchMonitors();
- }, [dateRange, monitorId, navigate]);
+ }, [dateRange, monitorId, navigate, trigger]);
return [monitor, monitorStats, isLoading, networkError];
};
diff --git a/client/src/Hooks/useMonitorControls.js b/client/src/Hooks/useMonitorControls.js
new file mode 100644
index 000000000..b47603b58
--- /dev/null
+++ b/client/src/Hooks/useMonitorControls.js
@@ -0,0 +1,28 @@
+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/Pages/Uptime/Configure/index.jsx b/client/src/Pages/Uptime/Configure/index.jsx
index 5afbc4a27..d52780eea 100644
--- a/client/src/Pages/Uptime/Configure/index.jsx
+++ b/client/src/Pages/Uptime/Configure/index.jsx
@@ -158,23 +158,6 @@ const Configure = () => {
}
};
- const handlePause = async () => {
- try {
- const action = await dispatch(pauseUptimeMonitor({ monitorId }));
- if (pauseUptimeMonitor.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 (pauseUptimeMonitor.rejected.match(action)) {
- throw new Error(action.error.message);
- }
- } catch (error) {
- logger.error("Error pausing monitor: " + monitorId);
- createToast({ body: "Failed to pause monitor" });
- }
- };
-
const handleSubmit = async (event) => {
event.preventDefault();
const action = await dispatch(updateUptimeMonitor({ monitor: monitor }));
@@ -329,38 +312,6 @@ const Configure = () => {
ml: "auto",
}}
>
-