From c7aa506b425a37d3014af8b9ae1d1021867274b0 Mon Sep 17 00:00:00 2001 From: Marc ETOURNEAU Date: Wed, 13 Nov 2024 18:22:14 +0100 Subject: [PATCH] [MOT DE PASSE OUBLIE] Appelle l'API patch pour enregistrer le nouveau mot de passe --- mon-aide-cyber-ui/src/domaine/Lien.ts | 3 +- .../EcranMotDePasseOublie.tsx | 4 +- .../ecran-mot-de-passe-oublie.scss | 21 ++- .../FormulaireMotDePasseOublie.tsx | 12 +- .../EcranReinitialiserMotDePasse.tsx | 10 +- .../ecran-reinitialiser-mot-de-passe.scss | 28 +++- .../FormulaireReinitialiserMotDePasse.tsx | 127 +++++++++++++++- ...mulaireReinitialiserMotDePasseConnecte.tsx | 73 ++++++++- ...ucteurFormulaireReinitialiserMotDePasse.ts | 140 ++++++++++++++++++ ...rFormulaireReinitialiserMotDePasse.spec.ts | 48 ++++++ 10 files changed, 441 insertions(+), 25 deletions(-) create mode 100644 mon-aide-cyber-ui/src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse.ts create mode 100644 mon-aide-cyber-ui/test/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse.spec.ts diff --git a/mon-aide-cyber-ui/src/domaine/Lien.ts b/mon-aide-cyber-ui/src/domaine/Lien.ts index 4810965ee..f773392c9 100644 --- a/mon-aide-cyber-ui/src/domaine/Lien.ts +++ b/mon-aide-cyber-ui/src/domaine/Lien.ts @@ -34,4 +34,5 @@ export type Action = | 'se-connecter' | 'se-deconnecter' | 'afficher-statistiques' - | 'reinitialisation-mot-de-passe'; + | 'reinitialisation-mot-de-passe' + | 'reinitialiser-mot-de-passe'; diff --git a/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/EcranMotDePasseOublie.tsx b/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/EcranMotDePasseOublie.tsx index adbf71c5f..dd00c635e 100644 --- a/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/EcranMotDePasseOublie.tsx +++ b/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/EcranMotDePasseOublie.tsx @@ -6,8 +6,8 @@ import { TypographieH3 } from '../../../composants/communs/typographie/Typograph export const EcranMotDePasseOublie = () => { return (
-
-
+
+
Réinitialisation de votre mot de passe diff --git a/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/ecran-mot-de-passe-oublie.scss b/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/ecran-mot-de-passe-oublie.scss index 310ff0952..65f79d4e9 100644 --- a/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/ecran-mot-de-passe-oublie.scss +++ b/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/ecran-mot-de-passe-oublie.scss @@ -12,7 +12,6 @@ height: 800px; - .formulaire-colonne-gauche { grid-area: formulaire; padding: 3rem 2.5rem; @@ -52,8 +51,24 @@ .actions { display: flex; - justify-content: flex-start; - gap: 1rem; + flex-direction: column; + align-items: center; + gap: .5rem; + + @include a-partir-de(lg) { + flex-direction: row; + align-items: center; + justify-content: flex-end; + gap: 1rem; + } + + button { + width: 100%; + + @include a-partir-de(lg) { + width: auto; + } + } } } } diff --git a/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/formulaire-mot-de-passe-oublie/FormulaireMotDePasseOublie.tsx b/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/formulaire-mot-de-passe-oublie/FormulaireMotDePasseOublie.tsx index d8a1fd7cf..93134c471 100644 --- a/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/formulaire-mot-de-passe-oublie/FormulaireMotDePasseOublie.tsx +++ b/mon-aide-cyber-ui/src/domaine/vitrine/mot-de-passe-oublie/formulaire-mot-de-passe-oublie/FormulaireMotDePasseOublie.tsx @@ -90,7 +90,6 @@ export const FormulaireMotDePasseOublie = ({ ); const soumetFormulaire = (e: FormEvent) => { e.preventDefault(); - console.log('soumet le formulaire', e); surSoumission(etatFormulaire.email); }; @@ -126,6 +125,11 @@ export const FormulaireMotDePasseOublie = ({ adresseElectroniqueSaisie(e.target.value) ) } + onChange={(e) => + declencheActionFormulaire( + adresseElectroniqueSaisie(e.target.value) + ) + } /> {etatFormulaire.erreurs?.adresseElectronique ? (

@@ -139,7 +143,11 @@ export const FormulaireMotDePasseOublie = ({ l’adresse mail indiquée afin de créer un nouveau mot de passe.

- +
+ +
); }; diff --git a/mon-aide-cyber-ui/src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/FormulaireReinitialiserMotDePasseConnecte.tsx b/mon-aide-cyber-ui/src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/FormulaireReinitialiserMotDePasseConnecte.tsx index 1aa3a16a8..b8aeda653 100644 --- a/mon-aide-cyber-ui/src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/FormulaireReinitialiserMotDePasseConnecte.tsx +++ b/mon-aide-cyber-ui/src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/FormulaireReinitialiserMotDePasseConnecte.tsx @@ -1,9 +1,80 @@ import { FormulaireReinitialiserMotDePasse } from './FormulaireReinitialiserMotDePasse.tsx'; +import { useNavigationMAC } from '../../../../fournisseurs/hooks.ts'; +import { useMACAPI } from '../../../../fournisseurs/api/useMACAPI.ts'; +import { useRecupereContexteNavigation } from '../../../../hooks/useRecupereContexteNavigation.ts'; +import { useMutation } from '@tanstack/react-query'; +import { MoteurDeLiens } from '../../../MoteurDeLiens.ts'; +import { constructeurParametresAPI } from '../../../../fournisseurs/api/ConstructeurParametresAPI.ts'; +import { useNavigate } from 'react-router-dom'; +import { Toast } from '../../../../composants/communs/Toasts/Toast.tsx'; +export type CorpsReinitialiserMotDePasse = { + token: string; + motDePasse: string; + confirmationMotDePasse: string; +}; export const FormulaireReinitialiserMotDePasseConnecte = ({ token, }: { token: string; }) => { - return ; + const navigationMAC = useNavigationMAC(); + const macAPI = useMACAPI(); + const navigate = useNavigate(); + + useRecupereContexteNavigation( + 'reinitialisation-mot-de-passe:reinitialiser-mot-de-passe' + ); + + const { mutate, isError } = useMutation({ + mutationKey: ['changer-mot-de-passe'], + mutationFn: ({ + motDePasse, + confirmationMotDePasse, + }: { + motDePasse: string; + confirmationMotDePasse: string; + }) => { + if (!token || token.length === 0 || token === '') + throw new Error( + 'Une erreur est survenue lors de la demande de réinitialisation de mot de passe' + ); + if (!motDePasse) Promise.reject('Aucun mot de passe renseigné !'); + + const actionSoumettre = new MoteurDeLiens( + navigationMAC.etat + ).trouveEtRenvoie('reinitialiser-mot-de-passe'); + + if (!actionSoumettre) + throw new Error( + 'Une erreur est survenue lors de la demande de réinitialisation de mot de passe' + ); + + return macAPI.execute( + constructeurParametresAPI() + .url(actionSoumettre.url) + .methode(actionSoumettre.methode!) + .corps({ + token: token, + motDePasse: motDePasse, + confirmationMotDePasse: confirmationMotDePasse, + }) + .construis(), + (corps) => corps + ); + }, + onSuccess: () => { + navigate('/aidant/tableau-de-bord'); + }, + }); + + if (isError) + return ( + + ); + + return ; }; diff --git a/mon-aide-cyber-ui/src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse.ts b/mon-aide-cyber-ui/src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse.ts new file mode 100644 index 000000000..bfb10fff3 --- /dev/null +++ b/mon-aide-cyber-ui/src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse.ts @@ -0,0 +1,140 @@ +import { construisErreurSimple, ErreurFormulaire } from '../../../../composants/alertes/Erreurs.tsx'; + +const supprimeObjetErreursSiFormulaireValide = ( + etatCourant: EtatFormulaireReinitialiserMotDePasse, +) => { + if (etatCourant.erreurs && Object.keys(etatCourant.erreurs).length === 0) { + delete etatCourant['erreurs']; + } +}; + +type ChampNouvelEtat = { [clef: string]: boolean | string }; + +type ParametreGenerationNouvelEtat = { + champ: keyof ErreursFormulaireReinitialiserMotDePasse; + champValide: () => boolean; + elementsFormulairesValides: () => boolean; + construisErreurChamp: ( + bool: boolean, + ) => { [p: string]: ErreurFormulaire } | undefined; + ajouteAuNouvelEtat: () => ChampNouvelEtat; +}; + +const regenereEtatFormulaire = ( + etatPrecedent: EtatFormulaireReinitialiserMotDePasse, + { + champ, + champValide, + elementsFormulairesValides, + construisErreurChamp, + ajouteAuNouvelEtat, + }: ParametreGenerationNouvelEtat, +): EtatFormulaireReinitialiserMotDePasse => { + const etatCourant = { ...etatPrecedent }; + const estChampValide = champValide(); + + if (estChampValide) { + delete etatCourant.erreurs?.[champ]; + supprimeObjetErreursSiFormulaireValide(etatCourant); + } + + return { + ...etatCourant, + pretPourEnvoi: estChampValide && elementsFormulairesValides(), + ...(!estChampValide && { + erreurs: { + ...etatCourant.erreurs, + ...construisErreurChamp(estChampValide), + }, + }), + ...Object.entries(ajouteAuNouvelEtat()).reduce((prev, [clef, valeur]) => { + prev[clef] = valeur; + return prev; + }, {} as ChampNouvelEtat), + }; +}; + +const construisErreurConfirmationMotDePasse = (motDePasseValide: boolean) => + !motDePasseValide + ? construisErreurSimple( + 'confirmationMotDePasse', + 'Les deux mots de passe doivent correspondre', + ) + : undefined; + +type ErreursFormulaireReinitialiserMotDePasse = { + motDePasse?: ErreurFormulaire; + confirmationMotDePasse?: ErreurFormulaire; +}; + +export type EtatFormulaireReinitialiserMotDePasse = { + motDePasse: string; + confirmationMotDePasse: string; + pretPourEnvoi: boolean; + erreurs?: ErreursFormulaireReinitialiserMotDePasse; +}; + +enum TypeActionFormulaireReinitialiserMotDePasse { + MOT_DE_PASSE_SAISI = 'MOT_DE_PASSE_SAISI', + CONFIRMATION_MOT_DE_PASSE_SAISI = 'CONFIRMATION_MOT_DE_PASSE_SAISI', +} + +type ActionFormulaireReinitialiserMotDePasse = { + type: TypeActionFormulaireReinitialiserMotDePasse.MOT_DE_PASSE_SAISI; + motDePasse: string; +} | { + type: TypeActionFormulaireReinitialiserMotDePasse.CONFIRMATION_MOT_DE_PASSE_SAISI; + confirmationMotDePasse: string; +}; + +export const reducteurFormulaireReinitialiserMotDePasse = ( + etat: EtatFormulaireReinitialiserMotDePasse, + action: ActionFormulaireReinitialiserMotDePasse, +): EtatFormulaireReinitialiserMotDePasse => { + switch (action.type) { + case TypeActionFormulaireReinitialiserMotDePasse.MOT_DE_PASSE_SAISI: { + const etatCourant = { ...etat }; + + return regenereEtatFormulaire(etatCourant, { + ajouteAuNouvelEtat: () => ({ motDePasse: action.motDePasse }), + champ: 'motDePasse', + champValide: () => !!action.motDePasse, + construisErreurChamp: (_bool: boolean) => undefined, + elementsFormulairesValides: () => action.motDePasse.trim().length > 0, + }); + } + case TypeActionFormulaireReinitialiserMotDePasse.CONFIRMATION_MOT_DE_PASSE_SAISI: { + const etatCourant = { ...etat }; + + return regenereEtatFormulaire(etatCourant, { + ajouteAuNouvelEtat: () => ({ confirmationMotDePasse: action.confirmationMotDePasse }), + champ: 'confirmationMotDePasse', + champValide: () => !!action.confirmationMotDePasse && action.confirmationMotDePasse === etat.motDePasse, + construisErreurChamp: (bool: boolean) => + construisErreurConfirmationMotDePasse(bool), + elementsFormulairesValides: () => etat.motDePasse === action.confirmationMotDePasse, + }); + } + } +}; + +export const motDePasseSaisi = ( + motDePasse: string, +): ActionFormulaireReinitialiserMotDePasse => ({ + motDePasse, + type: TypeActionFormulaireReinitialiserMotDePasse.MOT_DE_PASSE_SAISI, +}); + +export const confirmationMotDePasseSaisi = ( + confirmationMotDePasse: string, +): ActionFormulaireReinitialiserMotDePasse => ({ + confirmationMotDePasse: confirmationMotDePasse, + type: TypeActionFormulaireReinitialiserMotDePasse.CONFIRMATION_MOT_DE_PASSE_SAISI, +}); + +export const initialiseFormulaireReinitialiserMotDePasse = + (): EtatFormulaireReinitialiserMotDePasse => ({ + motDePasse: '', + confirmationMotDePasse: '', + pretPourEnvoi: false, + }); diff --git a/mon-aide-cyber-ui/test/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse.spec.ts b/mon-aide-cyber-ui/test/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse.spec.ts new file mode 100644 index 000000000..a9dc90ae9 --- /dev/null +++ b/mon-aide-cyber-ui/test/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse.spec.ts @@ -0,0 +1,48 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import { confirmationMotDePasseSaisi, EtatFormulaireReinitialiserMotDePasse, initialiseFormulaireReinitialiserMotDePasse, motDePasseSaisi, reducteurFormulaireReinitialiserMotDePasse } from '../../../../../src/domaine/vitrine/reinitialiser-mot-de-passe/formulaire-reinitialiser-mot-de-passe/reducteurFormulaireReinitialiserMotDePasse'; + +describe('Formulaire de modification du mot de passe, suite à une demande dé réinitialisation', () => { + let etatInitial: EtatFormulaireReinitialiserMotDePasse = {} as EtatFormulaireReinitialiserMotDePasse; + + beforeEach(() => { + etatInitial = initialiseFormulaireReinitialiserMotDePasse(); + }); + + describe('Le mot de passe est modifié', () => { + it('Le prend en compte dans le formulaire', () => { + const etat = reducteurFormulaireReinitialiserMotDePasse(etatInitial, motDePasseSaisi('123456789')); + + expect(etat).toStrictEqual({ + motDePasse: '123456789', + confirmationMotDePasse: '', + pretPourEnvoi: true, + }); + }); + + it('La confirmation du mot de passe est prise en compte dans le formulaire', () => { + const etat = reducteurFormulaireReinitialiserMotDePasse(etatInitial, confirmationMotDePasseSaisi('123456789')); + + expect(etat).toStrictEqual({ + motDePasse: '', + confirmationMotDePasse: '123456789', + erreurs: { + confirmationMotDePasse: 'Les deux mots de passe doivent correspondre', + }, + pretPourEnvoi: false, + }); + }); + + it('le formulaire est valide car les deux mots de passes correspondent', () => { + const etat = reducteurFormulaireReinitialiserMotDePasse({ + ...etatInitial, + confirmationMotDePasse: '123456789', + }, motDePasseSaisi('123456789')); + + expect(etat).toStrictEqual({ + motDePasse: '123456789', + confirmationMotDePasse: '123456789', + pretPourEnvoi: true, + }); + }); + }); +}); \ No newline at end of file