Skip to content

Commit

Permalink
feat(admin): add page to update organization import format
Browse files Browse the repository at this point in the history
  • Loading branch information
xav-car authored Oct 2, 2024
1 parent 397ca2a commit 4e05654
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 16 deletions.
10 changes: 0 additions & 10 deletions admin/app/adapters/campaigns-import.js

This file was deleted.

14 changes: 14 additions & 0 deletions admin/app/adapters/import-files.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,18 @@ export default class ImportFilesAdapter extends ApplicationAdapter {
const url = `${this.host}/${this.namespace}/campaigns/archive-campaigns`;
return this.ajax(url, 'POST', { data: files[0] });
}

updateOrganizationImportFormat(files) {
if (!files || files.length === 0) return;

const url = `${this.host}/${this.namespace}/import-organization-learners-format`;
return this.ajax(url, 'POST', { data: files[0] });
}

addCampaignsCsv(files) {
if (!files || files.length === 0) return;

const url = `${this.host}/${this.namespace}/campaigns`;
return this.ajax(url, 'POST', { data: files[0] });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class CampaignsImport extends Component {
async importCampaigns(files) {
this.notifications.clearAll();

const adapter = this.store.adapterFor('campaigns-import');
const adapter = this.store.adapterFor('import-files');
try {
await adapter.addCampaignsCsv(files);
this.notifications.success(this.intl.t('components.administration.campaigns-import.notifications.success'));
Expand Down
4 changes: 4 additions & 0 deletions admin/app/components/administration/nav.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { t } from 'ember-intl';
{{t "pages.administration.navigation.campaigns.label"}}
</LinkTo>

<LinkTo @route="authenticated.administration.organizations" class="navbar-item">
{{t "pages.administration.navigation.organizations.label"}}
</LinkTo>

<LinkTo @route="authenticated.administration.certification" class="navbar-item">
{{t "pages.administration.navigation.certification.label"}}
</LinkTo>
Expand Down
3 changes: 3 additions & 0 deletions admin/app/components/administration/organizations/index.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import UpdateOrganizationImportFormat from './update-organization-import-format';

<template><UpdateOrganizationImportFormat /></template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import PixButtonUpload from '@1024pix/pix-ui/components/pix-button-upload';
import { action } from '@ember/object';
import { service } from '@ember/service';
import Component from '@glimmer/component';
import { t } from 'ember-intl';

import AdministrationBlockLayout from '../block-layout';

export default class UpdateOrganizationImportFormat extends Component {
@service intl;
@service notifications;
@service router;
@service store;

@action
async uploadOrganizationImportFile(files) {
this.notifications.clearAll();
const adapter = this.store.adapterFor('import-files');
try {
await adapter.updateOrganizationImportFormat(files);
this.notifications.success(
this.intl.t('components.administration.organization-import-format.notifications.success'),
);
} catch (errorResponse) {
const errors = errorResponse.errors;
if (!errors) {
return this.notifications.error(this.intl.t('common.notifications.generic-error'));
}

errors.forEach((error) => {
switch (error.code) {
case 'MISSING_REQUIRED_FIELD_NAMES':
this.notifications.error(`${error.meta}`, { autoClear: false });
break;
default:
this.notifications.error(error.detail, { autoClear: false });
}
});
} finally {
this.isLoading = false;
}
}
<template>
<AdministrationBlockLayout
@title={{t "components.administration.organization-import-format.title"}}
@description={{t "components.administration.organization-import-format.description"}}
>
<PixButtonUpload
@id="organization-import-file-upload"
@onChange={{this.uploadOrganizationImportFile}}
accept=".json"
>
{{t "components.administration.organization-import-format.upload-button"}}
</PixButtonUpload>
</AdministrationBlockLayout>
</template>
}
1 change: 1 addition & 0 deletions admin/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Router.map(function () {
this.route('administration', function () {
this.route('common');
this.route('campaigns');
this.route('organizations');
this.route('certification');
this.route('deployment');
this.route('access');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<Administration::Organizations />
2 changes: 0 additions & 2 deletions admin/mirage/handlers/badge-criteria.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ function updateBadgeCriterion(schema, request) {
const badgeCriterionId = request.params.id;
const params = JSON.parse(request.requestBody);

console.log(schema);

const badgeCriterion = schema.badgeCriteria.find(badgeCriterionId);
badgeCriterion.update(params.data.attributes);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module('Integration | Component | administration/campaigns-import', function (h
let store, adapter, notificationSuccessStub, clearAllStub, saveAdapterStub, notificationErrorStub;
hooks.beforeEach(function () {
store = this.owner.lookup('service:store');
adapter = store.adapterFor('campaigns-import');
adapter = store.adapterFor('import-files');
saveAdapterStub = sinon.stub(adapter, 'addCampaignsCsv');
notificationSuccessStub = sinon.stub();
notificationErrorStub = sinon.stub().returns();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { render } from '@1024pix/ember-testing-library';
import Service from '@ember/service';
import { triggerEvent } from '@ember/test-helpers';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { t } from 'ember-intl/test-support';
import UpdateOrganizationImportFormat from 'pix-admin/components/administration/organizations/update-organization-import-format';
import { module, test } from 'qunit';
import sinon from 'sinon';

import setupIntlRenderingTest from '../../../../helpers/setup-intl-rendering';

module('Integration | Component | administration/update-organization-import-format', function (hooks) {
setupIntlRenderingTest(hooks);
setupMirage(hooks);

let store, adapter, notificationSuccessStub, clearAllStub, saveAdapterStub, notificationErrorStub;
hooks.beforeEach(function () {
store = this.owner.lookup('service:store');
adapter = store.adapterFor('import-files');
saveAdapterStub = sinon.stub(adapter, 'updateOrganizationImportFormat');
notificationSuccessStub = sinon.stub();
notificationErrorStub = sinon.stub().returns();

clearAllStub = sinon.stub();
});

module('when import succeeds', function () {
test('it displays a success notification', async function (assert) {
// given
const files = Symbol('file');
class NotificationsStub extends Service {
success = notificationSuccessStub;
error = notificationErrorStub;
clearAll = clearAllStub;
}
this.owner.register('service:notifications', NotificationsStub);
saveAdapterStub.withArgs([files]).resolves();

// when
const screen = await render(<template><UpdateOrganizationImportFormat /></template>);
const input = await screen.findByLabelText(
t('components.administration.organization-import-format.upload-button'),
);
await triggerEvent(input, 'change', { files: [files] });

// then
assert.ok(true);
assert.ok(notificationErrorStub.notCalled);
assert.ok(
notificationSuccessStub.calledWith(
t('components.administration.organization-import-format.notifications.success'),
),
);
});
});

module('when import fails', function () {
test('it displays a specific error notification on missing required field', async function (assert) {
// given
const files = Symbol('file');
class NotificationsStub extends Service {
error = notificationErrorStub;
success = notificationSuccessStub;
clearAll = clearAllStub;
}
saveAdapterStub.withArgs([files]).rejects({
errors: [{ status: '422', meta: 'POUET', code: 'MISSING_REQUIRED_FIELD_NAMES' }],
});
this.owner.register('service:notifications', NotificationsStub);

// when
const screen = await render(<template><UpdateOrganizationImportFormat /></template>);
const input = await screen.findByLabelText(
t('components.administration.organization-import-format.upload-button'),
);
await triggerEvent(input, 'change', { files: [files] });

// then
assert.ok(notificationSuccessStub.notCalled);
assert.ok(notificationErrorStub.calledWithExactly('POUET', { autoClear: false }));
});

test('it displays an error notification', async function (assert) {
// given
const files = Symbol('file');
class NotificationsStub extends Service {
error = notificationErrorStub;
success = notificationSuccessStub;
clearAll = clearAllStub;
}
saveAdapterStub.withArgs([files]).rejects({
errors: [{ status: '422', title: "Un soucis avec l'import", code: '422', detail: 'Erreur d’import' }],
});
this.owner.register('service:notifications', NotificationsStub);

// when
const screen = await render(<template><UpdateOrganizationImportFormat /></template>);
const input = await screen.findByLabelText(
t('components.administration.organization-import-format.upload-button'),
);
await triggerEvent(input, 'change', { files: [files] });

// then
assert.ok(notificationSuccessStub.notCalled);
assert.ok(notificationErrorStub.called);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module('Integration | Component | users | campaign-participation', function (hoo
assert.dom(screen.getByRole('link', { name: 'SOMECODE' })).exists();
});

test('it should display orgnaization learner information', async function (assert) {
test('it should display organization learner information', async function (assert) {
// given
const participation = EmberObject.create({
organizationLearnerFullName: 'Un nom bien long',
Expand Down
22 changes: 21 additions & 1 deletion admin/tests/unit/adapters/import-files-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,32 @@ module('Unit | Adapter | ImportFiles', function (hooks) {
});

module('#importCampaignsToArchive', function () {
test('should build importCampaignsToArchive url from organizationId', async function (assert) {
test('should build importCampaignsToArchive url', async function (assert) {
// when
await adapter.importCampaignsToArchive([Symbol()]);

// then
assert.ok(adapter.ajax.calledWith('http://localhost:3000/api/admin/campaigns/archive-campaigns', 'POST'));
});
});

module('#updateOrganizationImportFormat', function () {
test('should build updateOrganizationImportFormat url', async function (assert) {
// when
await adapter.updateOrganizationImportFormat([Symbol()]);

// then
assert.ok(adapter.ajax.calledWith('http://localhost:3000/api/admin/import-organization-learners-format', 'POST'));
});
});

module('#addCampaignsCsv', function () {
test('should build addCampaignsCsv url', async function (assert) {
// when
await adapter.addCampaignsCsv([Symbol()]);

// then
assert.ok(adapter.ajax.calledWith('http://localhost:3000/api/admin/campaigns', 'POST'));
});
});
});
11 changes: 11 additions & 0 deletions admin/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@
},
"upload-button": "Import JSON file"
},
"organization-import-format": {
"title": "Update existing import formats",
"description": "Only existing format imports will be updated. If the name does not match, no format import will be created.",
"notifications": {
"success": "Format imports are updated correctly"
},
"upload-button": "Importing a JSON file"
},
"organization-tags-import": {
"title": "Bulk addition of tags on organisations",
"description": "Allow to add tags to organisations. Each line of the CSV file must be made of the ID of an organisation, followed by the name of a tag.",
Expand Down Expand Up @@ -407,6 +415,9 @@
},
"deployment": {
"label": "Deployment"
},
"organizations": {
"label": "Organizations"
}
}
},
Expand Down
11 changes: 11 additions & 0 deletions admin/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@
},
"upload-button": "Importer un fichier JSON"
},
"organization-import-format": {
"title": "Mise à jour d'import à format existants",
"description": "Seul les imports à format existants seront mis à jour. Dans le cas où le nom ne correspond pas, il n'y aura pas de création d'import à format.",
"notifications": {
"success": "Les imports à formats sont bien mis à jour"
},
"upload-button": "Importer un fichier JSON"
},
"organization-tags-import": {
"title": "Ajout de tags en masse sur des organisations",
"description": "Permet d’ajouter des tags à des organisations. Chaque ligne du fichier CSV doit être constituée de l’ID d’une organisation, suivie du nom d’un tag.",
Expand Down Expand Up @@ -417,6 +425,9 @@
},
"deployment": {
"label": "Déploiement"
},
"organizations": {
"label": "Organisations"
}
}
},
Expand Down

0 comments on commit 4e05654

Please sign in to comment.