diff --git a/Client/src/Components/TabPanels/Account/TeamPanel.jsx b/Client/src/Components/TabPanels/Account/TeamPanel.jsx index 1d4a0fa41..9c63bd1c2 100644 --- a/Client/src/Components/TabPanels/Account/TeamPanel.jsx +++ b/Client/src/Components/TabPanels/Account/TeamPanel.jsx @@ -73,7 +73,14 @@ const TeamPanel = () => { useEffect(() => { let team = members; if (filter !== "all") - team = members.filter((member) => member.role.includes(filter)); + team = members.filter((member) => { + if (filter === "admin") { + return ( + member.role.includes("admin") || member.role.includes("superadmin") + ); + } + return member.role.includes(filter); + }); const data = { cols: [ diff --git a/Client/src/Features/Auth/authSlice.js b/Client/src/Features/Auth/authSlice.js index fa1f0471e..f12758715 100644 --- a/Client/src/Features/Auth/authSlice.js +++ b/Client/src/Features/Auth/authSlice.js @@ -1,6 +1,7 @@ import { networkService } from "../../main"; import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { jwtDecode } from "jwt-decode"; +import axios from "axios"; const initialState = { isLoading: false, @@ -57,9 +58,8 @@ export const update = createAsyncThunk( form.password && fd.append("password", form.password); form.newPassword && fd.append("newPassword", form.newPassword); if (form.file && form.file !== "") { - const imageResult = await networkService.get(form.file, { + const imageResult = await axios.get(form.file, { responseType: "blob", - baseURL: "", }); fd.append("profileImage", imageResult.data); } diff --git a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js index 237d9d3dc..79af537e0 100644 --- a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js +++ b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js @@ -28,6 +28,26 @@ export const createUptimeMonitor = createAsyncThunk( } ); +export const getUptimeMonitorById = createAsyncThunk( + "monitors/getMonitorById", + async (data, thunkApi) => { + try { + const { authToken, monitorId } = data; + const res = await networkService.getMonitorByid(authToken, 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 getUptimeMonitorsByTeamId = createAsyncThunk( "montiors/getMonitorsByTeamId", async (token, thunkApi) => { @@ -109,6 +129,26 @@ export const deleteUptimeMonitor = createAsyncThunk( } ); +export const pauseUptimeMonitor = createAsyncThunk( + "monitors/pauseMonitor", + async (data, thunkApi) => { + try { + const { authToken, monitorId } = data; + const res = await networkService.pauseMonitorById(authToken, 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 uptimeMonitorsSlice = createSlice({ name: "uptimeMonitors", initialState, @@ -160,7 +200,24 @@ const uptimeMonitorsSlice = createSlice({ ? action.payload.msg : "Failed to create uptime monitor"; }) - + // ***************************************************** + // 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 pause uptime monitor"; + }) // ***************************************************** // update Monitor // ***************************************************** @@ -197,6 +254,24 @@ const uptimeMonitorsSlice = createSlice({ state.msg = action.payload ? action.payload.msg : "Failed to delete uptime monitor"; + }) + // ***************************************************** + // 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"; }); }, }); diff --git a/Client/src/Pages/Incidents/IncidentTable/index.jsx b/Client/src/Pages/Incidents/IncidentTable/index.jsx index 6c1767446..aa80b35ce 100644 --- a/Client/src/Pages/Incidents/IncidentTable/index.jsx +++ b/Client/src/Pages/Incidents/IncidentTable/index.jsx @@ -146,6 +146,7 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => { Monitor Name Status Date & Time + Status Code Message @@ -166,7 +167,10 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => { {new Date(check.createdAt).toLocaleString()} - {check.statusCode} + + {check.statusCode ? check.statusCode : "N/A"} + + {check.message} ); })} diff --git a/Client/src/Pages/Monitors/Configure/index.jsx b/Client/src/Pages/Monitors/Configure/index.jsx index 3a546c582..9a90c5512 100644 --- a/Client/src/Pages/Monitors/Configure/index.jsx +++ b/Client/src/Pages/Monitors/Configure/index.jsx @@ -2,13 +2,15 @@ import { useNavigate, useParams } from "react-router"; import { useTheme } from "@emotion/react"; import { useDispatch, useSelector } from "react-redux"; import { useEffect, useState } from "react"; -import { Box, Modal, Skeleton, Stack, Typography } from "@mui/material"; +import { Box, Modal, Stack, Typography } from "@mui/material"; import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; import { logger } from "../../../Utils/Logger"; import { ConfigBox } from "../styled"; import { updateUptimeMonitor, + pauseUptimeMonitor, + getUptimeMonitorById, getUptimeMonitorsByTeamId, deleteUptimeMonitor, } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; @@ -20,7 +22,8 @@ import Checkbox from "../../../Components/Inputs/Checkbox"; import Breadcrumbs from "../../../Components/Breadcrumbs"; import PulseDot from "../../../Components/Animated/PulseDot"; import "./index.css"; - +import SkeletonLayout from "./skeleton"; +import ButtonSpinner from "../../../Components/ButtonSpinner"; /** * Parses a URL string and returns a URL object. * @@ -35,54 +38,6 @@ const parseUrl = (url) => { } }; -/** - * Renders a skeleton layout. - * - * @returns {JSX.Element} - */ -const SkeletonLayout = () => { - const theme = useTheme(); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - ); -}; - /** * Configure page displays monitor configurations and allows for editing actions. * @component @@ -93,7 +48,7 @@ const Configure = () => { const theme = useTheme(); const dispatch = useDispatch(); const { user, authToken } = useSelector((state) => state.auth); - const { monitors } = useSelector((state) => state.uptimeMonitors); + const { isLoading } = useSelector((state) => state.uptimeMonitors); const [monitor, setMonitor] = useState({}); const [errors, setErrors] = useState({}); const { monitorId } = useParams(); @@ -107,15 +62,25 @@ const Configure = () => { }; useEffect(() => { - const data = monitors.find((monitor) => monitor._id === monitorId); - if (!data) { - logger.error("Error fetching monitor of id: " + monitorId); - navigate("/not-found", { replace: true }); - } - setMonitor({ - ...data, - }); - }, [monitorId, authToken, monitors, navigate]); + const fetchMonitor = async () => { + try { + const action = await dispatch( + getUptimeMonitorById({ authToken, 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) { + logger.error("Error fetching monitor of id: " + monitorId); + navigate("/not-found", { replace: true }); + } + }; + fetchMonitor(); + }, [monitorId, authToken, navigate]); const handleChange = (event, name) => { let { value, id } = event.target; @@ -171,6 +136,23 @@ const Configure = () => { } }; + const handlePause = async () => { + try { + const action = await dispatch( + pauseUptimeMonitor({ authToken, monitorId }) + ); + if (pauseUptimeMonitor.fulfilled.match(action)) { + const monitor = action.payload.data; + setMonitor(monitor); + } 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( @@ -207,11 +189,9 @@ const Configure = () => { const parsedUrl = parseUrl(monitor?.url); const protocol = parsedUrl?.protocol?.replace(":", "") || ""; - let loading = Object.keys(monitor).length === 0; - return ( - {loading ? ( + {Object.keys(monitor).length === 0 ? ( ) : ( <> @@ -268,9 +248,10 @@ const Configure = () => { ml: "auto", }} > -