Skip to content

Commit

Permalink
[FEATURE] Ajouter le nouveau design du formulaire de réinitialisation…
Browse files Browse the repository at this point in the history
… de mot de passe (PIX-14113)

 #10370
  • Loading branch information
pix-service-auto-merge authored Oct 23, 2024
2 parents 09b6e14 + 3739461 commit dc68232
Show file tree
Hide file tree
Showing 21 changed files with 606 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { on } from '@ember/modifier';
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { t } from 'ember-intl';

import PasswordChecklist from './password-checklist';

Expand Down Expand Up @@ -34,7 +33,7 @@ export default class NewPasswordInput extends Component {
autocomplete="new-password"
...attributes
>
<:label>{{t "components.authentication.new-password-input.label"}}</:label>
<:label>{{yield to="label"}}</:label>
</PixInputPassword>

<PasswordChecklist id="password-checklist" @rules={{@rules}} @value={{this.value}} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { tracked } from '@glimmer/tracking';

import isPasswordValid from '../../../utils/password-validator';

export class PasswordResetFormValidation {
passwordField = new PasswordField();

constructor(intl) {
this.intl = intl;
}

get isValid() {
return this.passwordField.status !== 'error';
}

validateField(value) {
const isValidInput = this.passwordField.validate(value);
const status = isValidInput ? 'success' : 'error';

this.passwordField.status = status;
this.passwordField.errorMessage = status === 'error' ? this.intl.t('common.validation.password.error') : null;
}
}

class PasswordField {
@tracked status = 'default';
@tracked errorMessage = null;

validate(value) {
return isPasswordValid(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import PixButton from '@1024pix/pix-ui/components/pix-button';
import PixMessage from '@1024pix/pix-ui/components/pix-message';
import { on } from '@ember/modifier';
import { action } from '@ember/object';
import { service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { t } from 'ember-intl';
import get from 'lodash/get';

import { PASSWORD_RULES } from '../../../utils/password-validator.js';
import NewPasswordInput from '../new-password-input';
import { PasswordResetFormValidation } from './password-reset-form-validation';

const HTTP_ERROR_MESSAGES = {
400: 'common.validation.password.error',
403: 'components.authentication.password-reset-form.errors.forbidden',
404: 'components.authentication.password-reset-form.errors.expired-demand',
default: 'common.api-error-messages.internal-server-error',
};

export default class PasswordResetForm extends Component {
@service intl;

@tracked hasSucceeded = false;
@tracked isLoading = false;
@tracked validation = new PasswordResetFormValidation(this.intl);
@tracked globalErrorMessage;

@action
handleInputChange(event) {
const { user } = this.args;
user.password = event.target.value;
this.validation.validateField(user.password);
}

@action
async handleResetPassword(event) {
if (event) event.preventDefault();

if (!this.validation.isValid) return;

this.hasSucceeded = false;
this.globalErrorMessage = null;
this.isLoading = true;
try {
const { user, temporaryKey } = this.args;
await user.save({ adapterOptions: { updatePassword: true, temporaryKey } });
user.password = null;
this.hasSucceeded = true;
} catch (response) {
const status = get(response, 'errors[0].status');
this.globalErrorMessage = this.intl.t(HTTP_ERROR_MESSAGES[status] || HTTP_ERROR_MESSAGES['default']);
} finally {
this.isLoading = false;
}
}

<template>
<form class="password-reset-form" type="submit" {{on "submit" this.handleResetPassword}}>
{{#if this.globalErrorMessage}}
<PixMessage @type="error" @withIcon={{true}} role="alert">
{{this.globalErrorMessage}}
</PixMessage>
{{/if}}

<p class="password-reset-form__mandatory-fields-message">
{{t "common.form.mandatory-all-fields"}}
</p>

<NewPasswordInput
@id="password"
class="password-reset-form__password-input"
name="password"
{{on "change" this.handleInputChange}}
@validationStatus={{this.validation.password.status}}
@errorMessage={{this.validation.password.errorMessage}}
@rules={{PASSWORD_RULES}}
required
>
<:label>{{t "components.authentication.password-reset-form.fields.password.label"}}</:label>
</NewPasswordInput>

<PixButton class="password-reset-form__submit-button" @isLoading={{this.isLoading}} @size="large" @type="submit">
{{t "components.authentication.password-reset-form.actions.submit"}}
</PixButton>
</form>
</template>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.password-reset-form {
display: flex;
flex-direction: column;
gap: var(--pix-spacing-4x);

input {
width: 100%;
padding: var(--pix-spacing-3x);
}

&__mandatory-fields-message {
@extend %pix-body-xs;

color: var(--pix-neutral-500);
}

&__password-input, &__submit-button {
width: 100%;
}
}
2 changes: 1 addition & 1 deletion mon-pix/app/components/authentication/signin-form.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default class SigninForm extends Component {
<template>
<form {{on "submit" this.signin}} class="signin-form">
{{#if this.globalError}}
<PixMessage @type="error" @withIcon="true" role="alert">
<PixMessage @type="error" @withIcon={{true}} role="alert">
{{t this.globalError.key this.globalError.values}}
</PixMessage>
{{/if}}
Expand Down
4 changes: 3 additions & 1 deletion mon-pix/app/components/authentication/signup-form/index.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ export default class SignupForm extends Component {
@errorMessage={{this.validation.password.errorMessage}}
@rules={{PASSWORD_RULES}}
required
/>
>
<:label>{{t "common.password"}}</:label>
</NewPasswordInput>

<CguCheckbox
@id="cgu"
Expand Down
10 changes: 10 additions & 0 deletions mon-pix/app/controllers/reset-password.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Controller from '@ember/controller';
import { service } from '@ember/service';

export default class ResetPasswordController extends Controller {
@service featureToggles;

get isNewAuthenticationDesignEnabled() {
return this.featureToggles.featureToggles.isNewAuthenticationDesignEnabled;
}
}
1 change: 1 addition & 0 deletions mon-pix/app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ of an adaptative/mobile-first approach — refactoring is welcome here */
@import 'authentication/oidc-provider-selector';
@import 'authentication/other-authentication-providers';
@import 'authentication/password-reset-demand';
@import 'authentication/password-reset-form/password-reset-form';
@import 'authentication/signin-form';
@import 'authentication/signup-form';
@import 'authentication/sso-selection-form';
Expand Down
24 changes: 21 additions & 3 deletions mon-pix/app/templates/reset-password.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
{{page-title (t "pages.reset-password.title")}}
<div class="sign-form-page">
<ResetPasswordForm @user={{@model.user}} @temporaryKey={{@model.temporaryKey}} />
</div>

{{#if this.isNewAuthenticationDesignEnabled}}
<AuthenticationLayout class="signup-page-layout">
<:header>
<PixButtonLink @variant="secondary" @route="authentication.login">
{{t "pages.sign-up.actions.login"}}
</PixButtonLink>
</:header>
<:content>
<h1 class="pix-title-m">{{t "pages.reset-password.first-title"}}</h1>
<Authentication::PasswordResetForm::PasswordResetForm
@temporaryKey={{@model.temporaryKey}}
@user={{@model.user}}
/>
</:content>
</AuthenticationLayout>
{{else}}
<div class="sign-form-page">
<ResetPasswordForm @user={{@model.user}} @temporaryKey={{@model.temporaryKey}} />
</div>
{{/if}}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { t } from 'ember-intl/test-support';
import { setupApplicationTest } from 'ember-qunit';
import { module, test } from 'qunit';

import { clickByLabel } from '../helpers/click-by-label';
import setupIntl from '../helpers/setup-intl';
import { clickByLabel } from '../../helpers/click-by-label';
import setupIntl from '../../helpers/setup-intl';

module('Acceptance | Password reset demand form', function (hooks) {
setupApplicationTest(hooks);
Expand Down
Loading

0 comments on commit dc68232

Please sign in to comment.