From 979c198702376de0fdca82c74138e1e13ae7753d Mon Sep 17 00:00:00 2001
From: Daniel C <122758391+danielcj2@users.noreply.github.com>
Date: Tue, 30 Jul 2024 13:40:31 -0400
Subject: [PATCH] Create page speed function (#483)
* Added pagespeed monitor create function
* Added pagespeed slice
* Updated slice name
* Displayed page speed monitors and updated directory name
---
Client/src/Components/Inputs/Field/index.jsx | 13 ++
.../PageSpeedMonitor/pageSpeedMonitorSlice.js | 146 ++++++++++++++++++
.../Pages/PageSpeed/CreatePageSpeed/index.css | 2 +-
.../Pages/PageSpeed/CreatePageSpeed/index.jsx | 92 +++++++++--
Client/src/Pages/PageSpeed/index.jsx | 67 ++------
Client/src/store.js | 4 +-
6 files changed, 251 insertions(+), 73 deletions(-)
create mode 100644 Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
diff --git a/Client/src/Components/Inputs/Field/index.jsx b/Client/src/Components/Inputs/Field/index.jsx
index acf6877be..1d9034c66 100644
--- a/Client/src/Components/Inputs/Field/index.jsx
+++ b/Client/src/Components/Inputs/Field/index.jsx
@@ -84,6 +84,17 @@ const Field = ({
value={value}
onChange={onChange}
disabled={disabled}
+ sx={
+ type === "url"
+ ? {
+ "& .MuiInputBase-root": { padding: 0 },
+ "& .MuiStack-root": {
+ borderTopLeftRadius: `${theme.shape.borderRadius}px`,
+ borderBottomLeftRadius: `${theme.shape.borderRadius}px`,
+ },
+ }
+ : {}
+ }
InputProps={{
startAdornment: type === "url" && (
diff --git a/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js b/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
new file mode 100644
index 000000000..6524a3417
--- /dev/null
+++ b/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js
@@ -0,0 +1,146 @@
+import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
+import { jwtDecode } from "jwt-decode";
+import axiosInstance from "../../Utils/axiosConfig";
+const initialState = {
+ isLoading: false,
+ monitors: [],
+ success: null,
+ msg: null,
+};
+
+export const createPageSpeed = createAsyncThunk(
+ "pageSpeedMonitors/createPageSpeed",
+ async (data, thunkApi) => {
+ try {
+ const { authToken, monitor } = data;
+
+ const res = await axiosInstance.post(`/monitors`, monitor, {
+ headers: {
+ Authorization: `Bearer ${authToken}`,
+ "Content-Type": "application/json",
+ },
+ });
+ return res.data;
+ } catch (error) {
+ if (error.response && error.response.data) {
+ return thunkApi.rejectWithValue(error.response.data);
+ }
+ return thunkApi.rejectWithValue(error.message);
+ }
+ }
+);
+
+export const getPageSpeedMonitors = createAsyncThunk(
+ "pageSpeedMonitors/getPageSpeedMonitors",
+ async (token, thunkApi) => {
+ try {
+ const res = await axiosInstance.get("/monitors");
+ return res.data;
+ } catch (error) {
+ if (error.response && error.response.data) {
+ return thunkApi.rejectWithValue(error.response.data);
+ }
+ return thunkApi.rejectWithValue(error.message);
+ }
+ }
+);
+
+export const getPageSpeedByUserId = createAsyncThunk(
+ "pageSpeedMonitors/getPageSpeedByUserId",
+ async (token, thunkApi) => {
+ const user = jwtDecode(token);
+ try {
+ const res = await axiosInstance.get(
+ `/monitors/user/${user._id}?limit=25&type=pagespeed&sortOrder=desc`,
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ );
+ return res.data;
+ } catch (error) {
+ if (error.response && error.response.data) {
+ return thunkApi.rejectWithValue(error.response.data);
+ }
+ return thunkApi.rejectWithValue(error.message);
+ }
+ }
+);
+
+const pageSpeedMonitorSlice = createSlice({
+ name: "pageSpeedMonitor",
+ initialState,
+ reducers: {
+ clearMonitorState: (state) => {
+ state.isLoading = false;
+ state.monitors = [];
+ state.success = null;
+ state.msg = null;
+ },
+ },
+ extraReducers: (builder) => {
+ builder
+ // *****************************************************
+ // All Monitors
+ // *****************************************************
+ .addCase(getPageSpeedMonitors.pending, (state) => {
+ state.isLoading = true;
+ })
+ .addCase(getPageSpeedMonitors.fulfilled, (state, action) => {
+ state.isLoading = false;
+ state.success = action.payload.success;
+ state.msg = action.payload.msg;
+ state.monitors = action.payload.data;
+ })
+ .addCase(getPageSpeedMonitors.rejected, (state, action) => {
+ state.isLoading = false;
+ state.success = false;
+ state.msg = action.payload
+ ? action.payload.msg
+ : "Getting montiors failed";
+ })
+ // *****************************************************
+ // Monitors by userId
+ // *****************************************************
+
+ .addCase(getPageSpeedByUserId.pending, (state) => {
+ state.isLoading = true;
+ })
+ .addCase(getPageSpeedByUserId.fulfilled, (state, action) => {
+ state.isLoading = false;
+ state.success = action.payload.msg;
+ state.monitors = action.payload.data;
+ })
+ .addCase(getPageSpeedByUserId.rejected, (state, action) => {
+ state.isLoading = false;
+ state.success = false;
+ state.msg = action.payload
+ ? action.payload.msg
+ : "Getting page speed monitors failed";
+ })
+
+ // *****************************************************
+ // 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";
+ });
+ },
+});
+
+export const { setMonitors, clearMonitorState } = pageSpeedMonitorSlice.actions;
+
+export default pageSpeedMonitorSlice.reducer;
diff --git a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.css b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.css
index ee447365c..3152d0e28 100644
--- a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.css
+++ b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.css
@@ -1,4 +1,4 @@
-.create-page-speed > .MuiStack-root {
+.create-page-speed form {
position: relative;
margin: auto;
margin-top: var(--env-var-default-2);
diff --git a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
index 5952b1c2f..c85f6f77e 100644
--- a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
+++ b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx
@@ -1,18 +1,25 @@
import { Box, IconButton, Stack, Typography } from "@mui/material";
+import { useState } from "react";
import { useTheme } from "@emotion/react";
-import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
+import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router";
+import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
import Field from "../../../Components/Inputs/Field";
import Select from "../../../Components/Inputs/Select";
import Button from "../../../Components/Button";
import Checkbox from "../../../Components/Inputs/Checkbox";
-import { useState } from "react";
import { monitorValidation } from "../../../Validation/validation";
+import { createToast } from "../../../Utils/toastUtils";
+import { createPageSpeed } from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice";
import "./index.css";
const CreatePageSpeed = () => {
const theme = useTheme();
const navigate = useNavigate();
+ const dispatch = useDispatch();
+
+ const MS_PER_MINUTE = 60000;
+ const { user, authToken } = useSelector((state) => state.auth);
const frequencies = [
{ _id: 1, name: "1 minute" },
@@ -26,7 +33,6 @@ const CreatePageSpeed = () => {
name: "",
url: "",
interval: 1,
- type: "pagespeed",
});
const [errors, setErrors] = useState({});
@@ -34,25 +40,68 @@ const CreatePageSpeed = () => {
const { value } = event.target;
setForm((prev) => ({ ...prev, [id]: value }));
- const validation = monitorValidation.validate(
+ const { error } = monitorValidation.validate(
{ [id]: value },
{ abortEarly: false }
);
setErrors((prev) => {
- const updatedErrors = { ...prev };
- if (validation.error) {
- updatedErrors[id] = validation.error.details[0].message;
- } else {
- delete updatedErrors[id];
- }
- return updatedErrors;
+ const newErrors = { ...prev };
+ if (error) newErrors[id] = error.details[0].message;
+ else delete newErrors[id];
+ return newErrors;
});
};
+ const handleCreate = async (event) => {
+ event.preventDefault();
+
+ let monitor = {
+ url: "http://" + form.url,
+ name: form.name === "" ? form.url : form.name,
+ };
+
+ const { error } = monitorValidation.validate(form, { abortEarly: false });
+
+ if (error) {
+ const newErrors = {};
+ error.details.forEach((err) => {
+ newErrors[err.path[0]] = err.message;
+ });
+ setErrors(newErrors);
+ createToast({ body: "Error validating data." });
+ } else {
+ monitor = {
+ ...monitor,
+ description: monitor.name,
+ userId: user._id,
+ interval: form.interval * MS_PER_MINUTE,
+ type: "pagespeed",
+ };
+ try {
+ const action = await dispatch(createPageSpeed({ authToken, monitor }));
+ if (action.meta.requestStatus === "fulfilled") {
+ navigate("/page-speed");
+ }
+ } catch (error) {
+ createToast({
+ body:
+ error.details && error.details.length > 0
+ ? error.details[0].message
+ : "Unknown error.",
+ });
+ }
+ }
+ };
+
return (
-
+
-
-
+
-
+
);
};
diff --git a/Client/src/Pages/PageSpeed/index.jsx b/Client/src/Pages/PageSpeed/index.jsx
index abd61da2d..7feedce77 100644
--- a/Client/src/Pages/PageSpeed/index.jsx
+++ b/Client/src/Pages/PageSpeed/index.jsx
@@ -1,11 +1,13 @@
import { Box, Grid, Stack, Typography } from "@mui/material";
-import Fallback from "../../Components/Fallback";
+import { useEffect } from "react";
import { useTheme } from "@emotion/react";
-import PageSpeedIcon from "../../assets/icons/page-speed.svg?react";
-
-import "./index.css";
import { formatDate, formatDurationRounded } from "../../Utils/timeUtils";
import { StatusLabel } from "../../Components/Label";
+import { useDispatch, useSelector } from "react-redux";
+import { getPageSpeedByUserId } from "../../Features/PageSpeedMonitor/pageSpeedMonitorSlice";
+import PageSpeedIcon from "../../assets/icons/page-speed.svg?react";
+import Fallback from "../../Components/Fallback";
+import "./index.css";
const Card = ({ data }) => {
const theme = useTheme();
@@ -61,56 +63,13 @@ const Card = ({ data }) => {
const PageSpeed = () => {
const theme = useTheme();
+ const dispatch = useDispatch();
- // sample data, remove later
- let monitors = [
- {
- success: true,
- msg: 'Got monitor for 66a3d58ecd42ab3ed1171cf1 successfully"',
- data: [
- {
- _id: "66a3ef558943628c59aabf00",
- userId: "66a3d58ecd42ab3ed1171cf1",
- name: "Google",
- description: "Google",
- status: true,
- type: "pagespeed",
- url: "https://www.google.com",
- isActive: true,
- interval: 10000,
- createdAt: "2024-07-26T18:47:49.212Z",
- updatedAt: "2024-07-26T18:47:49.212Z",
- __v: 0,
- checks: [
- {
- _id: "66a3f2266a073a2ff8dd0f7f",
- monitorId: "66a3ef558943628c59aabf00",
- status: true,
- accessibility: 90,
- bestPractices: 93,
- seo: 92,
- performance: 93,
- createdAt: "2024-07-26T18:59:50.103Z",
- updatedAt: "2024-07-26T18:59:50.103Z",
- __v: 0,
- },
- {
- _id: "66a3f2226a073a2ff8dd0f7d",
- monitorId: "66a3ef558943628c59aabf00",
- status: true,
- accessibility: 90,
- bestPractices: 93,
- seo: 92,
- performance: 87,
- createdAt: "2024-07-26T18:59:46.280Z",
- updatedAt: "2024-07-26T18:59:46.280Z",
- __v: 0,
- },
- ],
- },
- ],
- },
- ];
+ const { authToken } = useSelector((state) => state.auth);
+ const { monitors } = useSelector((state) => state.pageSpeedMonitors);
+ useEffect(() => {
+ dispatch(getPageSpeedByUserId(authToken));
+ }, []);
return (
@@ -121,7 +80,7 @@ const PageSpeed = () => {
Click on one of the monitors to get more site speed information.
- {monitors[0].data?.map((monitor) => (
+ {monitors?.map((monitor) => (
))}
diff --git a/Client/src/store.js b/Client/src/store.js
index ebb6a325b..7a6729bed 100644
--- a/Client/src/store.js
+++ b/Client/src/store.js
@@ -1,4 +1,5 @@
import { configureStore, combineReducers } from "@reduxjs/toolkit";
+import pageSpeedMonitorReducer from "./Features/PageSpeedMonitor/pageSpeedMonitorSlice";
import monitorsReducer from "./Features/Monitors/monitorsSlice";
import authReducer from "./Features/Auth/authSlice";
import storage from "redux-persist/lib/storage";
@@ -18,13 +19,14 @@ const authTransform = createTransform(
const persistConfig = {
key: "root",
storage,
- whitielist: ["auth", "monitors"],
+ whitielist: ["auth", "monitors", "pageSpeed"],
transforms: [authTransform],
};
const rootReducer = combineReducers({
monitors: monitorsReducer,
auth: authReducer,
+ pageSpeedMonitors: pageSpeedMonitorReducer,
});
const persistedReducer = persistReducer(persistConfig, rootReducer);