From ff17092e8a9d111ba6fec81f8cd15cba3142b413 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 28 Jan 2025 15:35:52 -0800 Subject: [PATCH 1/9] fix fallback condtion for Uptime --- Client/src/Pages/Uptime/Monitors/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Pages/Uptime/Monitors/index.jsx b/Client/src/Pages/Uptime/Monitors/index.jsx index 852ef2b4f..d60b5c495 100644 --- a/Client/src/Pages/Uptime/Monitors/index.jsx +++ b/Client/src/Pages/Uptime/Monitors/index.jsx @@ -100,7 +100,7 @@ const UptimeMonitors = () => { }); const totalMonitors = monitorsSummary?.totalMonitors ?? 0; - if (!isLoading && monitors?.length === 0) { + if (!isLoading && !monitorsAreLoading && monitors?.length === 0) { return ( Date: Tue, 28 Jan 2025 15:37:01 -0800 Subject: [PATCH 2/9] Passing as a child of breaks search wehn MonitorCountHeader rerenders --- .../Components/UptimeDataTable/index.jsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx index e96705cc1..1940c5543 100644 --- a/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx +++ b/Client/src/Pages/Uptime/Monitors/Components/UptimeDataTable/index.jsx @@ -1,6 +1,5 @@ // Components -import { Box, Stack, CircularProgress } from "@mui/material"; -import { Heading } from "../../../../../Components/Heading"; +import { Box, Stack } from "@mui/material"; import DataTable from "../../../../../Components/Table"; import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded"; import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded"; @@ -12,8 +11,8 @@ import { useState } from "react"; import SearchComponent from "../SearchComponent"; import CircularCount from "../../../../../Components/CircularCount"; import LoadingSpinner from "../LoadingSpinner"; -import MonitorCountHeader from "../../../../../Components/MonitorCountHeader"; import UptimeDataTableSkeleton from "./skeleton"; +import { Heading } from "../../../../../Components/Heading"; // Utils import { useTheme } from "@emotion/react"; @@ -216,19 +215,21 @@ const UptimeDataTable = (props) => { flex={1} py={theme.spacing(8)} > - + Uptime monitors + - + Date: Tue, 28 Jan 2025 15:37:49 -0800 Subject: [PATCH 3/9] fix key error in Search --- Client/src/Components/Inputs/Search/index.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Client/src/Components/Inputs/Search/index.jsx b/Client/src/Components/Inputs/Search/index.jsx index 1dff6b532..95930b0ec 100644 --- a/Client/src/Components/Inputs/Search/index.jsx +++ b/Client/src/Components/Inputs/Search/index.jsx @@ -139,7 +139,9 @@ const Search = ({ } return filtered; }} - getOptionKey={(option) => option.id} + getOptionKey={(option) => { + return option._id; + }} renderOption={(props, option) => { const { key, ...optionProps } = props; return ( @@ -196,6 +198,7 @@ const Search = ({ }; Search.propTypes = { + label: PropTypes.string, id: PropTypes.string, multiple: PropTypes.bool, options: PropTypes.array.isRequired, From 577c0b5d196d0406aae6bd60fe6b14a72decb509 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 28 Jan 2025 15:38:14 -0800 Subject: [PATCH 4/9] Safe property access in fallback --- Client/src/Components/Fallback/index.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Client/src/Components/Fallback/index.jsx b/Client/src/Components/Fallback/index.jsx index 48d9e2198..e5029d276 100644 --- a/Client/src/Components/Fallback/index.jsx +++ b/Client/src/Components/Fallback/index.jsx @@ -32,12 +32,14 @@ const Fallback = ({ title, checks, link = "/", isAdmin, vowelStart = false }) => border={1} borderColor={theme.palette.primary.lowContrast} borderRadius={theme.shape.borderRadius} - borderStyle="dashed" backgroundColor={theme.palette.primary.main} overflow="hidden" + sx={{ + borderStyle: "dashed", + }} > @@ -68,7 +70,7 @@ const Fallback = ({ title, checks, link = "/", isAdmin, vowelStart = false }) => > {vowelStart ? "An" : "A"} {title} is used to: - {checks.map((check, index) => ( + {checks?.map((check, index) => ( Date: Tue, 28 Jan 2025 15:38:43 -0800 Subject: [PATCH 5/9] fix incorrect variant type in --- Client/src/Utils/toastUtils.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Utils/toastUtils.jsx b/Client/src/Utils/toastUtils.jsx index 5920d06a7..dbec9117e 100644 --- a/Client/src/Utils/toastUtils.jsx +++ b/Client/src/Utils/toastUtils.jsx @@ -12,7 +12,7 @@ import Alert from "../Components/Alert"; */ export const createToast = ({ - variant = "secondary", + variant = "info", title, body, hasIcon = false, From 784dc4da634dcb9d0f3a36582c56a91682ec61e4 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 28 Jan 2025 15:42:09 -0800 Subject: [PATCH 6/9] Refactor reusable components out of Uptime Details --- Client/src/Components/StatusBoxes/index.jsx | 28 +++++++ .../src/Components/StatusBoxes/skeleton.jsx | 31 ++++++++ .../Details/Components/ChartBoxes/index.jsx | 8 +- .../Details/Components/Charts/ChartBox.jsx | 75 ------------------- .../Components/Charts/ResponseTimeChart.jsx | 2 +- .../Details/Components/ConfigButton/index.jsx | 40 ---------- .../Components/MonitorHeader/index.jsx | 55 -------------- .../Components/MonitorHeader/skeleton.jsx | 23 ------ .../Components/ResponseTable/index.jsx | 2 +- .../Components/StatusBoxes/skeleton.jsx | 30 -------- .../index.jsx | 35 +++------ Client/src/Pages/Uptime/Details/index.jsx | 8 +- 12 files changed, 80 insertions(+), 257 deletions(-) create mode 100644 Client/src/Components/StatusBoxes/index.jsx create mode 100644 Client/src/Components/StatusBoxes/skeleton.jsx delete mode 100644 Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx delete mode 100644 Client/src/Pages/Uptime/Details/Components/ConfigButton/index.jsx delete mode 100644 Client/src/Pages/Uptime/Details/Components/MonitorHeader/index.jsx delete mode 100644 Client/src/Pages/Uptime/Details/Components/MonitorHeader/skeleton.jsx delete mode 100644 Client/src/Pages/Uptime/Details/Components/StatusBoxes/skeleton.jsx rename Client/src/Pages/Uptime/Details/Components/{StatusBoxes => UptimeStatusBoxes}/index.jsx (73%) diff --git a/Client/src/Components/StatusBoxes/index.jsx b/Client/src/Components/StatusBoxes/index.jsx new file mode 100644 index 000000000..6e09d0cba --- /dev/null +++ b/Client/src/Components/StatusBoxes/index.jsx @@ -0,0 +1,28 @@ +// Components +import { Stack } from "@mui/material"; +import SkeletonLayout from "./skeleton"; +// Utils +import { useTheme } from "@mui/material/styles"; +import PropTypes from "prop-types"; +const StatusBoxes = ({ shouldRender, children }) => { + const theme = useTheme(); + if (!shouldRender) { + return ; + } + + return ( + + {children} + + ); +}; + +StatusBoxes.propTypes = { + shouldRender: PropTypes.bool, + children: PropTypes.node, +}; + +export default StatusBoxes; diff --git a/Client/src/Components/StatusBoxes/skeleton.jsx b/Client/src/Components/StatusBoxes/skeleton.jsx new file mode 100644 index 000000000..b0f04ff3f --- /dev/null +++ b/Client/src/Components/StatusBoxes/skeleton.jsx @@ -0,0 +1,31 @@ +import { Stack, Skeleton } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import PropTypes from "prop-types"; +const SkeletonLayout = ({ numBoxes }) => { + const theme = useTheme(); + return ( + + {Array.from({ length: numBoxes }).map((_, index) => { + const width = `${100 / numBoxes}%`; + return ( + + ); + })} + + ); +}; + +SkeletonLayout.propTypes = { + numBoxes: PropTypes.number, +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx b/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx index d9e581267..5565529fb 100644 --- a/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx +++ b/Client/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx @@ -1,6 +1,6 @@ // Components import { Stack, Typography, Box } from "@mui/material"; -import ChartBox from "../Charts/ChartBox"; +import ChartBox from "../../../../../Components/Charts/ChartBox"; import UptimeIcon from "../../../../../assets/icons/uptime-icon.svg?react"; import IncidentsIcon from "../../../../../assets/icons/incidents.svg?react"; import AverageResponseIcon from "../../../../../assets/icons/average-response-icon.svg?react"; @@ -40,7 +40,10 @@ const ChartBoxes = ({ icon={} header="Uptime" > - + Total Checks @@ -129,6 +132,7 @@ const ChartBoxes = ({ export default ChartBoxes; ChartBoxes.propTypes = { + shouldRender: PropTypes.bool, monitor: PropTypes.object.isRequired, dateRange: PropTypes.string.isRequired, uiTimezone: PropTypes.string.isRequired, diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx deleted file mode 100644 index 3d4d73421..000000000 --- a/Client/src/Pages/Uptime/Details/Components/Charts/ChartBox.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import { Stack, Typography } from "@mui/material"; -import { useTheme } from "@emotion/react"; -import IconBox from "../../../../../Components/IconBox"; -import PropTypes from "prop-types"; -const ChartBox = ({ children, icon, header, height = "300px" }) => { - const theme = useTheme(); - return ( - span": { - color: theme.palette.primary.contrastText, - fontSize: 20, - "& span": { - opacity: 0.8, - marginLeft: 2, - fontSize: 15, - }, - }, - "& .MuiStack-root": { - flexDirection: "row", - gap: theme.spacing(6), - }, - "& .MuiStack-root:first-of-type": { - alignItems: "center", - }, - "& tspan, & text": { - fill: theme.palette.primary.contrastTextTertiary, - }, - "& path": { - transition: "fill 300ms ease, stroke-width 400ms ease", - }, - }} - > - - {icon} - {header} - - - {children} - - ); -}; - -export default ChartBox; - -ChartBox.propTypes = { - children: PropTypes.node, - icon: PropTypes.node.isRequired, - header: PropTypes.string.isRequired, - height: PropTypes.string, -}; diff --git a/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx index 79e9473ef..8be1125c2 100644 --- a/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx +++ b/Client/src/Pages/Uptime/Details/Components/Charts/ResponseTimeChart.jsx @@ -1,4 +1,4 @@ -import ChartBox from "./ChartBox"; +import ChartBox from "../../../../../Components/Charts/ChartBox"; import MonitorDetailsAreaChart from "../../../../../Components/Charts/MonitorDetailsAreaChart"; import ResponseTimeIcon from "../../../../../assets/icons/response-time-icon.svg?react"; import SkeletonLayout from "./ResponseTimeChartSkeleton"; diff --git a/Client/src/Pages/Uptime/Details/Components/ConfigButton/index.jsx b/Client/src/Pages/Uptime/Details/Components/ConfigButton/index.jsx deleted file mode 100644 index 467655abf..000000000 --- a/Client/src/Pages/Uptime/Details/Components/ConfigButton/index.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Button, Box } from "@mui/material"; -import { useTheme } from "@emotion/react"; -import { useNavigate } from "react-router-dom"; -import SettingsIcon from "../../../../../assets/icons/settings-bold.svg?react"; -import PropTypes from "prop-types"; -const ConfigButton = ({ shouldRender, monitorId }) => { - const theme = useTheme(); - const navigate = useNavigate(); - - if (!shouldRender) return null; - - return ( - - - - ); -}; - -ConfigButton.propTypes = { - shouldRender: PropTypes.bool, - monitorId: PropTypes.string, -}; - -export default ConfigButton; diff --git a/Client/src/Pages/Uptime/Details/Components/MonitorHeader/index.jsx b/Client/src/Pages/Uptime/Details/Components/MonitorHeader/index.jsx deleted file mode 100644 index 53d4c4dfa..000000000 --- a/Client/src/Pages/Uptime/Details/Components/MonitorHeader/index.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Stack, Typography } from "@mui/material"; -import PulseDot from "../../../../../Components/Animated/PulseDot"; -import Dot from "../../../../../Components/Dot"; -import { useTheme } from "@emotion/react"; -import useUtils from "../../../Monitors/Hooks/useUtils"; -import { formatDurationRounded } from "../../../../../Utils/timeUtils"; -import ConfigButton from "../ConfigButton"; -import SkeletonLayout from "./skeleton"; -import PropTypes from "prop-types"; - -const MonitorHeader = ({ shouldRender = true, isAdmin, monitor }) => { - const theme = useTheme(); - const { statusColor, statusMsg, determineState } = useUtils(); - console.log(shouldRender); - if (!shouldRender) { - return ; - } - - return ( - - - {monitor.name} - - - - {monitor?.url?.replace(/^https?:\/\//, "") || "..."} - - - - Checking every {formatDurationRounded(monitor?.interval)}. - - - - - - ); -}; - -MonitorHeader.propTypes = { - shouldRender: PropTypes.bool, - isAdmin: PropTypes.bool, - monitor: PropTypes.object, -}; - -export default MonitorHeader; diff --git a/Client/src/Pages/Uptime/Details/Components/MonitorHeader/skeleton.jsx b/Client/src/Pages/Uptime/Details/Components/MonitorHeader/skeleton.jsx deleted file mode 100644 index 64dc0547f..000000000 --- a/Client/src/Pages/Uptime/Details/Components/MonitorHeader/skeleton.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Stack, Skeleton } from "@mui/material"; - -const SkeletonLayout = () => { - return ( - - - - - ); -}; - -export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx b/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx index 56ea8eca7..1f5a19203 100644 --- a/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx +++ b/Client/src/Pages/Uptime/Details/Components/ResponseTable/index.jsx @@ -1,4 +1,4 @@ -import ChartBox from "../Charts/ChartBox"; +import ChartBox from "../../../../../Components/Charts/ChartBox"; import PropTypes from "prop-types"; import HistoryIcon from "../../../../../assets/icons/history-icon.svg?react"; import Table from "../../../../../Components/Table"; diff --git a/Client/src/Pages/Uptime/Details/Components/StatusBoxes/skeleton.jsx b/Client/src/Pages/Uptime/Details/Components/StatusBoxes/skeleton.jsx deleted file mode 100644 index 993b410c8..000000000 --- a/Client/src/Pages/Uptime/Details/Components/StatusBoxes/skeleton.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Stack, Skeleton } from "@mui/material"; -import { useTheme } from "@emotion/react"; -const SkeletonLayout = () => { - const theme = useTheme(); - return ( - - - - - - ); -}; - -export default SkeletonLayout; diff --git a/Client/src/Pages/Uptime/Details/Components/StatusBoxes/index.jsx b/Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx similarity index 73% rename from Client/src/Pages/Uptime/Details/Components/StatusBoxes/index.jsx rename to Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx index 3f483889b..ad4620396 100644 --- a/Client/src/Pages/Uptime/Details/Components/StatusBoxes/index.jsx +++ b/Client/src/Pages/Uptime/Details/Components/UptimeStatusBoxes/index.jsx @@ -1,20 +1,13 @@ -// Components -import { Stack, Typography } from "@mui/material"; +import StatusBoxes from "../../../../../Components/StatusBoxes"; import StatBox from "../../../../../Components/StatBox"; -import SkeletonLayout from "./skeleton"; -// Utils -import { useTheme } from "@mui/material/styles"; -import useUtils from "../../../Monitors/Hooks/useUtils"; import { getHumanReadableDuration } from "../../../../../Utils/timeUtils"; -import PropTypes from "prop-types"; -const StatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => { - // Utils +import { useTheme } from "@mui/material/styles"; +import { Typography } from "@mui/material"; +import useUtils from "../../../Monitors/Hooks/useUtils"; + +const UptimeStatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => { const theme = useTheme(); const { determineState } = useUtils(); - - if (!shouldRender) { - return ; - } const { time: streakTime, units: streakUnits } = getHumanReadableDuration( monitor?.uptimeStreak ); @@ -22,12 +15,8 @@ const StatusBoxes = ({ shouldRender, monitor, certificateExpiry }) => { const { time: lastCheckTime, units: lastCheckUnits } = getHumanReadableDuration( monitor?.timeSinceLastCheck ); - return ( - + { } /> - + ); }; -StatusBoxes.propTypes = { - shouldRender: PropTypes.bool, - monitor: PropTypes.object, - certificateExpiry: PropTypes.string, -}; - -export default StatusBoxes; +export default UptimeStatusBoxes; diff --git a/Client/src/Pages/Uptime/Details/index.jsx b/Client/src/Pages/Uptime/Details/index.jsx index 81265eb56..33583b0cb 100644 --- a/Client/src/Pages/Uptime/Details/index.jsx +++ b/Client/src/Pages/Uptime/Details/index.jsx @@ -1,11 +1,11 @@ // Components import Breadcrumbs from "../../../Components/Breadcrumbs"; -import MonitorHeader from "./Components/MonitorHeader"; -import StatusBoxes from "./Components/StatusBoxes"; +import MonitorStatusHeader from "../../../Components/MonitorStatusHeader"; import TimeFramePicker from "./Components/TimeFramePicker"; import ChartBoxes from "./Components/ChartBoxes"; import ResponseTimeChart from "./Components/Charts/ResponseTimeChart"; import ResponseTable from "./Components/ResponseTable"; +import UptimeStatusBoxes from "./Components/UptimeStatusBoxes"; // MUI Components import { Stack } from "@mui/material"; @@ -80,12 +80,12 @@ const UptimeDetails = () => { return ( - - Date: Tue, 28 Jan 2025 15:42:29 -0800 Subject: [PATCH 7/9] fix speedometer name --- .../assets/icons/{spedometer-icon.svg => speedometer-icon.svg} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Client/src/assets/icons/{spedometer-icon.svg => speedometer-icon.svg} (100%) diff --git a/Client/src/assets/icons/spedometer-icon.svg b/Client/src/assets/icons/speedometer-icon.svg similarity index 100% rename from Client/src/assets/icons/spedometer-icon.svg rename to Client/src/assets/icons/speedometer-icon.svg From ac2cf490828cfca3f7774b352bd720e96ccdde35 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 28 Jan 2025 15:42:46 -0800 Subject: [PATCH 8/9] Refactor pagespeed details --- .../src/Components/Charts/ChartBox/index.jsx | 90 ++++ .../src/Components/Charts/LegendBox/index.jsx | 42 ++ .../ConfigButton/index.jsx | 40 ++ .../Components/MonitorStatusHeader/index.jsx | 54 ++ .../MonitorStatusHeader/skeleton.jsx | 23 + .../{ => Components}/Charts/AreaChart.jsx | 15 +- .../Components/Charts/AreaChartLegend.jsx | 56 ++ .../{ => Components}/Charts/PieChart.jsx | 6 +- .../Components/Charts/PieChartLegend.jsx | 96 ++++ .../Details/Components/EmptyView/index.jsx | 0 .../Components/PageSpeedAreaChart/index.jsx | 45 ++ .../Components/PageSpeedStatusBoxes/index.jsx | 41 ++ .../Components/PerformanceReport/index.jsx | 45 ++ .../Details/Hooks/useMonitorFetch.jsx | 40 ++ Client/src/Pages/PageSpeed/Details/index.css | 18 - Client/src/Pages/PageSpeed/Details/index.jsx | 477 +++--------------- .../src/Pages/PageSpeed/Details/skeleton.jsx | 93 ---- Client/src/Pages/PageSpeed/Details/styled.jsx | 42 -- Client/src/Pages/PageSpeed/Monitors/index.jsx | 2 +- 19 files changed, 643 insertions(+), 582 deletions(-) create mode 100644 Client/src/Components/Charts/ChartBox/index.jsx create mode 100644 Client/src/Components/Charts/LegendBox/index.jsx create mode 100644 Client/src/Components/MonitorStatusHeader/ConfigButton/index.jsx create mode 100644 Client/src/Components/MonitorStatusHeader/index.jsx create mode 100644 Client/src/Components/MonitorStatusHeader/skeleton.jsx rename Client/src/Pages/PageSpeed/Details/{ => Components}/Charts/AreaChart.jsx (95%) create mode 100644 Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx rename Client/src/Pages/PageSpeed/Details/{ => Components}/Charts/PieChart.jsx (99%) create mode 100644 Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx create mode 100644 Client/src/Pages/PageSpeed/Details/Components/EmptyView/index.jsx create mode 100644 Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx create mode 100644 Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx create mode 100644 Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx create mode 100644 Client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx delete mode 100644 Client/src/Pages/PageSpeed/Details/index.css delete mode 100644 Client/src/Pages/PageSpeed/Details/skeleton.jsx delete mode 100644 Client/src/Pages/PageSpeed/Details/styled.jsx diff --git a/Client/src/Components/Charts/ChartBox/index.jsx b/Client/src/Components/Charts/ChartBox/index.jsx new file mode 100644 index 000000000..eed3efd0d --- /dev/null +++ b/Client/src/Components/Charts/ChartBox/index.jsx @@ -0,0 +1,90 @@ +import { Stack, Typography } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import IconBox from "../../IconBox"; +import PropTypes from "prop-types"; + +const ChartBox = ({ + children, + icon, + header, + height = "300px", + justifyContent = "space-between", + Legend, + borderRadiusRight = 4, +}) => { + const theme = useTheme(); + return ( + + span": { + color: theme.palette.primary.contrastText, + fontSize: 20, + "& span": { + opacity: 0.8, + marginLeft: 2, + fontSize: 15, + }, + }, + + "& tspan, & text": { + fill: theme.palette.primary.contrastTextTertiary, + }, + "& path": { + transition: "fill 300ms ease, stroke-width 400ms ease", + }, + }} + > + + {icon} + {header} + + {children} + + {Legend && Legend} + + ); +}; + +export default ChartBox; + +ChartBox.propTypes = { + children: PropTypes.node, + icon: PropTypes.node.isRequired, + header: PropTypes.string.isRequired, + height: PropTypes.string, +}; diff --git a/Client/src/Components/Charts/LegendBox/index.jsx b/Client/src/Components/Charts/LegendBox/index.jsx new file mode 100644 index 000000000..ac14669e3 --- /dev/null +++ b/Client/src/Components/Charts/LegendBox/index.jsx @@ -0,0 +1,42 @@ +import { Stack, Typography } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import IconBox from "../../IconBox"; +import PropTypes from "prop-types"; + +const LegendBox = ({ children, icon, header, sx }) => { + const theme = useTheme(); + return ( + + + {icon} + {header} + + {children} + + ); +}; + +LegendBox.propTypes = { + children: PropTypes.node, + icon: PropTypes.node, + header: PropTypes.string, +}; + +export default LegendBox; diff --git a/Client/src/Components/MonitorStatusHeader/ConfigButton/index.jsx b/Client/src/Components/MonitorStatusHeader/ConfigButton/index.jsx new file mode 100644 index 000000000..fc6407f31 --- /dev/null +++ b/Client/src/Components/MonitorStatusHeader/ConfigButton/index.jsx @@ -0,0 +1,40 @@ +import { Button, Box } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import { useNavigate } from "react-router-dom"; +import SettingsIcon from "../../../assets/icons/settings-bold.svg?react"; +import PropTypes from "prop-types"; +const ConfigButton = ({ shouldRender, monitorId }) => { + const theme = useTheme(); + const navigate = useNavigate(); + + if (!shouldRender) return null; + + return ( + + + + ); +}; + +ConfigButton.propTypes = { + shouldRender: PropTypes.bool, + monitorId: PropTypes.string, +}; + +export default ConfigButton; diff --git a/Client/src/Components/MonitorStatusHeader/index.jsx b/Client/src/Components/MonitorStatusHeader/index.jsx new file mode 100644 index 000000000..6a7188775 --- /dev/null +++ b/Client/src/Components/MonitorStatusHeader/index.jsx @@ -0,0 +1,54 @@ +import { Stack, Typography } from "@mui/material"; +import PulseDot from "../Animated/PulseDot"; +import Dot from "../Dot"; +import { useTheme } from "@emotion/react"; +import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; +import { formatDurationRounded } from "../../Utils/timeUtils"; +import ConfigButton from "./ConfigButton"; +import SkeletonLayout from "./skeleton"; +import PropTypes from "prop-types"; + +const MonitorHeader = ({ shouldRender = true, isAdmin, monitor }) => { + const theme = useTheme(); + const { statusColor, statusMsg, determineState } = useUtils(); + if (!shouldRender) { + return ; + } + + return ( + + + {monitor.name} + + + + {monitor?.url?.replace(/^https?:\/\//, "") || "..."} + + + + Checking every {formatDurationRounded(monitor?.interval)}. + + + + + + ); +}; + +MonitorHeader.propTypes = { + shouldRender: PropTypes.bool, + isAdmin: PropTypes.bool, + monitor: PropTypes.object, +}; + +export default MonitorHeader; diff --git a/Client/src/Components/MonitorStatusHeader/skeleton.jsx b/Client/src/Components/MonitorStatusHeader/skeleton.jsx new file mode 100644 index 000000000..64dc0547f --- /dev/null +++ b/Client/src/Components/MonitorStatusHeader/skeleton.jsx @@ -0,0 +1,23 @@ +import { Stack, Skeleton } from "@mui/material"; + +const SkeletonLayout = () => { + return ( + + + + + ); +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/PageSpeed/Details/Charts/AreaChart.jsx b/Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx similarity index 95% rename from Client/src/Pages/PageSpeed/Details/Charts/AreaChart.jsx rename to Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx index a854eb094..40fec0ab6 100644 --- a/Client/src/Pages/PageSpeed/Details/Charts/AreaChart.jsx +++ b/Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChart.jsx @@ -11,7 +11,7 @@ import { import { useTheme } from "@emotion/react"; import { useMemo, useState } from "react"; import { Box, Stack, Typography } from "@mui/material"; -import { formatDateWithTz } from "../../../../Utils/timeUtils"; +import { formatDateWithTz } from "../../../../../Utils/timeUtils"; import { useSelector } from "react-redux"; const config = { @@ -211,10 +211,13 @@ CustomTick.propTypes = { * @returns {JSX.Element} The area chart component. */ -const PagespeedDetailsAreaChart = ({ data, interval, metrics }) => { +const PageSpeedAreaChart = ({ data, monitor, metrics }) => { const theme = useTheme(); const [isHovered, setIsHovered] = useState(false); - const memoizedData = useMemo(() => processDataWithGaps(data, interval), [data[0]]); + const memoizedData = useMemo( + () => processDataWithGaps(data, monitor.interval), + [data[0]] + ); const filteredConfig = Object.keys(config).reduce((result, key) => { if (metrics[key]) { @@ -310,7 +313,7 @@ const PagespeedDetailsAreaChart = ({ data, interval, metrics }) => { ); }; -PagespeedDetailsAreaChart.propTypes = { +PageSpeedAreaChart.propTypes = { data: PropTypes.arrayOf( PropTypes.shape({ createdAt: PropTypes.string.isRequired, @@ -320,8 +323,8 @@ PagespeedDetailsAreaChart.propTypes = { seo: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, }) ).isRequired, - interval: PropTypes.number.isRequired, + monitor: PropTypes.object.isRequired, metrics: PropTypes.object, }; -export default PagespeedDetailsAreaChart; +export default PageSpeedAreaChart; diff --git a/Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx b/Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx new file mode 100644 index 000000000..1d3d5ddef --- /dev/null +++ b/Client/src/Pages/PageSpeed/Details/Components/Charts/AreaChartLegend.jsx @@ -0,0 +1,56 @@ +import { Box, Typography, Divider } from "@mui/material"; +import Checkbox from "../../../../../Components/Inputs/Checkbox"; +import MetricsIcon from "../../../../../assets/icons/ruler-icon.svg?react"; +import LegendBox from "../../../../../Components/Charts/LegendBox"; + +import { useTheme } from "@emotion/react"; + +const AreaChartLegend = ({ metrics, handleMetrics }) => { + const theme = useTheme(); + return ( + } + header="Metrics" + > + + + Shown + + + + handleMetrics("accessibility")} + /> + + handleMetrics("bestPractices")} + /> + + handleMetrics("performance")} + /> + + handleMetrics("seo")} + /> + + + ); +}; + +export default AreaChartLegend; diff --git a/Client/src/Pages/PageSpeed/Details/Charts/PieChart.jsx b/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx similarity index 99% rename from Client/src/Pages/PageSpeed/Details/Charts/PieChart.jsx rename to Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx index 193841e7e..aad697f9f 100644 --- a/Client/src/Pages/PageSpeed/Details/Charts/PieChart.jsx +++ b/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChart.jsx @@ -115,6 +115,10 @@ const weights = { const PieChart = ({ audits }) => { const theme = useTheme(); + const [highlightedItem, setHighLightedItem] = useState(null); + const [expand, setExpand] = useState(false); + + if (typeof audits === "undefined") return null; /** * Retrieves color properties based on the performance value. @@ -216,8 +220,6 @@ const PieChart = ({ audits }) => { const pieData = getPieData(audits); const colorMap = getColors(performance); - const [highlightedItem, setHighLightedItem] = useState(null); - const [expand, setExpand] = useState(false); return ( setExpand(false)}> {expand ? ( diff --git a/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx b/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx new file mode 100644 index 000000000..c7612821f --- /dev/null +++ b/Client/src/Pages/PageSpeed/Details/Components/Charts/PieChartLegend.jsx @@ -0,0 +1,96 @@ +import { Stack, Box, Typography } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import LegendBox from "../../../../../Components/Charts/LegendBox"; +import SpeedometerIcon from "../../../../../assets/icons/speedometer-icon.svg?react"; +import PropTypes from "prop-types"; + +const PieChartLegend = ({ audits }) => { + const theme = useTheme(); + + if (typeof audits === "undefined") return null; + return ( + } + header="Performance metrics" + sx={{ flex: 1 }} + > + {Object.keys(audits).map((key) => { + if (key === "_id") return; + + let audit = audits[key]; + let score = audit.score * 100; + let bg = + score >= 90 + ? theme.palette.success.main + : score >= 50 + ? theme.palette.warning.main + : score >= 0 + ? theme.palette.error.main + : theme.palette.tertiary.main; + + // Find the position where the number ends and the unit begins + const match = audit.displayValue.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/); + let value; + let unit; + if (match) { + value = match[1]; + match[2] === "s" ? (unit = "seconds") : (unit = match[2]); + } else { + value = audit.displayValue; + } + + return ( + + + + {audit.title} + + + {value} + + {unit} + + + + + + ); + })} + + ); +}; + +PieChartLegend.propTypes = { + audits: PropTypes.object, +}; + +export default PieChartLegend; diff --git a/Client/src/Pages/PageSpeed/Details/Components/EmptyView/index.jsx b/Client/src/Pages/PageSpeed/Details/Components/EmptyView/index.jsx new file mode 100644 index 000000000..e69de29bb diff --git a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx new file mode 100644 index 000000000..1046a8ca3 --- /dev/null +++ b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx @@ -0,0 +1,45 @@ +import ChartBox from "../../../../../Components/Charts/ChartBox"; +import AreaChart from "../Charts/AreaChart"; +import AreaChartLegend from "../Charts/AreaChartLegend"; +import ScoreIcon from "../../../../../assets/icons/monitor-graph-line.svg?react"; +import { Stack } from "@mui/material"; + +import { useTheme } from "@emotion/react"; + +const PageSpeedAreaChart = ({ shouldRender, monitor, metrics, handleMetrics }) => { + const theme = useTheme(); + if (typeof monitor === "undefined") { + return null; + } + + const data = monitor?.checks ? [...monitor.checks].reverse() : []; + + return ( + + } + header="Score history" + height="100%" + borderRadiusRight={16} + Legend={ + + } + > + + + + ); +}; + +export default PageSpeedAreaChart; diff --git a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx new file mode 100644 index 000000000..5bce18df6 --- /dev/null +++ b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedStatusBoxes/index.jsx @@ -0,0 +1,41 @@ +import StatusBoxes from "../../../../../Components/StatusBoxes"; +import StatBox from "../../../../../Components/StatBox"; +import { Typography } from "@mui/material"; +import { getHumanReadableDuration } from "../../../../../Utils/timeUtils"; + +const PageSpeedStatusBoxes = ({ shouldRender, monitor }) => { + const { time: uptimeDuration, units: uptimeUnits } = getHumanReadableDuration( + monitor?.uptimeDuration + ); + + const { time: lastCheckTime, units: lastCheckUnits } = getHumanReadableDuration( + monitor?.lastChecked + ); + + return ( + + + {uptimeDuration} + {uptimeUnits} + ago + + } + /> + + {lastCheckTime} + {lastCheckUnits} + ago + + } + /> + + ); +}; + +export default PageSpeedStatusBoxes; diff --git a/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx b/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx new file mode 100644 index 000000000..8f34dbf19 --- /dev/null +++ b/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx @@ -0,0 +1,45 @@ +import ChartBox from "../../../../../Components/Charts/ChartBox"; +import PerformanceIcon from "../../../../../assets/icons/performance-report.svg?react"; +import PieChart from "../Charts/PieChart"; +import { Typography } from "@mui/material"; +import { useTheme } from "@emotion/react"; +import PieChartLegend from "../Charts/PieChartLegend"; + +const PerformanceReport = ({ audits }) => { + const theme = useTheme(); + return ( + } + header="Performance report" + Legend={} + borderRadiusRight={16} + > + + + Values are estimated and may vary.{" "} + + See calculator + + + + ); +}; + +export default PerformanceReport; diff --git a/Client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx b/Client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx new file mode 100644 index 000000000..340d4cf5e --- /dev/null +++ b/Client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx @@ -0,0 +1,40 @@ +import { useEffect, useState } from "react"; +import { networkService } from "../../../../main"; +import { logger } from "../../../../Utils/Logger"; +import { useNavigate } from "react-router-dom"; +const useMonitorFetch = ({ authToken, monitorId }) => { + const navigate = useNavigate(); + + const [monitor, setMonitor] = useState(undefined); + const [audits, setAudits] = useState(undefined); + const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + const fetchMonitor = async () => { + try { + setIsLoading(true); + const res = await networkService.getStatsByMonitorId({ + authToken: authToken, + monitorId: monitorId, + sortOrder: "desc", + limit: 50, + dateRange: "day", + numToDisplay: null, + normalize: null, + }); + setMonitor(res?.data?.data ?? undefined); + setAudits(res?.data?.data?.checks?.[0]?.audits ?? undefined); + } catch (error) { + logger.error(logger); + navigate("/not-found", { replace: true }); + } finally { + setIsLoading(false); + } + }; + + fetchMonitor(); + }, [authToken, monitorId, navigate]); + + return { monitor, audits, isLoading }; +}; + +export { useMonitorFetch }; diff --git a/Client/src/Pages/PageSpeed/Details/index.css b/Client/src/Pages/PageSpeed/Details/index.css deleted file mode 100644 index 0d0699b3d..000000000 --- a/Client/src/Pages/PageSpeed/Details/index.css +++ /dev/null @@ -1,18 +0,0 @@ -.page-speed-details button.MuiButtonBase-root { - min-height: 34px; -} - -.page-speed-details .MuiPieArcLabel-root { - font-size: var(--env-var-font-size-small-plus); - transition: fill 300ms ease; -} -.page-speed-details .MuiPieArcLabel-faded { - fill: rgba(0, 0, 0, 0.3); -} -.page-speed-details .pie-label, -.page-speed-details .pie-value-label { - transition: all 400ms ease; -} -.page-speed-details .MuiPieArc-root { - stroke: inherit; -} diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx index 4c4bceb1b..1a4cbc7e4 100644 --- a/Client/src/Pages/PageSpeed/Details/index.jsx +++ b/Client/src/Pages/PageSpeed/Details/index.jsx @@ -1,75 +1,37 @@ -import PropTypes from "prop-types"; -import { Box, Button, Divider, Stack, Tooltip, Typography } from "@mui/material"; -import { useEffect, useState } from "react"; -import { useTheme } from "@emotion/react"; -import { useNavigate, useParams } from "react-router-dom"; -import { useSelector } from "react-redux"; -import { formatDurationRounded, formatDurationSplit } from "../../../Utils/timeUtils"; -import { ChartBox } from "./styled"; -import { logger } from "../../../Utils/Logger"; -import { networkService } from "../../../main"; -import SkeletonLayout from "./skeleton"; -import SettingsIcon from "../../../assets/icons/settings-bold.svg?react"; -import SpedometerIcon from "../../../assets/icons/spedometer-icon.svg?react"; -import MetricsIcon from "../../../assets/icons/ruler-icon.svg?react"; -import ScoreIcon from "../../../assets/icons/monitor-graph-line.svg?react"; -import PerformanceIcon from "../../../assets/icons/performance-report.svg?react"; +// Components +import { Stack, Typography } from "@mui/material"; import Breadcrumbs from "../../../Components/Breadcrumbs"; -import PulseDot from "../../../Components/Animated/PulseDot"; -import PagespeedDetailsAreaChart from "./Charts/AreaChart"; -import Checkbox from "../../../Components/Inputs/Checkbox"; -import PieChart from "./Charts/PieChart"; -import useUtils from "../../Uptime/Monitors/Hooks/useUtils"; -import "./index.css"; +import MonitorStatusHeader from "../../../Components/MonitorStatusHeader"; +import PageSpeedStatusBoxes from "./Components/PageSpeedStatusBoxes"; +import PageSpeedAreaChart from "./Components/PageSpeedAreaChart"; +import PerformanceReport from "./Components/PerformanceReport"; +import Fallback from "../../../Components/Fallback"; +// Utils +import { useTheme } from "@emotion/react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import StatBox from "../../../Components/StatBox"; -import IconBox from "../../../Components/IconBox"; +import { useParams } from "react-router-dom"; +import { useSelector } from "react-redux"; +import { useMonitorFetch } from "./Hooks/useMonitorFetch"; +import { useState } from "react"; +// Constants +const BREADCRUMBS = [ + { name: "pagespeed", path: "/pagespeed" }, + { name: "details", path: `` }, + // { name: "details", path: `/pagespeed/${monitorId}` }, // Not needed? +]; const PageSpeedDetails = () => { const theme = useTheme(); - const navigate = useNavigate(); const isAdmin = useIsAdmin(); - const { statusColor, pagespeedStatusMsg, determineState } = useUtils(); - const [monitor, setMonitor] = useState({}); - const [audits, setAudits] = useState({}); const { monitorId } = useParams(); const { authToken } = useSelector((state) => state.auth); - useEffect(() => { - const fetchMonitor = async () => { - try { - const res = await networkService.getStatsByMonitorId({ - authToken: authToken, - monitorId: monitorId, - sortOrder: "desc", - limit: 50, - dateRange: "day", - numToDisplay: null, - normalize: null, - }); - setMonitor(res?.data?.data ?? {}); - setAudits(res?.data?.data?.checks?.[0]?.audits ?? {}); - } catch (error) { - logger.error(logger); - navigate("/not-found", { replace: true }); - } - }; + const { monitor, audits, isLoading } = useMonitorFetch({ + authToken, + monitorId, + }); - fetchMonitor(); - }, [monitorId, authToken, navigate]); - - let loading = Object.keys(monitor).length === 0; - const data = monitor?.checks ? [...monitor.checks].reverse() : []; - - const splitDuration = (duration) => { - const { time, format } = formatDurationSplit(duration); - return ( - <> - {time} - {format} - - ); - }; + console.log(monitor, audits, isLoading); const [metrics, setMetrics] = useState({ accessibility: true, @@ -77,371 +39,46 @@ const PageSpeedDetails = () => { performance: true, seo: true, }); + const handleMetrics = (id) => { setMetrics((prev) => ({ ...prev, [id]: !prev[id] })); }; + if (isLoading) { + return "funk"; + } + + if (typeof audits === "undefined") { + return "No Data"; + } + return ( - - {loading ? ( - - ) : ( - <> - - - - - {monitor.name} - - - - - - - - - {monitor?.url} - - - Checking every {formatDurationRounded(monitor?.interval)}. - - - - {isAdmin && ( - - )} - - - - {splitDuration(monitor?.uptimeDuration)} - ago - - } - /> - - {splitDuration(monitor?.lastChecked)} - ago - - } - /> - - - - Showing statistics for past 24 hours. - - - - - - - Score history - - - - - - - - - - Metrics - - - - - Shown - - - - handleMetrics("accessibility")} - /> - - handleMetrics("bestPractices")} - /> - - handleMetrics("performance")} - /> - - handleMetrics("seo")} - /> - - - - - - - - - - - Performance report - - - - - Values are estimated and may vary.{" "} - - See calculator - - - - - - - - - Performance Metrics - - - {Object.keys(audits).map((key) => { - if (key === "_id") return; - - let audit = audits[key]; - let score = audit.score * 100; - let bg = - score >= 90 - ? theme.palette.success.main - : score >= 50 - ? theme.palette.warning.main - : score >= 0 - ? theme.palette.error.contrastText - : theme.palette.tertiary.main; - - // Find the position where the number ends and the unit begins - const match = audit.displayValue.match(/(\d+\.?\d*)\s*([a-zA-Z]+)/); - let value; - let unit; - if (match) { - value = match[1]; - match[2] === "s" ? (unit = "seconds") : (unit = match[2]); - } else { - value = audit.displayValue; - } - - return ( - - - - {audit.title} - - - {value} - - {unit} - - - - - - ); - })} - - - - - )} + + + + + + Showing statistics for past 24 hours. + + + ); }; -PageSpeedDetails.propTypes = { - isAdmin: PropTypes.bool, - push: PropTypes.func, -}; - export default PageSpeedDetails; diff --git a/Client/src/Pages/PageSpeed/Details/skeleton.jsx b/Client/src/Pages/PageSpeed/Details/skeleton.jsx deleted file mode 100644 index 1d55a5039..000000000 --- a/Client/src/Pages/PageSpeed/Details/skeleton.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Box, Skeleton, Stack } from "@mui/material"; - -/** - * Renders a skeleton layout. - * - * @returns {JSX.Element} - */ -const SkeletonLayout = () => { - return ( - <> - - - - - - - - - - - - - - - - - - - - ); -}; - -export default SkeletonLayout; diff --git a/Client/src/Pages/PageSpeed/Details/styled.jsx b/Client/src/Pages/PageSpeed/Details/styled.jsx deleted file mode 100644 index 291d0c6f2..000000000 --- a/Client/src/Pages/PageSpeed/Details/styled.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Stack, styled } from "@mui/material"; - -export const ChartBox = styled(Stack)(({ theme }) => ({ - display: "grid", - minHeight: 300, - minWidth: 250, - border: 1, - borderStyle: "solid", - borderColor: theme.palette.primary.lowContrast, - borderRadius: 4, - borderTopRightRadius: 16, - borderBottomRightRadius: 16, - backgroundColor: theme.palette.primary.main, - "& h2": { - color: theme.palette.primary.contrastTextSecondary, - fontSize: 15, - fontWeight: 500, - }, - "& p": { color: theme.palette.primary.contrastTextTertiary }, - "& > :nth-of-type(1)": { - gridColumn: 1, - gridRow: 1, - height: "fit-content", - paddingTop: theme.spacing(8), - paddingLeft: theme.spacing(8), - }, - "& > :nth-of-type(2)": { gridColumn: 1, gridRow: 2 }, - "& > :nth-of-type(3)": { - gridColumn: 2, - gridRow: "span 2", - padding: theme.spacing(8), - borderLeft: 1, - borderLeftStyle: "solid", - borderLeftColor: theme.palette.primary.lowContrast, - borderRadius: 16, - backgroundColor: theme.palette.primary.main, - background: `linear-gradient(325deg, ${theme.palette.tertiary.main} 20%, ${theme.palette.primary.main} 45%)`, - }, - "& path": { - transition: "stroke-width 400ms ease", - }, -})); diff --git a/Client/src/Pages/PageSpeed/Monitors/index.jsx b/Client/src/Pages/PageSpeed/Monitors/index.jsx index 228e522ff..e921bc15a 100644 --- a/Client/src/Pages/PageSpeed/Monitors/index.jsx +++ b/Client/src/Pages/PageSpeed/Monitors/index.jsx @@ -1,6 +1,6 @@ // Components import Breadcrumbs from "../../../Components/Breadcrumbs"; -import { Stack, Box } from "@mui/material"; +import { Stack } from "@mui/material"; import CreateMonitorHeader from "../../../Components/CreateMonitorHeader"; import MonitorCountHeader from "../../../Components/MonitorCountHeader"; import MonitorGrid from "./Components/MonitorGrid"; From a33c0c16a003c1aa2aa64a5039a022df768614e2 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 28 Jan 2025 15:51:58 -0800 Subject: [PATCH 9/9] Add skeletons and loading conditions --- .../Components/PageSpeedAreaChart/index.jsx | 14 ++++++++++++++ .../Components/PageSpeedAreaChart/skeleton.jsx | 17 +++++++++++++++++ .../Components/PerformanceReport/index.jsx | 14 ++++++++++++-- .../Components/PerformanceReport/skeleton.jsx | 17 +++++++++++++++++ Client/src/Pages/PageSpeed/Details/index.jsx | 13 ++++--------- 5 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx create mode 100644 Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx diff --git a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx index 1046a8ca3..c818d04a3 100644 --- a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx +++ b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/index.jsx @@ -1,13 +1,20 @@ import ChartBox from "../../../../../Components/Charts/ChartBox"; import AreaChart from "../Charts/AreaChart"; import AreaChartLegend from "../Charts/AreaChartLegend"; +import SkeletonLayout from "./skeleton"; import ScoreIcon from "../../../../../assets/icons/monitor-graph-line.svg?react"; import { Stack } from "@mui/material"; +import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; const PageSpeedAreaChart = ({ shouldRender, monitor, metrics, handleMetrics }) => { const theme = useTheme(); + + if (!shouldRender) { + return ; + } + if (typeof monitor === "undefined") { return null; } @@ -42,4 +49,11 @@ const PageSpeedAreaChart = ({ shouldRender, monitor, metrics, handleMetrics }) = ); }; +PageSpeedAreaChart.propTypes = { + shouldRender: PropTypes.bool, + monitor: PropTypes.object, + metrics: PropTypes.object, + handleMetrics: PropTypes.func, +}; + export default PageSpeedAreaChart; diff --git a/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx new file mode 100644 index 000000000..b793908d4 --- /dev/null +++ b/Client/src/Pages/PageSpeed/Details/Components/PageSpeedAreaChart/skeleton.jsx @@ -0,0 +1,17 @@ +import { Box, Skeleton } from "@mui/material"; + +const SkeletonLayout = () => { + return ( + + + + ); +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx b/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx index 8f34dbf19..2db40e126 100644 --- a/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx +++ b/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/index.jsx @@ -4,9 +4,19 @@ import PieChart from "../Charts/PieChart"; import { Typography } from "@mui/material"; import { useTheme } from "@emotion/react"; import PieChartLegend from "../Charts/PieChartLegend"; - -const PerformanceReport = ({ audits }) => { +import SkeletonLayout from "./skeleton"; +const PerformanceReport = ({ shouldRender, audits }) => { + console.log("shouldRender", shouldRender); const theme = useTheme(); + + if (!shouldRender) { + return ; + } + + if (typeof audits === "undefined") { + return null; + } + return ( } diff --git a/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx b/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx new file mode 100644 index 000000000..b793908d4 --- /dev/null +++ b/Client/src/Pages/PageSpeed/Details/Components/PerformanceReport/skeleton.jsx @@ -0,0 +1,17 @@ +import { Box, Skeleton } from "@mui/material"; + +const SkeletonLayout = () => { + return ( + + + + ); +}; + +export default SkeletonLayout; diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx index 1a4cbc7e4..43eb0ed7b 100644 --- a/Client/src/Pages/PageSpeed/Details/index.jsx +++ b/Client/src/Pages/PageSpeed/Details/index.jsx @@ -44,14 +44,6 @@ const PageSpeedDetails = () => { setMetrics((prev) => ({ ...prev, [id]: !prev[id] })); }; - if (isLoading) { - return "funk"; - } - - if (typeof audits === "undefined") { - return "No Data"; - } - return ( @@ -76,7 +68,10 @@ const PageSpeedDetails = () => { metrics={metrics} handleMetrics={handleMetrics} /> - + ); };