diff --git a/mon-pix/app/components/campaigns/assessment/skill-review/evaluation-results-tabs/index.gjs b/mon-pix/app/components/campaigns/assessment/skill-review/evaluation-results-tabs/index.gjs index 9f429a8810a..e99bdea26a3 100644 --- a/mon-pix/app/components/campaigns/assessment/skill-review/evaluation-results-tabs/index.gjs +++ b/mon-pix/app/components/campaigns/assessment/skill-review/evaluation-results-tabs/index.gjs @@ -51,7 +51,11 @@ export default class EvaluationResultsTabs extends Component { {{#if this.showTrainingsTab}} - + {{/if}} diff --git a/mon-pix/app/components/campaigns/assessment/skill-review/evaluation-results-tabs/trainings.gjs b/mon-pix/app/components/campaigns/assessment/skill-review/evaluation-results-tabs/trainings.gjs index 74d55831aa3..440247d0d17 100644 --- a/mon-pix/app/components/campaigns/assessment/skill-review/evaluation-results-tabs/trainings.gjs +++ b/mon-pix/app/components/campaigns/assessment/skill-review/evaluation-results-tabs/trainings.gjs @@ -1,6 +1,81 @@ +import PixButton from '@1024pix/pix-ui/components/pix-button'; +import PixMessage from '@1024pix/pix-ui/components/pix-message'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; import { t } from 'ember-intl'; - +import TrainingCard from '../../../../training/card'; + +export default class EvaluationResultsTabsTrainings extends Component { + @service store; + + @tracked isShareResultsLoading = false; + @tracked isShareResultsError = false; + @tracked isParticipationShared = false; + + constructor() { + super(...arguments); + + this.isParticipationShared = this.args.isParticipationShared; + } + + @action + async shareResults() { + const adapter = this.store.adapterFor('campaign-participation-result'); + + try { + this.isShareResultsError = false; + this.isShareResultsLoading = true; + + await adapter.share(this.args.campaignParticipationResultId); + + this.isParticipationShared = true; + } catch { + this.isShareResultsError = true; + } finally { + this.isShareResultsLoading = false; + } + } + + +} diff --git a/mon-pix/app/components/routes/campaigns/assessment/evaluation-results.gjs b/mon-pix/app/components/routes/campaigns/assessment/evaluation-results.gjs index 07f73ee17a6..61a8a733ea4 100644 --- a/mon-pix/app/components/routes/campaigns/assessment/evaluation-results.gjs +++ b/mon-pix/app/components/routes/campaigns/assessment/evaluation-results.gjs @@ -14,7 +14,9 @@ import QuitResults from '../../../campaigns/assessment/skill-review/quit-results diff --git a/mon-pix/app/styles/components/campaigns/assessment/skill-review/evaluation-results-tabs/trainings.scss b/mon-pix/app/styles/components/campaigns/assessment/skill-review/evaluation-results-tabs/trainings.scss new file mode 100644 index 00000000000..efa40d92772 --- /dev/null +++ b/mon-pix/app/styles/components/campaigns/assessment/skill-review/evaluation-results-tabs/trainings.scss @@ -0,0 +1,52 @@ +.evaluation-results-tab__trainings { + position: relative; + + &--with-modal { + padding: 0 var(--pix-spacing-4x) var(--pix-spacing-4x); + } + + &--with-modal .evaluation-results-tab__trainings-content { + filter: blur(2px); + } +} + +.evaluation-results-tab__trainings-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); + gap: var(--pix-spacing-6x); + margin-top: var(--pix-spacing-8x); +} + +.evaluation-results-tab__share-results-modal { + position: absolute; + inset: -1rem 0 0; + z-index: 1; + + &::before { + position: absolute; + background-color: var(--pix-primary-900); + border-radius: 1.5rem; + opacity: 0.4; + content: ''; + inset: 0; + } +} + +.evaluation-results-tab-share-results-modal__content { + @extend %pix-body-m; + + position: absolute; + top: 50%; + left: 50%; + display: flex; + flex-direction: column; + gap: var(--pix-spacing-8x); + align-items: center; + max-width: 36rem; + padding: var(--pix-spacing-8x); + text-align: center; + background-color: var(--pix-neutral-0); + border: 1px solid var(--pix-neutral-100); + border-radius: 1.5rem; + transform: translate(-50%, -50%); +} diff --git a/mon-pix/app/styles/components/campaigns/index.scss b/mon-pix/app/styles/components/campaigns/index.scss index 7568f242171..3a19b355c76 100644 --- a/mon-pix/app/styles/components/campaigns/index.scss +++ b/mon-pix/app/styles/components/campaigns/index.scss @@ -1,5 +1,6 @@ +@import 'assessment/skill-review/quit-results'; @import 'assessment/skill-review/share-badge-icons'; @import 'assessment/skill-review/evaluation-results-tabs/results-details'; @import 'assessment/skill-review/evaluation-results-tabs/rewards'; -@import 'assessment/skill-review/quit-results'; +@import 'assessment/skill-review/evaluation-results-tabs/trainings'; @import 'invited/learner-reconciliation'; diff --git a/mon-pix/tests/integration/components/campaigns/assessment/skill-review/evaluation-results-tabs-test.js b/mon-pix/tests/integration/components/campaigns/assessment/skill-review/evaluation-results-tabs-test.js index 321e85db6c7..6f8b342196c 100644 --- a/mon-pix/tests/integration/components/campaigns/assessment/skill-review/evaluation-results-tabs-test.js +++ b/mon-pix/tests/integration/components/campaigns/assessment/skill-review/evaluation-results-tabs-test.js @@ -18,7 +18,7 @@ module('Integration | Components | Campaigns | Assessment | Skill Review | Evalu const acquiredBadge = store.createRecord('badge', { isAcquired: true }); this.set('badges', [acquiredBadge]); - const training = store.createRecord('training'); + const training = store.createRecord('training', { duration: { days: 2 } }); this.set('trainings', [training]); // when @@ -79,7 +79,7 @@ module('Integration | Components | Campaigns | Assessment | Skill Review | Evalu this.set('badges', []); this.set('competenceResults', []); - const training = store.createRecord('training'); + const training = store.createRecord('training', { duration: { days: 2 } }); this.set('trainings', [training]); // when diff --git a/mon-pix/tests/integration/components/campaigns/assessment/skill-review/rewards/trainings-test.js b/mon-pix/tests/integration/components/campaigns/assessment/skill-review/rewards/trainings-test.js new file mode 100644 index 00000000000..68b27e3e99e --- /dev/null +++ b/mon-pix/tests/integration/components/campaigns/assessment/skill-review/rewards/trainings-test.js @@ -0,0 +1,136 @@ +import { render } from '@1024pix/ember-testing-library'; +import { click } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import { t } from 'ember-intl/test-support'; +import { module, test } from 'qunit'; +import sinon from 'sinon'; + +import setupIntlRenderingTest from '../../../../../../helpers/setup-intl-rendering'; + +module('Integration | Components | Campaigns | Assessment | Evaluation Results Tabs | Trainings', function (hooks) { + setupIntlRenderingTest(hooks); + + module('when participation is already shared', function () { + test('it should display the trainings list', async function (assert) { + // given + const store = this.owner.lookup('service:store'); + const training1 = store.createRecord('training', { + title: 'Mon super training', + link: 'https://exemple.net/', + duration: { days: 2 }, + }); + const training2 = store.createRecord('training', { + title: 'Mon autre super training', + link: 'https://exemple.net/', + duration: { days: 2 }, + }); + + this.set('trainings', [training1, training2]); + + // when + const screen = await render( + hbs``, + ); + + // then + assert.dom(screen.getByRole('heading', { name: t('pages.skill-review.tabs.trainings.title') })).isVisible(); + assert.dom(screen.getByText(t('pages.skill-review.tabs.trainings.description'))).isVisible(); + + assert.strictEqual(screen.getAllByRole('link').length, 2); + assert.dom(screen.getByText('Mon super training')).isVisible(); + assert.dom(screen.getByText('Mon autre super training')).isVisible(); + + assert.dom(screen.queryByRole('dialog')).doesNotExist(); + }); + }); + + module('when participation is not already shared', function (hooks) { + let screen; + + hooks.beforeEach(async function () { + // given + this.set('isParticipationShared', false); + this.set('campaignParticipationResultId', 1); + + // when + screen = await render( + hbs``, + ); + }); + + test('it should display a dialog with share results button', async function (assert) { + // then + assert.dom(screen.getByRole('dialog')).isVisible(); + assert.dom(screen.getByText(/Envoyez vos résultats pour permettre/)).isVisible(); + assert.dom(screen.getByRole('button', { name: t('pages.skill-review.actions.send') })).isVisible(); + }); + + test('it should have an inert trainings list', async function (assert) { + // then + const trainingsListTitle = screen.getByRole('heading', { + name: t('pages.skill-review.tabs.trainings.title'), + }); + assert.dom(trainingsListTitle).isVisible(); + assert.dom(trainingsListTitle.closest('[role="presentation"]')).hasAttribute('inert'); + }); + + module('when clicking on the share results button', function (hooks) { + let adapter; + + hooks.beforeEach(function () { + const store = this.owner.lookup('service:store'); + adapter = store.adapterFor('campaign-participation-result'); + }); + + test('it should call the share method of the adapter', async function (assert) { + // given + const createShareStub = sinon.stub(adapter, 'share'); + + // when + await click(screen.queryByRole('button', { name: t('pages.skill-review.actions.send') })); + + // then + assert.ok(createShareStub.calledOnce); + sinon.assert.calledWithExactly(createShareStub, 1); + }); + + module('when share action works', function () { + test('it hide the dialog and show the trainings list', async function (assert) { + // given + sinon.stub(adapter, 'share'); + + // when + await click(screen.queryByRole('button', { name: t('pages.skill-review.actions.send') })); + + // then + assert.dom(screen.queryByRole('dialog')).doesNotExist(); + + const trainingsListTitle = screen.getByRole('heading', { + name: t('pages.skill-review.tabs.trainings.title'), + }); + assert.dom(trainingsListTitle.closest('[role="presentation"]')).doesNotExist(); + }); + }); + + module('when share action fails', function () { + test('it should display an error message', async function (assert) { + // given + sinon.stub(adapter, 'share').rejects(); + + // when + await click(screen.queryByRole('button', { name: t('pages.skill-review.actions.send') })); + + // then + assert.dom(screen.queryByRole('dialog')).exists(); + assert.dom(screen.getByText(t('pages.skill-review.tabs.trainings.modal.share-error'))).exists(); + }); + }); + }); + }); +}); diff --git a/mon-pix/translations/en.json b/mon-pix/translations/en.json index 3c3811b1151..3789b31bba8 100644 --- a/mon-pix/translations/en.json +++ b/mon-pix/translations/en.json @@ -1740,7 +1740,11 @@ "trainings": { "title": "Want to learn more?", "description": "Get personalized training recommendations to continue progressing and feed your curiosity to help you achieve your full potential.", - "tab-label": "Trainings" + "tab-label": "Trainings", + "modal": { + "content": "Send us your results to enable the course organizer to support you.'
'After sharing, you'll have access to these training courses!", + "share-error": "There was an error sending your results. Please try again." + } } }, "trainings": { diff --git a/mon-pix/translations/fr.json b/mon-pix/translations/fr.json index 2599e4ae8ee..ead77a9d93a 100644 --- a/mon-pix/translations/fr.json +++ b/mon-pix/translations/fr.json @@ -1740,7 +1740,11 @@ "trainings": { "title": "Vous souhaitez en apprendre plus ?", "description": "Obtenez des recommandations de formations personnalisées pour continuer à progresser et nourrir votre curiosité pour vous aider à réaliser votre plein potentiel.", - "tab-label": "Formations" + "tab-label": "Formations", + "modal": { + "content": "Envoyez vos résultats pour permettre à l’organisateur du parcours de vous accompagner.'
'Après avoir partagé, vous bénéficierez d'un accès à ces formations !", + "share-error": "Il y a eu une erreur lors de l'envoi de vos résultats. Veuillez réessayer." + } } }, "trainings": {