Merge pull request #217 from bluewave-labs/fix/create-monitor-page

Fix/create monitor page
This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-06-27 17:29:13 -04:00
commit fd8a0d455c
9 files changed
+284 -211

No files matched your search

@@ -33,5 +33,4 @@
font-size: var(--env-var-font-size-medium);
color: var(--env-var-color-5);
font-weight: 500;
cursor: pointer;
}
@@ -10,9 +10,9 @@ const CustomizableCheckbox = (props) => {
onChange={props.handleChange}
className="custom-checkbox"
type="checkbox"
id="checkbox"
id={props.id}
/>
<label className="custom-checkbox-label">{props.title}</label>
<label className="custom-checkbox-label" htmlFor={props.id}>{props.title}</label>
</div>
);
};
+2 -1
View File
@@ -11,6 +11,7 @@
* />
*
* @param {Object} props - The component props.
* @param {string} props.id - The id of the radio button.
* @param {string} props.title - The title of the radio button.
* @param {string} [props.desc] - The description of the radio button.
* @param {string} [props.size="small"] - The size of the radio button.
@@ -28,7 +29,7 @@ function RadioButton(props) {
<FormControlLabel
checked={props.checked}
value={props.value}
control={<Radio size={props.size} />}
control={<Radio id={props.id} size={props.size} />}
onChange={props.onChange}
label={
<div>
@@ -153,7 +153,7 @@ const ProfilePanel = () => {
<div className="edit-profile-form__wrapper">
<Stack>
<Typography variant="h4" component="h1">
First Name
First name
</Typography>
</Stack>
<Stack>
@@ -178,7 +178,7 @@ const ProfilePanel = () => {
<div className="edit-profile-form__wrapper">
<Stack>
<Typography variant="h4" component="h1">
Last Name
Last name
</Typography>
</Stack>
<Stack>
@@ -232,18 +232,17 @@ const ProfilePanel = () => {
<div className="edit-profile-form__wrapper">
<Stack>
<Typography variant="h4" component="h1">
Your Photo
Your photo
</Typography>
<Typography variant="h5" component="p">
This photo will be displayed in your profile page.
</Typography>
</Stack>
<Stack className="row-stack" direction="row" alignItems="center">
{/* TODO - Update placeholder values with redux data */}
<Avatar
src={picture ? picture.src : "/static/images/avatar/2.jpg"}
firstName={localData.firstname}
lastName={localData.lastname}
firstName={user?.firstname}
lastName={user?.lastname}
sx={{
width: "64px",
height: "64px",
@@ -1,11 +1,19 @@
import "./flexibileTextField.css";
import React from "react";
const FlexibileTextField = ({ title, type, placeholder, value, onChange }) => {
const FlexibileTextField = ({
id,
title,
type,
placeholder,
value,
onChange,
}) => {
return (
<div className="flexibile-textfield">
<div className="flexibile-textfield-title">{title}</div>
<input
id={id}
type={type}
className="flexibile-textfield-input"
placeholder={placeholder}
+223 -160
View File
@@ -3,122 +3,108 @@ import ConfigBox from "../../Components/ConfigBox";
import FlexibileTextField from "../../Components/TextFields/Flexibile/FlexibileTextField";
import React, { useState } from "react";
import RadioButton from "../../Components/RadioButton";
import Dropdown from "../../Components/Dropdown";
import CustomizableCheckBox from "../../Components/Checkbox/CustomizableCheckbox";
import Button from "../../Components/Button";
import { MenuItem, Select, Typography } from "@mui/material";
import { useSelector } from "react-redux";
import { createMonitorValidation } from "../../Validation/validation";
const CreateNewMonitor = () => {
const portOptions = [
{ label: "Port 1", value: "Port1" },
{ label: "Port 2", value: "Port2" },
// Add more Ports as needed
];
const { user } = useSelector((state) => state.auth);
const [errors, setErrors] = useState({});
const frequencyOptions = [
{ label: "1 minute", value: "1" },
{ label: "5 minutes", value: "5" },
// Add more Ports as needed
];
//General Settings Form
const [generalSettings, setGeneralSettings] = useState({ url: "", name: "" });
//Checks Form
const [checks, setChecks] = useState({
type: "",
// port: ""
});
//Incidents Form
// const [notifications, setNotifications] = useState({
// viaSms: false,
// viaEmail: false,
// viaOther: false,
// email: "",
// });
//Advanced Settings Form
const [advancedSettings, setAdvancedSettings] = useState({
frequency: "",
// retries: "",
// codes: "",
// redirects: "",
});
//Proxy Settings Form
// const [proxy, setProxy] = useState({
// enabled: false,
// protocol: "",
// address: "",
// proxy_port: "",
// });
const [incidentEmail, setIncidentEmail] = useState("");
const handleChange = (event, id, setState, checkbox) => {
const { value } = event.target;
setState((prev) => ({
...prev,
[id]: checkbox ? true : value,
}));
const [notifyViaSMS, setNotifyViaSMS] = useState(false);
const [notifyViaEmail, setNotifyViaEmail] = useState(false);
const [notifyViaCurrentEmail, setNotifyViaCurrentEmail] = useState(false);
const validation = createMonitorValidation.validate(
{ [id]: value },
{ abortEarly: false }
);
const [maxRetries, setMaxRetries] = useState(0);
const [acceptCode, setAcceptCode] = useState(0);
const [maxRedirects, setMaxRedirects] = useState(0);
setErrors((prev) => {
const updatedErrors = { ...prev };
const [proxy, setProxy] = useState(false);
if (validation.error) {
updatedErrors[id] = validation.error.details[0].message;
} else {
delete updatedErrors[id];
}
return updatedErrors;
});
};
// const handleCheck = (id, setState) => {
// setState((prev) => ({
// ...prev,
// [id]: !prev[id],
// }));
// };
const [proxyAddress, setProxyAddress] = useState("");
const [proxyPort, setProxyPort] = useState("");
const handleCreateNewMonitor = (event) => {
event.preventDefault();
//obj to submit
const monitor = {
...generalSettings,
...advancedSettings,
userId: user._id,
...checks,
};
const [selectedMonitorType, setSelectedMonitorType] = useState("");
const { error } = createMonitorValidation.validate(monitor, {
abortEarly: false,
});
const [urlToMonitor, setUrlToMonitor] = useState("");
const [friendlyName, setFriendlyName] = useState("");
const [port, setPort] = useState("Select a port to check");
const [frequncy, setFrequency] = useState("1 minute");
const handlePortOptionChange = (newValue) => {
setPort(newValue);
if (error) {
const newErrors = {};
error.details.forEach((err) => {
newErrors[err.path[0]] = err.message;
});
setErrors(newErrors);
} else {
//TODO - submit logic
}
};
const handleFrequencyOptionChange = (newValue) => {
setPort(newValue);
};
console.log(errors);
const handleUrlToMonitor = (event) => {
setUrlToMonitor(event.target.value);
console.log(urlToMonitor);
};
const handleFriendlyName = (event) => {
setFriendlyName(event.target.value);
console.log(friendlyName);
};
const handleIncidentEmail = (event) => {
setIncidentEmail(event.target.value);
console.log(incidentEmail);
};
const handleNotifyViaSMS = (event) => {
setNotifyViaSMS(event.target.checked);
};
const handleNotifyViaEmail = (event) => {
setNotifyViaEmail(event.target.checked);
};
const handleNotifyViaCurrentEmail = (event) => {
setNotifyViaCurrentEmail(event.target.checked);
};
const handleSetMaxRetries = (event) => {
setMaxRetries(event.target.value);
console.log(maxRetries);
};
const handleSetAcceptCode = (event) => {
setAcceptCode(event.target.value);
console.log(acceptCode);
};
const handleSetMaxRedirects = (event) => {
setMaxRedirects(event.target.value);
console.log(maxRedirects);
};
const handleEnableProxy = (event) => {
setProxy(event.target.value);
console.log(proxy);
};
const handleproxyAddress = (event) => {
setProxyAddress(event.target.value);
console.log(proxyAddress);
};
const handleproxyPort = (event) => {
setProxyPort(event.target.value);
console.log(proxyPort);
};
const handleMonitorTypeChange = (value) => {
setSelectedMonitorType(value);
console.log(value);
};
const handleCreateNewMonitor = () => {
console.log("Created");
};
//select values
// const ports = ["Port 1", "Port 2", "Port 3"];
const frequencies = [1, 2, 3, 4, 5];
return (
<div>
<form onSubmit={handleCreateNewMonitor} noValidate spellCheck="false">
<div className="new-monitor-header">Create new monitor</div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
@@ -134,18 +120,40 @@ const CreateNewMonitor = () => {
rightLayout={
<div className="config-box-inputs">
<FlexibileTextField
id="monitor-url"
title="URL to monitor"
type="url"
placeholder="https://google.com"
onChange={handleUrlToMonitor}
value={generalSettings.url}
onChange={(event) =>
handleChange(event, "url", setGeneralSettings)
}
/>
{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)"
type="text"
placeholder="Google"
onChange={handleFriendlyName}
value={generalSettings.name}
onChange={(event) =>
handleChange(event, "name", setGeneralSettings)
}
/>
{errors["name"] ? (
<Typography variant="h5" component="p" className="input-error">
{errors["name"]}
</Typography>
) : (
""
)}
</div>
}
/>
@@ -163,42 +171,62 @@ const CreateNewMonitor = () => {
rightLayout={
<div className="service-check-list">
<RadioButton
id="monitor-checks-http"
title="HTTP/website monitoring"
desc="Use HTTP(s) to monitor your website or API endpoint."
value="http"
checked={selectedMonitorType === "http"}
onChange={() => handleMonitorTypeChange("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."
value="ping"
checked={selectedMonitorType === "ping"}
onChange={() => handleMonitorTypeChange("ping")}
checked={checks.type === "ping"}
onChange={(event) => handleChange(event, "type", setChecks)}
/>
<div className="monitors-gaps-medium"></div>
<RadioButton
{/* TODO */}
{/* <RadioButton
id="monitor-checks-port"
title="Port monitoring"
desc="Monitor a specific service on your server."
value="port"
checked={selectedMonitorType === "port"}
onChange={() => handleMonitorTypeChange("port")}
checked={checks.type === "port"}
onChange={(event) => handleChange(event, "type", setChecks)}
/>
<div className="monitors-gaps-small-plus"></div>
<div className="monitors-dropdown-holder">
<Dropdown
id="ports-dropdown"
label="Select a port to check"
onChange={handlePortOptionChange}
options={portOptions}
value={port}
/>
</div>
<Select
id="monitor-ports"
value={checks.port || "placeholder"}
inputProps={{ id: "monitor-ports-select" }}
onChange={(event) => handleChange(event, "port", setChecks)}
>
<MenuItem id="port-placeholder" value="placeholder">
Select a port to check
</MenuItem>
{ports.map((port, index) => (
<MenuItem key={`port-${index}`} value={port}>
{port}
</MenuItem>
))}
</Select>
</div> */}
{errors["type"] ? (
<Typography variant="h5" component="p" className="input-error">
{errors["type"]}
</Typography>
) : (
""
)}
</div>
}
/>
<div className="monitors-gaps-medium"></div>
{/* TODO */}
{/* <div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
leftLayout={
@@ -217,35 +245,41 @@ const CreateNewMonitor = () => {
<div className="monitors-gaps-medium"></div>
<div className="incident-notif-config-checks">
<CustomizableCheckBox
isChecked={notifyViaSMS}
handleChange={handleNotifyViaSMS}
id="monitor-notify-sms"
title="Notify via SMS (to be implemented)"
isChecked={notifications.viaSms}
handleChange={() => handleCheck("viaSms", setNotifications)}
/>
<div className="monitors-gaps-medium"></div>
<CustomizableCheckBox
isChecked={notifyViaEmail}
handleChange={handleNotifyViaEmail}
id="monitor-notify-email"
title="Notify via email (to current email address)"
isChecked={notifications.viaEmail}
handleChange={() => handleCheck("viaEmail", setNotifications)}
/>
<div className="monitors-gaps-medium"></div>
<CustomizableCheckBox
isChecked={notifyViaCurrentEmail}
handleChange={handleNotifyViaCurrentEmail}
id="monitor-notify-other"
title="Notify via email (to another email address below)"
isChecked={notifications.viaOther}
handleChange={() => handleCheck("viaOther", setNotifications)}
/>
<div className="monitors-gaps-small-plus"></div>
<div className="monitors-dropdown-holder">
<FlexibileTextField
id="monitor-notify-other-email"
placeholder="notifications@gmail.com"
type="email"
value={incidentEmail}
onChange={handleIncidentEmail}
value={notifications.email}
onChange={(event) =>
handleChange(event, "email", setNotifications)
}
/>
</div>
</div>
</div>
}
/>
/> */}
<div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
@@ -258,40 +292,56 @@ const CreateNewMonitor = () => {
<div className="advanced-setting-config">
<div className="adv-setting-title">Check frequency</div>
<div className="monitors-gaps-small"></div>
<Dropdown
id="freq-dropdown"
label="1 minutes"
onChange={handleFrequencyOptionChange}
options={frequencyOptions}
value={frequencyOptions}
<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>
{/* TODO */}
{/* <FlexibileTextField
id="monitor-settings-retries"
title="Maximum retries before the service is marked as down"
type="number"
value={advancedSettings.retries}
onChange={(event) =>
handleChange(event, "retries", setAdvancedSettings)
}
/>
<div className="monitors-gaps-medium"></div>
<div className="adv-setting-title">
Maximum retries before the service is marked as down
</div>
<FlexibileTextField
onChange={handleSetMaxRetries}
id="monitor-settings-codes"
title="Accepted status codes"
type="number"
placeholder=""
value={advancedSettings.codes}
onChange={(event) =>
handleChange(event, "codes", setAdvancedSettings)
}
/>
<div className="monitors-gaps-medium"></div>
<div className="adv-setting-title">Accepted status codes</div>
<FlexibileTextField
onChange={handleSetAcceptCode}
id="monitor-settings-redirects"
title="Maximum redirects"
type="number"
placeholder=""
/>
<div className="monitors-gaps-medium"></div>
<div className="adv-setting-title">Maximum redirects</div>
<FlexibileTextField
onChange={handleSetMaxRedirects}
type="number"
placeholder=""
/>
value={advancedSettings.redirects}
onChange={(event) =>
handleChange(event, "redirects", setAdvancedSettings)
}
/> */}
</div>
}
/>
<div className="monitors-gaps-medium"></div>
{/* TODO */}
{/* <div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-medium"></div>
<ConfigBox
leftLayout={
@@ -302,22 +352,35 @@ const CreateNewMonitor = () => {
rightLayout={
<div className="proxy-setting-config">
<CustomizableCheckBox
isChecked={proxy}
handleChange={handleEnableProxy}
id="monitor-proxy-enable"
title="Enable proxy"
isChecked={proxy.enabled}
handleChange={() => handleCheck("enabled", setProxy)}
/>
<div className="monitors-gaps-medium"></div>
<div className="adv-setting-title">Proxy protocol</div>
<FlexibileTextField />
<FlexibileTextField
id="monitor-proxy-protocol"
title="Proxy protocol"
value={proxy.protocol}
onChange={(event) => handleChange(event, "protocol", setProxy)}
/>
<div className="monitors-gaps-medium"></div>
<div className="adv-setting-title">Proxy address</div>
<FlexibileTextField onChange={handleproxyAddress} />
<FlexibileTextField
id="monitor-proxy-address"
title="Proxy address"
value={proxy.address}
onChange={(event) => handleChange(event, "address", setProxy)}
/>
<div className="monitors-gaps-medium"></div>
<div className="adv-setting-title">Proxy port</div>
<FlexibileTextField onChange={handleproxyPort} />
<FlexibileTextField
id="monitor-proxy-port"
title="Proxy port"
value={proxy.proxy_port}
onChange={(event) => handleChange(event, "proxy_port", setProxy)}
/>
</div>
}
/>
/> */}
<div className="monitors-gaps-medium"></div>
<div className="monitors-gaps-small"></div>
<div className="monitors-create-button-holder">
@@ -326,10 +389,10 @@ const CreateNewMonitor = () => {
level="primary"
label="Create new monitor"
sx={{ width: "210px", fontSize: "var(--env-var-font-size-medium)" }}
onClick={() => handleCreateNewMonitor()}
onClick={handleCreateNewMonitor}
/>
</div>
</div>
</form>
);
};
+1 -2
View File
@@ -16,8 +16,7 @@ const Monitors = () => {
return (
<div className="monitors">
{/* <CurrentStats monitors={monitorState.monitors} /> */}
<CreateNewMonitor />
<CurrentStats monitors={monitorState.monitors} />
</div>
);
};
-1
View File
@@ -173,7 +173,6 @@
.settings .MuiStack-root:has(p.MuiTypography-root.input-error) {
position: relative;
margin-bottom: 8px;
}
.settings .text-field-icon {
position: absolute;
+42 -37
View File
@@ -82,43 +82,41 @@ const editProfileValidation = joi.object({
}),
});
const passwordSchema = joi
.string()
.trim()
.min(8)
.messages({
"string.empty": "*Password is required.",
"string.min": "*Password must be at least 8 characters long.",
})
.custom((value, helpers) => {
if (!/[A-Z]/.test(value)) {
return helpers.message(
"*Password must contain at least one uppercase letter."
);
}
if (!/[a-z]/.test(value)) {
return helpers.message(
"*Password must contain at least one lowercase letter."
);
}
if (!/\d/.test(value)) {
return helpers.message("*Password must contain at least one number.");
}
if (!/[!@#$%^&*]/.test(value)) {
return helpers.message(
"*Password must contain at least one special character."
);
}
return value;
});
const editPasswordValidation = joi.object({
// TBD - validation for current password ?
password : joi
.string().trim()
.messages({
"string.empty": "*Current password is required.",
}),
newpassword: joi
.string()
.trim()
.min(8)
.messages({
"string.empty": "*Password is required.",
"string.min": "*Password must be at least 8 characters long.",
})
.custom((value, helpers) => {
if (!/[A-Z]/.test(value)) {
return helpers.message(
"*Password must contain at least one uppercase letter."
);
}
if (!/[a-z]/.test(value)) {
return helpers.message(
"*Password must contain at least one lowercase letter."
);
}
if (!/\d/.test(value)) {
return helpers.message("*Password must contain at least one number.");
}
if (!/[!@#$%^&*]/.test(value)) {
return helpers.message(
"*Password must contain at least one special character."
);
}
return value;
}),
password: passwordSchema,
newpassword: passwordSchema,
confirm: joi
.string()
.trim()
@@ -126,15 +124,22 @@ const editPasswordValidation = joi.object({
"string.empty": "*Password confirmation is required.",
})
.custom((value, helpers) => {
const { password } = helpers.prefs.context;
if (value !== password) {
const { newpassword } = helpers.prefs.context;
if (value !== newpassword) {
return helpers.message("*Passwords do not match.");
}
return value;
}),
});
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." }),
});
export {
createMonitorValidation,
registerValidation,
loginValidation,
recoveryValidation,