Skip to content

Commit

Permalink
✨mon-pix: use PixCode on verification code form
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreiaPena committed Feb 6, 2025
1 parent 08dfb53 commit 0a4a25e
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 207 deletions.
96 changes: 96 additions & 0 deletions mon-pix/app/components/certification-verification-code-form.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import PixBackgroundHeader from '@1024pix/pix-ui/components/pix-background-header';
import PixBlock from '@1024pix/pix-ui/components/pix-block';
import PixButton from '@1024pix/pix-ui/components/pix-button';
import PixCode from '@1024pix/pix-ui/components/pix-code';
import PixNotificationAlert from '@1024pix/pix-ui/components/pix-notification-alert';
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';

export default class CertificationVerificationCodeForm extends Component {
@service intl;

@tracked certificateVerificationCode = null;
@tracked errorMessage = null;
@tracked status = 'default';

codeRegex = /^P-[0-9A-Z]{8}$/i;

@action
handleVerificationCodeInput(event) {
this.certificateVerificationCode = event.target.value;
}

@action
clearErrors() {
this.errorMessage = null;
this.status = 'default';
this.args.clearErrors();
}

get isVerificationCodeValid() {
return this.codeRegex.test(this.certificateVerificationCode);
}

@action
async checkCertificate(event) {
event.preventDefault();
this.clearErrors();

if (!this.certificateVerificationCode) {
this.errorMessage = this.intl.t('pages.fill-in-certificate-verification-code.errors.missing-code');
this.status = 'error';
return;
}

if (!this.isVerificationCodeValid) {
this.errorMessage = this.intl.t('pages.fill-in-certificate-verification-code.errors.wrong-format');
this.status = 'error';
return;
}

this.args.checkCertificate(this.certificateVerificationCode);
}

<template>
<PixBackgroundHeader id="main">
<PixBlock class="fill-in-certificate-verification-code">
<h1 class="fill-in-certificate-verification-code__title">
{{t "pages.fill-in-certificate-verification-code.first-title"}}
</h1>

<p class="fill-in-certificate-verification-code__description">
{{t "pages.fill-in-certificate-verification-code.description"}}
</p>

<form class="fill-in-certificate-verification-code__form" autocomplete="off">
<PixCode
@length="10"
@requiredLabel={{t "common.forms.mandatory"}}
@subLabel={{t "pages.fill-in-certificate-verification-code.sub-label"}}
@value={{this.certificateVerificationCode}}
@validationStatus={{this.status}}
@errorMessage={{this.errorMessage}}
{{on "keyup" this.clearErrors}}
{{on "input" this.handleVerificationCodeInput}}
>
<:label>{{t "pages.fill-in-certificate-verification-code.label"}}</:label>
</PixCode>

<PixButton @type="submit" @triggerAction={{this.checkCertificate}} class="form__actions">
{{t "pages.fill-in-certificate-verification-code.verify"}}
</PixButton>

{{#if @apiErrorMessage}}
<PixNotificationAlert @type="error" @withIcon={{true}}>
{{@apiErrorMessage}}
</PixNotificationAlert>
{{/if}}
</form>
</PixBlock>
</PixBackgroundHeader>
</template>
}
55 changes: 12 additions & 43 deletions mon-pix/app/controllers/fill-in-certificate-verification-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,73 +2,42 @@ import Controller from '@ember/controller';
import { action } from '@ember/object';
import { service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import ENV from 'mon-pix/config/environment';

export default class FillInCertificateVerificationCode extends Controller {
@service store;
@service intl;
@service router;

certificateVerificationCode = null;

codeRegex = /^P-[0-9A-Z]{8}$/i;

@tracked
errorMessage = null;

@tracked
showNotFoundCertificationErrorMessage = false;

@action
handleVerificationCodeInput(event) {
this.certificateVerificationCode = event.target.value;
}
@tracked apiErrorMessage = null;

@action
async checkCertificate(e) {
e.preventDefault();
this.clearErrors();

if (!this.certificateVerificationCode) {
this.errorMessage = this.intl.t('pages.fill-in-certificate-verification-code.errors.missing-code');
return;
}

if (!this.isVerificationCodeValid()) {
this.errorMessage = this.intl.t('pages.fill-in-certificate-verification-code.errors.wrong-format');
return;
}

async checkCertificate(certificateVerificationCode) {
try {
const certification = await this.store.queryRecord('certification', {
verificationCode: this.certificateVerificationCode.toUpperCase(),
verificationCode: certificateVerificationCode.toUpperCase(),
});
this.router.transitionTo('shared-certification', certification);
return;
} catch (error) {
this.onVerificateCertificationCodeError(error);
}
}

@action
async clearErrors() {
this.apiErrorMessage = null;
}

onVerificateCertificationCodeError(error) {
if (error.errors) {
const { status } = error.errors[0];
if (status === '404') {
this.showNotFoundCertificationErrorMessage = true;
this.apiErrorMessage = this.intl.t('pages.fill-in-certificate-verification-code.errors.not-found');
} else {
throw error;
this.apiErrorMessage = error;
}
} else {
throw error;
this.apiErrorMessage = this.intl.t(ENV.APP.API_ERROR_MESSAGES.INTERNAL_SERVER_ERROR.I18N_KEY);
}
}

isVerificationCodeValid() {
return this.codeRegex.test(this.certificateVerificationCode);
}

@action
clearErrors() {
this.errorMessage = null;
this.showNotFoundCertificationErrorMessage = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,36 @@
@use 'pix-design-tokens/typography';

.fill-in-certificate-verification-code {
max-width: max-content;
margin: 0 auto 32px;
padding: 48px 24px;
text-align: center;
width: 75%;
margin: 0 auto var(--pix-spacing-8x);
padding: var(--pix-spacing-12x) var(--pix-spacing-6x);

@extend %pix-body-m;

@include breakpoints.device-is('tablet') {
padding: 48px 60px;
padding: var(--pix-spacing-12x) 60px;
}

&__title {
text-align: center;

@extend %pix-title-m;
}

&__description {
max-width: 500px;
margin: var(--pix-spacing-6x) 0;
color: var(--pix-neutral-500);
line-height: 22px;
line-height: 1.375rem;
text-align: center;
}

&__form {
display: flex;
flex-direction: column;
align-items: center;

.form__input-and-label {
display: flex;
flex-direction: column;

label {
margin-bottom: 6px;
line-height: 22px;
letter-spacing: 0.15px;
text-align: left;

@extend %pix-body-s;
}

input {
width: 15.3ch;
}

@media all and (-ms-high-contrast: none) {
input {
width: 17.4ch;
}
}
}

.form__actions {
margin: var(--pix-spacing-6x) 0;
}

.form__error--validation {
color: var(--pix-error-700);

@extend %pix-body-s;
}
}
}
50 changes: 5 additions & 45 deletions mon-pix/app/templates/fill-in-certificate-verification-code.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,10 @@

<Global::AppLayout>
<main role="main">
<PixBackgroundHeader id="main">
<PixBlock class="fill-in-certificate-verification-code">
<h1 class="fill-in-certificate-verification-code__title">
{{t "pages.fill-in-certificate-verification-code.first-title"}}
</h1>

<p class="fill-in-certificate-verification-code__description">
{{t "pages.fill-in-certificate-verification-code.description"}}
</p>

<form class="fill-in-certificate-verification-code__form" autocomplete="off">
<div class="form__input-and-label">
<label for="certificate-verification-code">
{{t "pages.fill-in-certificate-verification-code.label"}}
</label>
<PixInput
required
class="input-code"
type="text"
id="certificate-verification-code"
@value={{this.certificateVerificationCode}}
maxlength="10"
{{on "keyup" this.clearErrors}}
{{on "input" this.handleVerificationCodeInput}}
/>
</div>

<PixButton @type="submit" @triggerAction={{this.checkCertificate}} class="form__actions">
{{t "pages.fill-in-certificate-verification-code.verify"}}
</PixButton>

{{#if this.errorMessage}}
<div class="form__error--validation" aria-live="polite">
{{this.errorMessage}}
</div>
{{/if}}

{{#if this.showNotFoundCertificationErrorMessage}}
<PixNotificationAlert @type="error" @withIcon={{true}}>
{{t "pages.fill-in-certificate-verification-code.errors.not-found"}}
</PixNotificationAlert>
{{/if}}
</form>
</PixBlock>
</PixBackgroundHeader>
<CertificationVerificationCodeForm
@checkCertificate={{this.checkCertificate}}
@apiErrorMessage={{this.apiErrorMessage}}
@clearErrors={{this.clearErrors}}
/>
</main>
</Global::AppLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,11 @@ module('Acceptance | Certificate verification', function (hooks) {
setupMirage(hooks);
setupIntl(hooks);

test('display a verification section', async function (assert) {
// given & when
const screen = await visit('/verification-certificat');

// then
assert.dom(screen.getByRole('heading', { name: 'Vérifier un certificat Pix' })).exists();
assert
.dom(
screen.getByText(
'La certification Pix atteste d’un niveau de maîtrise des compétences numériques : saisissez ci-après le "code de vérification" du certificat Pix à vérifier.',
),
)
.exists();
assert.dom(screen.getByRole('textbox', { name: 'Code de vérification (P-XXXXXXXX)' })).exists();
assert.dom(screen.getByRole('button', { name: 'Vérifier le certificat' })).exists();
});

module('when certificate verification code is valid', function () {
test('redirects to certificate details page', async function (assert) {
// given
const screen = await visit('/verification-certificat');
await fillIn(screen.getByRole('textbox', { name: 'Code de vérification (P-XXXXXXXX)' }), 'P-123VALID');
await fillIn(screen.getByRole('textbox', { name: 'Code de vérification * Exemple: P-XXXXXXXX' }), 'P-123VALID');

// when
await click(screen.getByRole('button', { name: 'Vérifier le certificat' }));
Expand All @@ -46,26 +29,14 @@ module('Acceptance | Certificate verification', function (hooks) {
test('does not redirect to certificate details page', async function (assert) {
// given
const screen = await visit('/verification-certificat');
await fillIn(screen.getByRole('textbox', { name: 'Code de vérification (P-XXXXXXXX)' }), 'P-12345678');
await fillIn(screen.getByRole('textbox', { name: 'Code de vérification * Exemple: P-XXXXXXXX' }), 'P-12345678');

// when
await click(screen.getByRole('button', { name: 'Vérifier le certificat' }));

// then
assert.strictEqual(currentURL(), '/verification-certificat');
});

test('shows error message', async function (assert) {
// given
const screen = await visit('/verification-certificat');
await fillIn(screen.getByRole('textbox', { name: 'Code de vérification (P-XXXXXXXX)' }), 'P-12345678');

// when
await click(screen.getByRole('button', { name: 'Vérifier le certificat' }));

// then
assert.dom(screen.getByText("Il n'y a pas de certificat Pix correspondant.")).exists();
});
});

module('when user visits /partage-certificat/200 directly', function () {
Expand Down
Loading

0 comments on commit 0a4a25e

Please sign in to comment.