diff --git a/junior/app/components/challenge/challenge-layout.gjs b/junior/app/components/challenge/challenge-layout.gjs index d64f5969216..385468ba064 100644 --- a/junior/app/components/challenge/challenge-layout.gjs +++ b/junior/app/components/challenge/challenge-layout.gjs @@ -1,7 +1,17 @@ +import Component from '@glimmer/component'; + +export default class ChallengeLayout extends Component { + + get class () { + return this.args.color ?? 'default'; + } + + +} diff --git a/junior/app/components/robot-dialog.gjs b/junior/app/components/robot-dialog.gjs index 14f7f8e930a..74a522db3f0 100644 --- a/junior/app/components/robot-dialog.gjs +++ b/junior/app/components/robot-dialog.gjs @@ -26,7 +26,7 @@ export default class RobotDialog extends Component { const robotImage = window.document.querySelector('.robot-speaking__logo'); if (robotImage) { robotImage.style.transition = `all 0.4s ease-in-out`; - robotImage.style.transform = `translateY(${bubblesHeight - ROBOT_IMAGE_OFFSET}px)`; + robotImage.style.transform = `translateY(${bubblesHeight - (this.args.robotOffSet || ROBOT_IMAGE_OFFSET)}px)`; } } diff --git a/junior/app/controllers/assessment/feedback.js b/junior/app/controllers/assessment/feedback.js new file mode 100644 index 00000000000..a8904557e0d --- /dev/null +++ b/junior/app/controllers/assessment/feedback.js @@ -0,0 +1,48 @@ +import Controller from '@ember/controller'; +import { action } from '@ember/object'; +import { service } from '@ember/service'; + +export default class FeedBack extends Controller { + @service router; + @service intl; + + get feedbackClass() { + if (this.model.assessment?.result?.global === 'exceeded' || this.model.assessment?.result?.global === 'reached') { + return 'feedback--success'; + } else { + return 'feedback'; + } + } + + get buttonLabel() { + const translationKeyArray = `pages.missions.feedback.result.${this.model.assessment?.result?.global ?? 'not-reached'}.button-label`; + + return this.intl.t(translationKeyArray); + } + + get robotMood() { + switch (this.model.assessment?.result?.global) { + case 'exceeded': + return 'happy'; + case 'reached': + return 'proud'; + case 'not-reached': + return 'retry'; + default: + return 'default'; + } + } + + get robotFeedBackMessage() { + const translationKeyArray = `pages.missions.feedback.result.${this.model.assessment?.result?.global ?? 'not-reached'}.robot-text`; + + return [this.intl.t(translationKeyArray + '.0'), this.intl.t(translationKeyArray + '.1')]; + } + + @action + routeUrl() { + const sessionId = this.model.assessment?.id; + const routeName = 'assessment.results'; + this.router.transitionTo(routeName, sessionId); + } +} diff --git a/junior/app/controllers/assessment/results.js b/junior/app/controllers/assessment/results.js index 60111e2c2ba..999fd21a1be 100644 --- a/junior/app/controllers/assessment/results.js +++ b/junior/app/controllers/assessment/results.js @@ -1,7 +1,10 @@ import Controller from '@ember/controller'; import { action } from '@ember/object'; +import { service } from '@ember/service'; export default class Missions extends Controller { + @service intl; + get validatedObjectives() { return this.model.mission.validatedObjectives?.split('\n') ?? []; } @@ -10,6 +13,20 @@ export default class Missions extends Controller { return 'pages.missions.end-page.result.' + this.model.assessment.result.global; } + get robotFeedBackMessage() { + const translationKeyArray = `pages.missions.feedback.result.${this.model.assessment.result?.global ?? 'not-reached'}.robot-text`; + + return [this.intl.t(translationKeyArray + '.0'), this.intl.t(translationKeyArray + '.1')]; + } + + get feedbackClass() { + if (this.model.assessment?.result?.global === 'exceeded' || this.model.assessment?.result?.global === 'reached') { + return 'result--success'; + } else { + return 'result'; + } + } + get robotMood() { switch (this.model.assessment.result.global) { case 'exceeded': diff --git a/junior/app/router.js b/junior/app/router.js index 0f69c6420d7..328a2c3fc05 100644 --- a/junior/app/router.js +++ b/junior/app/router.js @@ -40,6 +40,7 @@ Router.map(function () { this.route('assessment', { path: '/assessments/:assessment_id' }, function () { this.route('resume'); + this.route('feedback'); this.route('results'); this.route('challenge', { path: '/challenges' }); }); diff --git a/junior/app/routes/assessment/feedback.js b/junior/app/routes/assessment/feedback.js new file mode 100644 index 00000000000..294d20b79e8 --- /dev/null +++ b/junior/app/routes/assessment/feedback.js @@ -0,0 +1,13 @@ +import Route from '@ember/routing/route'; +import { service } from '@ember/service'; + +export default class Feedback extends Route { + @service router; + @service store; + + async model(params, transition) { + const assessment = await this.modelFor('assessment').reload(); + const mission = await this.store.findRecord('mission', assessment.missionId); + return { mission, assessment }; + } +} diff --git a/junior/app/routes/assessment/resume.js b/junior/app/routes/assessment/resume.js index dd77d21046a..5fb6405f28c 100644 --- a/junior/app/routes/assessment/resume.js +++ b/junior/app/routes/assessment/resume.js @@ -6,7 +6,7 @@ export default class ResumeRoute extends Route { @service store; async redirect(assessment, transition) { - if (transition.from.name === 'challenge-preview') { + if (transition.from?.name === 'challenge-preview') { return this.router.replaceWith('organization-code'); } if (transition.to.queryParams.assessmentHasNoMoreQuestions === 'true') { @@ -16,6 +16,6 @@ export default class ResumeRoute extends Route { } _routeToResults(assessment) { - return this.router.replaceWith('assessment.results', assessment.id); + return this.router.transitionTo('assessment.feedback', assessment.id); } } diff --git a/junior/app/styles/app.scss b/junior/app/styles/app.scss index eee899e72f4..46e44a87ec2 100644 --- a/junior/app/styles/app.scss +++ b/junior/app/styles/app.scss @@ -35,6 +35,8 @@ @use 'pages/loading' as *; @use 'pages/organization-code' as *; @use 'pages/school' as *; +@use 'pages/assessments/feedback' as *; + body { color: var(--pix-neutral-900); diff --git a/junior/app/styles/components/challenge/challenge-layout.scss b/junior/app/styles/components/challenge/challenge-layout.scss index ef2a6b59ee8..242365e40d8 100644 --- a/junior/app/styles/components/challenge/challenge-layout.scss +++ b/junior/app/styles/components/challenge/challenge-layout.scss @@ -19,6 +19,24 @@ border: 2px solid var(--pix-success-500); } + &--feedback { + background-color: rgba(var(--pix-secondary-500-inline), 10%); + } + + &--feedback--success { + background-color: rgba(var(--pix-secondary-500-inline), 10%); + background-image: url("/images/background-stars-success.svg"); + } + + &--result { + background-color: rgba(var(--pix-secondary-500-inline), 10%); + } + + &--result--success { + background-color: rgba(var(--pix-secondary-500-inline), 10%); + background-image: url("/images/background-success-result-page.svg"); + } + .container { width: 100%; max-width: 1440px; diff --git a/junior/app/styles/pages/assessments/feedback.scss b/junior/app/styles/pages/assessments/feedback.scss new file mode 100644 index 00000000000..dfa07a5c905 --- /dev/null +++ b/junior/app/styles/pages/assessments/feedback.scss @@ -0,0 +1,25 @@ +.feedback { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + + img { + width: 185px; + } + + button { + width: fit-content; + margin-top: 46px; + margin-left: 190px; + } + + .robot-speaking { + width: fit-content; + .bubbles { + margin-left: 190px; + } + } +} diff --git a/junior/app/templates/assessment/feedback.hbs b/junior/app/templates/assessment/feedback.hbs new file mode 100644 index 00000000000..f3434af78bb --- /dev/null +++ b/junior/app/templates/assessment/feedback.hbs @@ -0,0 +1,22 @@ +{{page-title (t "pages.missions.feedback.title")}} + + +
+
+ + + {{#each this.robotFeedBackMessage as |message|}} + + {{/each}} + + + {{this.buttonLabel}} +
+ +
+
\ No newline at end of file diff --git a/junior/app/templates/assessment/results.hbs b/junior/app/templates/assessment/results.hbs index b24e944f8c3..6fa453d162b 100644 --- a/junior/app/templates/assessment/results.hbs +++ b/junior/app/templates/assessment/results.hbs @@ -1,39 +1,43 @@ {{page-title (t "pages.missions.end-page.title")}} -
-
- - - - -
-
- + +
+
+ + {{#each this.robotFeedBackMessage as |message|}} + + {{/each}} + + +
+
+ -
-

{{t this.resultsTitle}}

-
    - {{#each this.validatedObjectives as |element index|}} +
    +

    {{t this.resultsTitle}}

    +
      + {{#each this.validatedObjectives as |element index|}} -
    • - {{#if (this.isStepSuccessFul index)}} - Valid - {{else}} - Invalid - {{/if}} - -
    • - {{/each}} -
    +
  • + {{#if (this.isStepSuccessFul index)}} + Valid + {{else}} + Invalid + {{/if}} + +
  • + {{/each}} +
- -

{{t "pages.missions.end-page.back-to-missions"}}

- -
+ +

{{t "pages.missions.end-page.back-to-missions"}}

+ +
+
-
\ No newline at end of file + \ No newline at end of file diff --git a/junior/app/templates/identified/missions/mission/introduction.hbs b/junior/app/templates/identified/missions/mission/introduction.hbs index 6c2018070b5..dad789bd4bc 100644 --- a/junior/app/templates/identified/missions/mission/introduction.hbs +++ b/junior/app/templates/identified/missions/mission/introduction.hbs @@ -5,7 +5,12 @@
+ + diff --git a/junior/public/images/background-stars-success.svg b/junior/public/images/background-stars-success.svg new file mode 100644 index 00000000000..e133f79262f --- /dev/null +++ b/junior/public/images/background-stars-success.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/junior/public/images/background-success-result-page.svg b/junior/public/images/background-success-result-page.svg new file mode 100644 index 00000000000..2fa3474317e --- /dev/null +++ b/junior/public/images/background-success-result-page.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/junior/tests/acceptance/display-mission-info-test.js b/junior/tests/acceptance/display-mission-info-test.js index 141d89454e3..85469c7acfc 100644 --- a/junior/tests/acceptance/display-mission-info-test.js +++ b/junior/tests/acceptance/display-mission-info-test.js @@ -14,7 +14,7 @@ module('Acceptance | Display informations about the mission', function (hooks) { const screen = await visit(`/missions/${mission.id}`); // then assert.dom(screen.getByText('Recherche sur internet')).exists(); - assert.dom(screen.getByText('learningObjectives')).exists(); + assert.dom(screen.getByText('validatedObjectives')).exists(); assert.dom(screen.getByText(t('pages.missions.start-page.start-mission'))).exists(); }); }); diff --git a/junior/tests/acceptance/end-mission-test.js b/junior/tests/acceptance/end-mission-test.js index 690770cb912..e69b944eb04 100644 --- a/junior/tests/acceptance/end-mission-test.js +++ b/junior/tests/acceptance/end-mission-test.js @@ -35,7 +35,8 @@ module('Acceptance | End mission', function (hooks) { const assessment = this.server.create('assessment', { missionId: mission.id, result: { global: 'exceeded' } }); identifyLearner(this.owner); const screen = await visit(`/assessments/${assessment.id}/results`); - assert.dom(screen.getByText(t('pages.missions.end-page.result.exceeded'))).exists(); + assert.dom(screen.getByText(t('pages.missions.feedback.result.exceeded.robot-text.0'))).exists(); + assert.dom(screen.getByText(t('pages.missions.feedback.result.exceeded.robot-text.1'))).exists(); }); test('when mission goal has been reached', async function (assert) { @@ -46,7 +47,8 @@ module('Acceptance | End mission', function (hooks) { }); identifyLearner(this.owner); const screen = await visit(`/assessments/${assessment.id}/results`); - assert.dom(screen.getByText(t('pages.missions.end-page.result.reached'))).exists(); + assert.dom(screen.getByText(t('pages.missions.feedback.result.reached.robot-text.0'))).exists(); + assert.dom(screen.getByText(t('pages.missions.feedback.result.reached.robot-text.1'))).exists(); }); test('when mission goal has been partially reached', async function (assert) { @@ -57,7 +59,8 @@ module('Acceptance | End mission', function (hooks) { }); identifyLearner(this.owner); const screen = await visit(`/assessments/${assessment.id}/results`); - assert.dom(screen.getByText(t('pages.missions.end-page.result.partially-reached'))).exists(); + assert.dom(screen.getByText(t('pages.missions.feedback.result.partially-reached.robot-text.0'))).exists(); + assert.dom(screen.getByText(t('pages.missions.feedback.result.partially-reached.robot-text.1'))).exists(); }); test('when mission goal has not been reached', async function (assert) { @@ -68,7 +71,8 @@ module('Acceptance | End mission', function (hooks) { }); identifyLearner(this.owner); const screen = await visit(`/assessments/${assessment.id}/results`); - assert.dom(screen.getByText(t('pages.missions.end-page.result.not-reached'))).exists(); + assert.dom(screen.getByText(t('pages.missions.feedback.result.not-reached.robot-text.0'))).exists(); + assert.dom(screen.getByText(t('pages.missions.feedback.result.not-reached.robot-text.1'))).exists(); }); test('redirect to home page after clicking on return button', async function (assert) { diff --git a/junior/translations/fr.json b/junior/translations/fr.json index ed18cd4cc5f..0d6046d2095 100644 --- a/junior/translations/fr.json +++ b/junior/translations/fr.json @@ -97,10 +97,46 @@ }, "title": "Fin de mission" }, + "feedback": { + "result": { + "exceeded": { + "button-label": "Voir ce que tu as réussi", + "robot-text": { + "0": "Tu as terminé la mission", + "1": "Bravo ! Tu es incollable sur le sujet et tu as réussi le défi !" + } + }, + "not-reached": { + "button-label": "Voir ce que tu as travaillé", + "robot-text": { + "0": "Cette mission semble difficile pour toi.", + "1": "Avec de l’aide, tu pourras réessayer et progresser." + } + }, + "partially-reached": { + "button-label": "Voir ce que tu as travaillé", + "robot-text": { + "0": "Tout ne semble pas encore acquis pour toi.", + "1": "As-tu bien tout lu ? Tu pourras bientôt réessayer la mission." + } + }, + "reached": { + "button-label": "Voir ce que tu as réussi", + "robot-text": { + "0": "Tu as terminé la mission.", + "1": "Bravo ! Tu as atteint les objectifs de la mission." + } + } + }, + "title": "Encouragements" + }, "introduction-page": { "start-mission": "Démarrer la mission", "title": "Introduction", - "welcome": "Avant de commencer, voici une présentation de ta mission" + "robot-text": { + "0":"Des élèves ont besoin de ton aide.", + "1":"Découvrons ensemble ta nouvelle mission !" + } }, "list": { "back-to-students": "Si tu n'es pas {prenom}, '<'a href=\"{backUrl}\" class=\"internal-link\" '>'clique ici pour retourner à la liste des élèves ''",