Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Ajouter le contenu de l'onglet Formations (PIX-12986). #10007

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ export default class EvaluationResultsTabs extends Component {
</Panel>
{{#if this.showTrainingsTab}}
<Panel @index={{2}}>
<Trainings />
<Trainings
@trainings={{@trainings}}
@isParticipationShared={{@isParticipationShared}}
@campaignParticipationResultId={{@campaignParticipationResultId}}
/>
</Panel>
{{/if}}
</:panels>
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

<template>
<h2 class="evaluation-results-tab__title">{{t "pages.skill-review.tabs.trainings.title"}}</h2>
<p class="evaluation-results-tab__description">{{t "pages.skill-review.tabs.trainings.description"}}</p>
</template>
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;
}
}

<template>
<div
class="evaluation-results-tab__trainings
{{unless this.isParticipationShared 'evaluation-results-tab__trainings--with-modal'}}"
>
<div
class="evaluation-results-tab__trainings-content"
inert={{unless this.isParticipationShared "true"}}
role={{unless this.isParticipationShared "presentation"}}
>
<h2 class="evaluation-results-tab__title">{{t "pages.skill-review.tabs.trainings.title"}}</h2>
<p class="evaluation-results-tab__description">{{t "pages.skill-review.tabs.trainings.description"}}</p>

<ul class="evaluation-results-tab__trainings-list">
{{#each @trainings as |training|}}
<li class="evaluation-results-tab__training">
<TrainingCard @training={{training}} />
</li>
{{/each}}
</ul>
</div>

{{#unless this.isParticipationShared}}
<div class="evaluation-results-tab__share-results-modal" role="dialog">
<div class="evaluation-results-tab-share-results-modal__content">
<p>{{t "pages.skill-review.tabs.trainings.modal.content" htmlSafe=true}}</p>
<PixButton @triggerAction={{this.shareResults}} @isLoading={{this.isShareResultsLoading}}>
{{t "pages.skill-review.actions.send"}}
</PixButton>
{{#if this.isShareResultsError}}
<PixMessage @type="error" @withIcon={{true}}>
{{t "pages.skill-review.tabs.trainings.modal.share-error"}}
</PixMessage>
{{/if}}
</div>
</div>
{{/unless}}
</div>
</template>
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import QuitResults from '../../../campaigns/assessment/skill-review/quit-results
</header>
<EvaluationResultsTabs
@badges={{@model.campaignParticipationResult.campaignParticipationBadges}}
@campaignParticipationResultId={{@model.campaignParticipationResult.id}}
@competenceResults={{@model.campaignParticipationResult.competenceResults}}
@isParticipationShared={{@model.campaignParticipationResult.isShared}}
@totalStage={{@model.campaignParticipationResult.reachedStage.totalStage}}
@trainings={{@model.trainings}}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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%);
}
3 changes: 2 additions & 1 deletion mon-pix/app/styles/components/campaigns/index.scss
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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`<Campaigns::Assessment::SkillReview::EvaluationResultsTabs::Trainings
@trainings={{this.trainings}}
@isParticipationShared='true'
/>`,
);

// 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`<Campaigns::Assessment::SkillReview::EvaluationResultsTabs::Trainings
@isParticipationShared={{this.isParticipationShared}}
@campaignParticipationResultId={{this.campaignParticipationResultId}}
/>`,
);
});

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 succeeds', 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();
});
});
});
});
});
6 changes: 5 additions & 1 deletion mon-pix/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.'<br />'After sharing, you'll have access to these training courses!",
"share-error": "There was an error sending your results. Please try again."
}
}
},
"trainings": {
Expand Down
6 changes: 5 additions & 1 deletion mon-pix/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.'<br />'Après avoir partagé, vous bénéficierez d'un accès à ces formations&nbsp;!",
"share-error": "Il y a eu une erreur lors de l'envoi de vos résultats. Veuillez réessayer."
}
}
},
"trainings": {
Expand Down
Loading