diff --git a/Client/src/Components/TabPanels/Account/TeamPanel.jsx b/Client/src/Components/TabPanels/Account/TeamPanel.jsx
index 8c4630b19..9809ac710 100644
--- a/Client/src/Components/TabPanels/Account/TeamPanel.jsx
+++ b/Client/src/Components/TabPanels/Account/TeamPanel.jsx
@@ -1,25 +1,13 @@
import { useTheme } from "@emotion/react";
import TabPanel from "@mui/lab/TabPanel";
-import {
- Box,
- Button,
- ButtonGroup,
- Divider,
- IconButton,
- Modal,
- Stack,
- TextField,
- Typography,
-} from "@mui/material";
+import { Button, ButtonGroup, Modal, Stack, Typography } from "@mui/material";
import { useEffect, useState } from "react";
-import EditSvg from "../../../assets/icons/edit.svg?react";
import Field from "../../Inputs/Field";
import { credentials } from "../../../Validation/validation";
import { networkService } from "../../../main";
import { createToast } from "../../../Utils/toastUtils";
import { useSelector } from "react-redux";
import BasicTable from "../../BasicTable";
-import Remove from "../../../assets/icons/trash-bin.svg?react";
import Select from "../../Inputs/Select";
import LoadingButton from "@mui/lab/LoadingButton";
@@ -54,6 +42,7 @@ const TeamPanel = () => {
const [members, setMembers] = useState([]);
const [filter, setFilter] = useState("all");
const [errors, setErrors] = useState({});
+ const [isSendingInvite, setIsSendingInvite] = useState(false);
useEffect(() => {
const fetchTeam = async () => {
@@ -173,6 +162,7 @@ const TeamPanel = () => {
};
const handleInviteMember = async () => {
+ setIsSendingInvite(true);
if (!toInvite.role.includes("user") || !toInvite.role.includes("admin"))
setToInvite((prev) => ({ ...prev, role: ["user"] }));
@@ -185,24 +175,28 @@ const TeamPanel = () => {
if (error) {
setErrors((prev) => ({ ...prev, email: error.details[0].message }));
- } else
- try {
- await networkService.requestInvitationToken(
- authToken,
- toInvite.email,
- toInvite.role
- );
+ return;
+ }
- closeInviteModal();
- createToast({
- body: "Member invited. They will receive an email with details on how to create their account.",
- });
- } catch (error) {
- createToast({
- body: error.message || "Unknown error.",
- });
- }
+ try {
+ await networkService.requestInvitationToken(
+ authToken,
+ toInvite.email,
+ toInvite.role
+ );
+ closeInviteModal();
+ createToast({
+ body: "Member invited. They will receive an email with details on how to create their account.",
+ });
+ } catch (error) {
+ createToast({
+ body: error.message || "Unknown error.",
+ });
+ } finally {
+ setIsSendingInvite(false);
+ }
};
+
const closeInviteModal = () => {
setIsOpen(false);
setToInvite({ email: "", role: ["0"] });
@@ -307,13 +301,14 @@ const TeamPanel = () => {
-
+
{
mt={theme.spacing(8)}
justifyContent="flex-end"
>
-
+
Send invite
diff --git a/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js b/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
index e32d673cc..317ab1320 100644
--- a/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
+++ b/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
@@ -28,6 +28,26 @@ export const createPageSpeed = createAsyncThunk(
}
);
+export const getPagespeedMonitorById = 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 getPageSpeedByTeamId = createAsyncThunk(
"pageSpeedMonitors/getPageSpeedByTeamId",
async (token, thunkApi) => {
@@ -109,6 +129,25 @@ export const deletePageSpeed = createAsyncThunk(
}
}
);
+export const pausePageSpeed = createAsyncThunk(
+ "pageSpeedMonitors/pausePageSpeed",
+ 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 pageSpeedMonitorSlice = createSlice({
name: "pageSpeedMonitor",
@@ -124,7 +163,7 @@ const pageSpeedMonitorSlice = createSlice({
extraReducers: (builder) => {
builder
// *****************************************************
- // Monitors by userId
+ // Monitors by teamId
// *****************************************************
.addCase(getPageSpeedByTeamId.pending, (state) => {
@@ -143,6 +182,23 @@ const pageSpeedMonitorSlice = createSlice({
: "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
// *****************************************************
@@ -163,7 +219,7 @@ const pageSpeedMonitorSlice = createSlice({
})
// *****************************************************
- // Create Monitor
+ // Update Monitor
// *****************************************************
.addCase(updatePageSpeed.pending, (state) => {
state.isLoading = true;
@@ -198,6 +254,24 @@ const pageSpeedMonitorSlice = createSlice({
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";
});
},
});
diff --git a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js
index 2c381812c..895bd1493 100644
--- a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js
+++ b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js
@@ -236,7 +236,7 @@ const uptimeMonitorsSlice = createSlice({
state.success = false;
state.msg = action.payload
? action.payload.msg
- : "Failed to pause uptime monitor";
+ : "Failed to get uptime monitor";
})
// *****************************************************
// update Monitor
diff --git a/Client/src/Pages/Monitors/Configure/index.jsx b/Client/src/Pages/Monitors/Configure/index.jsx
index 483a2fd79..6e3caeff7 100644
--- a/Client/src/Pages/Monitors/Configure/index.jsx
+++ b/Client/src/Pages/Monitors/Configure/index.jsx
@@ -275,14 +275,15 @@ const Configure = () => {
>
)}
-
+
diff --git a/Client/src/Pages/PageSpeed/Configure/index.jsx b/Client/src/Pages/PageSpeed/Configure/index.jsx
index 92dd5bb36..005583f2c 100644
--- a/Client/src/Pages/PageSpeed/Configure/index.jsx
+++ b/Client/src/Pages/PageSpeed/Configure/index.jsx
@@ -1,12 +1,14 @@
import { useEffect, useState } from "react";
import { useTheme } from "@emotion/react";
-import { Box, Button, Modal, Skeleton, Stack, Typography } from "@mui/material";
+import { Box, Button, Modal, Stack, Typography } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate, useParams } from "react-router";
import {
deletePageSpeed,
+ getPagespeedMonitorById,
getPageSpeedByTeamId,
updatePageSpeed,
+ pausePageSpeed,
} from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice";
import { monitorValidation } from "../../../Validation/validation";
import { createToast } from "../../../Utils/toastUtils";
@@ -17,63 +19,18 @@ import Checkbox from "../../../Components/Inputs/Checkbox";
import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline";
import Breadcrumbs from "../../../Components/Breadcrumbs";
import PulseDot from "../../../Components/Animated/PulseDot";
-
+import LoadingButton from "@mui/lab/LoadingButton";
+import PlayCircleOutlineRoundedIcon from "@mui/icons-material/PlayCircleOutlineRounded";
+import SkeletonLayout from "./skeleton";
import "./index.css";
-/**
- * Renders a skeleton layout.
- *
- * @returns {JSX.Element}
- */
-const SkeletonLayout = () => {
- const theme = useTheme();
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-};
-
const PageSpeedConfigure = () => {
const theme = useTheme();
const navigate = useNavigate();
const dispatch = useDispatch();
const MS_PER_MINUTE = 60000;
const { authToken } = useSelector((state) => state.auth);
- const { monitors } = useSelector((state) => state.pageSpeedMonitors);
+ const { isLoading } = useSelector((state) => state.pageSpeedMonitors);
const { monitorId } = useParams();
const [monitor, setMonitor] = useState({});
const [errors, setErrors] = useState({});
@@ -89,15 +46,25 @@ const PageSpeedConfigure = () => {
];
useEffect(() => {
- const data = monitors.find((monitor) => monitor._id === monitorId);
- if (!data) {
- logger.error("Error fetching pagespeed monitor of id: " + monitorId);
- navigate("/not-found", { replace: true });
- }
- setMonitor({
- ...data,
- });
- }, [monitorId, monitors, navigate]);
+ const fetchMonitor = async () => {
+ try {
+ const action = await dispatch(
+ getPagespeedMonitorById({ authToken, 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, authToken, monitorId, navigate]);
const handleChange = (event, id) => {
let { value } = event.target;
@@ -119,6 +86,21 @@ const PageSpeedConfigure = () => {
});
};
+ const handlePause = async () => {
+ try {
+ const action = await dispatch(pausePageSpeed({ authToken, monitorId }));
+ if (pausePageSpeed.fulfilled.match(action)) {
+ const monitor = action.payload.data;
+ setMonitor(monitor);
+ } else if (pausePageSpeed.rejected.match(action)) {
+ throw new Error(action.error.message);
+ }
+ } catch (error) {
+ logger.error("Error pausing monitor: " + monitorId);
+ createToast({ body: "Failed to pause monitor" });
+ }
+ };
+
const handleSave = async (event) => {
event.preventDefault();
const action = await dispatch(
@@ -143,11 +125,9 @@ const PageSpeedConfigure = () => {
}
};
- let loading = Object.keys(monitor).length === 0;
-
return (
- {loading ? (
+ {Object.keys(monitor).length === 0 ? (
) : (
<>
@@ -195,7 +175,9 @@ const PageSpeedConfigure = () => {
-
-
+
{
-
+
>
diff --git a/Client/src/Pages/PageSpeed/Configure/skeleton.jsx b/Client/src/Pages/PageSpeed/Configure/skeleton.jsx
new file mode 100644
index 000000000..301a00e52
--- /dev/null
+++ b/Client/src/Pages/PageSpeed/Configure/skeleton.jsx
@@ -0,0 +1,51 @@
+import { Box, Skeleton, Stack } from "@mui/material";
+import { useTheme } from "@emotion/react";
+
+/**
+ * Renders a skeleton layout.
+ *
+ * @returns {JSX.Element}
+ */
+const SkeletonLayout = () => {
+ const theme = useTheme();
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default SkeletonLayout;
diff --git a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
index 00c02a362..4c8204153 100644
--- a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
+++ b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
@@ -1,4 +1,6 @@
-import { Box, Button, Stack, Typography } from "@mui/material";
+import { Box, Stack, Typography } from "@mui/material";
+import LoadingButton from "@mui/lab/LoadingButton";
+
import { useState } from "react";
import { useTheme } from "@emotion/react";
import { useDispatch, useSelector } from "react-redux";
@@ -12,11 +14,11 @@ import { createPageSpeed } from "../../../Features/PageSpeedMonitor/pageSpeedMon
import Breadcrumbs from "../../../Components/Breadcrumbs";
import "./index.css";
import { logger } from "../../../Utils/Logger";
-
const CreatePageSpeed = () => {
const theme = useTheme();
const navigate = useNavigate();
const dispatch = useDispatch();
+ const { isLoading } = useSelector((state) => state.pageSpeedMonitors);
const MS_PER_MINUTE = 60000;
const { user, authToken } = useSelector((state) => state.auth);
@@ -226,7 +228,8 @@ const CreatePageSpeed = () => {
-
+
diff --git a/Client/src/Pages/Settings/index.jsx b/Client/src/Pages/Settings/index.jsx
index fc615d848..d1f15ea8c 100644
--- a/Client/src/Pages/Settings/index.jsx
+++ b/Client/src/Pages/Settings/index.jsx
@@ -16,6 +16,9 @@ const Settings = ({ isAdmin }) => {
const { isLoading } = useSelector((state) => state.uptimeMonitors);
const dispatch = useDispatch();
+
+ // TODO Handle saving
+
const handleClearStats = async () => {
try {
const action = await dispatch(
@@ -156,13 +159,14 @@ const Settings = ({ isAdmin }) => {
-
+