Merge pull request #229 from bluewave-labs/feat/create-monitor-refactor
Refactored create monitor page styling using @mui components, resolves #218
This commit is contained in:
14 files changed
+302
-243
No files matched your search
@@ -1,24 +1,15 @@
|
||||
.config-box {
|
||||
display: flex;
|
||||
padding: 30px 40px;
|
||||
padding: var(--env-var-spacing-4) 50px;
|
||||
padding-bottom: 60px;
|
||||
border: 1px solid var(--env-var-color-16);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
justify-content: space-between;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.config-box-desc {
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.config-box-desc-title {
|
||||
color: var(--env-var-color-5);
|
||||
font-size: var(--env-var-font-size-large);
|
||||
font-weight: 700;
|
||||
margin-bottom: var(--env-var-spacing-1);
|
||||
}
|
||||
|
||||
.config-box-desc-text {
|
||||
color: var(--env-var-color-5);
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
.config-box>.MuiStack-root{
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
}
|
||||
.config-box>.MuiStack-root:first-of-type{
|
||||
margin-right: var(--env-var-spacing-3);
|
||||
}
|
||||
@@ -1,3 +1,16 @@
|
||||
.MuiButtonBase-root.MuiRadio-root.MuiRadio-colorPrimary.PrivateSwitchBase-root.MuiRadio-root.MuiRadio-colorPrimary.MuiRadio-root.MuiRadio-colorPrimary.css-w5ovia-MuiButtonBase-root-MuiRadio-root {
|
||||
padding-top: 0;
|
||||
.create-monitor-form .custom-radio-button.MuiFormControlLabel-root {
|
||||
padding: 5px;
|
||||
margin: -5px;
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.create-monitor-form .custom-radio-button.MuiFormControlLabel-root:hover {
|
||||
background-color: var(--env-var-color-13);
|
||||
}
|
||||
.create-monitor-form .custom-radio-button.MuiFormControlLabel-root svg{
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
.custom-radio-button.MuiFormControlLabel-root .MuiButtonBase-root {
|
||||
margin-right: var(--env-var-spacing-1);
|
||||
padding: 0;
|
||||
}
|
||||
@@ -18,28 +18,37 @@
|
||||
* @returns {JSX.Element} - The rendered RadioButton component.
|
||||
*/
|
||||
|
||||
import { FormControlLabel, Radio } from "@mui/material";
|
||||
import React from "react";
|
||||
import "./index.css";
|
||||
import PropTypes from "prop-types";
|
||||
import { FormControlLabel, Radio, Typography } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import "./index.css";
|
||||
|
||||
function RadioButton(props) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div className="custom-radio-button">
|
||||
<FormControlLabel
|
||||
checked={props.checked}
|
||||
value={props.value}
|
||||
control={<Radio id={props.id} size={props.size} />}
|
||||
onChange={props.onChange}
|
||||
label={
|
||||
<div>
|
||||
<div className="service-check-list-title">{props.title}</div>
|
||||
<div className="service-check-list-desc">{props.desc}</div>
|
||||
</div>
|
||||
}
|
||||
labelPlacement="end"
|
||||
/>
|
||||
</div>
|
||||
<FormControlLabel
|
||||
className="custom-radio-button"
|
||||
checked={props.checked}
|
||||
value={props.value}
|
||||
control={
|
||||
<Radio
|
||||
id={props.id}
|
||||
size={props.size}
|
||||
sx={{ color: theme.palette.secondary.main }}
|
||||
/>
|
||||
}
|
||||
onChange={props.onChange}
|
||||
label={
|
||||
<>
|
||||
<Typography component="p">{props.title}</Typography>
|
||||
<Typography component="h6" mt="2px">
|
||||
{props.desc}
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
labelPlacement="end"
|
||||
sx={{ margin: 0, alignItems: "flex-start" }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,9 +107,7 @@ const PasswordPanel = () => {
|
||||
</div>
|
||||
<div className="edit-password-form__wrapper">
|
||||
<Stack>
|
||||
<Typography variant="h4" component="h1">
|
||||
Current password
|
||||
</Typography>
|
||||
<Typography component="h1">Current password</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<PasswordTextField
|
||||
@@ -123,7 +121,7 @@ const PasswordPanel = () => {
|
||||
error={errors[idToName["edit-current-password"]] ? true : false}
|
||||
/>
|
||||
{errors[idToName["edit-current-password"]] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
<Typography component="p" className="input-error">
|
||||
{errors[idToName["edit-current-password"]]}
|
||||
</Typography>
|
||||
) : (
|
||||
@@ -133,9 +131,7 @@ const PasswordPanel = () => {
|
||||
</div>
|
||||
<div className="edit-password-form__wrapper">
|
||||
<Stack>
|
||||
<Typography variant="h4" component="h1">
|
||||
New password
|
||||
</Typography>
|
||||
<Typography component="h1">New password</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<PasswordTextField
|
||||
@@ -149,7 +145,7 @@ const PasswordPanel = () => {
|
||||
error={errors[idToName["edit-new-password"]] ? true : false}
|
||||
/>
|
||||
{errors[idToName["edit-new-password"]] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
<Typography component="p" className="input-error">
|
||||
{errors[idToName["edit-new-password"]]}
|
||||
</Typography>
|
||||
) : (
|
||||
@@ -159,9 +155,7 @@ const PasswordPanel = () => {
|
||||
</div>
|
||||
<div className="edit-password-form__wrapper">
|
||||
<Stack>
|
||||
<Typography variant="h4" component="h1">
|
||||
Confirm new password
|
||||
</Typography>
|
||||
<Typography component="h1">Confirm new password</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<PasswordTextField
|
||||
@@ -175,7 +169,7 @@ const PasswordPanel = () => {
|
||||
error={errors[idToName["edit-confirm-password"]] ? true : false}
|
||||
/>
|
||||
{errors[idToName["edit-confirm-password"]] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
<Typography component="p" className="input-error">
|
||||
{errors[idToName["edit-confirm-password"]]}
|
||||
</Typography>
|
||||
) : (
|
||||
|
||||
@@ -152,9 +152,7 @@ const ProfilePanel = () => {
|
||||
<form className="edit-profile-form" noValidate spellCheck="false">
|
||||
<div className="edit-profile-form__wrapper">
|
||||
<Stack>
|
||||
<Typography variant="h4" component="h1">
|
||||
First name
|
||||
</Typography>
|
||||
<Typography component="h1">First name</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<StringTextField
|
||||
@@ -167,7 +165,7 @@ const ProfilePanel = () => {
|
||||
error={errors[idToName["edit-first-name"]] ? true : false}
|
||||
/>
|
||||
{errors[idToName["edit-first-name"]] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
<Typography component="p" className="input-error">
|
||||
{errors[idToName["edit-first-name"]]}
|
||||
</Typography>
|
||||
) : (
|
||||
@@ -177,9 +175,7 @@ const ProfilePanel = () => {
|
||||
</div>
|
||||
<div className="edit-profile-form__wrapper">
|
||||
<Stack>
|
||||
<Typography variant="h4" component="h1">
|
||||
Last name
|
||||
</Typography>
|
||||
<Typography component="h1">Last name</Typography>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<StringTextField
|
||||
@@ -192,7 +188,7 @@ const ProfilePanel = () => {
|
||||
error={errors[idToName["edit-last-name"]] ? true : false}
|
||||
/>
|
||||
{errors[idToName["edit-last-name"]] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
<Typography component="p" className="input-error">
|
||||
{errors[idToName["edit-last-name"]]}
|
||||
</Typography>
|
||||
) : (
|
||||
@@ -202,10 +198,8 @@ const ProfilePanel = () => {
|
||||
</div>
|
||||
<div className="edit-profile-form__wrapper">
|
||||
<Stack>
|
||||
<Typography variant="h4" component="h1">
|
||||
Email
|
||||
</Typography>
|
||||
<Typography variant="h5" component="p">
|
||||
<Typography component="h1">Email</Typography>
|
||||
<Typography component="p">
|
||||
This is your current email address — it cannot be changed.
|
||||
</Typography>
|
||||
</Stack>
|
||||
@@ -221,7 +215,7 @@ const ProfilePanel = () => {
|
||||
disabled={true}
|
||||
/>
|
||||
{errors[idToName["edit-email"]] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
<Typography component="p" className="input-error">
|
||||
{errors[idToName["edit-email"]]}
|
||||
</Typography>
|
||||
) : (
|
||||
@@ -231,10 +225,8 @@ const ProfilePanel = () => {
|
||||
</div>
|
||||
<div className="edit-profile-form__wrapper">
|
||||
<Stack>
|
||||
<Typography variant="h4" component="h1">
|
||||
Your photo
|
||||
</Typography>
|
||||
<Typography variant="h5" component="p">
|
||||
<Typography component="h1">Your photo</Typography>
|
||||
<Typography component="p">
|
||||
This photo will be displayed in your profile page.
|
||||
</Typography>
|
||||
</Stack>
|
||||
@@ -292,10 +284,8 @@ const ProfilePanel = () => {
|
||||
<form className="delete-profile-form" noValidate spellCheck="false">
|
||||
<div className="delete-profile-form__wrapper">
|
||||
<Stack direction="column" gap="15px">
|
||||
<Typography variant="h4" component="h1">
|
||||
Delete account
|
||||
</Typography>
|
||||
<Typography variant="h5" component="p">
|
||||
<Typography component="h1">Delete account</Typography>
|
||||
<Typography component="p">
|
||||
Note that deleting your account will remove all data from our
|
||||
system. This is permanent and non-recoverable.
|
||||
</Typography>
|
||||
@@ -335,14 +325,10 @@ const ProfilePanel = () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography id="modal-delete-account" variant="h4" component="h1">
|
||||
<Typography id="modal-delete-account" component="h1">
|
||||
Really delete this account?
|
||||
</Typography>
|
||||
<Typography
|
||||
id="delete-account-confirmation"
|
||||
variant="h5"
|
||||
component="p"
|
||||
>
|
||||
<Typography id="delete-account-confirmation" component="p">
|
||||
If you delete your account, you will no longer be able to sign in,
|
||||
and all of your data will be deleted. Deleting your account is
|
||||
permanent and non-recoverable action.
|
||||
@@ -386,7 +372,7 @@ const ProfilePanel = () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography id="modal-update-picture" variant="h4" component="h1">
|
||||
<Typography id="modal-update-picture" component="h1">
|
||||
Upload Image
|
||||
</Typography>
|
||||
<ImageField
|
||||
|
||||
@@ -186,13 +186,9 @@ const TeamPanel = () => {
|
||||
return (
|
||||
<TabPanel value="team">
|
||||
<form className="edit-organization-form">
|
||||
<div
|
||||
className="edit-organization-form__wrapper"
|
||||
>
|
||||
<div className="edit-organization-form__wrapper">
|
||||
<Stack>
|
||||
<Typography variant="h4" component="h1">
|
||||
Organization name
|
||||
</Typography>
|
||||
<Typography component="h1">Organization name</Typography>
|
||||
</Stack>
|
||||
<Stack
|
||||
id="org-name-flex-container"
|
||||
@@ -247,7 +243,7 @@ const TeamPanel = () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography id="modal-edit-org-name" variant="h4" component="h1">
|
||||
<Typography id="modal-edit-org-name" component="h1">
|
||||
Rename this organization?
|
||||
</Typography>
|
||||
<TextField
|
||||
@@ -280,9 +276,7 @@ const TeamPanel = () => {
|
||||
</Modal>
|
||||
<form className="edit-team-form" noValidate spellCheck="false">
|
||||
<div className="edit-team-form__wrapper">
|
||||
<Typography variant="h4" component="h1">
|
||||
Team members
|
||||
</Typography>
|
||||
<Typography component="h1">Team members</Typography>
|
||||
</div>
|
||||
<div className="edit-team-form__wrapper compact">
|
||||
<Stack
|
||||
@@ -488,10 +482,10 @@ const TeamPanel = () => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography id="modal-invite-member" variant="h4" component="h1">
|
||||
<Typography id="modal-invite-member" component="h1">
|
||||
Invite new team member
|
||||
</Typography>
|
||||
<Typography id="invite-member-to-team" variant="h5" component="p">
|
||||
<Typography id="invite-member-to-team" component="p">
|
||||
When you add a new team member, they will get access to all
|
||||
monitors.
|
||||
</Typography>
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import "./flexibileTextField.css";
|
||||
import React from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
/**
|
||||
* @param {Object} props - The component props.
|
||||
* @param {string} props.id - The ID of the input field.
|
||||
* @param {string} props.title - The title or label for the input field.
|
||||
* @param {string} props.type - The type of the input field (e.g., 'text', 'password').
|
||||
* @param {string} props.placeholder - The placeholder text for the input field.
|
||||
* @param {string} props.value - The current value of the input field.
|
||||
* @param {function} props.onChange - The function to call when the input field value changes.
|
||||
* @param {string} [props.error] - Optional. Error message to display if there's an error with the input.
|
||||
* @returns {JSX.Element} JSX Element representing the flexible text input field.
|
||||
*/
|
||||
|
||||
const FlexibileTextField = ({
|
||||
id,
|
||||
@@ -8,20 +22,38 @@ const FlexibileTextField = ({
|
||||
placeholder,
|
||||
value,
|
||||
onChange,
|
||||
error,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div className="flexibile-textfield">
|
||||
<div className="flexibile-textfield-title">{title}</div>
|
||||
<Stack className="flexible-textfield" gap={theme.gap.small}>
|
||||
<Typography component="h2">{title}</Typography>
|
||||
<input
|
||||
id={id}
|
||||
type={type}
|
||||
className="flexibile-textfield-input"
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
{error ? (
|
||||
<Typography component="p" className="input-error">
|
||||
{error}
|
||||
</Typography>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
FlexibileTextField.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
onChange: PropTypes.func.isRequired,
|
||||
error: PropTypes.string,
|
||||
};
|
||||
|
||||
export default FlexibileTextField;
|
||||
@@ -1,22 +1,30 @@
|
||||
.flexibile-textfield-title {
|
||||
.flexible-textfield{
|
||||
position: relative;
|
||||
}
|
||||
.flexible-textfield h2.MuiTypography-root{
|
||||
color: var(--env-var-color-5);
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.flexibile-textfield-input {
|
||||
width: 292.8px;
|
||||
height: 6.8px;
|
||||
padding: var(--env-var-spacing-1);
|
||||
margin-top: 10px;
|
||||
.flexible-textfield h2.MuiTypography-root span{
|
||||
opacity: 0.6;
|
||||
}
|
||||
.flexible-textfield input {
|
||||
min-width: 300px;
|
||||
height: 34px;
|
||||
padding: 0 var(--env-var-spacing-1);
|
||||
border: 1px solid var(--env-var-color-4);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
|
||||
.flexibile-textfield-input:focus {
|
||||
.flexible-textfield p.MuiTypography-root.input-error {
|
||||
color: var(--env-var-color-24);
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
top: calc(100% + 5px);
|
||||
}
|
||||
.flexible-textfield:has(p.MuiTypography-root.input-error) input{
|
||||
border-color: var(--env-var-color-24);
|
||||
}
|
||||
.flexible-textfield input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.flexibile-textfield-input::placeholder {
|
||||
color: var(--env-var-color-25);
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,8 @@ const Account = ({ open = "profile" }) => {
|
||||
//TODO - breakpoints for responsive design
|
||||
minWidth={theme.spacing(55)}
|
||||
maxWidth="1200px"
|
||||
py={theme.spacing(5)}
|
||||
px={theme.spacing(10)}
|
||||
py={theme.content.pY}
|
||||
px={theme.content.pX}
|
||||
className="account"
|
||||
>
|
||||
<TabContext value={tab}>
|
||||
|
||||
@@ -1,82 +1,67 @@
|
||||
.monitors,
|
||||
.new-monitor {
|
||||
width: 70vw;
|
||||
padding: 4vw;
|
||||
.create-monitor-form h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large-plus);
|
||||
color: var(--env-var-color-1);
|
||||
}
|
||||
.create-monitor-form h1.MuiTypography-root,
|
||||
.create-monitor-form h2.MuiTypography-root {
|
||||
font-weight: 600;
|
||||
}
|
||||
.create-monitor-form h2.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
}
|
||||
.create-monitor-form p.MuiTypography-root,
|
||||
#monitor-frequencies {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.create-monitor-form h2.MuiTypography-root,
|
||||
.create-monitor-form h6.MuiTypography-root,
|
||||
.create-monitor-form p.MuiTypography-root,
|
||||
#monitor-frequencies {
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
.create-monitor-form h6.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.monitors-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.create-monitor-form .error-container {
|
||||
position: relative;
|
||||
}
|
||||
.create-monitor-form .error-container p.MuiTypography-root.input-error {
|
||||
color: var(--env-var-color-24);
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.monitors-bar-title {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
/* for testing, will remove later */
|
||||
/* .config-box:has(.error-container):has(.input-error) {
|
||||
border-color: var(--env-var-color-24);
|
||||
} */
|
||||
|
||||
.monitors-stats {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
.create-monitor-form .MuiSelect-select.MuiSelect-outlined.MuiInputBase-input {
|
||||
padding: var(--env-var-spacing-1);
|
||||
}
|
||||
.create-monitor-form .MuiInputBase-root:has(#monitor-frequencies) {
|
||||
min-width: 300px;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
border: 1px solid var(--env-var-color-4);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
overflow: hidden;
|
||||
}
|
||||
.create-monitor-form .MuiInputBase-root:has(#monitor-frequencies) fieldset {
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.monitors-gaps-medium {
|
||||
height: var(--env-var-spacing-2);
|
||||
}
|
||||
|
||||
.monitors-gaps-small-plus {
|
||||
height: var(--env-var-spacing-1-plus);
|
||||
}
|
||||
|
||||
.new-monitor-header {
|
||||
color: var(--env-var-color-1);
|
||||
font-size: var(--env-var-font-size-large-plus);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.service-check-list-title {
|
||||
color: var(--env-var-color-5);
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
|
||||
.service-check-list-desc {
|
||||
color: var(--env-var-color-25);
|
||||
font-size: var(--env-var-font-size-small);
|
||||
}
|
||||
|
||||
.host-status-box-text {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
|
||||
.monitors-dropdown-holder {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
#ports-list {
|
||||
width: 172.92px;
|
||||
height: 12px;
|
||||
font-size: var(--env-var-font-size-small);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.incident-notif-config-title,
|
||||
.adv-setting-title {
|
||||
color: var(--env-var-color-5);
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
|
||||
.adv-setting-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.config-box-inputs,
|
||||
.service-check-list,
|
||||
.incident-notif-config,
|
||||
.advanced-setting-config,
|
||||
.proxy-setting-config {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.monitors-create-button-holder {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
|
||||
@@ -5,17 +5,19 @@ import React, { useState } from "react";
|
||||
import RadioButton from "../../Components/RadioButton";
|
||||
import CustomizableCheckBox from "../../Components/Checkbox/CustomizableCheckbox";
|
||||
import Button from "../../Components/Button";
|
||||
import { MenuItem, Select, Typography } from "@mui/material";
|
||||
import { Box, MenuItem, Select, Stack, Typography } from "@mui/material";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { createMonitorValidation } from "../../Validation/validation";
|
||||
import { createMonitor } from "../../Features/Monitors/monitorsSlice";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
const CreateNewMonitor = () => {
|
||||
const MS_PER_MINUTE = 60000;
|
||||
const { user, authToken } = useSelector((state) => state.auth);
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
@@ -120,21 +122,31 @@ const CreateNewMonitor = () => {
|
||||
const frequencies = [1, 2, 3, 4, 5];
|
||||
|
||||
return (
|
||||
<form onSubmit={handleCreateNewMonitor} noValidate spellCheck="false">
|
||||
<div className="new-monitor-header">Create new monitor</div>
|
||||
<form
|
||||
className="create-monitor-form"
|
||||
onSubmit={handleCreateNewMonitor}
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
style={{
|
||||
maxWidth: "1200px",
|
||||
flex: 1,
|
||||
padding: `${theme.content.pY} ${theme.content.pX}`,
|
||||
}}
|
||||
>
|
||||
<Typography component="h1">Create new monitor</Typography>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<ConfigBox
|
||||
leftLayout={
|
||||
<div className="config-box-desc">
|
||||
<div className="config-box-desc-title">General settings</div>
|
||||
<div className="config-box-desc-text">
|
||||
<Stack gap={theme.gap.small}>
|
||||
<Typography component="h2">General settings</Typography>
|
||||
<Typography component="p">
|
||||
Here you can select the URL of the host, together with the type of
|
||||
monitor.
|
||||
</div>
|
||||
</div>
|
||||
</Typography>
|
||||
</Stack>
|
||||
}
|
||||
rightLayout={
|
||||
<div className="config-box-inputs">
|
||||
<Stack gap={theme.gap.xl}>
|
||||
<FlexibileTextField
|
||||
id="monitor-url"
|
||||
title="URL to monitor"
|
||||
@@ -144,66 +156,57 @@ const CreateNewMonitor = () => {
|
||||
onChange={(event) =>
|
||||
handleChange(event, "url", setGeneralSettings)
|
||||
}
|
||||
error={errors["url"]}
|
||||
/>
|
||||
{errors["url"] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
{errors["url"]}
|
||||
</Typography>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<FlexibileTextField
|
||||
id="monitor-name"
|
||||
title="Friendly name (optional)"
|
||||
title={
|
||||
<>
|
||||
Friendly name <span>(optional)</span>
|
||||
</>
|
||||
}
|
||||
type="text"
|
||||
placeholder="Google"
|
||||
value={generalSettings.name}
|
||||
onChange={(event) =>
|
||||
handleChange(event, "name", setGeneralSettings)
|
||||
}
|
||||
error={errors["name"]}
|
||||
/>
|
||||
{errors["name"] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
{errors["name"]}
|
||||
</Typography>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<ConfigBox
|
||||
leftLayout={
|
||||
<div className="config-box-desc">
|
||||
<div className="config-box-desc-title">Checks to perform</div>
|
||||
<div className="config-box-desc-text">
|
||||
<Stack gap={theme.gap.small}>
|
||||
<Typography component="h2">Checks to perform</Typography>
|
||||
<Typography component="p">
|
||||
You can always add or remove checks after adding your site.
|
||||
</div>
|
||||
</div>
|
||||
</Typography>
|
||||
</Stack>
|
||||
}
|
||||
rightLayout={
|
||||
<div className="service-check-list">
|
||||
<Stack gap={theme.gap.large}>
|
||||
<RadioButton
|
||||
id="monitor-checks-http"
|
||||
title="HTTP/website monitoring"
|
||||
desc="Use HTTP(s) to monitor your website or API endpoint."
|
||||
size="small"
|
||||
value="http"
|
||||
checked={checks.type === "http"}
|
||||
onChange={(event) => handleChange(event, "type", setChecks)}
|
||||
/>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<RadioButton
|
||||
id="monitor-checks-ping"
|
||||
title="Ping monitoring"
|
||||
desc="Check whether your server is available or not."
|
||||
size="small"
|
||||
value="ping"
|
||||
checked={checks.type === "ping"}
|
||||
onChange={(event) => handleChange(event, "type", setChecks)}
|
||||
/>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
{/* TODO */}
|
||||
{/* <RadioButton
|
||||
id="monitor-checks-port"
|
||||
@@ -231,14 +234,16 @@ const CreateNewMonitor = () => {
|
||||
))}
|
||||
</Select>
|
||||
</div> */}
|
||||
{errors["type"] ? (
|
||||
<Typography variant="h5" component="p" className="input-error">
|
||||
{errors["type"]}
|
||||
</Typography>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
<Box className="error-container">
|
||||
{errors["type"] ? (
|
||||
<Typography component="p" className="input-error">
|
||||
{errors["type"]}
|
||||
</Typography>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
{/* TODO */}
|
||||
@@ -300,29 +305,47 @@ const CreateNewMonitor = () => {
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<ConfigBox
|
||||
leftLayout={
|
||||
<div className="config-box-desc">
|
||||
<div className="config-box-desc-title">Advanced settings</div>
|
||||
</div>
|
||||
<Stack gap={theme.gap.small}>
|
||||
<Typography component="h2">Advanced settings</Typography>
|
||||
</Stack>
|
||||
}
|
||||
rightLayout={
|
||||
<div className="advanced-setting-config">
|
||||
<div className="adv-setting-title">Check frequency</div>
|
||||
<div className="monitors-gaps-small"></div>
|
||||
<Select
|
||||
id="monitor-frequencies"
|
||||
value={advancedSettings.frequency || 1}
|
||||
inputProps={{ id: "monitor-frequencies-select" }}
|
||||
onChange={(event) =>
|
||||
handleChange(event, "frequency", setAdvancedSettings)
|
||||
}
|
||||
>
|
||||
{frequencies.map((freq) => (
|
||||
<MenuItem key={`port-${freq}`} value={freq}>
|
||||
{freq} {freq === 1 ? "minute" : "minutes"}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<div className="monitors-gaps-medium"></div>
|
||||
<Stack gap={theme.gap.large}>
|
||||
{/* TODO - refactor select component */}
|
||||
<Box>
|
||||
<Typography component="p" mb={theme.gap.small}>
|
||||
Check frequency
|
||||
</Typography>
|
||||
<Select
|
||||
id="monitor-frequencies"
|
||||
value={advancedSettings.frequency || 1}
|
||||
inputProps={{ id: "monitor-frequencies-select" }}
|
||||
onChange={(event) =>
|
||||
handleChange(event, "frequency", setAdvancedSettings)
|
||||
}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
style: {
|
||||
marginTop: "10px"
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{frequencies.map((freq) => (
|
||||
<MenuItem
|
||||
key={`port-${freq}`}
|
||||
value={freq}
|
||||
sx={{
|
||||
fontSize: "13px",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
margin: "5px",
|
||||
}}
|
||||
>
|
||||
{freq} {freq === 1 ? "minute" : "minutes"}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Box>
|
||||
{/* TODO */}
|
||||
{/* <FlexibileTextField
|
||||
id="monitor-settings-retries"
|
||||
@@ -353,7 +376,7 @@ const CreateNewMonitor = () => {
|
||||
handleChange(event, "redirects", setAdvancedSettings)
|
||||
}
|
||||
/> */}
|
||||
</div>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
{/* TODO */}
|
||||
@@ -406,6 +429,7 @@ const CreateNewMonitor = () => {
|
||||
label="Create new monitor"
|
||||
sx={{ width: "210px", fontSize: "var(--env-var-font-size-medium)" }}
|
||||
onClick={handleCreateNewMonitor}
|
||||
disabled={Object.keys(errors).length !== 0 && true}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -86,6 +86,16 @@ const theme = createTheme({
|
||||
borderThick: 2,
|
||||
boxShadow: shadow,
|
||||
},
|
||||
content: {
|
||||
pX: "80px",
|
||||
pY: "40px"
|
||||
},
|
||||
gap: {
|
||||
small: "8px",
|
||||
medium: "12px",
|
||||
large: "24px",
|
||||
xl: "40px"
|
||||
}
|
||||
});
|
||||
|
||||
export default theme;
|
||||
@@ -133,9 +133,21 @@ const editPasswordValidation = joi.object({
|
||||
});
|
||||
|
||||
const createMonitorValidation = joi.object({
|
||||
url: joi.string().trim().messages({ "string.empty": "*Url is required." }),
|
||||
name: joi.string().trim().messages({ "string.empty": "*Name is required." }),
|
||||
type: joi.string().trim().messages({ "string.empty": "*Type is required." }),
|
||||
url: joi
|
||||
.string()
|
||||
.trim()
|
||||
.messages({ "string.empty": "*This field is required." }),
|
||||
name: joi.string().trim().max(50).allow("").messages({
|
||||
"string.max": "*This field should not exceed the 50 characters limit.",
|
||||
}),
|
||||
type: joi
|
||||
.string()
|
||||
.trim()
|
||||
.messages({ "string.empty": "*This field is required." }),
|
||||
frequency: joi.number().messages({
|
||||
"number.base": "*Frequency must be a number.",
|
||||
"any.required": "*Frequency is required.",
|
||||
}),
|
||||
});
|
||||
|
||||
export {
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
--env-var-spacing-4: 40px;
|
||||
|
||||
--env-var-font-size-small: 11px;
|
||||
--env-var-font-size-small-plus: 12px;
|
||||
--env-var-font-size-medium: 13px;
|
||||
--env-var-font-size-medium-plus: 14px;
|
||||
--env-var-font-size-large: 16px;
|
||||
|
||||
Reference in new issue
Block a user