From c87daa3d536a343d43c90911a88c49ba00bb17f4 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 28 Jun 2024 20:05:08 -0400 Subject: [PATCH 1/2] Refactored some of the component code and removed unnecessary params --- Client/src/Components/ConfigBox/index.css | 25 +-- Client/src/Components/RadioButton/index.css | 17 +- Client/src/Components/RadioButton/index.jsx | 45 +++--- .../TabPanels/Account/PasswordPanel.jsx | 18 +-- .../TabPanels/Account/ProfilePanel.jsx | 42 ++--- .../TabPanels/Account/TeamPanel.jsx | 18 +-- .../Flexibile/FlexibileTextField.jsx | 18 ++- .../Flexibile/flexibileTextField.css | 38 +++-- Client/src/Pages/Account/index.jsx | 4 +- Client/src/Pages/CreateNewMonitor/index.css | 115 ++++++------- Client/src/Pages/CreateNewMonitor/index.jsx | 152 ++++++++++-------- Client/src/Utils/Theme.js | 10 ++ Client/src/Validation/validation.js | 18 ++- Client/src/index.css | 1 + 14 files changed, 279 insertions(+), 242 deletions(-) diff --git a/Client/src/Components/ConfigBox/index.css b/Client/src/Components/ConfigBox/index.css index d5d6c96b2..35ea4ba04 100644 --- a/Client/src/Components/ConfigBox/index.css +++ b/Client/src/Components/ConfigBox/index.css @@ -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); +} \ No newline at end of file diff --git a/Client/src/Components/RadioButton/index.css b/Client/src/Components/RadioButton/index.css index 8193795eb..dab37dda0 100644 --- a/Client/src/Components/RadioButton/index.css +++ b/Client/src/Components/RadioButton/index.css @@ -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; } diff --git a/Client/src/Components/RadioButton/index.jsx b/Client/src/Components/RadioButton/index.jsx index 40f3fc230..acc87c992 100644 --- a/Client/src/Components/RadioButton/index.jsx +++ b/Client/src/Components/RadioButton/index.jsx @@ -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 ( -
- } - onChange={props.onChange} - label={ -
-
{props.title}
-
{props.desc}
-
- } - labelPlacement="end" - /> -
+ + } + onChange={props.onChange} + label={ + <> + {props.title} + + {props.desc} + + + } + labelPlacement="end" + sx={{ margin: 0, alignItems: "flex-start" }} + /> ); } diff --git a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx index 31664e2a2..14b9a016f 100644 --- a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx +++ b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx @@ -107,9 +107,7 @@ const PasswordPanel = () => {
- - Current password - + Current password { error={errors[idToName["edit-current-password"]] ? true : false} /> {errors[idToName["edit-current-password"]] ? ( - + {errors[idToName["edit-current-password"]]} ) : ( @@ -133,9 +131,7 @@ const PasswordPanel = () => {
- - New password - + New password { error={errors[idToName["edit-new-password"]] ? true : false} /> {errors[idToName["edit-new-password"]] ? ( - + {errors[idToName["edit-new-password"]]} ) : ( @@ -159,9 +155,7 @@ const PasswordPanel = () => {
- - Confirm new password - + Confirm new password { error={errors[idToName["edit-confirm-password"]] ? true : false} /> {errors[idToName["edit-confirm-password"]] ? ( - + {errors[idToName["edit-confirm-password"]]} ) : ( diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index 9c1b25d55..dd6398911 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -152,9 +152,7 @@ const ProfilePanel = () => {
- - First name - + First name { error={errors[idToName["edit-first-name"]] ? true : false} /> {errors[idToName["edit-first-name"]] ? ( - + {errors[idToName["edit-first-name"]]} ) : ( @@ -177,9 +175,7 @@ const ProfilePanel = () => {
- - Last name - + Last name { error={errors[idToName["edit-last-name"]] ? true : false} /> {errors[idToName["edit-last-name"]] ? ( - + {errors[idToName["edit-last-name"]]} ) : ( @@ -202,10 +198,8 @@ const ProfilePanel = () => {
- - Email - - + Email + This is your current email address — it cannot be changed. @@ -221,7 +215,7 @@ const ProfilePanel = () => { disabled={true} /> {errors[idToName["edit-email"]] ? ( - + {errors[idToName["edit-email"]]} ) : ( @@ -231,10 +225,8 @@ const ProfilePanel = () => {
- - Your photo - - + Your photo + This photo will be displayed in your profile page. @@ -292,10 +284,8 @@ const ProfilePanel = () => {
- - Delete account - - + Delete account + Note that deleting your account will remove all data from our system. This is permanent and non-recoverable. @@ -335,14 +325,10 @@ const ProfilePanel = () => { }, }} > - + Really delete this account? - + 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 = () => { }, }} > - + Upload Image { return ( -
+
- - Organization name - + Organization name { }, }} > - + Rename this organization? {
- - Team members - + Team members
{ }, }} > - + Invite new team member - + When you add a new team member, they will get access to all monitors. diff --git a/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx b/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx index 9f2998ef9..0b39447d5 100644 --- a/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx +++ b/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx @@ -1,5 +1,7 @@ +import { Stack, Typography } from "@mui/material"; import "./flexibileTextField.css"; import React from "react"; +import { useTheme } from "@emotion/react"; const FlexibileTextField = ({ id, @@ -8,19 +10,27 @@ const FlexibileTextField = ({ placeholder, value, onChange, + error, }) => { + const theme = useTheme(); return ( -
-
{title}
+ + {title} -
+ {error ? ( + + {error} + + ) : ( + "" + )} +
); }; diff --git a/Client/src/Components/TextFields/Flexibile/flexibileTextField.css b/Client/src/Components/TextFields/Flexibile/flexibileTextField.css index 2e0422876..2c5dcc988 100644 --- a/Client/src/Components/TextFields/Flexibile/flexibileTextField.css +++ b/Client/src/Components/TextFields/Flexibile/flexibileTextField.css @@ -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); -} +} \ No newline at end of file diff --git a/Client/src/Pages/Account/index.jsx b/Client/src/Pages/Account/index.jsx index 1a24bb36e..2bb4dc018 100644 --- a/Client/src/Pages/Account/index.jsx +++ b/Client/src/Pages/Account/index.jsx @@ -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" > diff --git a/Client/src/Pages/CreateNewMonitor/index.css b/Client/src/Pages/CreateNewMonitor/index.css index 099f6e151..26b41fb83 100644 --- a/Client/src/Pages/CreateNewMonitor/index.css +++ b/Client/src/Pages/CreateNewMonitor/index.css @@ -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; diff --git a/Client/src/Pages/CreateNewMonitor/index.jsx b/Client/src/Pages/CreateNewMonitor/index.jsx index 19dd1386a..dd94f907a 100644 --- a/Client/src/Pages/CreateNewMonitor/index.jsx +++ b/Client/src/Pages/CreateNewMonitor/index.jsx @@ -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 ( - -
Create new monitor
+ + Create new monitor
-
General settings
-
+ + General settings + Here you can select the URL of the host, together with the type of monitor. -
-
+
+
} rightLayout={ -
+ { onChange={(event) => handleChange(event, "url", setGeneralSettings) } + error={errors["url"]} /> - {errors["url"] ? ( - - {errors["url"]} - - ) : ( - "" - )} -
+ Friendly name (optional) + + } type="text" placeholder="Google" value={generalSettings.name} onChange={(event) => handleChange(event, "name", setGeneralSettings) } + error={errors["name"]} /> - {errors["name"] ? ( - - {errors["name"]} - - ) : ( - "" - )} -
+ } />
-
Checks to perform
-
+ + Checks to perform + You can always add or remove checks after adding your site. -
-
+ + } rightLayout={ -
+ handleChange(event, "type", setChecks)} /> -
handleChange(event, "type", setChecks)} /> -
{/* TODO */} {/* { ))}
*/} - {errors["type"] ? ( - - {errors["type"]} - - ) : ( - "" - )} -
+ + {errors["type"] ? ( + + {errors["type"]} + + ) : ( + "" + )} + +
} /> {/* TODO */} @@ -300,29 +305,47 @@ const CreateNewMonitor = () => {
-
Advanced settings
-
+ + Advanced settings + } rightLayout={ -
-
Check frequency
-
- -
+ + {/* TODO - refactor select component */} + + + Check frequency + + + {/* TODO */} {/* { handleChange(event, "redirects", setAdvancedSettings) } /> */} -
+ } /> {/* 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} />
diff --git a/Client/src/Utils/Theme.js b/Client/src/Utils/Theme.js index 2051bc160..c9f20126f 100644 --- a/Client/src/Utils/Theme.js +++ b/Client/src/Utils/Theme.js @@ -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; diff --git a/Client/src/Validation/validation.js b/Client/src/Validation/validation.js index 802965870..4aea7e48e 100644 --- a/Client/src/Validation/validation.js +++ b/Client/src/Validation/validation.js @@ -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 { diff --git a/Client/src/index.css b/Client/src/index.css index fe84c8914..4af5f4e84 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -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; From df27ae8976369dda5bad3291f99d9e2421e29f6c Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 28 Jun 2024 20:09:34 -0400 Subject: [PATCH 2/2] Added proptypes and jsdocs --- .../Flexibile/FlexibileTextField.jsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx b/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx index 0b39447d5..66b1743c5 100644 --- a/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx +++ b/Client/src/Components/TextFields/Flexibile/FlexibileTextField.jsx @@ -1,8 +1,20 @@ +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, title, @@ -34,4 +46,14 @@ const FlexibileTextField = ({ ); }; +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;