feat: getting rid of unnecessary ids and changing validation format

This commit is contained in:
Caio Cabral
2024-10-23 19:11:06 -04:00
parent b7c3fb9291
commit 9b3f73da0e
5 changed files with 112 additions and 81 deletions

12
.chores.md Normal file
View File

@@ -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

View File

@@ -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={<AdminCheckedRegister />}
/>
<Route
exact
path="/register/:token"
element={<Register />}
/>
{/* <Route path="/toast" element={<ToastComponent />} /> */}
<Route
path="*"
element={<NotFound />}
/>
<Route
path="/forgot-password"
element={<ForgotPassword />}

View File

@@ -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}
<Typography component="span">{noHighlightText}</Typography> {text}
</Typography>
</Stack>
);
@@ -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,
};

View File

@@ -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 = () => {
>
<Field
type="password"
id="register-password-input"
name="password"
label="Password"
isRequired={true}
placeholder="••••••••"
@@ -206,7 +221,7 @@ const SetNewPassword = () => {
>
<Field
type="password"
id="confirm-password-input"
name="confirm"
label="Confirm password"
isRequired={true}
placeholder="••••••••"
@@ -226,49 +241,43 @@ const SetNewPassword = () => {
characters long
</>
}
variant={
errors?.password === "Password is required"
? "error"
: form.password === ""
? "info"
: form.password.length < 8
? "error"
: "success"
}
/>
<Check
text={
<>
<Typography component="span">Must contain</Typography> 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}
/>
<Check
text={
<>
<Typography component="span">Must contain at least</Typography> 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}
/>
<Check
text={
<>
<Typography component="span">Must contain at least</Typography> one
one number
</>
}
variant={feedbacks.number}
/>
<Check
text={
<>
<Typography component="span">Must contain at least</Typography> one
upper character
</>
}
variant={feedbacks.upper}
/>
<Check
text={
<>
<Typography component="span">Must contain at least</Typography> one
lower character
</>
}
variant={feedbacks.lower}
/>
</Stack>
</Box>

View File

@@ -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;
}),