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 a116239
Show file tree
Hide file tree
Showing 19 changed files with 102 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: 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
31 changes: 31 additions & 0 deletions pix-editor/app/components/form/mission.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default class MissionForm extends Component {
@service store;

@tracked name = new NameField();
@tracked cardImageUrl = new CardImageUrlField();
@tracked thematicIds = new ThematicIdsField();
@tracked selectedCompetenceId = new CompetenceIdField();
@tracked selectedStatus = 'ACTIVE';
Expand All @@ -29,6 +30,8 @@ export default class MissionForm extends Component {
if (this.editMode()) {
this.name.setValue(this.args.mission.name);
this.name.validate();
this.cardImageUrl.setValue(this.args.mission.cardImageUrl);
this.cardImageUrl.validate();
this.selectedStatus = this.args.mission.status;
this.validatedObjectives = this.args.mission.validatedObjectives;
this.learningObjectives = this.args.mission.learningObjectives;
Expand Down Expand Up @@ -79,6 +82,7 @@ export default class MissionForm extends Component {
this.isSubmitting = true;
const formData = {
name: this.name.getValueForSubmit(),
cardImageUrl: this.cardImageUrl.getValueForSubmit(),
competenceId: this.selectedCompetenceId.value,
thematicIds: this.thematicIds.getValueForSubmit(),
status: this.selectedStatus,
Expand Down Expand Up @@ -111,6 +115,19 @@ export default class MissionForm extends Component {
this.checkFormValidity();
}

@action
updateCardImageUrl(event) {
this.cardImageUrl.setValue(event.target.value);
this.cardImageUrl.validate();
this.checkFormValidity();
}

@action
validateCardImageUrl() {
this.cardImageUrl.validate();
this.checkFormValidity();
}

@action
updateThematicIds(event) {
this.thematicIds.setValue(event.target.value);
Expand Down Expand Up @@ -256,6 +273,20 @@ class NameField extends FormField {
getValueForSubmit() { return this.value.trim(); }
}

class CardImageUrlField extends FormField {
constructor() {
super({ errorMessage: 'L\'url d\'image de carte est obligatoire' });
}

validate() {
this.state = this.value.trim().length > 0
? STATES.SUCCESS
: STATES.ERROR;
}

getValueForSubmit() { return this.value.trim(); }
}

class CompetenceIdField extends FormField {
constructor() {
super();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default class MissionEditController extends Controller {
async submitMission(formData) {
try {
this.model.mission.name = formData.name;
this.model.mission.cardImageUrl = formData.cardImageUrl;
this.model.mission.competenceId = formData.competenceId;
this.model.mission.status = formData.status;
this.model.mission.thematicIds = formData.thematicIds;
Expand Down
1 change: 1 addition & 0 deletions pix-editor/app/models/mission.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { attr } from '@ember-data/model';
import MissionSummary from './mission-summary';

export default class Mission extends MissionSummary {
@attr cardImageUrl;
@attr competenceId;
@attr thematicIds;
@attr learningObjectives;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Card class="mission-details__card-information" @title="1. Informations">
<ul>
<li><span class="bold">Nom : </span>{{this.model.mission.name}}</li>
<li><span class="bold">Image carte : </span>{{this.model.mission.cardImageUrl}}</li>
<li>
<span class="bold">Compétence : </span>
{{this.model.competence}}
Expand Down
3 changes: 3 additions & 0 deletions pix-editor/tests/acceptance/missions/creation-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ module('Acceptance | Missions | Creation', function(hooks) {
await fillByLabel('* Nom de la mission', 'Nouvelle mission de test');
await triggerEvent(find('#mission-name'), 'keyup', '');

await fillByLabel('* URL de l\'image de la carte', 'https://example.pix.fr/ma-image.png');
await triggerEvent(find('#mission-card-image-url'), 'keyup', '');

await clickByText('Compétence');
await screen.findByRole('listbox');

Expand Down
7 changes: 7 additions & 0 deletions pix-editor/tests/acceptance/missions/edit_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module('Acceptance | Missions | Edit', function(hooks) {
this.server.create('mission', {
id: 2,
name: 'Mission 1',
cardImageUrl: 'https://example.net/card-image.png',
competenceId: 'recCompetence1.1',
createdAt: '2023/12/11',
status: 'VALIDATED',
Expand Down Expand Up @@ -53,6 +54,7 @@ module('Acceptance | Missions | Edit', function(hooks) {
this.server.create('mission', {
id: 3,
name: 'Mission',
cardImageUrl: 'https://example.net/card-image.png',
competenceId: 'recCompetence1.1',
thematicIds: '',
createdAt: '2023/12/11',
Expand All @@ -67,6 +69,9 @@ module('Acceptance | Missions | Edit', function(hooks) {
await fillByLabel('* Nom de la mission', 'Nouvelle mission de test');
await triggerEvent(find('#mission-name'), 'keyup', '');

await fillByLabel('* URL de l\'image de la carte', 'https://images.pix.fr/badges/Pix_Plus-Donnee-Visualisation_des_donnees.svg.svg');
await triggerEvent(find('#mission-card-image-url'), 'keyup', '');

await clickByText('Compétence');
await screen.findByRole('listbox');
await click(screen.getByRole('option', { name: 'Notre compétence' }));
Expand All @@ -78,6 +83,7 @@ module('Acceptance | Missions | Edit', function(hooks) {
// then
assert.strictEqual(currentURL(), '/missions/3');
assert.dom(screen.getByText('Nouvelle mission de test')).exists();
assert.dom(screen.getByText('https://images.pix.fr/badges/Pix_Plus-Donnee-Visualisation_des_donnees.svg.svg')).exists();
assert.dom(screen.getByText('http://example.com')).exists();
assert.dom(screen.getByText('http://doc.com')).exists();
});
Expand All @@ -87,6 +93,7 @@ module('Acceptance | Missions | Edit', function(hooks) {
this.server.create('mission', {
id: 3,
name: 'Mission',
cardImageUrl: 'https://example.net/image.png',
competenceId: 'recCompetence1.1',
thematicIds: '',
createdAt: '2023/12/11',
Expand Down
3 changes: 3 additions & 0 deletions pix-editor/tests/integration/components/form/missions-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ module('Integration | Component | mission', function(hooks) {
await fillByLabel('* Nom de la mission', 'Nouvelle mission de test');
await triggerEvent(find('#mission-name'), 'keyup', '');

await fillByLabel('* URL de l\'image de la carte', 'https://images.pix.fr/badges/Pix_Plus-Donnee-Visualisation_des_donnees.svg.svg');
await triggerEvent(find('#mission-card-image-url'), 'keyup', '');

await clickByText('Compétence');
await screen.findByRole('listbox');
await click(screen.getByRole('option', { name: 'Notre compétence' }));
Expand Down

0 comments on commit a116239

Please sign in to comment.