From 2f2b8e03652e38c47a7532b0c1f9e8a792a5a90b Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Wed, 13 Nov 2024 12:32:25 +0100 Subject: [PATCH 01/12] add optionnal bold to panel input label, update panel input in mon-compte --- src/components/mon-compte/PanelInput.tsx | 4 +++- src/pages/mon-compte.tsx | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/mon-compte/PanelInput.tsx b/src/components/mon-compte/PanelInput.tsx index e9be8eaa6..53b066763 100644 --- a/src/components/mon-compte/PanelInput.tsx +++ b/src/components/mon-compte/PanelInput.tsx @@ -10,6 +10,7 @@ interface PanelInputProps { placeholder?: string; isRequired?: boolean; isEditMode?: boolean; + isBold?: boolean; errorMsg?: string; helperText?: string; hasError?: boolean; @@ -26,6 +27,7 @@ export const PanelInput = ({ placeholder = '', isRequired = false, isEditMode = true, + isBold = false, errorMsg, helperText, type = 'text', @@ -36,7 +38,7 @@ export const PanelInput = ({ }: PanelInputProps) => { return (
-
+ + Professionnel de l'éducation + + { + setNewUser((u) => ({ ...u, lastname })); + }} + /> + { + setNewUser((u) => ({ ...u, firstname })); + }} + /> + + Établissement + { )} { From b302329e0f5baa3c130ae8c3c24342f3ad827750 Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Wed, 20 Nov 2024 12:48:38 +0100 Subject: [PATCH 03/12] fix button disabling depend on required fields --- src/components/WelcomeModal/FirstPhase.tsx | 16 +++++++++++++--- src/pages/mon-compte.tsx | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/components/WelcomeModal/FirstPhase.tsx b/src/components/WelcomeModal/FirstPhase.tsx index f2a59445c..0cd8db20e 100644 --- a/src/components/WelcomeModal/FirstPhase.tsx +++ b/src/components/WelcomeModal/FirstPhase.tsx @@ -63,7 +63,7 @@ export const FirstPhase = () => { }; const updateUser = async () => { - if (!newUser.city || !newUser.address || !newUser.pseudo || !newUser.school || !newUser.email || !newUser.postalCode) { + if (!newUser.firstname || !newUser.lastname || !newUser.school || !newUser.level || !newUser.address || !newUser.city || !newUser.postalCode) { return; } setIsLoading(true); @@ -159,7 +159,17 @@ export const FirstPhase = () => { color={currentStep === 2 || currentStep === 4 ? 'primary' : 'inherit'} variant={currentStep === 2 || currentStep === 4 ? 'contained' : 'text'} sx={currentStep === 2 || currentStep === 4 ? undefined : defaultTextButtonStyle} - disabled={(currentStep === 3 && (!newUser.city || !newUser.address || !newUser.postalCode)) || (currentStep === 2 && !cguChecked)} + disabled={ + (currentStep === 3 && + (!newUser.firstname || + !newUser.lastname || + !newUser.school || + !newUser.level || + !newUser.address || + !newUser.city || + !newUser.postalCode)) || + (currentStep === 2 && !cguChecked) + } onClick={() => { if (currentStep === 3) { getNewUserPosition().catch(); @@ -270,7 +280,7 @@ export const FirstPhase = () => {
- Professionnel de l'éducation + Professionnel de l'éducation { Date: Wed, 20 Nov 2024 15:29:34 +0100 Subject: [PATCH 04/12] modify reset password message on inexistant email --- src/pages/reset-password.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/pages/reset-password.tsx b/src/pages/reset-password.tsx index 55dd56b41..e325f6087 100644 --- a/src/pages/reset-password.tsx +++ b/src/pages/reset-password.tsx @@ -10,7 +10,7 @@ import { axiosRequest } from 'src/utils/axiosRequest'; const errorMessages = { 0: `Une erreur inconnue s'est produite`, - 1: `L'email renseignée est incorrect`, + 1: `Veuillez renseigner une adresse mail valide`, }; const ResetPassword: React.FunctionComponent = () => { @@ -20,6 +20,8 @@ const ResetPassword: React.FunctionComponent = () => { const [isSuccess, setIsSuccess] = React.useState(false); const [loading, setLoading] = React.useState(false); + const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; + const handleEmailInputChange = (event: React.ChangeEvent) => { setEmail(event.target.value); setErrorCode(-1); @@ -27,7 +29,12 @@ const ResetPassword: React.FunctionComponent = () => { const submit = async (event: React.FormEvent) => { event.preventDefault(); - setErrorCode(-1); + + if (!emailRegex.test(email)) { + setErrorCode(1); + return; + } + setLoading(true); const response = await axiosRequest({ method: 'POST', @@ -37,8 +44,8 @@ const ResetPassword: React.FunctionComponent = () => { }, }); setLoading(false); - if (response.error) { - setErrorCode(response.status === 400 ? 1 : 0); + if (response.status === 400) { + setErrorCode(0); } else { setTimeout(() => { setIsSuccess(true); @@ -203,7 +210,7 @@ const ResetPassword: React.FunctionComponent = () => {
Un email vient de vous être envoyé à l'adresse donnée

-
Vous allez être redirigé(e) vers la page de connexion...
+
Si cette adresse e-mail est enregistrée, nous y avons envoyé les instructions afin de réinitialiser votre mot de passe.
)} From 42f9ed9bea45ecbeca46aa4641eebc6ace5595c8 Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Wed, 20 Nov 2024 15:39:23 +0100 Subject: [PATCH 05/12] update password validation criterias --- src/pages/inscription.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pages/inscription.tsx b/src/pages/inscription.tsx index b85ef8e31..96550d025 100644 --- a/src/pages/inscription.tsx +++ b/src/pages/inscription.tsx @@ -60,7 +60,7 @@ const Inscription = () => { setIsPasswordValid(false); if (!password.match(/\d/)) { - passwordMessageRef.current = 'Le mot de passe doit contenir un chiffre'; + passwordMessageRef.current = 'Le mot de passe doit contenir au moins un chiffre'; } if (!password.match(/[A-Z]/)) { if (passwordMessageRef.current) { @@ -68,14 +68,20 @@ const Inscription = () => { } passwordMessageRef.current += ' une lettre majuscule'; } - if (password.length < 8) { + if (!password.match(/[\w\s]/)) { if (passwordMessageRef.current) { passwordMessageRef.current += ' et '; } - passwordMessageRef.current += 'faire au moins 8 caractères'; + passwordMessageRef.current += 'un caractère spécial'; + } + if (password.length < 12) { + if (passwordMessageRef.current) { + passwordMessageRef.current += ' et '; + } + passwordMessageRef.current += 'faire au moins 12 caractères'; } - if (!password.match(/\d/) || !password.match(/[A-Z]/) || password.length < 8) { + if (!password.match(/\d/) || !password.match(/[A-Z]/) || password.length < 12) { setIsPasswordValid(false); } else { setIsPasswordValid(true); From 36c7584a75d8067c38d38f2aeb1676156a2fe5a5 Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Wed, 20 Nov 2024 15:42:18 +0100 Subject: [PATCH 06/12] change messages text --- src/pages/reset-password.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pages/reset-password.tsx b/src/pages/reset-password.tsx index e325f6087..2084f52ef 100644 --- a/src/pages/reset-password.tsx +++ b/src/pages/reset-password.tsx @@ -151,8 +151,6 @@ const ResetPassword: React.FunctionComponent = () => { Veuillez renseigner l'email lié à votre compte. -
- Nous vous enverrons un email avec un lien qui vous permettra de réinitialiser votre mot de passe.
{ ) : ( -
Un email vient de vous être envoyé à l'adresse donnée
-
Si cette adresse e-mail est enregistrée, nous y avons envoyé les instructions afin de réinitialiser votre mot de passe.
)} From 3ecf00a054556aafc3e839a47abfcae989d260da Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Wed, 27 Nov 2024 10:45:51 +0100 Subject: [PATCH 07/12] add firstname and lastname in user put route, update FirstPhase --- server/controllers/user.ts | 2 ++ src/components/WelcomeModal/FirstPhase.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/server/controllers/user.ts b/server/controllers/user.ts index 95a473d0d..31cdee956 100644 --- a/server/controllers/user.ts +++ b/server/controllers/user.ts @@ -390,6 +390,8 @@ userController.put({ path: '/:id', userType: UserType.OBSERVATOR }, async (req: user.countryCode = valueOrDefault(data.countryCode, user.countryCode); user.avatar = valueOrDefault(data.avatar, user.avatar) || null; user.displayName = valueOrDefault(data.displayName, user.displayName) || null; + user.firstname = valueOrDefault(data.firstname, user.firstname); + user.lastname = valueOrDefault(data.lastname, user.lastname); user.firstLogin = valueOrDefault(data.firstLogin, user.firstLogin); if (req.user !== undefined && req.user.type <= UserType.ADMIN) { user.type = valueOrDefault(data.type, user.type); diff --git a/src/components/WelcomeModal/FirstPhase.tsx b/src/components/WelcomeModal/FirstPhase.tsx index 0cd8db20e..2d26b741a 100644 --- a/src/components/WelcomeModal/FirstPhase.tsx +++ b/src/components/WelcomeModal/FirstPhase.tsx @@ -75,6 +75,8 @@ export const FirstPhase = () => { address: newUser.address, pseudo: newUser.pseudo, email: newUser.email, + firstname: newUser.firstname, + lastname: newUser.lastname, firstLogin: 1, displayName: newUser.displayName || '', }; From af2e9200c00548af0ce095485207becc96d57db1 Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Thu, 5 Dec 2024 11:04:38 +0100 Subject: [PATCH 08/12] add PasswordMessageDisplayer component --- src/components/PasswordMessagesDisplayer.tsx | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/components/PasswordMessagesDisplayer.tsx diff --git a/src/components/PasswordMessagesDisplayer.tsx b/src/components/PasswordMessagesDisplayer.tsx new file mode 100644 index 000000000..a15adb593 --- /dev/null +++ b/src/components/PasswordMessagesDisplayer.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +interface PasswordMessagesDisplayerProps { + isErrors: boolean; + badLengthMessage: string; + missingDigitsMessage: string; +} + +const PasswordMessagesDisplayer = ({ isErrors, badLengthMessage, missingDigitsMessage }: PasswordMessagesDisplayerProps) => { + const passwordRequirementsMessage = '12 lettres minimum, une majuscule, une minusucle, un caractère spécial et un chiffre'; + return isErrors ? ( + <> + {badLengthMessage.length > 0 && ( + <> + {badLengthMessage} +
+ + )} + {missingDigitsMessage} + + ) : ( + <>{passwordRequirementsMessage} + ); +}; + +export default PasswordMessagesDisplayer; From 16a52bc6335e046141218d31bd677e3840e3d697 Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Thu, 5 Dec 2024 11:05:00 +0100 Subject: [PATCH 09/12] update password validation rules --- src/utils/accountChecks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/accountChecks.ts b/src/utils/accountChecks.ts index df6d2ed2d..9bdff0c21 100644 --- a/src/utils/accountChecks.ts +++ b/src/utils/accountChecks.ts @@ -2,7 +2,7 @@ import { axiosRequest } from './axiosRequest'; const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/i; -const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/; +const strongPassword = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{12,}$/; export async function isPseudoValid(pseudo: string, userPseudo: string): Promise { if (pseudo.length === 0) { From 51a47a864b72907958893d61bee2cf6b3c8a1d81 Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Thu, 5 Dec 2024 11:05:23 +0100 Subject: [PATCH 10/12] add utils funct invalidPasswordMessageBuilder --- src/utils/invalidPasswordMessageBuilder.ts | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/utils/invalidPasswordMessageBuilder.ts diff --git a/src/utils/invalidPasswordMessageBuilder.ts b/src/utils/invalidPasswordMessageBuilder.ts new file mode 100644 index 000000000..fa900174e --- /dev/null +++ b/src/utils/invalidPasswordMessageBuilder.ts @@ -0,0 +1,44 @@ +export function invalidPasswordMessageBuilder(password: string) { + let badLengthMessage = ''; + let missingDigitMessage = ''; + + const missingDigitErrors = []; + + if (password !== '') { + if (!password.match(/\d/)) { + missingDigitErrors.push('un chiffre'); + } + if (!password.match(/[A-Z]/)) { + missingDigitErrors.push('une lettre majuscule'); + } + if (!password.match(/[a-z]/)) { + missingDigitErrors.push('une lettre minusucle'); + } + if (!password.match(/[#?!@$%^&*-]/)) { + missingDigitErrors.push(' un caractère spécial'); + } + + if (missingDigitErrors.length > 0) { + missingDigitMessage += 'Le mot de passe doit contenir au moins '; + for (let i = 0; i < missingDigitErrors.length; i++) { + missingDigitMessage += missingDigitErrors[i]; + switch (i) { + case missingDigitErrors.length - 1: + missingDigitMessage += '.'; + break; + case missingDigitErrors.length - 2: + missingDigitMessage += ' et '; + break; + default: + missingDigitMessage += ', '; + break; + } + } + } + if (password.length < 12) { + badLengthMessage += 'Le mot de passe doit faire au moins 12 caractères.'; + } + } + + return { badLengthMessage, missingDigitMessage }; +} From feb299577b27d7b9066f8f77b4615531a1a107bc Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Thu, 5 Dec 2024 11:05:52 +0100 Subject: [PATCH 11/12] update pages with non password message tools --- src/pages/inscription.tsx | 53 +++++++++-------------------------- src/pages/update-password.tsx | 52 ++++++++++++---------------------- 2 files changed, 32 insertions(+), 73 deletions(-) diff --git a/src/pages/inscription.tsx b/src/pages/inscription.tsx index 96550d025..38d3acf20 100644 --- a/src/pages/inscription.tsx +++ b/src/pages/inscription.tsx @@ -8,10 +8,12 @@ import { Box, Button, Checkbox, FormControlLabel, Grid, IconButton, InputAdornme import LanguageFilter from 'src/components/LanguageFilter'; import { Modal } from 'src/components/Modal'; import { ParentsCGU } from 'src/components/ParentsCGU'; +import PasswordMessagesDisplayer from 'src/components/PasswordMessagesDisplayer'; import { useLanguages } from 'src/services/useLanguages'; import { useUserRequests } from 'src/services/useUsers'; import ArrowBack from 'src/svg/arrow_back.svg'; import Logo from 'src/svg/logo_1village_famille.svg'; +import { invalidPasswordMessageBuilder } from 'src/utils/invalidPasswordMessageBuilder'; import { UserType } from 'types/user.type'; import type { UserForm } from 'types/user.type'; @@ -22,7 +24,7 @@ const Inscription = () => { const [lastname, setLastname] = useState(''); const [isLastnameValid, setIsLastnameValid] = useState(true); const [password, setPassword] = useState(''); - const passwordMessageRef = useRef(''); + const [passwordMessages, setPasswordMessages] = useState<{ badLength: string; missingDigits: string }>({ badLength: '', missingDigits: '' }); const [confirmPassword, setConfirmPassword] = useState(''); const [isPasswordValid, setIsPasswordValid] = useState(true); const [isPasswordMatch, setIsPasswordMatch] = useState(true); @@ -56,44 +58,13 @@ const Inscription = () => { const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; if (password !== '') { - passwordMessageRef.current = ''; - setIsPasswordValid(false); - - if (!password.match(/\d/)) { - passwordMessageRef.current = 'Le mot de passe doit contenir au moins un chiffre'; - } - if (!password.match(/[A-Z]/)) { - if (passwordMessageRef.current) { - passwordMessageRef.current += ' et '; - } - passwordMessageRef.current += ' une lettre majuscule'; - } - if (!password.match(/[\w\s]/)) { - if (passwordMessageRef.current) { - passwordMessageRef.current += ' et '; - } - passwordMessageRef.current += 'un caractère spécial'; - } - if (password.length < 12) { - if (passwordMessageRef.current) { - passwordMessageRef.current += ' et '; - } - passwordMessageRef.current += 'faire au moins 12 caractères'; - } - - if (!password.match(/\d/) || !password.match(/[A-Z]/) || password.length < 12) { - setIsPasswordValid(false); - } else { - setIsPasswordValid(true); - } + const invalidPasswordMessage = invalidPasswordMessageBuilder(password); + setPasswordMessages({ badLength: invalidPasswordMessage.badLengthMessage, missingDigits: invalidPasswordMessage.missingDigitMessage }); + setIsPasswordValid(invalidPasswordMessage.badLengthMessage === '' && invalidPasswordMessage.missingDigitMessage === ''); } if (confirmPassword !== '') { - if (password !== confirmPassword) { - setIsPasswordMatch(false); - } else { - setIsPasswordMatch(true); - } + setIsPasswordMatch(password === confirmPassword); } if (email !== '') { @@ -200,8 +171,6 @@ const Inscription = () => { setIsConfirmationPasswordVisible(!isConfirmationPasswordVisible); }; - const passwordMessage = passwordMessageRef.current; - return ( { }} type={isPasswordVisible === false ? 'password' : 'text'} error={isPasswordValid === false} - helperText={isPasswordValid === true ? '8 lettres minimum, une majuscule et un chiffre' : passwordMessage} + helperText={ + + } InputLabelProps={{ shrink: true }} onChange={(event) => { setPassword(event.target.value); diff --git a/src/pages/update-password.tsx b/src/pages/update-password.tsx index ac0e04d32..569eab604 100644 --- a/src/pages/update-password.tsx +++ b/src/pages/update-password.tsx @@ -1,15 +1,17 @@ import { useRouter } from 'next/router'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff'; import { Button, IconButton, InputAdornment, Link, TextField } from '@mui/material'; import { KeepRatio } from '../components/KeepRatio'; +import PasswordMessagesDisplayer from 'src/components/PasswordMessagesDisplayer'; import { useUserRequests } from 'src/services/useUsers'; import ArrowBack from 'src/svg/arrow_back.svg'; import Logo from 'src/svg/logo_1village_famille.svg'; import PelicoSouriant from 'src/svg/pelico/pelico-souriant.svg'; +import { invalidPasswordMessageBuilder } from 'src/utils/invalidPasswordMessageBuilder'; import type { UserUpdatePassword } from 'types/user.type'; const UpdatePassword = () => { @@ -17,7 +19,7 @@ const UpdatePassword = () => { const [isConfirmationPasswordTouched, setIsConfirmationPasswordTouched] = useState(false); const [password, setPassword] = useState(''); const [isSubmitSuccessfull, setIsSubmitSuccessfull] = useState(false); - const passwordMessageRef = useRef(''); + const [passwordMessages, setPasswordMessages] = useState<{ badLength: string; missingDigits: string }>({ badLength: '', missingDigits: '' }); const [confirmPassword, setConfirmPassword] = useState(''); const [isPasswordValid, setIsPasswordValid] = useState(false); const [isPasswordMatch, setIsPasswordMatch] = useState(false); @@ -35,38 +37,16 @@ const UpdatePassword = () => { useEffect(() => { if (password !== '') { - passwordMessageRef.current = ''; - setIsPasswordValid(false); - - if (!password.match(/\d/)) { - passwordMessageRef.current = 'Le mot de passe doit contenir un chiffre'; - } - if (!password.match(/[A-Z]/)) { - if (passwordMessageRef.current) { - passwordMessageRef.current += ' et '; - } - passwordMessageRef.current += ' une lettre majuscule'; - } - if (password.length < 8) { - if (passwordMessageRef.current) { - passwordMessageRef.current += ' et '; - } - passwordMessageRef.current += 'faire au moins 8 caractères'; - } - - if (!password.match(/\d/) || !password.match(/[A-Z]/) || password.length < 8) { - setIsPasswordValid(false); - } else { - setIsPasswordValid(true); - } + const invalidPasswordMessage = invalidPasswordMessageBuilder(password); + setPasswordMessages({ + badLength: invalidPasswordMessage.badLengthMessage, + missingDigits: invalidPasswordMessage.missingDigitMessage, + }); + setIsPasswordValid(invalidPasswordMessage.badLengthMessage.length === 0 && invalidPasswordMessage.missingDigitMessage.length === 0); } if (confirmPassword !== '') { - if (password !== confirmPassword) { - setIsPasswordMatch(false); - } else { - setIsPasswordMatch(true); - } + setIsPasswordMatch(password === confirmPassword); } setUpdatedUser({ @@ -103,8 +83,6 @@ const UpdatePassword = () => { setIsConfirmationPasswordVisible(!isConfirmationPasswordVisible); }; - const passwordMessage = passwordMessageRef.current; - return ( <>
@@ -186,7 +164,13 @@ const UpdatePassword = () => { }} type={!isPasswordVisible ? 'password' : 'text'} error={!isPasswordValid && isPasswordTouched} - helperText={isPasswordValid ? '8 lettres minimum, une majuscule et un chiffre' : passwordMessage} + helperText={ + + } InputLabelProps={{ shrink: true }} sx={{ width: '40ch', From daf209f938f1d8870c38f5fdd3fcf8ffe16fa5af Mon Sep 17 00:00:00 2001 From: Simon Nedjari Date: Thu, 5 Dec 2024 11:13:23 +0100 Subject: [PATCH 12/12] clean ts errors --- src/pages/inscription.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/inscription.tsx b/src/pages/inscription.tsx index 38d3acf20..ca48b6f93 100644 --- a/src/pages/inscription.tsx +++ b/src/pages/inscription.tsx @@ -1,5 +1,5 @@ import { useRouter } from 'next/router'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import Visibility from '@mui/icons-material/Visibility'; import VisibilityOff from '@mui/icons-material/VisibilityOff';