Skip to content

Commit

Permalink
✨ feat: add card image url to mission
Browse files Browse the repository at this point in the history
  • Loading branch information
yaf committed Oct 4, 2024
1 parent 668446b commit 65939eb
Show file tree
Hide file tree
Showing 22 changed files with 118 additions and 2 deletions.
21 changes: 21 additions & 0 deletions api/db/migrations/20241004083929_add-card-image-to-mission.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const TABLE_NAME = 'missions';

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export async function up(knex) {
await knex.schema.alterTable(TABLE_NAME, function(table) {
table.string('cardImageUrl').notNullable();
});
}

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export async function down(knex) {
await knex.schema.alterTable(TABLE_NAME, function(table) {
table.dropColumn('cardImageUrl');
});
}
4 changes: 4 additions & 0 deletions api/db/seeds/data/missions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Mission } from '../../../lib/domain/models/Mission.js';
export function buildMissions(databaseBuilder) {
databaseBuilder.factory.buildMission({
name: 'Mission test active',
cardImageUrl: 'https://example.net/image.png',
competenceId: 'competence1NC9NE3IIOa0ym',
learningObjectives: 'Que tu sois le meilleur',
thematicIds: 'recOO8OsMJpe5cZzi,recOO8OsMJpe5cZzi',
Expand All @@ -13,6 +14,7 @@ export function buildMissions(databaseBuilder) {

databaseBuilder.factory.buildMission({
name: 'Mission test inactive',
cardImageUrl: 'https://example.net/image.png',
competenceId: 'competence2k2eVZ2GRLwqFL',
learningObjectives: 'Y\'en a plus',
thematicIds: 'rec98EBX88mkQR3gx,rec98EBX88mkQR3gx',
Expand All @@ -22,6 +24,7 @@ export function buildMissions(databaseBuilder) {
});
databaseBuilder.factory.buildMission({
name: 'Mission test inactive',
cardImageUrl: 'https://example.net/image.png',
competenceId: 'competence1NC9NE3IIOa0ym',
learningObjectives: 'Y\'en a plus',
thematicIds: 'recOO8OsMJpe5cZzi,recj6ITlVfU0vByrR,recRSPkFIgrY6Ps61',
Expand All @@ -31,6 +34,7 @@ export function buildMissions(databaseBuilder) {
});
databaseBuilder.factory.buildMission({
name: 'Mission test expérimentale',
cardImageUrl: 'https://example.net/image.png',
competenceId: 'competence1NC9NE3IIOa0ym',
learningObjectives: 'Y\'en a plus',
thematicIds: 'recOO8OsMJpe5cZzi,recj6ITlVfU0vByrR,recRSPkFIgrY6Ps61',
Expand Down
2 changes: 2 additions & 0 deletions api/lib/domain/models/Mission.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export class Mission {
constructor({
id,
name_i18n,
cardImageUrl,
competenceId,
thematicIds,
createdAt,
Expand All @@ -15,6 +16,7 @@ export class Mission {
}) {
this.id = id;
this.name_i18n = name_i18n;
this.cardImageUrl = cardImageUrl;
this.competenceId = competenceId;
this.thematicIds = thematicIds;
this.createdAt = createdAt;
Expand Down
2 changes: 2 additions & 0 deletions api/lib/domain/models/release/MissionForRelease.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export class MissionForRelease {
constructor({
id,
name_i18n,
cardImageUrl,
competenceId,
learningObjectives_i18n,
validatedObjectives_i18n,
Expand All @@ -14,6 +15,7 @@ export class MissionForRelease {
}) {
this.id = id;
this.name_i18n = name_i18n;
this.cardImageUrl = cardImageUrl;
this.competenceId = competenceId;
this.learningObjectives_i18n = learningObjectives_i18n;
this.validatedObjectives_i18n = validatedObjectives_i18n;
Expand Down
2 changes: 2 additions & 0 deletions api/lib/infrastructure/repositories/mission-repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export async function listActive() {
export async function save(mission) {
const [insertedMission] = await knex('missions').insert({
id: mission.id,
cardImageUrl: mission.cardImageUrl,
competenceId: mission.competenceId,
thematicIds: mission.thematicIds,
status: mission.status,
Expand All @@ -69,6 +70,7 @@ function _toDomain(mission, translations) {
const translationsByMissionId = _.groupBy(translations, 'entityId');
return new Mission({
id: mission.id,
cardImageUrl: mission.cardImageUrl,
createdAt: mission.createdAt,
status: mission.status,
competenceId: mission.competenceId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function serializeMission(mission, warnings) {
},
attributes: [
'name',
'cardImageUrl',
'competenceId',
'thematicIds',
'learningObjectives',
Expand All @@ -51,6 +52,7 @@ export function deserializeMission(attributes) {
return new Mission({
id: attributes.id,
name_i18n: { fr: attributes.name },
cardImageUrl: attributes['card-image-url'] || null,
competenceId: attributes['competence-id'],
thematicIds: attributes['thematic-ids'],
learningObjectives_i18n: { fr: attributes['learning-objectives'] },
Expand Down
3 changes: 2 additions & 1 deletion api/tests/acceptance/application/mission/get_mission_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('Acceptance | API | mission | GET /api/missions', function() {

//given
const user = databaseBuilder.factory.buildAdminUser();
const mission = databaseBuilder.factory.buildMission({ name: 'Condor', status: Mission.status.VALIDATED, competenceId: 'recCompetence0', thematicIds: null, validatedObjectives: 'Être forte', learningObjectives: 'Être imbattable', createdAt: new Date('2024-01-01') });
const mission = databaseBuilder.factory.buildMission({ name: 'Condor', cardImageUrl: 'https://example.com/image.png', status: Mission.status.VALIDATED, competenceId: 'recCompetence0', thematicIds: null, validatedObjectives: 'Être forte', learningObjectives: 'Être imbattable', createdAt: new Date('2024-01-01') });
await databaseBuilder.commit();

//when
Expand All @@ -36,6 +36,7 @@ describe('Acceptance | API | mission | GET /api/missions', function() {
id: mission.id.toString(),
attributes: {
name: 'Condor',
'card-image-url': 'https://example.com/image.png',
status: Mission.status.VALIDATED,
'competence-id': 'recCompetence0',
'thematic-ids': null,
Expand Down
3 changes: 3 additions & 0 deletions api/tests/acceptance/application/mission/post_mission_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('Acceptance | API | mission | POST /api/missions', function() {
data: {
attributes: {
name: 'Mission impossible',
'card-image-url': 'https://example.com/image.png',
'competence-id': 'AZERTY',
'thematic-id': null,
status: Mission.status.INACTIVE,
Expand Down Expand Up @@ -52,6 +53,7 @@ describe('Acceptance | API | mission | POST /api/missions', function() {
type: 'missions',
id: missionId.toString(),
'attributes': {
'card-image-url': 'https://example.com/image.png',
'competence-id': 'AZERTY',
'documentation-url': null,
'introduction-media-alt': null,
Expand Down Expand Up @@ -79,6 +81,7 @@ describe('Acceptance | API | mission | POST /api/missions', function() {
data: {
attributes: {
name: 'Mission impossible',
'card-image-url': 'https://example.com/image.png',
'competence-id': 'AZERTY',
'thematic-id': null,
status: Mission.status.INACTIVE,
Expand Down
3 changes: 3 additions & 0 deletions api/tests/acceptance/application/mission/put_mission_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('Acceptance | API | mission | PATCH /api/missions/{id}', function() {
data: {
attributes: {
name: 'Mission à mettre à jour',
'card-image-url': 'https://example.com/image.png',
'competence-id': 'TYUI',
'thematic-ids': '',
status: Mission.status.EXPERIMENTAL,
Expand Down Expand Up @@ -54,6 +55,7 @@ describe('Acceptance | API | mission | PATCH /api/missions/{id}', function() {
type: 'missions',
id: missionId.toString(),
'attributes': {
'card-image-url': 'https://example.com/image.png',
'competence-id': 'TYUI',
'documentation-url': null,
'introduction-media-alt': null,
Expand Down Expand Up @@ -82,6 +84,7 @@ describe('Acceptance | API | mission | PATCH /api/missions/{id}', function() {
data: {
attributes: {
name: 'Mission impossible',
'card-image-url': 'https://example.com/image.png',
'competence-id': 'AZERTY',
'thematic-id': null,
status: Mission.status.INACTIVE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ async function mockCurrentContent() {
missions: [{
id: 1,
name_i18n: { fr: 'Ma première mission' },
cardImageUrl: 'https://example.com/image0.png',
competenceId: 'competenceId',
thematicIds: 'thematicId,thematicId',
learningObjectives_i18n: { fr: 'Que tu sois le meilleur' },
Expand All @@ -173,6 +174,7 @@ async function mockCurrentContent() {
}, {
id: 2,
name_i18n: { fr: 'Alt name' },
cardImageUrl: 'https://example.com/image1.png',
competenceId: 'competenceId',
thematicIds: 'thematicId,thematicId',
learningObjectives_i18n: { fr: 'Alt objectives' },
Expand Down Expand Up @@ -236,6 +238,7 @@ async function mockCurrentContent() {
databaseBuilder.factory.buildMission({
id: 1,
name: 'Ma première mission',
cardImageUrl: 'https://example.com/image0.png',
competenceId: 'competenceId',
thematicIds: 'thematicId,thematicId',
learningObjectives: 'Que tu sois le meilleur',
Expand All @@ -249,6 +252,7 @@ async function mockCurrentContent() {
databaseBuilder.factory.buildMission({
id: 2,
name: 'Alt name',
cardImageUrl: 'https://example.com/image1.png',
competenceId: 'competenceId',
thematicIds: 'thematicId,thematicId',
learningObjectives: 'Alt objectives',
Expand All @@ -262,6 +266,7 @@ async function mockCurrentContent() {
databaseBuilder.factory.buildMission({
id: 3,
name: 'Alt name',
cardImageUrl: 'https://example.com/image2.png',
competenceId: 'competenceId',
thematicIds: 'thematicId,thematicId',
learningObjectives: 'Alt objectives',
Expand Down Expand Up @@ -500,6 +505,7 @@ async function mockContentForRelease() {
missions: [new MissionForRelease({
id: 1,
name_i18n: { fr: 'Ma première mission' },
cardImageUrl: 'https://example.com/image2.png',
competenceId: 'competenceId',
learningObjectives_i18n: { fr: 'Que tu sois le meilleur' },
validatedObjectives_i18n: { fr: 'Rien' },
Expand All @@ -511,6 +517,7 @@ async function mockContentForRelease() {
}), new MissionForRelease({
id: 2,
name_i18n: { fr: 'Alt name' },
cardImageUrl: 'https://example.com/image2.png',
competenceId: 'competenceId',
learningObjectives_i18n: { fr: 'Alt objectives' },
validatedObjectives_i18n: { fr: 'Alt validated objectives' },
Expand Down Expand Up @@ -779,6 +786,7 @@ describe('Acceptance | Controller | release-controller', () => {
databaseBuilder.factory.buildMission({
id: 1,
name: 'Ma première mission',
cardImageUrl: 'https://example.com/image2.png',
competenceId: 'competenceId',
thematicIds: 'thematicId,thematicId',
learningObjectives: 'Que tu sois le meilleur',
Expand All @@ -792,6 +800,7 @@ describe('Acceptance | Controller | release-controller', () => {
databaseBuilder.factory.buildMission({
id: 2,
name: 'Alt name',
cardImageUrl: 'https://example.com/image2.png',
competenceId: 'competenceId',
thematicIds: 'thematicId,thematicId',
learningObjectives: 'Alt objectives',
Expand All @@ -805,6 +814,7 @@ describe('Acceptance | Controller | release-controller', () => {
databaseBuilder.factory.buildMission({
id: 3,
name: 'Alt name',
cardImageUrl: 'https://example.com/image2.png',
competenceId: 'competenceId',
thematicIds: 'thematicId,thematicId',
learningObjectives: 'Alt objectives',
Expand Down
3 changes: 2 additions & 1 deletion api/tests/tooling/database-builder/factory/build-mission.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { buildTranslation } from './build-translation.js';
export function buildMission({
id = databaseBuffer.nextId++,
name = 'Ma première mission',
cardImageUrl = 'https://images.pix.fr/badges/Pix_Plus-Donnee-Visualisation_des_donnees.svg.svg',
competenceId = 'competenceId',
learningObjectives = 'Que tu sois le meilleur',
thematicIds = 'thematicIds',
Expand All @@ -18,7 +19,7 @@ export function buildMission({
createdAt = new Date('2010-01-04'),
} = {}) {

const values = { id, competenceId, thematicIds, createdAt, status, introductionMediaUrl, introductionMediaType, documentationUrl };
const values = { id, cardImageUrl, competenceId, thematicIds, createdAt, status, introductionMediaUrl, introductionMediaType, documentationUrl };

buildTranslation({
key: `mission.${id}.name`,
Expand Down
2 changes: 2 additions & 0 deletions api/tests/tooling/domain-builder/factory/build-mission.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Mission } from '../../../../lib/domain/models/index.js';
export function buildMission({
id = 3,
name = 'Ma mission',
cardImageUrl = null,
competenceId = 'recCompetence1',
thematicIds = 'recThematic1',
createdAt = new Date('2023-10-14'),
Expand All @@ -17,6 +18,7 @@ export function buildMission({
return new Mission ({
id,
name_i18n: { fr: name },
cardImageUrl,
competenceId,
thematicIds,
createdAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('Unit | Serializer | JSONAPI | mission-serializer', () => {
id: 12,
learningObjectives_i18n: { fr: 'learning objectives-value', },
name_i18n: { fr: 'Mission Name', },
cardImageUrl: 'card-image-url',
validatedObjectives_i18n: { fr: 'validated-objectives-value', },
competenceId: 'rec12E12EFZF',
thematicIds: 'someThematicIds',
Expand All @@ -25,6 +26,7 @@ describe('Unit | Serializer | JSONAPI | mission-serializer', () => {
const attributes = {
id: expectedMission.id,
name: expectedMission.name_i18n.fr,
'card-image-url': expectedMission.cardImageUrl,
'competence-id': expectedMission.competenceId,
'thematic-ids': expectedMission.thematicIds,
'learning-objectives': expectedMission.learningObjectives_i18n.fr,
Expand All @@ -46,12 +48,14 @@ describe('Unit | Serializer | JSONAPI | mission-serializer', () => {

const attributes = {
id: expectedMission.id,
'card-image-url': '',
'introduction-media-url': '',
'introduction-media-type': '',
'documentation-url': '',
};
const deserializedMission = deserializeMission(attributes);

expect(deserializedMission.cardMissionUrl).to.be.null;
expect(deserializedMission.introductionMediaUrl).to.be.null;
expect(deserializedMission.introductionMediaType).to.be.null;
expect(deserializedMission.documentationUrl).to.be.null;
Expand Down
1 change: 1 addition & 0 deletions pix-editor/app/adapters/mission.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function preparePayloadForCreateAndUpdate(payload, adapterOptions) {
payload.data.attributes = {};
payload.data.attributes.name = adapterOptions.name;
payload.data.attributes.status = adapterOptions.status;
payload.data.attributes['card-image-url'] = adapterOptions.cardImageUrl;
payload.data.attributes['competence-id'] = adapterOptions.competenceId;
payload.data.attributes['thematic-ids'] = adapterOptions.thematicIds;
payload.data.attributes['learning-objectives'] = adapterOptions.learningObjectives;
Expand Down
11 changes: 11 additions & 0 deletions pix-editor/app/components/form/mission.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
>
<:label>Nom de la mission</:label>
</PixInput>
<PixInput
@id="mission-card-image-url"
@value={{this.cardImageUrl.value}}
@errorMessage="{{this.cardImageUrl.errorMessage}}"
@requiredLabel="{{this.cardImageUrl.errorMessage}}"
@validationStatus={{this.cardImageUrl.state}}
{{on "focusout" this.validateCardImageUrl}}
{{on "keyup" this.updateCardImageUrl}}
>
<:label>URL de l'image de la carte</:label>
</PixInput>
<PixSelect
@options={{this.statusOptions}}
@value={{this.selectedStatus}}
Expand Down
Loading

0 comments on commit 65939eb

Please sign in to comment.