Merge remote-tracking branch 'upstream/develop' into feat/fe/monitor-refactor

This commit is contained in:
Alex Holliday
2024-08-27 19:35:43 -07:00
28 changed files with 241 additions and 113 deletions

View File

@@ -18,7 +18,7 @@
"@mui/x-data-grid": "7.3.2",
"@mui/x-date-pickers": "7.3.2",
"@reduxjs/toolkit": "2.2.5",
"axios": "1.7.4",
"axios": "^1.7.4",
"chart.js": "^4.4.3",
"dayjs": "1.11.11",
"joi": "17.13.1",

View File

@@ -20,7 +20,7 @@
"@mui/x-data-grid": "7.3.2",
"@mui/x-date-pickers": "7.3.2",
"@reduxjs/toolkit": "2.2.5",
"axios": "1.7.4",
"axios": "^1.7.4",
"chart.js": "^4.4.3",
"dayjs": "1.11.11",
"joi": "17.13.1",

View File

@@ -5,8 +5,7 @@
white-space: nowrap;
}
.MuiTable-root .host span {
font-size: var(--env-var-font-size-small);
margin-left: 8px;
font-size: 11px;
}
.MuiTable-root .label {
@@ -180,3 +179,16 @@ body:has(.pagination-dropdown) p:has(+ .MuiTablePagination-root) {
body:has(.pagination-dropdown) p:has(+ .MuiTablePagination-root) {
font-size: var(--env-var-font-size-small-plus);
}
.monitors .MuiTable-root .MuiTableHead-root .MuiTableCell-root {
text-transform: uppercase;
color: var(--env-var-color-5);
opacity: 0.8;
font-size: var(--env-var-font-size-small-plus);
font-weight: 400;
}
.monitors .MuiTableCell-root:not(:first-of-type):not(:last-of-type),
.monitors .MuiTableCell-root:not(:first-of-type):not(:last-of-type) {
padding-left: var(--env-var-spacing-1);
padding-right: var(--env-var-spacing-1);
}

View File

@@ -233,6 +233,7 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
direction="row"
alignItems="center"
justifyContent="space-between"
px={theme.gap.small}
>
<Typography sx={{ opacity: 0.7 }}>
Showing {getRange()} of {data.rows.length} monitor(s)
@@ -277,7 +278,10 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
},
},
}}
sx={{ mt: theme.gap.medium }}
sx={{
mt: theme.gap.medium,
color: theme.palette.otherColors.bluishGray,
}}
/>
</Stack>
)}

View File

@@ -1,28 +0,0 @@
import React from "react";
import "./serverStatus.css";
import PropTypes from "prop-types";
/**
* @component
* @param {Object} props
* @param {string} props.title - The title text for the server status (required)
* @param {number} props.value - The value text to be displayed (required)
* @param {string} props.state - The state of the server (e.g., "online", "offline", "warning") (required)
* @returns {JSX.Element} - Renders the server status component
*/
const ServerStatus = ({ title, value, state }) => {
return (
<div className="server-status-tile">
<div className="server-status-tile-title">{title}</div>
<div className={`server-status-tile-value ` + " " + state}>{value}</div>
</div>
);
};
ServerStatus.propTypes = {
title: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
state: PropTypes.string.isRequired,
};
export default ServerStatus;

View File

@@ -1,46 +0,0 @@
:root {
--spacing-general-0: 12px 18px;
--spacing-general-1: 20px;
--color-border-0: #eaecf0;
--border-radius-0: 4px;
--font-size-0: 1rem;
--font-size-2: 36px;
--color-up: #17b26a;
--color-down: #f04438;
--color-pause: #fdb022;
}
.server-status-tile {
min-width: 100px;
width: calc(100% - 310px);
padding: var(--spacing-general-0);
border: 1px solid var(--color-border-0);
border-radius: var(--border-radius-0);
background-color: var(--env-var-color-8);
}
.server-status-tile-title {
font-size: var(--font-size-0);
font-weight: 500;
margin-bottom: calc(var(--spacing-general-1) / 2);
color: var(--env-var-color-5);
opacity: 0.6;
}
.server-status-tile-value {
font-size: var(--font-size-2);
font-weight: bolder;
}
.up {
color: var(--color-up);
}
.down {
color: var(--color-down);
}
.pause {
color: var(--color-pause);
}

View File

@@ -181,7 +181,14 @@ const Field = forwardRef(
Field.displayName = "Field";
Field.propTypes = {
type: PropTypes.oneOf(["text", "password", "url", "email", "description"]),
type: PropTypes.oneOf([
"text",
"password",
"url",
"email",
"description",
"number",
]),
id: PropTypes.string.isRequired,
label: PropTypes.string,
https: PropTypes.bool,

View File

@@ -15,6 +15,9 @@ aside span.MuiTypography-root {
font-size: var(--env-var-font-size-medium);
line-height: 1;
}
aside .MuiStack-root + span.MuiTypography-root {
font-size: var(--env-var-font-size-medium-plus);
}
aside .MuiListSubheader-root {
font-size: var(--env-var-font-size-small);
font-weight: 500;

View File

@@ -194,6 +194,7 @@ function Sidebar() {
sx={{
pt: theme.gap.small,
px: collapsed ? theme.gap.xs : theme.gap.small,
backgroundColor: "transparent"
}}
>
Menu
@@ -289,7 +290,7 @@ function Sidebar() {
if (
child.name === "Team" &&
authState.user?.role &&
!authState.user.role.includes("admin")
!authState.user.role.includes("superadmin")
) {
return null;
}
@@ -349,7 +350,7 @@ function Sidebar() {
if (
child.name === "Team" &&
authState.user?.role &&
!authState.user.role.includes("admin")
!authState.user.role.includes("superadmin")
) {
return null;
}
@@ -394,6 +395,7 @@ function Sidebar() {
sx={{
pt: theme.gap.small,
px: collapsed ? 0 : theme.gap.small,
backgroundColor: "transparent"
}}
>
Other

View File

@@ -20,7 +20,7 @@
height: calc(100vh - var(--env-var-spacing-2) * 2);
max-width: var(--env-var-side-bar-width);
border: 1px solid var(--color-border-0);
border: 1px solid var(--env-var-color-6);
border-radius: var(--env-var-radius-1);
background-color: var(--env-var-color-8);

View File

@@ -57,7 +57,6 @@ const Incidents = () => {
null,
null
);
console.log(res);
// Reduce to a lookup object for 0(1) lookup
if (res.data && res.data.data.length > 0) {
const monitorLookup = res.data.data.reduce((acc, monitor) => {

View File

@@ -23,7 +23,7 @@
}
.duration-config .field-text .MuiTextField-root,
.duration-config .field {
.maintenance-options .duration-config .field {
width: 70px;
min-width: 70px;
max-width: 70px;

View File

@@ -10,6 +10,9 @@ import dayjs from "dayjs";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { MobileTimePicker } from "@mui/x-date-pickers/MobileTimePicker";
import Field from "../../../Components/Inputs/Field";
import { maintenanceWindowValidation } from "../../../Validation/validation";
import { logger } from "../../../Utils/Logger";
import { createToast } from "../../../Utils/toastUtils";
const directory = {
title: "Create a maintenance window",
@@ -59,9 +62,10 @@ const CreateNewMaintenanceWindow = () => {
startTime: dayjs(),
duration: "60",
unit: "minutes",
friendlyName: "",
displayName: "",
AddMonitors: "",
});
const [errors, setErrors] = useState({});
const handleChange = (event, name) => {
const { value } = event.target;
@@ -71,6 +75,41 @@ const CreateNewMaintenanceWindow = () => {
}));
};
const handleSubmit = async () => {
const data = {
repeat: values.repeat,
date: values.date.format("YYYY-MM-DD"),
startTime: values.startTime.format("HH:mm"),
duration: values.duration,
unit: values.unit,
displayName: values.displayName,
addMonitors: values.AddMonitors,
};
const { error } = maintenanceWindowValidation.validate(data, {
abortEarly: false,
});
logger.log("error: ", error);
if (!error || error.details.length === 0) {
setErrors({});
} else {
const newErrors = {};
error.details.forEach((err) => {
newErrors[err.path[0]] = err.message;
});
setErrors(newErrors);
createToast({
body:
error.details && error.details.length > 0
? error.details[0].message
: "Error validating data",
});
logger.error("Validation errors:", error.details);
}
logger.log("Submitting data: ", data);
};
const configOptions = [
{
title: "Repeat",
@@ -126,6 +165,8 @@ const CreateNewMaintenanceWindow = () => {
placeholder="60"
onChange={(e) => handleChange(e, "duration")}
value={values.duration}
error={errors.duration}
type="number"
/>
<Select
onChange={(e) => handleChange(e, "unit")}
@@ -137,13 +178,14 @@ const CreateNewMaintenanceWindow = () => {
),
},
{
title: "Friendly name",
title: "Display name",
component: (
<Field
id="friendly-name"
id="display-name"
placeholder="Maintanence at __ : __ for ___ minutes"
value={values.friendlyName}
onChange={(e) => handleChange(e, "friendlyName")}
value={values.displayName}
onChange={(e) => handleChange(e, "displayName")}
error={errors.displayName}
/>
),
},
@@ -160,6 +202,7 @@ const CreateNewMaintenanceWindow = () => {
placeholder="Start typing to search for current monitors"
value={values.AddMonitors}
onChange={(e) => handleChange(e, "AddMonitors")}
error={errors.addMonitors}
/>
<Typography
sx={{
@@ -246,6 +289,7 @@ const CreateNewMaintenanceWindow = () => {
}}
level="primary"
label="Create"
onClick={handleSubmit}
/>
</Stack>
</Stack>

View File

@@ -314,7 +314,7 @@ const Configure = () => {
<Field
type="text"
id="monitor-name"
label="Friendly name"
label="Display name"
isOptional={true}
placeholder="Google"
value={monitor?.name || ""}

View File

@@ -1,13 +1,11 @@
.create-monitor h1.MuiTypography-root {
font-size: var(--env-var-font-size-large-plus);
color: var(--env-var-color-1);
}
.create-monitor h1.MuiTypography-root,
.create-monitor h2.MuiTypography-root {
font-weight: 600;
font-weight: 500;
}
.create-monitor h2.MuiTypography-root {
font-size: var(--env-var-font-size-large);
font-weight: 600;
}
.create-monitor p.MuiTypography-root,
.create-monitor button.MuiButtonBase-root {

View File

@@ -122,7 +122,6 @@ const CreateMonitor = () => {
userId: user._id,
notifications: monitor.notifications,
};
console.log(form);
const action = await dispatch(
createUptimeMonitor({ authToken, monitor: form })
);
@@ -159,9 +158,20 @@ const CreateMonitor = () => {
noValidate
spellCheck="false"
gap={theme.gap.large}
mt={theme.gap.mlplus}
mt={theme.gap.medium}
>
<Typography component="h1">Create new monitor</Typography>
<Typography component="h1">
<Typography
component="span"
fontSize="inherit"
color={theme.palette.otherColors.bluishGray}
>
Create your{" "}
</Typography>
<Typography component="span" fontSize="inherit" fontWeight="inherit">
monitor
</Typography>
</Typography>
<Stack className="config-box">
<Box>
<Typography component="h2">General settings</Typography>
@@ -184,7 +194,7 @@ const CreateMonitor = () => {
<Field
type="text"
id="monitor-name"
label="Friendly name"
label="Display name"
isOptional={true}
placeholder="Google"
value={monitor.name}
@@ -391,9 +401,9 @@ const CreateMonitor = () => {
/> */}
<Stack direction="row" justifyContent="flex-end">
<Button
id="create-new-monitor-btn"
id="create-monitor-btn"
level="primary"
label="Create new monitor"
label="Create monitor"
onClick={handleCreateMonitor}
disabled={Object.keys(errors).length !== 0 && true}
/>

View File

@@ -1,18 +1,20 @@
.monitors h1.MuiTypography-root,
.monitors h2.MuiTypography-root {
color: var(--env-var-color-1);
font-weight: 600;
}
.monitors h1.MuiTypography-root {
font-size: var(--env-var-font-size-large-plus);
color: var(--env-var-color-1);
font-weight: 500;
}
.monitors h2.MuiTypography-root {
font-size: var(--env-var-font-size-large);
color: var(--env-var-color-5);
font-weight: 500;
}
.monitors p.MuiTypography-root {
font-size: var(--env-var-font-size-medium);
color: var(--env-var-color-5);
}
.monitors h1.MuiTypography-root + p.MuiTypography-root {
font-size: var(--env-var-font-size-medium-plus);
}
.monitors .MuiStack-root > button:not(.MuiIconButton-root) {
font-size: var(--env-var-font-size-medium);
height: var(--env-var-height-2);
@@ -31,7 +33,7 @@
border-radius: 50%;
font-size: var(--env-var-font-size-medium);
font-weight: 500;
color: var(--env-var-color-5);
color: var(--env-var-color-1);
margin-left: 10px;
line-height: 0.8;
}

View File

View File

@@ -223,7 +223,7 @@ const PageSpeedConfigure = () => {
</Stack>
<Stack gap={theme.gap.xl}>
<Stack direction="row">
<Typography component="h3">Monitor friendly name</Typography>
<Typography component="h3">Monitor display name</Typography>
<Field
type="text"
id="monitor-name"

View File

@@ -116,7 +116,7 @@ const CreatePageSpeed = () => {
<Typography component="h1">Create pagespeed monitor</Typography>
<Stack gap={theme.gap.xl}>
<Stack direction="row">
<Typography component="h3">Monitor friendly name</Typography>
<Typography component="h3">Monitor display name</Typography>
<Field
type="text"
id="monitor-name"

View File

@@ -112,4 +112,43 @@ const imageValidation = joi.object({
}),
});
export { credentials, imageValidation, monitorValidation };
const maintenanceWindowValidation = joi.object({
repeat: joi.number().valid(1, 2, 3).required().messages({
"number.base": "Repeat must be a number.",
"any.only": "Repeat must be one of [1, 2, 3].",
"any.required": "Repeat is required.",
}),
date: joi.date().required().messages({
"date.base": "Date must be a valid date.",
"any.required": "Date is required.",
}),
startTime: joi.string().required().messages({
"string.base": "Start time must be a valid time.",
"any.required": "Start time is required.",
}),
duration: joi.number().required().messages({
"number.empty": "duration is required.",
"number.base": "Duration must be a number.",
"any.required": "Duration is required.",
}),
unit: joi.string().valid("minutes", "hours", "days").required().messages({
"string.base": "Unit must be a string.",
"any.only": "Unit must be one of ['minutes', 'hours', 'days'].",
"any.required": "Unit is required.",
}),
displayName: joi.string().max(50).required().messages({
"string.empty": "Display name is required.",
"string.max": "Display name must be less than 50 characters long",
}),
addMonitors: joi.string().max(50).required().messages({
"string.empty": "Add monitors is required.",
"string.max": "Add monitors must be less than 50 characters long",
}),
});
export {
credentials,
imageValidation,
monitorValidation,
maintenanceWindowValidation,
};

View File

@@ -0,0 +1,67 @@
<svg width="768" height="768" viewBox="0 0 768 768" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_14_2)">
<mask id="mask0_14_2" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="768" height="768">
<path d="M768 0H0V768H768V0Z" fill="url(#paint0_radial_14_2)"/>
</mask>
<g mask="url(#mask0_14_2)">
<mask id="mask1_14_2" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="768" height="768">
<path d="M768 0H0V768H768V0Z" fill="white"/>
</mask>
<g mask="url(#mask1_14_2)">
<mask id="mask2_14_2" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="768" height="768">
<path d="M768 0H0V768H768V0Z" fill="white"/>
</mask>
<g mask="url(#mask2_14_2)">
<path d="M0.5 0V768" stroke="#b8b9bd"/>
<path d="M48.5 0V768" stroke="#b8b9bd"/>
<path d="M96.5 0V768" stroke="#b8b9bd"/>
<path d="M144.5 0V768" stroke="#b8b9bd"/>
<path d="M192.5 0V768" stroke="#b8b9bd"/>
<path d="M240.5 0V768" stroke="#b8b9bd"/>
<path d="M288.5 0V768" stroke="#b8b9bd"/>
<path d="M336.5 0V768" stroke="#b8b9bd"/>
<path d="M384.5 0V768" stroke="#b8b9bd"/>
<path d="M432.5 0V768" stroke="#b8b9bd"/>
<path d="M480.5 0V768" stroke="#b8b9bd"/>
<path d="M528.5 0V768" stroke="#b8b9bd"/>
<path d="M576.5 0V768" stroke="#b8b9bd"/>
<path d="M624.5 0V768" stroke="#b8b9bd"/>
<path d="M672.5 0V768" stroke="#b8b9bd"/>
<path d="M720.5 0V768" stroke="#b8b9bd"/>
</g>
<path d="M767.5 0.5H0.5V767.5H767.5V0.5Z" stroke="#b8b9bd"/>
<mask id="mask3_14_2" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="768" height="768">
<path d="M768 0H0V768H768V0Z" fill="white"/>
</mask>
<g mask="url(#mask3_14_2)">
<path d="M0 47.5H768" stroke="#b8b9bd"/>
<path d="M0 95.5H768" stroke="#b8b9bd"/>
<path d="M0 143.5H768" stroke="#b8b9bd"/>
<path d="M0 191.5H768" stroke="#b8b9bd"/>
<path d="M0 239.5H768" stroke="#b8b9bd"/>
<path d="M0 287.5H768" stroke="#b8b9bd"/>
<path d="M0 335.5H768" stroke="#b8b9bd"/>
<path d="M0 383.5H768" stroke="#b8b9bd"/>
<path d="M0 431.5H768" stroke="#b8b9bd"/>
<path d="M0 479.5H768" stroke="#b8b9bd"/>
<path d="M0 527.5H768" stroke="#b8b9bd"/>
<path d="M0 575.5H768" stroke="#b8b9bd"/>
<path d="M0 623.5H768" stroke="#b8b9bd"/>
<path d="M0 671.5H768" stroke="#b8b9bd"/>
<path d="M0 719.5H768" stroke="#b8b9bd"/>
<path d="M0 767.5H768" stroke="#b8b9bd"/>
</g>
<path d="M767.5 0.5H0.5V767.5H767.5V0.5Z" stroke="#b8b9bd"/>
</g>
</g>
</g>
<defs>
<radialGradient id="paint0_radial_14_2" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(384 384) rotate(90) scale(384)">
<stop/>
<stop offset="1" stop-opacity="0"/>
</radialGradient>
<clipPath id="clip0_14_2">
<rect width="768" height="768" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.5 17H21.5L16.5 22H21.5M21.9506 13C21.9833 12.6711 22 12.3375 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C12.1677 22 12.3344 21.9959 12.5 21.9877C12.6678 21.9795 12.8345 21.9671 13 21.9506M12 6V12L15.7384 13.8692" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 454 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 17L17 7M17 7H7M17 7V17" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 224 B

View File

@@ -85,7 +85,7 @@
--env-var-font-size-medium: 13px;
--env-var-font-size-medium-plus: 14px;
--env-var-font-size-large: 16px;
--env-var-font-size-large-plus: 24px;
--env-var-font-size-large-plus: 22px;
--env-var-font-size-xlarge: 30px;
--env-var-img-width-1: 20px;