From f075e15ab78290a749e08d106743bd03eac382b6 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 16 Sep 2024 09:48:48 +0800 Subject: [PATCH 1/8] Remove duplicate method --- Server/db/mongo/modules/checkModule.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Server/db/mongo/modules/checkModule.js b/Server/db/mongo/modules/checkModule.js index c0f4b2d2e..845a431ee 100644 --- a/Server/db/mongo/modules/checkModule.js +++ b/Server/db/mongo/modules/checkModule.js @@ -259,20 +259,6 @@ const updateChecksTTL = async (teamId, ttl) => { } }; -const updateChecksTTL = async (teamId, ttl) => { - try { - await Check.collection.dropIndex("expiry_1"); - await Check.collection.createIndex( - { expiry: 1 }, - { expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary - ); - } catch (error) { - error.service = SERVICE_NAME; - error.method = "updateChecksTTL"; - throw error; - } -}; - module.exports = { createCheck, getChecksCount, From afa5cf1fc006af4c5545acf8c569271148c92eef Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 16 Sep 2024 12:13:29 +0800 Subject: [PATCH 2/8] Implement setting TTL on FE --- Client/src/Features/Auth/authSlice.js | 2 +- Client/src/Pages/Settings/index.jsx | 126 +++++++++++++++++-------- Client/src/Pages/Settings/styled.jsx | 28 ++++++ Client/src/Utils/NetworkService.js | 13 +++ Client/src/Validation/validation.js | 7 ++ Server/controllers/checkController.js | 11 ++- Server/db/mongo/modules/checkModule.js | 17 ++++ Server/models/user.js | 3 + 8 files changed, 163 insertions(+), 44 deletions(-) create mode 100644 Client/src/Pages/Settings/styled.jsx diff --git a/Client/src/Features/Auth/authSlice.js b/Client/src/Features/Auth/authSlice.js index f12758715..f6d40c38a 100644 --- a/Client/src/Features/Auth/authSlice.js +++ b/Client/src/Features/Auth/authSlice.js @@ -67,7 +67,7 @@ export const update = createAsyncThunk( fd.append("deleteProfileImage", form.deleteProfileImage); const res = await networkService.updateUser(token, user._id, fd); - + console.log(res); return res.data; } catch (error) { if (error.response && error.response.data) { diff --git a/Client/src/Pages/Settings/index.jsx b/Client/src/Pages/Settings/index.jsx index a9f11f072..06605a383 100644 --- a/Client/src/Pages/Settings/index.jsx +++ b/Client/src/Pages/Settings/index.jsx @@ -1,5 +1,5 @@ import { useTheme } from "@emotion/react"; -import { Box, Stack, styled, Typography } from "@mui/material"; +import { Box, Stack, Typography } from "@mui/material"; import Field from "../../Components/Inputs/Field"; import Link from "../../Components/Link"; import Select from "../../Components/Inputs/Select"; @@ -12,20 +12,92 @@ import { addDemoMonitors, deleteAllMonitors, } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import { update } from "../../Features/Auth/authSlice"; import PropTypes from "prop-types"; import LoadingButton from "@mui/lab/LoadingButton"; import { setTimezone } from "../../Features/UI/uiSlice"; import timezones from "../../Utils/timezones.json"; +import { useState } from "react"; +import { ConfigBox } from "./styled"; +import { networkService } from "../../main"; +import { settingsValidation } from "../../Validation/validation"; + +const SECONDS_PER_DAY = 86400; const Settings = ({ isAdmin }) => { const theme = useTheme(); const { user, authToken } = useSelector((state) => state.auth); + const { checkTTL } = user; const { isLoading } = useSelector((state) => state.uptimeMonitors); + const { isLoading: authIsLoading } = useSelector((state) => state.auth); const { timezone } = useSelector((state) => state.ui); - + const [checksIsLoading, setChecksIsLoading] = useState(false); + const [form, setForm] = useState({ + ttl: (checkTTL / SECONDS_PER_DAY).toString(), + }); + const [errors, setErrors] = useState({}); const dispatch = useDispatch(); + + const handleChange = (event) => { + const { value, id } = event.target; + const { error } = settingsValidation.validate( + { [id]: value }, + { + abortEarly: false, + } + ); + if (!error || error.details.length === 0) { + setErrors({}); + } else { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + console.log(newErrors); + logger.error("Validation errors:", error.details); + } + let inputValue = value; + id === "ttl" && (inputValue = value.replace(/[^0-9]/g, "")); + setForm((prev) => ({ + ...prev, + [id]: inputValue, + })); + }; + // TODO Handle saving - const handleSave = async () => {}; + const handleSave = async () => { + try { + setChecksIsLoading(true); + await networkService.updateChecksTTL(authToken, form.ttl); + const updatedUser = { ...user, checkTTL: form.ttl }; + const action = await dispatch( + update({ authToken, localData: updatedUser }) + ); + if (action.payload.success) { + createToast({ + body: "Settings saved successfully", + }); + } else { + if (action.payload) { + // dispatch errors + createToast({ + body: action.payload.msg, + }); + } else { + // unknown errors + createToast({ + body: "Unknown error.", + }); + } + } + } catch (error) { + console.log(error); + createToast({ body: "Failed to save settings" }); + } finally { + setChecksIsLoading(false); + } + }; const handleClearStats = async () => { try { @@ -72,33 +144,6 @@ const Settings = ({ isAdmin }) => { } }; - const ConfigBox = styled("div")({ - display: "flex", - flexDirection: "row", - justifyContent: "space-between", - gap: theme.spacing(20), - paddingTop: theme.spacing(12), - paddingInline: theme.spacing(15), - paddingBottom: theme.spacing(25), - backgroundColor: theme.palette.background.main, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.border.light, - borderRadius: theme.spacing(2), - "& > div:first-of-type": { - flex: 0.7, - }, - "& > div:last-of-type": { - flex: 1, - }, - "& h1, & h2": { - color: theme.palette.text.secondary, - }, - "& p": { - color: theme.palette.text.tertiary, - }, - }); - return ( { /> - {isAdmin && ( + {true && ( History and monitoring @@ -143,21 +188,19 @@ const Settings = ({ isAdmin }) => { logger.warn("Disabled")} + value={form.ttl} + onChange={handleChange} + error={errors.ttl} /> Clear all stats. This is irreversible. @@ -181,7 +224,7 @@ const Settings = ({ isAdmin }) => { @@ -193,7 +236,7 @@ const Settings = ({ isAdmin }) => { @@ -223,7 +266,8 @@ const Settings = ({ isAdmin }) => { 0} variant="contained" color="primary" sx={{ px: theme.spacing(12), mt: theme.spacing(20) }} diff --git a/Client/src/Pages/Settings/styled.jsx b/Client/src/Pages/Settings/styled.jsx new file mode 100644 index 000000000..19da4815b --- /dev/null +++ b/Client/src/Pages/Settings/styled.jsx @@ -0,0 +1,28 @@ +import { Stack, styled } from "@mui/material"; + +export const ConfigBox = styled(Stack)(({ theme }) => ({ + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + gap: theme.spacing(20), + paddingTop: theme.spacing(12), + paddingInline: theme.spacing(15), + paddingBottom: theme.spacing(25), + backgroundColor: theme.palette.background.main, + border: 1, + borderStyle: "solid", + borderColor: theme.palette.border.light, + borderRadius: theme.spacing(2), + "& > div:first-of-type": { + flex: 0.7, + }, + "& > div:last-of-type": { + flex: 1, + }, + "& h1, & h2": { + color: theme.palette.text.secondary, + }, + "& p": { + color: theme.palette.text.tertiary, + }, +})); diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index a9d93b301..683ce1aba 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -582,6 +582,19 @@ class NetworkService { } ); } + + async updateChecksTTL(authToken, ttl) { + return this.axiosInstance.put( + `/checks/ttl`, + { ttl }, + { + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + } + ); + } } export default NetworkService; diff --git a/Client/src/Validation/validation.js b/Client/src/Validation/validation.js index 948c228cc..3d11482d1 100644 --- a/Client/src/Validation/validation.js +++ b/Client/src/Validation/validation.js @@ -146,9 +146,16 @@ const maintenanceWindowValidation = joi.object({ }), }); +const settingsValidation = joi.object({ + ttl: joi.number().required().messages({ + "string.empty": "TTL is required", + }), +}); + export { credentials, imageValidation, monitorValidation, maintenanceWindowValidation, + settingsValidation, }; diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js index 49dfad1af..591703f57 100644 --- a/Server/controllers/checkController.js +++ b/Server/controllers/checkController.js @@ -10,6 +10,8 @@ const { updateChecksTTLBodyValidation, } = require("../validation/joi"); const { successMessages } = require("../utils/messages"); +const jwt = require("jsonwebtoken"); +const { getTokenFromHeaders } = require("../utils/utils"); const SERVICE_NAME = "checkController"; const createCheck = async (req, res, next) => { @@ -144,6 +146,8 @@ const deleteChecksByTeamId = async (req, res, next) => { }; const updateChecksTTL = async (req, res, next) => { + const SECONDS_PER_DAY = 86400; + try { await updateChecksTTLBodyValidation.validateAsync(req.body); } catch (error) { @@ -157,8 +161,11 @@ const updateChecksTTL = async (req, res, next) => { } try { - const ttl = req.body.ttl; - await req.db.updateChecksTTL(1, ttl); + // Get user's teamId + const token = getTokenFromHeaders(req.headers); + const { teamId } = jwt.verify(token, process.env.JWT_SECRET); + const ttl = parseInt(req.body.ttl, 10) * SECONDS_PER_DAY; + await req.db.updateChecksTTL(teamId, ttl); return res.status(200).json({ success: true, msg: successMessages.CHECK_UPDATE_TTL, diff --git a/Server/db/mongo/modules/checkModule.js b/Server/db/mongo/modules/checkModule.js index 845a431ee..4bc405835 100644 --- a/Server/db/mongo/modules/checkModule.js +++ b/Server/db/mongo/modules/checkModule.js @@ -1,5 +1,6 @@ const Check = require("../../../models/Check"); const Monitor = require("../../../models/Monitor"); +const User = require("../../../models/user"); const logger = require("../../../utils/logger"); const SERVICE_NAME = "checkModule"; const dateRangeLookup = { @@ -248,6 +249,14 @@ const deleteChecksByTeamId = async (teamId) => { const updateChecksTTL = async (teamId, ttl) => { try { await Check.collection.dropIndex("expiry_1"); + } catch (error) { + logger.error("Failed to drop index", { + service: SERVICE_NAME, + method: "updateChecksTTL", + }); + } + + try { await Check.collection.createIndex( { expiry: 1 }, { expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary @@ -257,6 +266,14 @@ const updateChecksTTL = async (teamId, ttl) => { error.method = "updateChecksTTL"; throw error; } + // Update user + try { + await User.updateMany({ teamId: teamId }, { checkTTL: ttl }); + } catch (error) { + error.service = SERVICE_NAME; + error.method = "updateChecksTTL"; + throw error; + } }; module.exports = { diff --git a/Server/models/user.js b/Server/models/user.js index 888bfd7eb..e56840c48 100644 --- a/Server/models/user.js +++ b/Server/models/user.js @@ -45,6 +45,9 @@ const UserSchema = mongoose.Schema( ref: "Team", immutable: true, }, + checkTTL: { + type: Number, + }, }, { timestamps: true, From 2b5c76de6ca0e49f61e71ecfd5b09607d416cd6c Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 16 Sep 2024 12:44:27 +0800 Subject: [PATCH 3/8] Restore admin cehck --- Client/src/Pages/Settings/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Pages/Settings/index.jsx b/Client/src/Pages/Settings/index.jsx index 06605a383..ac25c480d 100644 --- a/Client/src/Pages/Settings/index.jsx +++ b/Client/src/Pages/Settings/index.jsx @@ -177,7 +177,7 @@ const Settings = ({ isAdmin }) => { /> - {true && ( + {isAdmin && ( History and monitoring From 267ef14bb80d78966deab4d70c34611ad8f60cde Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Mon, 16 Sep 2024 12:53:59 -0400 Subject: [PATCH 4/8] Basic grid structure --- Client/src/Pages/Monitors/Home/index.jsx | 7 ++- Client/src/Pages/PageSpeed/card.jsx | 77 ++++++++++-------------- Client/src/Pages/PageSpeed/index.css | 5 +- Client/src/Pages/PageSpeed/index.jsx | 10 +-- Server/db/mongo/modules/checkModule.js | 14 ----- 5 files changed, 42 insertions(+), 71 deletions(-) diff --git a/Client/src/Pages/Monitors/Home/index.jsx b/Client/src/Pages/Monitors/Home/index.jsx index 31f4800a7..b66076387 100644 --- a/Client/src/Pages/Monitors/Home/index.jsx +++ b/Client/src/Pages/Monitors/Home/index.jsx @@ -96,7 +96,12 @@ const Monitors = ({ isAdmin }) => { alignItems="center" mb={theme.spacing(8)} > - + Actively monitoring { const { determineState, pagespeedStatusMsg } = useUtils(); @@ -14,19 +15,8 @@ const Card = ({ monitor }) => { const navigate = useNavigate(); const monitorState = determineState(monitor); return ( - .MuiStack-root": { - backgroundColor: theme.palette.background.accent, - }, - }} - > - + navigate(`/pagespeed/${monitor._id}`)} border={1} @@ -34,41 +24,40 @@ const Card = ({ monitor }) => { borderRadius={theme.shape.borderRadius} backgroundColor={theme.palette.background.main} sx={{ + display: "grid", + gridTemplateColumns: "34px 2fr 1fr", + columnGap: theme.spacing(5), + gridTemplateRows: "34px 1fr 3fr", cursor: "pointer", - "& svg path": { stroke: theme.palette.other.icon, strokeWidth: 0.8 }, + "&:hover": { + backgroundColor: theme.palette.background.accent, + }, }} > - + + + + {monitor.name} + + - - - - {monitor.name} - - - - {monitor.url.replace(/^https?:\/\//, "")} - - - Last checked:{" "} - - {formatDate(getLastChecked(monitor.checks, false))}{" "} - - ({formatDurationRounded(getLastChecked(monitor.checks))} ago) - - - - + + {monitor.url} + + ); }; diff --git a/Client/src/Pages/PageSpeed/index.css b/Client/src/Pages/PageSpeed/index.css index bbf75c96c..c39a20fe0 100644 --- a/Client/src/Pages/PageSpeed/index.css +++ b/Client/src/Pages/PageSpeed/index.css @@ -1,9 +1,6 @@ -.page-speed h2.MuiTypography-root { - line-height: 1.1; -} .page-speed .label { padding: 7px; - height: 22px; + height: 24px; font-size: var(--env-var-font-size-small); } .page-speed:not(:has([class*="fallback__"])) button { diff --git a/Client/src/Pages/PageSpeed/index.jsx b/Client/src/Pages/PageSpeed/index.jsx index 6dd03c0b7..68da033e4 100644 --- a/Client/src/Pages/PageSpeed/index.jsx +++ b/Client/src/Pages/PageSpeed/index.jsx @@ -56,7 +56,7 @@ const PageSpeed = ({ isAdmin }) => { // will show skeletons only on initial load // since monitor state is being added to redux persist, there's no reason to display skeletons on every render let isActuallyLoading = isLoading && monitors?.length === 0; - console.log(monitors); + return ( { {isActuallyLoading ? ( ) : monitors?.length !== 0 ? ( - + { } }; -const updateChecksTTL = async (teamId, ttl) => { - try { - await Check.collection.dropIndex("expiry_1"); - await Check.collection.createIndex( - { expiry: 1 }, - { expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary - ); - } catch (error) { - error.service = SERVICE_NAME; - error.method = "updateChecksTTL"; - throw error; - } -}; - module.exports = { createCheck, getChecksCount, From 78680d036c3847112e3740eaafba9f3689b1a0f4 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Mon, 16 Sep 2024 15:01:29 -0400 Subject: [PATCH 5/8] Added area chart --- Client/src/Pages/Monitors/utils.jsx | 23 ++++++ Client/src/Pages/PageSpeed/card.jsx | 100 +++++++++++++++++++++++++-- Client/src/Pages/PageSpeed/index.jsx | 2 +- 3 files changed, 120 insertions(+), 5 deletions(-) diff --git a/Client/src/Pages/Monitors/utils.jsx b/Client/src/Pages/Monitors/utils.jsx index c6f0a8476..cc079fedc 100644 --- a/Client/src/Pages/Monitors/utils.jsx +++ b/Client/src/Pages/Monitors/utils.jsx @@ -52,12 +52,35 @@ const useUtils = () => { "& h2": { color: theme.palette.warning.main }, }, }; + const pagespeedStyles = { + up: { + bg: theme.palette.success.bg, + light: theme.palette.success.light, + stroke: theme.palette.success.main, + }, + down: { + bg: theme.palette.error.bg, + light: theme.palette.error.light, + stroke: theme.palette.error.main, + }, + paused: { + bg: theme.palette.warning.bg, + light: theme.palette.warning.light, + stroke: theme.palette.warning.main, + }, + pending: { + bg: theme.palette.warning.bg, + light: theme.palette.warning.light, + stroke: theme.palette.warning.main, + }, + }; return { determineState, statusColor, statusMsg, pagespeedStatusMsg, + pagespeedStyles, statusStyles, }; }; diff --git a/Client/src/Pages/PageSpeed/card.jsx b/Client/src/Pages/PageSpeed/card.jsx index eeb1c77a7..00b717ce4 100644 --- a/Client/src/Pages/PageSpeed/card.jsx +++ b/Client/src/Pages/PageSpeed/card.jsx @@ -1,19 +1,103 @@ import PageSpeedIcon from "../../assets/icons/page-speed.svg?react"; import { StatusLabel } from "../../Components/Label"; -import { Box, Grid, Stack, Typography } from "@mui/material"; +import { Box, Grid, Typography } from "@mui/material"; import { useNavigate } from "react-router"; import { useTheme } from "@emotion/react"; -import { formatDate, formatDurationRounded } from "../../Utils/timeUtils"; -import { getLastChecked } from "../../Utils/monitorUtils"; +import { IconBox } from "./Details/styled"; import useUtils from "../Monitors/utils"; import PropTypes from "prop-types"; -import { IconBox } from "./Details/styled"; +import { Area, AreaChart, CartesianGrid, ResponsiveContainer } from "recharts"; + +const processData = (data) => { + if (data.length === 0) return []; + let formattedData = []; + + const calculateScore = (entry) => { + return ( + (entry.accessibility + + entry.bestPractices + + entry.performance + + entry.seo) / + 4 + ); + }; + + data.forEach((entry) => { + entry = { ...entry, score: calculateScore(entry) }; + formattedData.push(entry); + }); + + return formattedData; +}; + +const PagespeedAreaChart = ({ data, status }) => { + const theme = useTheme(); + const { pagespeedStyles } = useUtils(); + + const formattedData = processData(data); + + return ( + + + + {/* } + /> */} + + + + + + + + + + ); +}; const Card = ({ monitor }) => { const { determineState, pagespeedStatusMsg } = useUtils(); const theme = useTheme(); const navigate = useNavigate(); const monitorState = determineState(monitor); + return ( { > {monitor.url} + + + ); diff --git a/Client/src/Pages/PageSpeed/index.jsx b/Client/src/Pages/PageSpeed/index.jsx index 68da033e4..a76e4cca1 100644 --- a/Client/src/Pages/PageSpeed/index.jsx +++ b/Client/src/Pages/PageSpeed/index.jsx @@ -56,7 +56,6 @@ const PageSpeed = ({ isAdmin }) => { // will show skeletons only on initial load // since monitor state is being added to redux persist, there's no reason to display skeletons on every render let isActuallyLoading = isLoading && monitors?.length === 0; - return ( { variant="contained" color="primary" onClick={() => navigate("/pagespeed/create")} + sx={{ whiteSpace: "nowrap" }} > Create new From 205e82f1b33db89cb51da7854270c6968a33e2be Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Mon, 16 Sep 2024 15:03:36 -0400 Subject: [PATCH 6/8] Added docs --- Client/src/Pages/PageSpeed/card.jsx | 34 +++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/Client/src/Pages/PageSpeed/card.jsx b/Client/src/Pages/PageSpeed/card.jsx index 00b717ce4..6b87703f5 100644 --- a/Client/src/Pages/PageSpeed/card.jsx +++ b/Client/src/Pages/PageSpeed/card.jsx @@ -1,13 +1,18 @@ +import PropTypes from "prop-types"; import PageSpeedIcon from "../../assets/icons/page-speed.svg?react"; import { StatusLabel } from "../../Components/Label"; import { Box, Grid, Typography } from "@mui/material"; import { useNavigate } from "react-router"; import { useTheme } from "@emotion/react"; import { IconBox } from "./Details/styled"; -import useUtils from "../Monitors/utils"; -import PropTypes from "prop-types"; import { Area, AreaChart, CartesianGrid, ResponsiveContainer } from "recharts"; +import useUtils from "../Monitors/utils"; +/** + * Processes the raw data to include a score for each entry. + * @param {Array} data - The raw data array. + * @returns {Array} - The formatted data array with scores. + */ const processData = (data) => { if (data.length === 0) return []; let formattedData = []; @@ -30,6 +35,13 @@ const processData = (data) => { return formattedData; }; +/** + * Renders an area chart displaying page speed scores. + * @param {Object} props + * @param {Array} props.data - The raw data to be displayed in the chart. + * @param {string} props.status - The status of the page speed which determines the chart's color scheme. + * @returns {JSX.Element} - The rendered area chart. + */ const PagespeedAreaChart = ({ data, status }) => { const theme = useTheme(); const { pagespeedStyles } = useUtils(); @@ -92,6 +104,24 @@ const PagespeedAreaChart = ({ data, status }) => { ); }; +PagespeedAreaChart.propTypes = { + data: PropTypes.arrayOf( + PropTypes.shape({ + accessibility: PropTypes.number.isRequired, + bestPractices: PropTypes.number.isRequired, + performance: PropTypes.number.isRequired, + seo: PropTypes.number.isRequired, + }) + ).isRequired, + status: PropTypes.string.isRequired, +}; + +/** + * Renders a card displaying monitor details and an area chart. + * @param {Object} props + * @param {Object} props.monitor - The monitor data to be displayed in the card. + * @returns {JSX.Element} - The rendered card. + */ const Card = ({ monitor }) => { const { determineState, pagespeedStatusMsg } = useUtils(); const theme = useTheme(); From 78914c9566da660733ed0744221db45019bfea67 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Mon, 16 Sep 2024 15:18:41 -0400 Subject: [PATCH 7/8] Added tooltip --- Client/src/Pages/PageSpeed/card.jsx | 93 ++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/Client/src/Pages/PageSpeed/card.jsx b/Client/src/Pages/PageSpeed/card.jsx index 6b87703f5..e45cc3b45 100644 --- a/Client/src/Pages/PageSpeed/card.jsx +++ b/Client/src/Pages/PageSpeed/card.jsx @@ -1,13 +1,92 @@ import PropTypes from "prop-types"; import PageSpeedIcon from "../../assets/icons/page-speed.svg?react"; import { StatusLabel } from "../../Components/Label"; -import { Box, Grid, Typography } from "@mui/material"; +import { Box, Grid, Stack, Typography } from "@mui/material"; import { useNavigate } from "react-router"; import { useTheme } from "@emotion/react"; import { IconBox } from "./Details/styled"; -import { Area, AreaChart, CartesianGrid, ResponsiveContainer } from "recharts"; +import { + Area, + AreaChart, + CartesianGrid, + ResponsiveContainer, + Tooltip, +} from "recharts"; +import { useSelector } from "react-redux"; +import { formatDateWithTz } from "../../Utils/timeUtils"; import useUtils from "../Monitors/utils"; +/** + * CustomToolTip displays a tooltip with formatted date and score information. + * @param {Object} props + * @param {Array} props.payload - Data to display in the tooltip + * @returns {JSX.Element} The rendered tooltip component + */ +const CustomToolTip = ({ payload }) => { + const theme = useTheme(); + const uiTimezone = useSelector((state) => state.ui.timezone); + + return ( + + + {formatDateWithTz( + payload[0]?.payload.createdAt, + "ddd, MMMM D, YYYY, h:mm A", + uiTimezone + )} + + + + + + {payload[0]?.name} + {" "} + {payload[0]?.payload.score} + + + ); +}; + +CustomToolTip.propTypes = { + payload: PropTypes.array, +}; + /** * Processes the raw data to include a score for each entry. * @param {Array} data - The raw data array. @@ -64,10 +143,10 @@ const PagespeedAreaChart = ({ data, status }) => { fill="transparent" vertical={false} /> - {/* } - /> */} + content={} + /> { strokeWidth={1.5} fill={`url(#pagespeed-chart-${status})`} activeDot={{ - stroke: pagespeedStyles[status].stroke, + stroke: pagespeedStyles[status].light, fill: pagespeedStyles[status].stroke, r: 4.5, }} @@ -118,7 +197,7 @@ PagespeedAreaChart.propTypes = { /** * Renders a card displaying monitor details and an area chart. - * @param {Object} props + * @param {Object} props * @param {Object} props.monitor - The monitor data to be displayed in the card. * @returns {JSX.Element} - The rendered card. */ From 5bab1514fcc5431f2bd1bd7bf10e50959849ae78 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Mon, 16 Sep 2024 17:06:45 -0400 Subject: [PATCH 8/8] Added monitor interval check --- Client/src/Pages/PageSpeed/card.jsx | 53 ++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/Client/src/Pages/PageSpeed/card.jsx b/Client/src/Pages/PageSpeed/card.jsx index e45cc3b45..5c688a041 100644 --- a/Client/src/Pages/PageSpeed/card.jsx +++ b/Client/src/Pages/PageSpeed/card.jsx @@ -13,7 +13,11 @@ import { Tooltip, } from "recharts"; import { useSelector } from "react-redux"; -import { formatDateWithTz } from "../../Utils/timeUtils"; +import { + formatDateWithTz, + formatDurationRounded, + formatDurationSplit, +} from "../../Utils/timeUtils"; import useUtils from "../Monitors/utils"; /** @@ -128,7 +132,7 @@ const PagespeedAreaChart = ({ data, status }) => { const formattedData = processData(data); return ( - + { return ( navigate(`/pagespeed/${monitor._id}`)} border={1} @@ -230,7 +235,12 @@ const Card = ({ monitor }) => { - + {monitor.name} { /> {monitor.url} @@ -258,6 +268,41 @@ const Card = ({ monitor }) => { > + + + Checking every{" "} + {(() => { + const { time, format } = formatDurationSplit(monitor?.interval); + return ( + <> + + {time}{" "} + + {format} + + ); + })()} + + );