diff --git a/api/src/school/application/mission-learner-controller.js b/api/src/school/application/mission-learner-controller.js index c3a965fcee7..62a86997d05 100644 --- a/api/src/school/application/mission-learner-controller.js +++ b/api/src/school/application/mission-learner-controller.js @@ -11,6 +11,11 @@ const findPaginatedMissionLearners = async function (request) { if (filter.results && !Array.isArray(filter.results)) { filter.results = [filter.results]; } + + if (filter.statuses && !Array.isArray(filter.statuses)) { + filter.statuses = [filter.statuses]; + } + const result = await usecases.findPaginatedMissionLearners({ organizationId, missionId, page, filter }); return missionLearnerSerializer.serialize(result); }; diff --git a/api/src/school/application/mission-learner-route.js b/api/src/school/application/mission-learner-route.js index f337e26f9b8..07f75ba8ddf 100644 --- a/api/src/school/application/mission-learner-route.js +++ b/api/src/school/application/mission-learner-route.js @@ -27,6 +27,7 @@ const register = async function (server) { filter: Joi.object({ divisions: [Joi.string(), Joi.array().items(Joi.string())], results: [Joi.string(), Joi.array().items(Joi.string())], + statuses: [Joi.string(), Joi.array().items(Joi.string())], name: Joi.string().empty(''), }).default({}), }), diff --git a/api/src/school/domain/usecases/find-paginated-mission-learners.js b/api/src/school/domain/usecases/find-paginated-mission-learners.js index 1f2c52025ee..24565a498a1 100644 --- a/api/src/school/domain/usecases/find-paginated-mission-learners.js +++ b/api/src/school/domain/usecases/find-paginated-mission-learners.js @@ -1,4 +1,4 @@ -export { filterByGlobalResult, findPaginatedMissionLearners }; +export { filterByGlobalResult, filterByStatuses, findPaginatedMissionLearners }; const findPaginatedMissionLearners = async function ({ missionLearnerRepository, @@ -18,9 +18,25 @@ const findPaginatedMissionLearners = async function ({ missionLearners, ); - return _paginateMissionLearner(filterByGlobalResult(missionLearnersWithStatus, filter.results), page); + const missionLearnerWithStatusFilteredByGlobalResult = filterByGlobalResult( + missionLearnersWithStatus, + filter.results, + ); + const missionLearnerWithStatusFilteredByGlobalResultAndStatuses = filterByStatuses( + missionLearnerWithStatusFilteredByGlobalResult, + filter.statuses, + ); + return _paginateMissionLearner(missionLearnerWithStatusFilteredByGlobalResultAndStatuses, page); }; +function filterByStatuses(missionLearners, statusesFilter) { + console.log('YAF DEBUG !', statusesFilter); + if (!statusesFilter) { + return missionLearners; + } + return missionLearners.filter((missionLearner) => statusesFilter.includes(missionLearner.missionStatus)); +} + function filterByGlobalResult(missionLearners, resultFilter) { if (!resultFilter) { return missionLearners; diff --git a/api/tests/school/unit/application/mission-learner-route_test.js b/api/tests/school/unit/application/mission-learner-route_test.js index 42ab844347f..a15bcd5bef9 100644 --- a/api/tests/school/unit/application/mission-learner-route_test.js +++ b/api/tests/school/unit/application/mission-learner-route_test.js @@ -33,7 +33,7 @@ describe('Unit | Route | mission-learner-route', function () { // when const response = await httpTestServer.request( 'GET', - `/api/organizations/4/missions/1/learners?filter[name]=Henry&filter[divisions][]=CM2-C&filter[divisions][]=CM2-B&page[number]=1&page[size]=25&filter[results][]=exceeded`, + `/api/organizations/4/missions/1/learners?filter[name]=Henry&filter[divisions][]=CM2-C&filter[divisions][]=CM2-B&page[number]=1&page[size]=25&filter[results][]=exceeded&filter[statuses][]=completed`, ); // then @@ -45,6 +45,7 @@ describe('Unit | Route | mission-learner-route', function () { 'filter', sinon.match.has('divisions', ['CM2-C', 'CM2-B']), sinon.match.has('results', ['exceeded']), + sinon.match.has('statuses', ['completed']), sinon.match.has('name', 'Henry'), ), sinon.match.has('page', sinon.match.has('number', '1'), sinon.match.has('size', '25')), diff --git a/api/tests/school/unit/domain/usecases/find-paginated-mission-learners_test.js b/api/tests/school/unit/domain/usecases/find-paginated-mission-learners_test.js index 837b6196ad4..9d0a4e49f04 100644 --- a/api/tests/school/unit/domain/usecases/find-paginated-mission-learners_test.js +++ b/api/tests/school/unit/domain/usecases/find-paginated-mission-learners_test.js @@ -1,8 +1,38 @@ import { MissionLearner } from '../../../../../src/school/domain/models/MissionLearner.js'; -import { filterByGlobalResult } from '../../../../../src/school/domain/usecases/find-paginated-mission-learners.js'; +import { + filterByGlobalResult, + filterByStatuses, +} from '../../../../../src/school/domain/usecases/find-paginated-mission-learners.js'; import { expect } from '../../../../test-helper.js'; describe('Unit | Domain | Use Cases | find-paginated-mission-learners', function () { + context('filterByStatuses', function () { + it('with empty mission learners, returns empty filtered array', function () { + const filter = []; + const missionLearners = []; + const filteredMissionLearners = filterByStatuses(missionLearners, filter); + + expect(filteredMissionLearners).to.deep.equals([]); + }); + + it('should returns mission learner corresponding to the status filter', function () { + const notStartedMissionAssessement = new MissionLearner({ + missionStatus: 'not-started', + }); + const completedMissionAssessement = new MissionLearner({ + missionStatus: 'completed', + }); + const startedMissionAssessement = new MissionLearner({ + missionStatus: 'started', + }); + + const missionLearners = [notStartedMissionAssessement, completedMissionAssessement, startedMissionAssessement]; + const statusFilter = ['completed']; + const filteredMissionLearners = filterByStatuses(missionLearners, statusFilter); + + expect(filteredMissionLearners).to.deep.equals([completedMissionAssessement]); + }); + }); context('filterByGlobalResult', function () { it('with empty mission learners, returns empty filtered array', function () { const filterGlobalResults = []; diff --git a/orga/app/controllers/authenticated/missions/mission/activities.js b/orga/app/controllers/authenticated/missions/mission/activities.js index 6c9e6884a39..7207ab7b32c 100644 --- a/orga/app/controllers/authenticated/missions/mission/activities.js +++ b/orga/app/controllers/authenticated/missions/mission/activities.js @@ -7,11 +7,22 @@ const DEFAULT_PAGE_NUMBER = 1; export default class MissionActivitiesController extends Controller { @service router; + @service intl; @tracked pageNumber = DEFAULT_PAGE_NUMBER; @tracked pageSize = 25; @tracked divisions = []; + @tracked statuses = []; + @tracked statusOptions = [ + { value: 'completed', label: this.translateResultOptionKey('completed') }, + { value: 'started', label: this.translateResultOptionKey('started') }, + { value: 'not-started', label: this.translateResultOptionKey('not-started') }, + ]; @tracked name = ''; + translateResultOptionKey(key) { + return this.intl.t(`pages.missions.mission.table.activities.filters.status.options.${key}`); + } + get learnersCount() { return this.model.missionLearners.meta.rowCount; } @@ -27,6 +38,12 @@ export default class MissionActivitiesController extends Controller { this.pageNumber = null; } + @action + onSelectStatuses(status) { + this.statuses = status; + this.pageNumber = null; + } + @action onFilter(inputText, value) { this[inputText] = value; @@ -35,6 +52,7 @@ export default class MissionActivitiesController extends Controller { @action onResetFilter() { this.divisions = []; + this.statuses = []; this.name = ''; } diff --git a/orga/app/routes/authenticated/missions/mission/activities.js b/orga/app/routes/authenticated/missions/mission/activities.js index 46412defe1f..9f3f3c9316e 100644 --- a/orga/app/routes/authenticated/missions/mission/activities.js +++ b/orga/app/routes/authenticated/missions/mission/activities.js @@ -10,6 +10,7 @@ export default class MissionActivitiesRoute extends Route { queryParams = { divisions: { refreshModel: true }, name: { refreshModel: true }, + statuses: { refreshModel: true }, pageNumber: { refreshModel: true, }, @@ -38,6 +39,7 @@ export default class MissionActivitiesRoute extends Route { filter: { divisions: params.divisions, name: params.name, + statuses: params.statuses, }, page: { number: params.pageNumber, diff --git a/orga/app/templates/authenticated/missions/mission/activities.hbs b/orga/app/templates/authenticated/missions/mission/activities.hbs index 11745381e13..bd20a099a7d 100644 --- a/orga/app/templates/authenticated/missions/mission/activities.hbs +++ b/orga/app/templates/authenticated/missions/mission/activities.hbs @@ -20,5 +20,16 @@ @triggerFiltering={{this.onFilter}} /> + + <:label>{{t "pages.missions.mission.table.activity.filters.status.label"}} + <:default as |option|>{{option.label}} + \ No newline at end of file diff --git a/orga/translations/en.json b/orga/translations/en.json index 3fb0dd012f0..9601386e13b 100644 --- a/orga/translations/en.json +++ b/orga/translations/en.json @@ -918,7 +918,15 @@ "caption": "List of students with their activity for the mission {missionName}", "filters": { "aria-label": "Filter on students", - "learners-count": "{count, plural, =0 {No student} =1 {1 student} other {{count} students}}" + "learners-count": "{count, plural, =0 {No student} =1 {1 student} other {{count} students}}", + "status": { + "label": "Status de la mission", + "options": { + "completed": "Completed", + "not-started": "Not started", + "started": "Started" + } + } }, "headers": { "division": "Division", diff --git a/orga/translations/fr.json b/orga/translations/fr.json index 8dc0cd45b33..73b42ccba93 100644 --- a/orga/translations/fr.json +++ b/orga/translations/fr.json @@ -926,7 +926,15 @@ "caption": "Tableau des élèves avec leur participation à la mission {missionName}", "filters": { "aria-label": "Filtre sur les élèves", - "learners-count": "{count, plural, =0 {Aucun élève} =1 {1 élève} other {{count} élèves}}" + "learners-count": "{count, plural, =0 {Aucun élève} =1 {1 élève} other {{count} élèves}}", + "status": { + "label": "Status de la mission", + "options": { + "completed": "Complété", + "not-started": "Non démarré", + "started": "Démmarré" + } + } }, "headers": { "division": "Classe",