From 3b88ea01d0dca5a43c73e408e4eed3eef4f566b0 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 27 Jun 2024 13:35:12 -0400 Subject: [PATCH 1/5] Fixed some small styling issues and validation schema --- .../ProfileSettings/ProfilePanel.jsx | 11 ++- Client/src/Pages/Settings/index.css | 1 - Client/src/Validation/validation.js | 72 +++++++++---------- 3 files changed, 40 insertions(+), 44 deletions(-) diff --git a/Client/src/Components/TabPanels/ProfileSettings/ProfilePanel.jsx b/Client/src/Components/TabPanels/ProfileSettings/ProfilePanel.jsx index 2675aedd9..14a3ba1b0 100644 --- a/Client/src/Components/TabPanels/ProfileSettings/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/ProfileSettings/ProfilePanel.jsx @@ -153,7 +153,7 @@ const ProfilePanel = () => {
- First Name + First name @@ -178,7 +178,7 @@ const ProfilePanel = () => {
- Last Name + Last name @@ -232,18 +232,17 @@ const ProfilePanel = () => {
- Your Photo + Your photo This photo will be displayed in your profile page. - {/* TODO - Update placeholder values with redux data */} { + 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,8 +124,8 @@ 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; From 081eb1727aaa1a5a5219745f1e3b7779a55b5a38 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 27 Jun 2024 16:29:50 -0400 Subject: [PATCH 2/5] Refactored useStates --- Client/src/Components/Checkbox/Checkbox.css | 1 - .../Checkbox/CustomizableCheckbox.jsx | 4 +- Client/src/Components/ConfigBox/index.jsx | 4 +- Client/src/Components/RadioButton/index.jsx | 3 +- .../Flexibile/FlexibileTextField.jsx | 10 +- Client/src/Pages/CreateNewMonitor/index.jsx | 310 +++++++++--------- Client/src/Pages/Monitors/index.jsx | 1 + 7 files changed, 168 insertions(+), 165 deletions(-) diff --git a/Client/src/Components/Checkbox/Checkbox.css b/Client/src/Components/Checkbox/Checkbox.css index 27c6310cc..e78fff6da 100644 --- a/Client/src/Components/Checkbox/Checkbox.css +++ b/Client/src/Components/Checkbox/Checkbox.css @@ -33,5 +33,4 @@ font-size: var(--env-var-font-size-medium); color: var(--env-var-color-5); font-weight: 500; - cursor: pointer; } diff --git a/Client/src/Components/Checkbox/CustomizableCheckbox.jsx b/Client/src/Components/Checkbox/CustomizableCheckbox.jsx index 807562f26..e39ea134b 100644 --- a/Client/src/Components/Checkbox/CustomizableCheckbox.jsx +++ b/Client/src/Components/Checkbox/CustomizableCheckbox.jsx @@ -10,9 +10,9 @@ const CustomizableCheckbox = (props) => { onChange={props.handleChange} className="custom-checkbox" type="checkbox" - id="checkbox" + id={props.id} /> - +
); }; diff --git a/Client/src/Components/ConfigBox/index.jsx b/Client/src/Components/ConfigBox/index.jsx index 7cf547232..28776752f 100644 --- a/Client/src/Components/ConfigBox/index.jsx +++ b/Client/src/Components/ConfigBox/index.jsx @@ -3,10 +3,10 @@ import React from "react"; const ConfigBox = ({ leftLayout, rightLayout }) => { return ( -
+
{leftLayout} {rightLayout} -
+ ); }; diff --git a/Client/src/Components/RadioButton/index.jsx b/Client/src/Components/RadioButton/index.jsx index e9c874bee..40f3fc230 100644 --- a/Client/src/Components/RadioButton/index.jsx +++ b/Client/src/Components/RadioButton/index.jsx @@ -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) { } + control={} onChange={props.onChange} label={
diff --git a/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx b/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx index f7b05ab12..9f2998ef9 100644 --- a/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx +++ b/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx @@ -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 (
{title}
{ - const portOptions = [ - { label: "Port 1", value: "Port1" }, - { label: "Port 2", value: "Port2" }, - // Add more Ports as needed - ]; + //General Settings Form + const [generalSettings, setGeneralSettings] = useState({ url: "", name: "" }); + //Checks Form + const [checks, setChecks] = useState({ monitor: "", 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 frequencyOptions = [ - { label: "1 minute", value: "1" }, - { label: "5 minutes", value: "5" }, - // Add more Ports as needed - ]; - - const [incidentEmail, setIncidentEmail] = useState(""); - - const [notifyViaSMS, setNotifyViaSMS] = useState(false); - const [notifyViaEmail, setNotifyViaEmail] = useState(false); - const [notifyViaCurrentEmail, setNotifyViaCurrentEmail] = useState(false); - - const [maxRetries, setMaxRetries] = useState(0); - const [acceptCode, setAcceptCode] = useState(0); - const [maxRedirects, setMaxRedirects] = useState(0); - - const [proxy, setProxy] = useState(false); - - const [proxyAddress, setProxyAddress] = useState(""); - const [proxyPort, setProxyPort] = useState(""); - - const [selectedMonitorType, setSelectedMonitorType] = useState(""); - - 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); + const handleChange = (event, id, setState, checkbox) => { + const { value } = event.target; + setState((prev) => ({ + ...prev, + [id]: checkbox ? true : value, + })); + }; + const handleCheck = (id, setState) => { + setState((prev) => ({ + ...prev, + [id]: !prev[id], + })); }; - const handleFrequencyOptionChange = (newValue) => { - setPort(newValue); - }; + console.log({ + ...generalSettings, + ...checks, + ...notifications, + ...advancedSettings, + ...proxy, + }); - 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"); - }; + const ports = ["Port 1", "Port 2", "Port 3"]; + const frequencies = [1, 2, 3, 4, 5]; return (
@@ -134,17 +76,25 @@ const CreateNewMonitor = () => { rightLayout={
+ handleChange(event, "url", setGeneralSettings) + } />
+ handleChange(event, "name", setGeneralSettings) + } />
} @@ -163,37 +113,48 @@ const CreateNewMonitor = () => { rightLayout={
handleMonitorTypeChange("http")} + checked={checks.monitor === "http"} + onChange={(event) => handleChange(event, "monitor", setChecks)} />
handleMonitorTypeChange("ping")} + checked={checks.monitor === "ping"} + onChange={(event) => handleChange(event, "monitor", setChecks)} />
handleMonitorTypeChange("port")} + checked={checks.monitor === "port"} + onChange={(event) => handleChange(event, "monitor", setChecks)} />
- +
} @@ -217,29 +178,35 @@ const CreateNewMonitor = () => {
handleCheck("viaSms", setNotifications)} />
handleCheck("viaEmail", setNotifications)} />
handleCheck("viaOther", setNotifications)} />
+ handleChange(event, "email", setNotifications) + } />
@@ -258,35 +225,49 @@ const CreateNewMonitor = () => {
Check frequency
- + handleChange(event, "frequency", setAdvancedSettings) + } + > + {frequencies.map((freq) => ( + + {freq} {freq === 1 ? "minute" : "minutes"} + + ))} + +
+ + handleChange(event, "retries", setAdvancedSettings) + } />
-
- Maximum retries before the service is marked as down -
+ handleChange(event, "codes", setAdvancedSettings) + } />
-
Accepted status codes
-
-
Maximum redirects
- + handleChange(event, "redirects", setAdvancedSettings) + } />
} @@ -302,19 +283,32 @@ const CreateNewMonitor = () => { rightLayout={
handleCheck("enabled", setProxy)} />
-
Proxy protocol
- + handleChange(event, "protocol", setProxy)} + />
-
Proxy address
- + handleChange(event, "address", setProxy)} + />
-
Proxy port
- + handleChange(event, "proxy_port", setProxy)} + />
} /> @@ -326,7 +320,7 @@ const CreateNewMonitor = () => { level="primary" label="Create new monitor" sx={{ width: "210px", fontSize: "var(--env-var-font-size-medium)" }} - onClick={() => handleCreateNewMonitor()} + // onClick={() => handleCreateNewMonitor()} />
diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index 00df4ee24..94bd6e74f 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -4,6 +4,7 @@ import { useEffect } from "react"; import { useSelector } from "react-redux"; import { useDispatch } from "react-redux"; import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice"; +import CreateNewMonitor from "../CreateNewMonitor"; const Monitors = () => { const monitorState = useSelector((state) => state.monitors); From 57be438ea7a9a07cf74dcc03e163d8d80e317fbe Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 27 Jun 2024 13:42:18 -0700 Subject: [PATCH 3/5] fixed monitor page --- Client/src/Pages/Monitors/index.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index 94bd6e74f..5f7b4b9af 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -4,7 +4,6 @@ import { useEffect } from "react"; import { useSelector } from "react-redux"; import { useDispatch } from "react-redux"; import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice"; -import CreateNewMonitor from "../CreateNewMonitor"; const Monitors = () => { const monitorState = useSelector((state) => state.monitors); @@ -17,8 +16,7 @@ const Monitors = () => { return (
- {/* */} - +
); }; From 16006934cdd435f324bf54d896a0c7b393fda34b Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 27 Jun 2024 16:44:56 -0400 Subject: [PATCH 4/5] Commented out unfinished features --- Client/src/Components/ConfigBox/index.jsx | 4 +-- Client/src/Pages/CreateNewMonitor/index.jsx | 38 ++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Client/src/Components/ConfigBox/index.jsx b/Client/src/Components/ConfigBox/index.jsx index 28776752f..7cf547232 100644 --- a/Client/src/Components/ConfigBox/index.jsx +++ b/Client/src/Components/ConfigBox/index.jsx @@ -3,10 +3,10 @@ import React from "react"; const ConfigBox = ({ leftLayout, rightLayout }) => { return ( -
+
{leftLayout} {rightLayout} - +
); }; diff --git a/Client/src/Pages/CreateNewMonitor/index.jsx b/Client/src/Pages/CreateNewMonitor/index.jsx index 669f1975c..2ca0f0392 100644 --- a/Client/src/Pages/CreateNewMonitor/index.jsx +++ b/Client/src/Pages/CreateNewMonitor/index.jsx @@ -11,7 +11,7 @@ const CreateNewMonitor = () => { //General Settings Form const [generalSettings, setGeneralSettings] = useState({ url: "", name: "" }); //Checks Form - const [checks, setChecks] = useState({ monitor: "", port: "" }); + const [checks, setChecks] = useState({ type: "", port: "" }); //Incidents Form const [notifications, setNotifications] = useState({ viaSms: false, @@ -48,19 +48,15 @@ const CreateNewMonitor = () => { })); }; - console.log({ - ...generalSettings, - ...checks, - ...notifications, - ...advancedSettings, - ...proxy, - }); + const handleCreateNewMonitor = () => { + + } const ports = ["Port 1", "Port 2", "Port 3"]; const frequencies = [1, 2, 3, 4, 5]; return ( -
+
Create new monitor
{ onChange={(event) => handleChange(event, "monitor", setChecks)} />
- { ))} -
+
*/}
} /> -
+ {/* TODO */} + {/*
{
} - /> + /> */}
{ ))}
- { onChange={(event) => handleChange(event, "redirects", setAdvancedSettings) } - /> + /> */} } /> -
+ {/* TODO */} + {/*
{ /> } - /> + /> */}
@@ -320,10 +320,10 @@ const CreateNewMonitor = () => { level="primary" label="Create new monitor" sx={{ width: "210px", fontSize: "var(--env-var-font-size-medium)" }} - // onClick={() => handleCreateNewMonitor()} + onClick={() => handleCreateNewMonitor()} />
- + ); }; From f3e222ac3a0aaa069851c807729be061a480a3df Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 27 Jun 2024 17:26:00 -0400 Subject: [PATCH 5/5] Added some basic validation --- Client/src/Pages/CreateNewMonitor/index.jsx | 135 +++++++++++++++----- Client/src/Validation/validation.js | 7 + 2 files changed, 109 insertions(+), 33 deletions(-) diff --git a/Client/src/Pages/CreateNewMonitor/index.jsx b/Client/src/Pages/CreateNewMonitor/index.jsx index 2ca0f0392..8f59f5225 100644 --- a/Client/src/Pages/CreateNewMonitor/index.jsx +++ b/Client/src/Pages/CreateNewMonitor/index.jsx @@ -5,34 +5,42 @@ import React, { useState } from "react"; import RadioButton from "../../Components/RadioButton"; import CustomizableCheckBox from "../../Components/Checkbox/CustomizableCheckbox"; import Button from "../../Components/Button"; -import { MenuItem, Select } from "@mui/material"; +import { MenuItem, Select, Typography } from "@mui/material"; +import { useSelector } from "react-redux"; +import { createMonitorValidation } from "../../Validation/validation"; const CreateNewMonitor = () => { + const { user } = useSelector((state) => state.auth); + const [errors, setErrors] = useState({}); + //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: "", + 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: "", + // retries: "", + // codes: "", + // redirects: "", }); //Proxy Settings Form - const [proxy, setProxy] = useState({ - enabled: false, - protocol: "", - address: "", - proxy_port: "", - }); + // const [proxy, setProxy] = useState({ + // enabled: false, + // protocol: "", + // address: "", + // proxy_port: "", + // }); const handleChange = (event, id, setState, checkbox) => { const { value } = event.target; @@ -40,19 +48,59 @@ const CreateNewMonitor = () => { ...prev, [id]: checkbox ? true : value, })); + + const validation = createMonitorValidation.validate( + { [id]: value }, + { abortEarly: false } + ); + + setErrors((prev) => { + const updatedErrors = { ...prev }; + + 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 handleCheck = (id, setState) => { + // setState((prev) => ({ + // ...prev, + // [id]: !prev[id], + // })); + // }; + + const handleCreateNewMonitor = (event) => { + event.preventDefault(); + //obj to submit + const monitor = { + ...generalSettings, + ...advancedSettings, + userId: user._id, + ...checks, + }; + + const { error } = createMonitorValidation.validate(monitor, { + abortEarly: false, + }); + + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + } else { + //TODO - submit logic + } }; - const handleCreateNewMonitor = () => { + console.log(errors); - } - - const ports = ["Port 1", "Port 2", "Port 3"]; + //select values + // const ports = ["Port 1", "Port 2", "Port 3"]; const frequencies = [1, 2, 3, 4, 5]; return ( @@ -81,6 +129,13 @@ const CreateNewMonitor = () => { handleChange(event, "url", setGeneralSettings) } /> + {errors["url"] ? ( + + {errors["url"]} + + ) : ( + "" + )}
{ handleChange(event, "name", setGeneralSettings) } /> + {errors["name"] ? ( + + {errors["name"]} + + ) : ( + "" + )} } /> @@ -113,8 +175,8 @@ const CreateNewMonitor = () => { title="HTTP/website monitoring" desc="Use HTTP(s) to monitor your website or API endpoint." value="http" - checked={checks.monitor === "http"} - onChange={(event) => handleChange(event, "monitor", setChecks)} + checked={checks.type === "http"} + onChange={(event) => handleChange(event, "type", setChecks)} />
{ title="Ping monitoring" desc="Check whether your server is available or not." value="ping" - checked={checks.monitor === "ping"} - onChange={(event) => handleChange(event, "monitor", setChecks)} + checked={checks.type === "ping"} + onChange={(event) => handleChange(event, "type", setChecks)} />
{/* TODO */} @@ -132,8 +194,8 @@ const CreateNewMonitor = () => { title="Port monitoring" desc="Monitor a specific service on your server." value="port" - checked={checks.monitor === "port"} - onChange={(event) => handleChange(event, "monitor", setChecks)} + checked={checks.type === "port"} + onChange={(event) => handleChange(event, "type", setChecks)} />
@@ -153,6 +215,13 @@ const CreateNewMonitor = () => { ))}
*/} + {errors["type"] ? ( + + {errors["type"]} + + ) : ( + "" + )} } /> @@ -320,7 +389,7 @@ const CreateNewMonitor = () => { level="primary" label="Create new monitor" sx={{ width: "210px", fontSize: "var(--env-var-font-size-medium)" }} - onClick={() => handleCreateNewMonitor()} + onClick={handleCreateNewMonitor} /> diff --git a/Client/src/Validation/validation.js b/Client/src/Validation/validation.js index d8bfd8ed1..802965870 100644 --- a/Client/src/Validation/validation.js +++ b/Client/src/Validation/validation.js @@ -132,7 +132,14 @@ 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." }), +}); + export { + createMonitorValidation, registerValidation, loginValidation, recoveryValidation,