From 83187dbf57b6e97d3ff65bd485b80357c8fb55b0 Mon Sep 17 00:00:00 2001 From: AndreiaPena Date: Wed, 5 Feb 2025 18:33:09 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8mon-pix:=20use=20PixCode=20on=20candid?= =?UTF-8?q?ate=20code=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/components/certification-starter.hbs | 62 ++++---- .../app/components/certification-starter.js | 34 +++-- .../components/_certification-starter.scss | 144 +++--------------- .../components/certification-starter-test.js | 73 ++++++++- .../components/certifications/start-test.gjs | 2 +- mon-pix/translations/en.json | 1 + mon-pix/translations/es.json | 1 + mon-pix/translations/fr.json | 1 + mon-pix/translations/nl.json | 1 + 9 files changed, 144 insertions(+), 175 deletions(-) diff --git a/mon-pix/app/components/certification-starter.hbs b/mon-pix/app/components/certification-starter.hbs index 6372c4989eb..934ee6efb19 100644 --- a/mon-pix/app/components/certification-starter.hbs +++ b/mon-pix/app/components/certification-starter.hbs @@ -1,5 +1,5 @@
-

{{t "pages.certification-start.first-title"}}

+

{{t "pages.certification-start.first-title"}}

{{#if @certificationCandidateSubscription.hasSubscription}} {{#unless @certificationCandidateSubscription.isV3CoreOnly}} @@ -52,39 +52,37 @@ {{/unless}} {{/if}} - -
-
- - {{#if this.errorMessage}} -
-

{{this.errorMessage}}

- {{#if this.technicalErrorInfo}} -
- {{t "pages.certification-start.error-messages.unknown.summary-label"}} -

{{this.technicalErrorInfo}}

-
- {{/if}} -
- {{/if}} -
-
+ + + {{t "pages.certification-start.access-code"}} + + + + {{#if this.apiErrorMessage}} + + {{this.apiErrorMessage}} + + {{/if}} + {{#if this.technicalErrorInformation}} +
+ {{t "pages.certification-start.error-messages.unknown.summary-label"}} +

{{this.technicalErrorInformation}}

+
+ {{/if}} - - {{t "pages.certification-start.actions.submit"}} - -
+ + {{t "pages.certification-start.actions.submit"}} +
diff --git a/mon-pix/app/components/certification-starter.js b/mon-pix/app/components/certification-starter.js index dde13fe5d29..43e1f87a00e 100644 --- a/mon-pix/app/components/certification-starter.js +++ b/mon-pix/app/components/certification-starter.js @@ -12,10 +12,12 @@ export default class CertificationStarter extends Component { @service pixCompanion; @tracked inputAccessCode = ''; - @tracked errorMessage = null; - @tracked technicalErrorInfo = ''; + @tracked apiErrorMessage = null; + @tracked validationErrorMessage = null; + @tracked technicalErrorInformation = null; @tracked classNames = []; @tracked certificationCourse = null; + @tracked validationStatus = 'default'; get accessCode() { return this.inputAccessCode.toUpperCase(); @@ -41,12 +43,22 @@ export default class CertificationStarter extends Component { this.inputAccessCode = event.target.value; } + @action + clearErrorMessage() { + this.apiErrorMessage = null; + this.validationStatus = 'default'; + this.validationErrorMessage = null; + this.technicalErrorInformation = null; + } + @action async submit(e) { e.preventDefault(); - this.errorMessage = null; + this.clearErrorMessage(); + if (!this.accessCode) { - this.errorMessage = this.intl.t('pages.certification-start.error-messages.access-code-error'); + this.validationStatus = 'error'; + this.validationErrorMessage = this.intl.t('pages.certification-start.error-messages.missing-code'); return; } @@ -63,22 +75,24 @@ export default class CertificationStarter extends Component { newCertificationCourse.deleteRecord(); const statusCode = error.errors?.[0]?.status; if (statusCode === '404') { - this.errorMessage = this.intl.t('pages.certification-start.error-messages.access-code-error'); + this.apiErrorMessage = this.intl.t('pages.certification-start.error-messages.access-code-error'); } else if (statusCode === '412') { - this.errorMessage = this.intl.t('pages.certification-start.error-messages.session-not-accessible'); + this.apiErrorMessage = this.intl.t('pages.certification-start.error-messages.session-not-accessible'); } else if (statusCode === '403') { const errorCode = error.errors?.[0]?.code; if (errorCode === 'CANDIDATE_NOT_AUTHORIZED_TO_JOIN_SESSION') { - this.errorMessage = this.intl.t('pages.certification-start.error-messages.candidate-not-authorized-to-start'); + this.apiErrorMessage = this.intl.t( + 'pages.certification-start.error-messages.candidate-not-authorized-to-start', + ); } else if (errorCode === 'CANDIDATE_NOT_AUTHORIZED_TO_RESUME_SESSION') { - this.errorMessage = this.intl.t( + this.apiErrorMessage = this.intl.t( 'pages.certification-start.error-messages.candidate-not-authorized-to-resume', ); } } else { // This should not happen, but in case it does, let give as much info as possible - this.technicalErrorInfo = `${error.message} ${error.stack}`; - this.errorMessage = this.intl.t('pages.certification-start.error-messages.generic'); + this.technicalErrorInformation = `${error.message} ${error.stack}`; + this.apiErrorMessage = this.intl.t('pages.certification-start.error-messages.generic'); } } } diff --git a/mon-pix/app/styles/components/_certification-starter.scss b/mon-pix/app/styles/components/_certification-starter.scss index cc553e6aa4c..2591b4516eb 100644 --- a/mon-pix/app/styles/components/_certification-starter.scss +++ b/mon-pix/app/styles/components/_certification-starter.scss @@ -14,55 +14,30 @@ .certification-start-page { &__block { max-width: 980px; - margin-bottom: 32px; - padding: 12px; + margin-bottom: var(--pix-spacing-8x); + padding: var(--pix-spacing-3x); @include breakpoints.device-is('mobile') { - padding: 16px; + padding: var(--pix-spacing-4x); } } &__information { - display: flex; - flex-direction: column; - gap: var(--pix-spacing-3x); - align-items: center; margin-top: var(--pix-spacing-3x); + text-align: center; @extend %pix-body-s; - - &--error { - color: var(--pix-error-500); - } - - details { - text-align: center; - } - } - - &__link-to-user-certifications { - display: flex; - gap: 8px; - justify-content: center; - margin-top: 16px; - padding: 16px; - color: var(--pix-neutral-800); - background-color: var(--pix-neutral-20); - border-radius: 8px; - - a, - a:visited { - color: var(--pix-neutral-800); - text-decoration-line: underline; - } } &__title { - margin-bottom: 20px; - color: var(--pix-neutral-900); - font-size: 2.625rem; - font-family: fonts.$font-open-sans; + margin-bottom: var(--pix-spacing-6x); text-align: center; + + @extend %pix-title-m; + + @include breakpoints.device-is('tablet') { + margin-bottom: var(--pix-spacing-10x); + } } } @@ -122,102 +97,23 @@ } } -.certification-start-page__order { - display: block; - margin-top: 5px; - margin-bottom: 5px; - color: var(--pix-neutral-800); - font-size: 1.25rem; - font-family: fonts.$font-open-sans; - line-height: 28px; - text-align: center; -} - .certification-start-page__cgu { - margin: 5px 30px; - color: var(--pix-neutral-800); - font-size: 0.75rem; + margin: var(--pix-spacing-2x) var(--pix-spacing-10x); line-height: 18px; text-align: justify; -} -.certification-start-page__session-code-input { - display: flex; - flex-direction: column; - flex-grow: 1; - align-items: center; - margin-top: 20px; - margin-bottom: 15px; + @extend %pix-body-xs; } -#certificationStarterSessionCode { - @extend %pix-monospace; - - $space-between-dashes: 0.6ch; - $nb-characters: 6; - $total-width: $nb-characters * (1ch + $space-between-dashes); - - display: inline-block; - box-sizing: content-box; - width: $total-width; - height: 50px; - padding: 0 0.2ch 1px 0.5ch; - font-size: 1.875rem; - letter-spacing: $space-between-dashes; - text-transform: uppercase; - background: repeating-linear-gradient( - 90deg, - var(--pix-neutral-100) 0, - var(--pix-neutral-100) 1ch, - transparent 0, - transparent 1ch + $space-between-dashes - ) - 0 100% / #{$total-width - $space-between-dashes} 2px no-repeat; - background-position-x: 0.5ch; - background-position-y: 2.5ch; - border: solid 2px var(--pix-neutral-100); - border-radius: 3px; - outline: none; - - @media all and (-ms-high-contrast: none) { - $ie11-modifier: 1.162; - $space-between-dashes: $ie11-modifier * 0.6ch; - $total-width: $nb-characters * ($ie11-modifier * 1.2ch + $space-between-dashes); - - width: $total-width; - padding: 0 0 1px 1ch; - - // display differently for IE because 1 character (ch) is equal to 1.162 - line-height: 0.7; - letter-spacing: $space-between-dashes; - background: repeating-linear-gradient( - 90deg, - var(--pix-neutral-100) 0, - var(--pix-neutral-100) 1.3ch, - transparent 0, - transparent 1.3ch + $space-between-dashes - ) - 0 100% / #{$total-width - $space-between-dashes} 2px no-repeat; - background-position-x: 0.1ch * $ie11-modifier; - background-position-y: 2.8ch * $ie11-modifier; - background-origin: content-box; - } - - &:focus { - color: var(--pix-neutral-800); - outline: none; - } +.certification-start-page__form { + text-align: center; } -.certification-start-page__field-button { - display: flex; - justify-content: center; - margin-top: 25px; - margin-bottom: 25px; +.certification-start-page__error-message { + display: inline-flex; + margin-top: var(--pix-spacing-4x); } -.certification-start-page__submit_button { - width: content-box; - min-width: 150px; - white-space: nowrap; +.certification-start-page__field-button { + margin: var(--pix-spacing-6x) auto; } diff --git a/mon-pix/tests/integration/components/certification-starter-test.js b/mon-pix/tests/integration/components/certification-starter-test.js index 9802fe0bb7a..4a126dfe98f 100644 --- a/mon-pix/tests/integration/components/certification-starter-test.js +++ b/mon-pix/tests/integration/components/certification-starter-test.js @@ -193,6 +193,28 @@ module('Integration | Component | certification-starter', function (hooks) { }); module('#submit', function () { + module('when access code is not provided', function () { + test('should display an error message', async function (assert) { + // given + this.set('certificationCandidateSubscription', { sessionId: 123 }); + const screen = await render( + hbs``, + ); + await fillIn( + screen.getByRole('textbox', { + name: `${t('pages.certification-start.access-code')} *`, + }), + '', + ); + + // when + await clickByLabel(t('pages.certification-start.actions.submit')); + + // then + assert.dom(screen.getByText(t('pages.certification-start.error-messages.missing-code'))).exists(); + }); + }); + module('when access code is provided', function () { module('when the creation of certification course is successful', function () { test('should redirect to certifications.resume', async function (assert) { @@ -228,10 +250,15 @@ module('Integration | Component | certification-starter', function (hooks) { routerObserver.replaceWith = sinon.stub(); this.set('certificationCandidateSubscription', { sessionId: 123 }); - await render( + const screen = await render( hbs``, ); - await fillIn('#certificationStarterSessionCode', 'ABC123'); + await fillIn( + screen.getByRole('textbox', { + name: `${t('pages.certification-start.access-code')} *`, + }), + 'ABC123', + ); routerObserver.replaceWith.returns('ok'); // when @@ -285,7 +312,12 @@ module('Integration | Component | certification-starter', function (hooks) { const screen = await render( hbs``, ); - await fillIn('#certificationStarterSessionCode', 'ABC123'); + await fillIn( + screen.getByRole('textbox', { + name: `${t('pages.certification-start.access-code')} *`, + }), + 'ABC123', + ); certificationCourse.save.rejects({ errors: [{ status: '404' }] }); // when @@ -322,7 +354,12 @@ module('Integration | Component | certification-starter', function (hooks) { const screen = await render( hbs``, ); - await fillIn('#certificationStarterSessionCode', 'ABC123'); + await fillIn( + screen.getByRole('textbox', { + name: `${t('pages.certification-start.access-code')} *`, + }), + 'ABC123', + ); certificationCourse.save.rejects({ errors: [{ status: '404' }] }); // when @@ -358,7 +395,12 @@ module('Integration | Component | certification-starter', function (hooks) { const screen = await render( hbs``, ); - await fillIn('#certificationStarterSessionCode', 'ABC123'); + await fillIn( + screen.getByRole('textbox', { + name: `${t('pages.certification-start.access-code')} *`, + }), + 'ABC123', + ); certificationCourse.save.rejects({ errors: [{ status: '412' }] }); // when @@ -395,7 +437,12 @@ module('Integration | Component | certification-starter', function (hooks) { const screen = await render( hbs``, ); - await fillIn('#certificationStarterSessionCode', 'ABC123'); + await fillIn( + screen.getByRole('textbox', { + name: `${t('pages.certification-start.access-code')} *`, + }), + 'ABC123', + ); certificationCourse.save.rejects({ errors: [{ status: '403', code: 'CANDIDATE_NOT_AUTHORIZED_TO_JOIN_SESSION' }], }); @@ -435,7 +482,12 @@ module('Integration | Component | certification-starter', function (hooks) { const screen = await render( hbs``, ); - await fillIn('#certificationStarterSessionCode', 'ABC123'); + await fillIn( + screen.getByRole('textbox', { + name: `${t('pages.certification-start.access-code')} *`, + }), + 'ABC123', + ); certificationCourse.save.rejects({ errors: [{ status: '403', code: 'CANDIDATE_NOT_AUTHORIZED_TO_RESUME_SESSION' }], }); @@ -477,7 +529,12 @@ module('Integration | Component | certification-starter', function (hooks) { const screen = await render( hbs``, ); - await fillIn('#certificationStarterSessionCode', 'ABC123'); + await fillIn( + screen.getByRole('textbox', { + name: `${t('pages.certification-start.access-code')} *`, + }), + 'ABC123', + ); certificationCourse.save.throws(new Error("Détails de l'erreur à envoyer à Pix")); // when diff --git a/mon-pix/tests/integration/components/certifications/start-test.gjs b/mon-pix/tests/integration/components/certifications/start-test.gjs index a2c15dd311c..f4662f09d09 100644 --- a/mon-pix/tests/integration/components/certifications/start-test.gjs +++ b/mon-pix/tests/integration/components/certifications/start-test.gjs @@ -33,7 +33,7 @@ module('Integration | Component | Certifications | start', function (hooks) { ); // then - assert.dom(screen.queryByRole('heading', { level: 2, name: t('pages.certification-start.first-title') })).exists(); + assert.dom(screen.queryByRole('heading', { level: 1, name: t('pages.certification-start.first-title') })).exists(); }); test('it displays companion blocker page when extension is disabled', async function (assert) { diff --git a/mon-pix/translations/en.json b/mon-pix/translations/en.json index a0f065523de..34832049cf2 100644 --- a/mon-pix/translations/en.json +++ b/mon-pix/translations/en.json @@ -800,6 +800,7 @@ "candidate-not-authorized-to-resume": "Please contact your invigilator to resume your certification test.", "candidate-not-authorized-to-start": "Your invigilator has not confirmed your presence in the test room. Therefore, you cannot start your certification test yet. Please inform your invigilator.", "generic": "An unexpected server error has occurred.", + "missing-code": "You have not entered your access code.", "session-not-accessible": "The certification session is no longer available.", "unknown": { "summary-label": "Show more details:" diff --git a/mon-pix/translations/es.json b/mon-pix/translations/es.json index a8e314060db..546b169ae4b 100644 --- a/mon-pix/translations/es.json +++ b/mon-pix/translations/es.json @@ -806,6 +806,7 @@ "candidate-not-authorized-to-resume": "Ponte en contacto con el supervisor para que autorice la reanudación de tu examen.", "candidate-not-authorized-to-start": "El supervisor no ha confirmado tu presencia en la sala de prueba. Por lo tanto, aún no puedes empezar la prueba de certificación. Por favor, informa al supervisor.", "generic": "Acaba de producirse un error inesperado en el servidor.", + "missing-code": "You have not entered your access code.", "session-not-accessible": "La sesión de certificación ya no está disponible.", "unknown": { "summary-label": "Ver más detalles:" diff --git a/mon-pix/translations/fr.json b/mon-pix/translations/fr.json index cdf8c90f446..61321dd9d6c 100644 --- a/mon-pix/translations/fr.json +++ b/mon-pix/translations/fr.json @@ -800,6 +800,7 @@ "candidate-not-authorized-to-resume": "Merci de contacter votre surveillant afin qu'il autorise la reprise de votre test.", "candidate-not-authorized-to-start": "Votre surveillant n’a pas confirmé votre présence dans la salle de test. Vous ne pouvez donc pas encore commencer votre test de certification. Merci de prévenir votre surveillant.", "generic": "Une erreur serveur inattendue vient de se produire.", + "missing-code": "Votre code d'accès n'est pas renseigné.", "session-not-accessible": "La session de certification n'est plus accessible.", "unknown": { "summary-label": "Afficher plus de détails :" diff --git a/mon-pix/translations/nl.json b/mon-pix/translations/nl.json index 9e5f1e4d1a8..57a2dd4551e 100644 --- a/mon-pix/translations/nl.json +++ b/mon-pix/translations/nl.json @@ -806,6 +806,7 @@ "candidate-not-authorized-to-resume": "Neem contact op met je surveillant om toestemming te vragen om je toets te hervatten.", "candidate-not-authorized-to-start": "Je surveillant heeft je aanwezigheid in de testruimte nog niet bevestigd. Je kunt daarom nog niet beginnen met je certificeringstoets. Stel je surveillant hiervan op de hoogte.", "generic": "Er is zojuist een onverwachte serverfout opgetreden.", + "missing-code": "You have not entered your access code.", "session-not-accessible": "De certificeringssessie is niet langer toegankelijk.", "unknown": { "summary-label": "Bekijk meer details:"