From 9b3f73da0e4c5db34a069650e024788ec95da5a2 Mon Sep 17 00:00:00 2001 From: Caio Cabral Date: Wed, 23 Oct 2024 19:11:06 -0400 Subject: [PATCH] feat: getting rid of unnecessary ids and changing validation format --- .chores.md | 12 ++ Client/src/App.jsx | 16 +-- Client/src/Components/Check/Check.jsx | 5 +- Client/src/Pages/Auth/SetNewPassword.jsx | 133 ++++++++++++----------- Client/src/Validation/validation.js | 27 +++-- 5 files changed, 112 insertions(+), 81 deletions(-) create mode 100644 .chores.md diff --git a/.chores.md b/.chores.md new file mode 100644 index 000000000..ba0fd4dd7 --- /dev/null +++ b/.chores.md @@ -0,0 +1,12 @@ +- [ ] Take validation out from the component. + - [ ] SetNewPassword + - [ ] Register +- [ ] Create Check with highlight component. Check receives: status and message +- [ ] Split some of these user feedbacks? Like: + - [ ] Must contain at least one special character, + - [ ] Must contain at least one number, + - [ ] Must contain at least one upper character, + - [ ] Must contain at least one lower character +- [ ] Pass mismatching password user feedback as the other messages, for consistency +- [ ] Get rid of toast message +- [ ] Do it on blur diff --git a/Client/src/App.jsx b/Client/src/App.jsx index 98772ecca..6cb2f433a 100644 --- a/Client/src/App.jsx +++ b/Client/src/App.jsx @@ -1,7 +1,10 @@ +import { useEffect } from "react"; import { Routes, Route } from "react-router-dom"; +import { useSelector } from "react-redux"; +import { useDispatch } from "react-redux"; +// import "./App.css"; import "react-toastify/dist/ReactToastify.css"; import { ToastContainer } from "react-toastify"; -// import "./App.css"; import NotFound from "./Pages/NotFound"; import Login from "./Pages/Auth/Login"; import Register from "./Pages/Auth/Register/Register"; @@ -21,21 +24,18 @@ import ProtectedRoute from "./Components/ProtectedRoute"; import Details from "./Pages/Monitors/Details"; import AdvancedSettings from "./Pages/AdvancedSettings"; import Maintenance from "./Pages/Maintenance"; -import withAdminCheck from "./HOC/withAdminCheck"; -import withAdminProp from "./HOC/withAdminProp"; import Configure from "./Pages/Monitors/Configure"; import PageSpeed from "./Pages/PageSpeed"; import CreatePageSpeed from "./Pages/PageSpeed/CreatePageSpeed"; import CreateNewMaintenanceWindow from "./Pages/Maintenance/CreateMaintenance"; import PageSpeedDetails from "./Pages/PageSpeed/Details"; import PageSpeedConfigure from "./Pages/PageSpeed/Configure"; +import withAdminCheck from "./HOC/withAdminCheck"; +import withAdminProp from "./HOC/withAdminProp"; import { ThemeProvider } from "@emotion/react"; import lightTheme from "./Utils/Theme/lightTheme"; import darkTheme from "./Utils/Theme/darkTheme"; -import { useSelector } from "react-redux"; import { CssBaseline } from "@mui/material"; -import { useEffect } from "react"; -import { useDispatch } from "react-redux"; import { getAppSettings } from "./Features/Settings/settingsSlice"; import { logger } from "./Utils/Logger"; // Import the logger import { networkService } from "./main"; @@ -181,16 +181,18 @@ function App() { path="/register" element={} /> + } /> - {/* } /> */} + } /> + } diff --git a/Client/src/Components/Check/Check.jsx b/Client/src/Components/Check/Check.jsx index 7ef51137e..9c1ec57fb 100644 --- a/Client/src/Components/Check/Check.jsx +++ b/Client/src/Components/Check/Check.jsx @@ -20,7 +20,7 @@ import { useTheme } from "@emotion/react"; * * @returns {React.Element} The `Check` component with a check icon and a label, defined by the `text` prop. */ -const Check = ({ text, variant = "info", outlined = false }) => { +const Check = ({ text, noHighlightText, variant = "info", outlined = false }) => { const theme = useTheme(); const colors = { success: theme.palette.success.main, @@ -54,7 +54,7 @@ const Check = ({ text, variant = "info", outlined = false }) => { opacity: 0.8, }} > - {text} + {noHighlightText} {text} ); @@ -62,6 +62,7 @@ const Check = ({ text, variant = "info", outlined = false }) => { Check.propTypes = { text: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired, + noHighlightText: PropTypes.string, variant: PropTypes.oneOf(["info", "error", "success"]), outlined: PropTypes.bool, }; diff --git a/Client/src/Pages/Auth/SetNewPassword.jsx b/Client/src/Pages/Auth/SetNewPassword.jsx index 6c8f7418e..9d1497b47 100644 --- a/Client/src/Pages/Auth/SetNewPassword.jsx +++ b/Client/src/Pages/Auth/SetNewPassword.jsx @@ -1,20 +1,20 @@ +import { useState } from "react"; import { useDispatch, useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { useParams } from "react-router-dom"; +import { useTheme } from "@emotion/react"; +import { Box, Stack, Typography } from "@mui/material"; +import LoadingButton from "@mui/lab/LoadingButton"; import { setNewPassword } from "../../Features/Auth/authSlice"; import { createToast } from "../../Utils/toastUtils"; -import { Box, Stack, Typography } from "@mui/material"; -import { useTheme } from "@emotion/react"; -import { useParams } from "react-router-dom"; -import { useState } from "react"; import { credentials } from "../../Validation/validation"; -import { useNavigate } from "react-router-dom"; import Check from "../../Components/Check/Check"; import Field from "../../Components/Inputs/Field"; -import LockIcon from "../../assets/icons/lock.svg?react"; -import Background from "../../assets/Images/background-grid.svg?react"; -import Logo from "../../assets/icons/bwu-icon.svg?react"; -import LoadingButton from "@mui/lab/LoadingButton"; -import "./index.css"; import { IconBox } from "./styled"; +import LockIcon from "../../assets/icons/lock.svg?react"; +import Logo from "../../assets/icons/bwu-icon.svg?react"; +import Background from "../../assets/Images/background-grid.svg?react"; +import "./index.css"; const SetNewPassword = () => { const navigate = useNavigate(); @@ -27,11 +27,6 @@ const SetNewPassword = () => { confirm: "", }); - const idMap = { - "register-password-input": "password", - "confirm-password-input": "confirm", - }; - const { isLoading } = useSelector((state) => state.auth); const { token } = useParams(); @@ -41,7 +36,7 @@ const SetNewPassword = () => { const passwordForm = { ...form }; const { error } = credentials.validate(passwordForm, { abortEarly: false, - context: { password: form.password }, + /* context: { password: form.password }, */ }); if (error) { @@ -82,8 +77,8 @@ const SetNewPassword = () => { }; const handleChange = (event) => { - const { value, id } = event.target; - const name = idMap[id]; + //TODO Change from id to name + const { value, name } = event.target; setForm((prev) => ({ ...prev, [name]: value, @@ -91,15 +86,35 @@ const SetNewPassword = () => { const { error } = credentials.validate( { [name]: value }, - { abortEarly: false, context: { password: form.password } } + { + abortEarly: false, + context: { password: form.password }, + } ); - setErrors((prev) => { - const prevErrors = { ...prev }; - if (error) prevErrors[name] = error.details[0].message; - else delete prevErrors[name]; - return prevErrors; - }); + const errors = error.details.map((error) => error.message); + + setErrors((prev) => ({ ...prev, [name]: errors })); + }; + + console.log(errors); + + /* function getCheckStatus() */ + + const feedbacks = { + length: "info" /* !errors.password + ? "info" + : errors.password === "length" || errors.password === "empty" + ? "error" + : "success", */, + special: "info" /* !errors + ? "info" + : errors.password === "length" || errors.password === "empty" + ? "error" + : "success", */, + number: "info", + upper: "success", + lower: "error", }; return ( @@ -189,7 +204,7 @@ const SetNewPassword = () => { > { > { characters long } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : form.password.length < 8 - ? "error" - : "success" - } - /> - - Must contain one special - character and a number - - } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : !/^(?=.*[!@#$%^&*(),.?":{}|])(?=.*\d).+$/.test(form.password) - ? "error" - : "success" - } + variant={feedbacks.length} /> Must contain at least one - upper and lower character + special character } - variant={ - errors?.password === "Password is required" - ? "error" - : form.password === "" - ? "info" - : !/^(?=.*[A-Z])(?=.*[a-z]).+$/.test(form.password) - ? "error" - : "success" + variant={feedbacks.special} + /> + + Must contain at least one + one number + } + variant={feedbacks.number} + /> + + Must contain at least one + upper character + + } + variant={feedbacks.upper} + /> + + Must contain at least one + lower character + + } + variant={feedbacks.lower} /> diff --git a/Client/src/Validation/validation.js b/Client/src/Validation/validation.js index c620bdf43..82dee8541 100644 --- a/Client/src/Validation/validation.js +++ b/Client/src/Validation/validation.js @@ -17,26 +17,33 @@ const passwordSchema = joi .trim() .min(8) .messages({ - "string.empty": "Password is required", - "string.min": "Password must be at least 8 characters long", + "string.empty": "empty", + "string.min": "length", }) .custom((value, helpers) => { if (!/[A-Z]/.test(value)) { - return helpers.message("Password must contain at least one uppercase letter"); + return helpers.message("uppercase"); } + return value; + }) + .custom((value, helpers) => { if (!/[a-z]/.test(value)) { - return helpers.message("Password must contain at least one lowercase letter"); + return helpers.message("lowercase"); } + return value; + }) + .custom((value, helpers) => { if (!/\d/.test(value)) { - return helpers.message("Password must contain at least one number"); + return helpers.message("number"); } + return value; + }) + .custom((value, helpers) => { if (!/[!@#$%^&*]/.test(value)) { - return helpers.message("Password must contain at least one special character"); + return helpers.message("special"); } - return value; }); - const credentials = joi.object({ firstName: nameSchema, lastName: nameSchema, @@ -61,12 +68,12 @@ const credentials = joi.object({ .string() .trim() .messages({ - "string.empty": "Password confirmation is required", + "string.empty": "empty", }) .custom((value, helpers) => { const { password } = helpers.prefs.context; if (value !== password) { - return helpers.message("Passwords do not match"); + return helpers.message("different"); } return value; }),