Merge branch 'develop' into feat/fe/ttl

This commit is contained in:
Alexander Holliday
2024-09-16 17:09:02 -07:00
committed by GitHub
8 changed files with 553 additions and 209 deletions
+6 -1
View File
@@ -96,7 +96,12 @@ const Monitors = ({ isAdmin }) => {
alignItems="center"
mb={theme.spacing(8)}
>
<Typography component="h2" variant="h2" letterSpacing={-0.5}>
<Typography
component="h2"
variant="h2"
fontWeight={500}
letterSpacing={-0.2}
>
Actively monitoring
</Typography>
<Box
+23
View File
@@ -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,
};
};
@@ -1,37 +1,7 @@
.configure-pagespeed span.MuiTypography-root {
font-size: var(--env-var-font-size-small-plus);
}
.configure-pagespeed h3.MuiTypography-root {
font-weight: 500;
}
.configure-pagespeed .MuiBox-root > .field + p.MuiTypography-root {
font-size: var(--env-var-font-size-small-plus);
}
.configure-pagespeed .MuiBox-root > .field + p.MuiTypography-root,
.configure-pagespeed h3.MuiTypography-root > span.MuiTypography-root {
opacity: 0.6;
}
.configure-pagespeed .checkbox-wrapper {
margin-bottom: 1px;
}
/* to be removed */
.configure-pagespeed .section-disabled {
opacity: 0.4;
padding: 10px;
border-radius: 8px;
user-select: none;
pointer-events: none;
}
.configure-pagespeed button {
height: var(--env-var-height-2);
}
.configure-pagespeed h3.MuiTypography-root {
flex: 0.7;
}
.configure-pagespeed .field,
.configure-pagespeed .section-disabled,
.configure-pagespeed .select-wrapper {
+201 -101
View File
@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { useTheme } from "@emotion/react";
import { Box, Button, Modal, Stack, Typography } from "@mui/material";
import { Box, Button, Modal, Stack, Tooltip, Typography } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate, useParams } from "react-router";
import {
@@ -13,6 +13,7 @@ import {
import { monitorValidation } from "../../../Validation/validation";
import { createToast } from "../../../Utils/toastUtils";
import { logger } from "../../../Utils/Logger";
import { ConfigBox } from "../../Monitors/styled";
import Field from "../../../Components/Inputs/Field";
import Select from "../../../Components/Inputs/Select";
import Checkbox from "../../../Components/Inputs/Checkbox";
@@ -22,20 +23,27 @@ 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";
import useUtils from "../../Monitors/utils";
import "./index.css";
const PageSpeedConfigure = () => {
const theme = useTheme();
const navigate = useNavigate();
const dispatch = useDispatch();
const MS_PER_MINUTE = 60000;
const { authToken } = useSelector((state) => state.auth);
const { user, authToken } = useSelector((state) => state.auth);
const { isLoading } = useSelector((state) => state.pageSpeedMonitors);
const { monitorId } = useParams();
const [monitor, setMonitor] = useState({});
const [errors, setErrors] = useState({});
const { determineState, statusColor } = useUtils();
const { statusColor, pagespeedStatusMsg, determineState } = useUtils();
const idMap = {
"monitor-url": "url",
"monitor-name": "name",
"monitor-checks-http": "type",
"monitor-checks-ping": "type",
"notify-email-default": "notification-email",
};
const frequencies = [
{ _id: 3, name: "3 minutes" },
@@ -68,24 +76,58 @@ const PageSpeedConfigure = () => {
fetchMonitor();
}, [dispatch, authToken, monitorId, navigate]);
const handleChange = (event, id) => {
let { value } = event.target;
if (id === "interval") {
value = value * MS_PER_MINUTE;
const handleChange = (event, name) => {
let { value, id } = event.target;
if (!name) name = idMap[id];
if (name.includes("notification-")) {
name = name.replace("notification-", "");
let hasNotif = monitor.notifications.some(
(notification) => notification.type === name
);
setMonitor((prev) => {
const notifs = [...prev.notifications];
if (hasNotif) {
return {
...prev,
notifications: notifs.filter((notif) => notif.type !== name),
};
} else {
return {
...prev,
notifications: [
...notifs,
name === "email"
? { type: name, address: value }
: // TODO - phone number
{ type: name, phone: value },
],
};
}
});
} else {
if (name === "interval") {
value = value * MS_PER_MINUTE;
}
setMonitor((prev) => ({
...prev,
[name]: value,
}));
const validation = monitorValidation.validate(
{ [name]: value },
{ abortEarly: false }
);
setErrors((prev) => {
const updatedErrors = { ...prev };
if (validation.error)
updatedErrors[name] = validation.error.details[0].message;
else delete updatedErrors[name];
return updatedErrors;
});
}
setMonitor((prev) => ({ ...prev, [id]: value }));
const { error } = monitorValidation.validate(
{ [id]: value },
{ abortEarly: false }
);
setErrors((prev) => {
const newErrors = { ...prev };
if (error) newErrors[id] = error.details[0].message;
else delete newErrors[id];
return newErrors;
});
};
const handlePause = async () => {
@@ -128,7 +170,7 @@ const PageSpeedConfigure = () => {
};
return (
<Stack className="configure-pagespeed" gap={theme.spacing(12)}>
<Stack className="configure-pagespeed" gap={theme.spacing(10)}>
{Object.keys(monitor).length === 0 ? (
<SkeletonLayout />
) : (
@@ -146,22 +188,65 @@ const PageSpeedConfigure = () => {
spellCheck="false"
onSubmit={handleSave}
flex={1}
gap={theme.spacing(12)}
gap={theme.spacing(10)}
>
<Stack direction="row" gap={theme.spacing(2)}>
<PulseDot color={statusColor[determineState(monitor)]} />
<Box>
<Typography component="h1" variant="h1" mb={theme.spacing(2)}>
{monitor?.url}
<Typography component="h1" variant="h1">
{monitor.name}
</Typography>
<Typography
component="span"
sx={{
color: statusColor[determineState(monitor)],
}}
<Stack
direction="row"
alignItems="center"
height="fit-content"
gap={theme.spacing(2)}
>
Your pagespeed monitor is {determineState(monitor)}.
</Typography>
<Tooltip
title={pagespeedStatusMsg[determineState(monitor)]}
disableInteractive
slotProps={{
popper: {
modifiers: [
{
name: "offset",
options: {
offset: [0, -8],
},
},
],
},
}}
>
<Box>
<PulseDot color={statusColor[determineState(monitor)]} />
</Box>
</Tooltip>
<Typography component="h2" variant="h2">
{monitor.url?.replace(/^https?:\/\//, "") || "..."}
</Typography>
<Typography
position="relative"
variant="body2"
ml={theme.spacing(6)}
mt={theme.spacing(1)}
sx={{
"&:before": {
position: "absolute",
content: `""`,
width: 4,
height: 4,
borderRadius: "50%",
backgroundColor: theme.palette.text.tertiary,
opacity: 0.8,
left: -10,
top: "50%",
transform: "translateY(-50%)",
},
}}
>
Editting...
</Typography>
</Stack>
</Box>
<Box alignSelf="flex-end" ml="auto">
<LoadingButton
@@ -206,102 +291,116 @@ const PageSpeedConfigure = () => {
</LoadingButton>
</Box>
</Stack>
<Stack
border={1}
borderColor={theme.palette.border.light}
borderRadius={theme.shape.borderRadius}
backgroundColor={theme.palette.background.main}
p={theme.spacing(20)}
pl={theme.spacing(15)}
gap={theme.spacing(20)}
sx={{
"& h3, & p": {
color: theme.palette.text.secondary,
},
}}
>
<Stack direction="row">
<Typography component="h3">Monitor display name</Typography>
<Field
type="text"
id="monitor-name"
placeholder="Example monitor"
value={monitor?.name || ""}
onChange={(event) => handleChange(event, "name")}
error={errors.name}
/>
</Stack>
<ConfigBox>
<Box>
<Typography component="h2">General settings</Typography>
<Typography component="p">
Here you can select the URL of the host, together with the
type of monitor.
</Typography>
</Box>
<Stack
direction="row"
gap={theme.spacing(20)}
sx={{
".MuiInputBase-root:has(> .Mui-disabled)": {
backgroundColor: theme.palette.background.accent,
},
}}
>
<Typography component="h3">URL</Typography>
<Field
type="url"
id="monitor-url"
label="URL"
placeholder="random.website.com"
value={monitor?.url?.replace("http://", "") || ""}
onChange={(event) => handleChange(event, "url")}
onChange={handleChange}
error={errors.url}
disabled={true}
/>
</Stack>
<Stack direction="row">
<Typography component="h3">Check frequency</Typography>
<Select
id="monitor-frequency"
items={frequencies}
value={monitor?.interval / MS_PER_MINUTE || 3}
onChange={(event) => handleChange(event, "interval")}
<Field
type="text"
id="monitor-name"
label="Monitor display name"
placeholder="Example monitor"
isOptional={true}
value={monitor?.name || ""}
onChange={handleChange}
error={errors.name}
/>
</Stack>
<Stack direction="row">
<Typography component="h3">
Incidents notifications{" "}
<Typography component="span">(coming soon)</Typography>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Incident notifications</Typography>
<Typography component="p">
When there is an incident, notify users.
</Typography>
<Stack
className="section-disabled"
backgroundColor={theme.palette.background.fill}
>
<Typography mb={theme.spacing(4)}>
When there is a new incident,
</Typography>
<Checkbox
id="notify-sms"
label="Notify via SMS (coming soon)"
isChecked={false}
isDisabled={true}
/>
<Checkbox
id="notify-email"
label="Notify via email (to gorkem.cetin@bluewavelabs.ca)"
isChecked={false}
/>
<Checkbox
id="notify-emails"
label="Notify via email to following emails"
isChecked={false}
/>
</Box>
<Stack gap={theme.spacing(6)}>
<Typography component="p">
When there is a new incident,
</Typography>
<Checkbox
id="notify-sms"
label="Notify via SMS (coming soon)"
isChecked={false}
value=""
onChange={() => logger.warn("disabled")}
isDisabled={true}
/>
<Checkbox
id="notify-email-default"
label={`Notify via email (to ${user.email})`}
isChecked={
monitor?.notifications?.some(
(notification) => notification.type === "email"
) || false
}
value={user?.email}
onChange={(event) => handleChange(event)}
/>
<Checkbox
id="notify-email"
label="Also notify via email to multiple addresses (coming soon)"
isChecked={false}
value=""
onChange={() => logger.warn("disabled")}
isDisabled={true}
/>
{monitor?.notifications?.some(
(notification) => notification.type === "emails"
) ? (
<Box mx={theme.spacing(16)}>
<Field
id="notify-emails-list"
placeholder="notifications@gmail.com"
id="notify-email-list"
type="text"
placeholder="name@gmail.com"
value=""
onChange={() => logger.warn("disabled")}
error=""
/>
<Typography mt={theme.spacing(4)}>
You can separate multiple emails with a comma
</Typography>
</Box>
</Stack>
) : (
""
)}
</Stack>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Advanced settings</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<Select
id="monitor-frequency"
label="Check frequency"
items={frequencies}
value={monitor?.interval / MS_PER_MINUTE || 3}
onChange={(event) => handleChange(event, "interval")}
/>
</Stack>
</ConfigBox>
<Stack direction="row" justifyContent="flex-end" mt="auto">
<LoadingButton
loading={isLoading}
@@ -347,6 +446,7 @@ const PageSpeedConfigure = () => {
id="modal-delete-pagespeed-monitor"
component="h2"
variant="h2"
fontWeight={500}
>
Do you really want to delete this monitor?
</Typography>
+4 -12
View File
@@ -29,11 +29,13 @@ 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 "../../Monitors/utils";
import "./index.css";
const PageSpeedDetails = () => {
const theme = useTheme();
const navigate = useNavigate();
const { statusColor, pagespeedStatusMsg, determineState } = useUtils();
const [monitor, setMonitor] = useState({});
const [audits, setAudits] = useState({});
const { monitorId } = useParams();
@@ -109,11 +111,7 @@ const PageSpeedDetails = () => {
gap={theme.spacing(2)}
>
<Tooltip
title={
monitor?.status
? "Your pagespeed monitor is live."
: "Your pagespeed monitor is down."
}
title={pagespeedStatusMsg[determineState(monitor)]}
disableInteractive
slotProps={{
popper: {
@@ -129,13 +127,7 @@ const PageSpeedDetails = () => {
}}
>
<Box>
<PulseDot
color={
monitor?.status
? theme.palette.success.main
: theme.palette.error.main
}
/>
<PulseDot color={statusColor[determineState(monitor)]} />
</Box>
</Tooltip>
<Typography component="h2" variant="h2">
+280 -45
View File
@@ -1,32 +1,220 @@
import PropTypes from "prop-types";
import PageSpeedIcon from "../../assets/icons/page-speed.svg?react";
import { StatusLabel } from "../../Components/Label";
import { Box, Grid, Stack, 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 {
Area,
AreaChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
} from "recharts";
import { useSelector } from "react-redux";
import {
formatDateWithTz,
formatDurationRounded,
formatDurationSplit,
} from "../../Utils/timeUtils";
import useUtils from "../Monitors/utils";
import PropTypes from "prop-types";
/**
* 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 (
<Box
sx={{
backgroundColor: theme.palette.background.main,
border: 1,
borderColor: theme.palette.border.dark,
borderRadius: theme.shape.borderRadius,
py: theme.spacing(2),
px: theme.spacing(4),
}}
>
<Typography
sx={{
color: theme.palette.text.tertiary,
fontSize: 12,
fontWeight: 500,
}}
>
{formatDateWithTz(
payload[0]?.payload.createdAt,
"ddd, MMMM D, YYYY, h:mm A",
uiTimezone
)}
</Typography>
<Stack
direction="row"
alignItems="center"
gap={theme.spacing(3)}
mt={theme.spacing(1)}
sx={{
"& span": {
color: theme.palette.text.tertiary,
fontSize: 11,
fontWeight: 500,
},
}}
>
<Box
width={theme.spacing(4)}
height={theme.spacing(4)}
backgroundColor={payload[0]?.color}
sx={{ borderRadius: "50%" }}
/>
<Typography
component="span"
textTransform="capitalize"
sx={{ opacity: 0.8 }}
>
{payload[0]?.name}
</Typography>{" "}
<Typography component="span">{payload[0]?.payload.score}</Typography>
</Stack>
</Box>
);
};
CustomToolTip.propTypes = {
payload: PropTypes.array,
};
/**
* Processes the raw data to include a score for each entry.
* @param {Array<Object>} data - The raw data array.
* @returns {Array<Object>} - The formatted data array with scores.
*/
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;
};
/**
* Renders an area chart displaying page speed scores.
* @param {Object} props
* @param {Array<Object>} 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();
const formattedData = processData(data);
return (
<ResponsiveContainer width="100%" minWidth={25} height={85}>
<AreaChart
width="100%"
height="100%"
data={formattedData}
margin={{ top: 10, bottom: -5 }}
style={{ cursor: "pointer" }}
>
<CartesianGrid
stroke={theme.palette.border.light}
strokeWidth={1}
strokeOpacity={1}
fill="transparent"
vertical={false}
/>
<Tooltip
cursor={{ stroke: theme.palette.border.light }}
content={<CustomToolTip />}
/>
<defs>
<linearGradient
id={`pagespeed-chart-${status}`}
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="0%"
stopColor={pagespeedStyles[status].stroke}
stopOpacity={0.8}
/>
<stop
offset="100%"
stopColor={pagespeedStyles[status].light}
stopOpacity={0}
/>
</linearGradient>
</defs>
<Area
dataKey="score"
stroke={pagespeedStyles[status].stroke}
strokeWidth={1.5}
fill={`url(#pagespeed-chart-${status})`}
activeDot={{
stroke: pagespeedStyles[status].light,
fill: pagespeedStyles[status].stroke,
r: 4.5,
}}
/>
</AreaChart>
</ResponsiveContainer>
);
};
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();
const navigate = useNavigate();
const monitorState = determineState(monitor);
return (
<Grid
item
lg={6}
flexGrow={1}
sx={{
"&:hover > .MuiStack-root": {
backgroundColor: theme.palette.background.accent,
},
}}
>
<Stack
direction="row"
gap={theme.spacing(6)}
<Grid item lg={6} flexGrow={1}>
<Box
position="relative"
p={theme.spacing(8)}
onClick={() => navigate(`/pagespeed/${monitor._id}`)}
border={1}
@@ -34,41 +222,88 @@ 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,
},
}}
>
<PageSpeedIcon
style={{ width: theme.spacing(8), height: theme.spacing(8) }}
<IconBox>
<PageSpeedIcon />
</IconBox>
<Typography
component="h2"
variant="h2"
fontWeight={500}
alignSelf="center"
>
{monitor.name}
</Typography>
<StatusLabel
status={monitorState}
text={pagespeedStatusMsg[monitorState] || "Pending..."}
customStyles={{
width: "max-content",
textTransform: "capitalize",
alignSelf: "flex-start",
justifySelf: "flex-end",
}}
/>
<Box flex={1}>
<Stack direction="row" justifyContent="space-between">
<Typography
component="h2"
mb={theme.spacing(2)}
fontSize={16}
color={theme.palette.primary.main}
>
{monitor.name}
</Typography>
<StatusLabel
status={monitorState}
text={pagespeedStatusMsg[monitorState] || "Pending..."}
customStyles={{ textTransform: "capitalize" }}
/>
</Stack>
<Typography>{monitor.url.replace(/^https?:\/\//, "")}</Typography>
<Typography mt={theme.spacing(12)}>
<Typography component="span" variant="body2" fontWeight={600}>
Last checked:{" "}
</Typography>
{formatDate(getLastChecked(monitor.checks, false))}{" "}
<Typography component="span" variant="body2" fontStyle="italic">
({formatDurationRounded(getLastChecked(monitor.checks))} ago)
</Typography>
<Typography
variant="body2"
mt={theme.spacing(-2)}
sx={{ gridColumnStart: 2 }}
>
{monitor.url}
</Typography>
<Box
mx={theme.spacing(-8)}
mt={theme.spacing(4)}
mb={theme.spacing(-8)}
sx={{ gridColumnStart: 1, gridColumnEnd: 4 }}
>
<PagespeedAreaChart data={monitor.checks} status={monitorState} />
</Box>
<Box
position="absolute"
bottom={0}
py={theme.spacing(1)}
px={theme.spacing(4)}
borderTop={1}
borderRight={1}
borderColor={theme.palette.border.light}
backgroundColor={theme.palette.background.accent}
sx={{
pointerEvents: "none",
userSelect: "none",
borderTopRightRadius: 8,
borderBottomLeftRadius: 4,
}}
>
<Typography fontSize={11} color={theme.palette.text.accent}>
Checking every{" "}
{(() => {
const { time, format } = formatDurationSplit(monitor?.interval);
return (
<>
<Typography
component="span"
fontSize={12}
color={theme.palette.text.primary}
>
{time}{" "}
</Typography>
{format}
</>
);
})()}
</Typography>
</Box>
</Stack>
</Box>
</Grid>
);
};
+1 -4
View File
@@ -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 {
+38 -16
View File
@@ -1,5 +1,5 @@
import { Box, Button, Grid, Stack } from "@mui/material";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useTheme } from "@emotion/react";
import { useDispatch, useSelector } from "react-redux";
import { getPageSpeedByTeamId } from "../../Features/PageSpeedMonitor/pageSpeedMonitorSlice";
@@ -11,24 +11,51 @@ import Breadcrumbs from "../../Components/Breadcrumbs";
import Greeting from "../../Utils/greeting";
import SkeletonLayout from "./skeleton";
import Card from "./card";
import { networkService } from "../../main";
const PageSpeed = ({ isAdmin }) => {
const theme = useTheme();
const dispatch = useDispatch();
const navigate = useNavigate();
const { authToken } = useSelector((state) => state.auth);
const { monitorsSummary, isLoading } = useSelector(
(state) => state.pageSpeedMonitors
);
const { user, authToken } = useSelector((state) => state.auth);
const [isLoading, setIsLoading] = useState(false);
const [monitors, setMonitors] = useState([]);
useEffect(() => {
dispatch(getPageSpeedByTeamId(authToken));
}, [authToken, dispatch]);
useEffect(() => {
const fetchMonitors = async () => {
try {
setIsLoading(true);
const res = await networkService.getMonitorsByTeamId(
authToken,
user.teamId,
10,
["pagespeed"],
null,
"desc",
true,
null,
null
);
if (res?.data?.data?.monitors) {
setMonitors(res.data.data.monitors);
}
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
fetchMonitors();
}, []);
// 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 && monitorsSummary?.monitors?.length === 0;
let isActuallyLoading = isLoading && monitors?.length === 0;
return (
<Box
className="page-speed"
@@ -46,14 +73,8 @@ const PageSpeed = ({ isAdmin }) => {
>
{isActuallyLoading ? (
<SkeletonLayout />
) : monitorsSummary?.monitors?.length !== 0 ? (
<Box
sx={{
"& p": {
color: theme.palette.text.secondary,
},
}}
>
) : monitors?.length !== 0 ? (
<Box>
<Box mb={theme.spacing(12)}>
<Breadcrumbs list={[{ name: `pagespeed`, path: "/pagespeed" }]} />
<Stack
@@ -67,13 +88,14 @@ const PageSpeed = ({ isAdmin }) => {
variant="contained"
color="primary"
onClick={() => navigate("/pagespeed/create")}
sx={{ whiteSpace: "nowrap" }}
>
Create new
</Button>
</Stack>
</Box>
<Grid container spacing={theme.spacing(12)}>
{monitorsSummary?.monitors?.map((monitor) => (
{monitors?.map((monitor) => (
<Card monitor={monitor} key={monitor._id} />
))}
</Grid>