From c74ecd58cede06ce4435c0c2c76b487228a52152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Mur=C3=A7a?= Date: Sun, 1 Oct 2023 19:56:24 -0300 Subject: [PATCH] Add password validation hints --- src/screens/Register/View.js | 69 ++++++++++++++++++++++++++++--- src/screens/Register/index.js | 74 +++++++++++++++++++++++++++++++++- src/screens/Register/styles.js | 27 +++++++++++++ src/utils/validations.js | 28 +++++++++++++ 4 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 src/utils/validations.js diff --git a/src/screens/Register/View.js b/src/screens/Register/View.js index 162b9c1..1a8d7e9 100644 --- a/src/screens/Register/View.js +++ b/src/screens/Register/View.js @@ -5,7 +5,12 @@ import { Container, FormControl, InputLabel, + LinearProgress, Link, + List, + ListItem, + ListItemIcon, + ListItemText, MenuItem, Paper, Select, @@ -13,6 +18,7 @@ import { TextField, Typography, } from '@mui/material'; +import React from 'react'; import SnackbarIndicator from '../../components/SnackbarIndicator'; import LoadingIndicator from '../../components/LoadingIndicator'; @@ -23,20 +29,31 @@ import logoImage from '../../assets/if-salas-logo.svg'; import styles from './styles'; import { createArrayFrom1ToN } from '../../utils/createArrayFrom1ToN'; import { COURSES } from '../../utils/constants'; +import { Done } from '@mui/icons-material'; function View({ isPending, isError, error, layoutType, + isPasswordFocusedForTheFirstTime, data, onChangeInput, + onChangePasswordInput, onChangeCheckbox, + onFocusInput, onTryRegister, currentYear, }) { - const { container, paper, boxLogo, boxForm, logoContainer } = - styles[layoutType]; + const { + container, + paper, + boxLogo, + boxForm, + logoContainer, + passwordRulesBox, + passwordRulesStrength, + } = styles[layoutType]; return ( @@ -72,7 +89,7 @@ function View({ type="text" value={data.ra} onChange={onChangeInput} - placeholder="00#####" + placeholder="#######" InputProps={{ inputComponent: InputMask, }} @@ -117,7 +134,6 @@ function View({ ))} - {/* TODO: Add field mask */} + {isPasswordFocusedForTheFirstTime && ( + +

+ Força da senha: {data.password.strength}% +

+ + + + {data.password.rules.map(rule => ( + + + + + + + ))} + +
+ )} + { - register(data); + register({ ...data, password: data.password.value }); + }; + + const onChangePasswordInput = e => { + const value = e.target.value; + const appliedRules = [ + hasAtLeastLength(value, 8), + hasLowerCase(value), + hasUpperCase(value), + hasSpecialChars(value), + hasNumber(value), + ]; + + setData(prev => ({ + ...prev, + password: { + value, + strength: + (appliedRules.filter(r => r === true).length * 100) / + appliedRules.length, + rules: prev.password.rules.map((rule, i) => ({ + ...rule, + applied: appliedRules[i], + })), + }, + })); + }; + + const onFocusInput = e => { + if (isPasswordFocusedForTheFirstTime) return; + const name = e.target.name; + setIsPasswordFocusedForTheFirstTime(name === 'password'); }; const onChangeInput = e => { @@ -47,9 +114,12 @@ function Register() { isError={isError} error={error} layoutType={layoutType} + isPasswordFocusedForTheFirstTime={isPasswordFocusedForTheFirstTime} data={data} onChangeInput={onChangeInput} + onChangePasswordInput={onChangePasswordInput} onChangeCheckbox={onChangeCheckbox} + onFocusInput={onFocusInput} onTryRegister={onTryRegister} currentYear={currentYear} /> diff --git a/src/screens/Register/styles.js b/src/screens/Register/styles.js index 8fbd44a..31ffbe1 100644 --- a/src/screens/Register/styles.js +++ b/src/screens/Register/styles.js @@ -47,12 +47,27 @@ const desktopBoxForm = { const logoContainerDesktop = {}; +const passwordRulesBoxDesktop = { + width: '100%', + backgroundColor: '#f2f2f2', + border: '1px solid black', + padding: '16px', +}; + +const passwordRulesStrengthDesktop = { + color: 'black', + fontSize: '0.9em', + marginBottom: '10px', +}; + const desktop = { container: desktopContainer, paper: desktopPaper, boxLogo: desktopBoxLogo, boxForm: desktopBoxForm, logoContainer: logoContainerDesktop, + passwordRulesBox: passwordRulesBoxDesktop, + passwordRulesStrength: passwordRulesStrengthDesktop, }; // ========== Mobile ========== @@ -83,12 +98,22 @@ const logoContainerMobile = { padding: '20px 16px', }; +const passwordRulesBoxMobile = { + ...passwordRulesBoxDesktop, +}; + +const passwordRulesStrengthMobile = { + ...passwordRulesStrengthDesktop, +}; + const mobile = { container: mobileContainer, paper: mobilePaper, boxLogo: mobileBoxLogo, boxForm: mobileBoxForm, logoContainer: logoContainerMobile, + passwordRulesBox: passwordRulesBoxMobile, + passwordRulesStrength: passwordRulesStrengthMobile, }; // ========== Unset ========== @@ -98,6 +123,8 @@ const unset = { boxLogo: null, boxForm: null, logoContainer: null, + passwordRulesBox: null, + passwordRulesStrength: null, }; const styles = { desktop, mobile, unset }; diff --git a/src/utils/validations.js b/src/utils/validations.js new file mode 100644 index 0000000..c23916f --- /dev/null +++ b/src/utils/validations.js @@ -0,0 +1,28 @@ +function hasLowerCase(str) { + return str.toUpperCase() !== str; +} + +function hasUpperCase(str) { + return str.toLowerCase() !== str; +} + +function hasSpecialChars(str) { + const specialChars = /[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/; + return specialChars.test(str); +} + +function hasAtLeastLength(str, length) { + return str.length >= length; +} + +function hasNumber(myString) { + return /\d/.test(myString); +} + +export { + hasLowerCase, + hasUpperCase, + hasSpecialChars, + hasAtLeastLength, + hasNumber, +};