diff --git a/admin/app/components/organizations/invitations.gjs b/admin/app/components/organizations/invitations.gjs index 7ee5c105181..16ceead34b2 100644 --- a/admin/app/components/organizations/invitations.gjs +++ b/admin/app/components/organizations/invitations.gjs @@ -3,9 +3,11 @@ import { fn } from '@ember/helper'; import { service } from '@ember/service'; import Component from '@glimmer/component'; import dayjsFormat from 'ember-dayjs/helpers/dayjs-format'; +import { t } from 'ember-intl'; export default class OrganizationInvitations extends Component { @service accessControl; + @service intl; get sortedInvitations() { return this.args.invitations.sortBy('updatedAt').reverse(); @@ -38,16 +40,25 @@ export default class OrganizationInvitations extends Component { {{dayjsFormat invitation.updatedAt "DD/MM/YYYY [-] HH:mm"}} {{#if this.accessControl.hasAccessToOrganizationActionsScope}} - - Annuler l’invitation - +
+ + {{t "common.invitations.send-new"}} + + + Annuler l’invitation + +
{{/if}} diff --git a/admin/app/controllers/authenticated/organizations/get/invitations.js b/admin/app/controllers/authenticated/organizations/get/invitations.js index f09ce3684c7..ceaeb8494f0 100644 --- a/admin/app/controllers/authenticated/organizations/get/invitations.js +++ b/admin/app/controllers/authenticated/organizations/get/invitations.js @@ -14,6 +14,7 @@ export default class InvitationsController extends Controller { @service store; @service accessControl; @service errorResponseHandler; + @service intl; CUSTOM_ERROR_MESSAGES = { DEFAULT: 'Une erreur s’est produite, veuillez réessayer.', @@ -47,6 +48,27 @@ export default class InvitationsController extends Controller { this.isLoading = false; } + @action + async sendNewInvitation(organizationInvitation) { + this.isLoading = true; + try { + await this.store.queryRecord('organization-invitation', { + email: organizationInvitation.email, + lang: organizationInvitation.lang, + role: organizationInvitation.role, + organizationId: this.model.organization.id, + }); + this.pixToast.sendSuccessNotification({ + message: this.intl.t('common.invitations.send-new-confirm', { + invitationEmail: organizationInvitation.email, + }), + }); + } catch (error) { + this.errorResponseHandler.notify(error, this.CUSTOM_ERROR_MESSAGES); + } + this.isLoading = false; + } + _isEmailToInviteValid(email) { if (!email) { this.userEmailToInviteError = 'Ce champ est requis.'; diff --git a/admin/app/styles/components/organization-invitations.scss b/admin/app/styles/components/organization-invitations.scss index 77a1ab11b7b..b94c680f807 100644 --- a/admin/app/styles/components/organization-invitations.scss +++ b/admin/app/styles/components/organization-invitations.scss @@ -12,7 +12,14 @@ text-align: center; } - &-actions__button { + &__actions-buttons { + display: flex; + flex-direction: column; + + button { + width: 13rem; + margin: 0.2rem; + } svg { margin-right: 6px; diff --git a/admin/app/templates/authenticated/organizations/get/invitations.hbs b/admin/app/templates/authenticated/organizations/get/invitations.hbs index 9a8b16d8a67..6767067425c 100644 --- a/admin/app/templates/authenticated/organizations/get/invitations.hbs +++ b/admin/app/templates/authenticated/organizations/get/invitations.hbs @@ -9,5 +9,6 @@ {{/if}} \ No newline at end of file diff --git a/admin/tests/integration/components/organizations/invitations-test.gjs b/admin/tests/integration/components/organizations/invitations-test.gjs index 047c8a04008..39403f9c008 100644 --- a/admin/tests/integration/components/organizations/invitations-test.gjs +++ b/admin/tests/integration/components/organizations/invitations-test.gjs @@ -2,6 +2,7 @@ import { render } from '@1024pix/ember-testing-library'; import Service from '@ember/service'; import { click } from '@ember/test-helpers'; import dayjs from 'dayjs'; +import { setupIntl } from 'ember-intl/test-support'; import { setupRenderingTest } from 'ember-qunit'; import Invitations from 'pix-admin/components/organizations/invitations'; import { module, test } from 'qunit'; @@ -9,6 +10,7 @@ import sinon from 'sinon'; module('Integration | Component | organization-invitations', function (hooks) { setupRenderingTest(hooks); + setupIntl(hooks, 'fr'); module('when the admin member have access to organization scope', function (hooks) { hooks.beforeEach(function () { @@ -40,6 +42,7 @@ module('Integration | Component | organization-invitations', function (hooks) { { email: 'riri@example.net', role: 'ADMIN', + lang: 'fr', roleInFrench: 'Administrateur', updatedAt: dayjs('2019-10-08T10:50:00Z').utcOffset(2), }, @@ -47,11 +50,13 @@ module('Integration | Component | organization-invitations', function (hooks) { email: 'fifi@example.net', role: 'MEMBER', roleInFrench: 'Membre', + lang: 'en', updatedAt: dayjs('2019-10-08T10:50:00Z').utcOffset(2), }, { email: 'loulou@example.net', role: null, + lang: 'nl', roleInFrench: '-', updatedAt: dayjs('2019-10-08T10:50:00Z').utcOffset(2), }, diff --git a/admin/tests/unit/controllers/authenticated/organizations/get/invitations-test.js b/admin/tests/unit/controllers/authenticated/organizations/get/invitations-test.js index a693319d0c4..bebeaf5e482 100644 --- a/admin/tests/unit/controllers/authenticated/organizations/get/invitations-test.js +++ b/admin/tests/unit/controllers/authenticated/organizations/get/invitations-test.js @@ -96,4 +96,48 @@ module('Unit | Controller | authenticated/organizations/get/invitations', functi assert.ok(notifyStub.calledWithExactly(anError, customErrors)); }); }); + + module('#sendNewInvitation', function () { + test('sends a new invitation', function (assert) { + // given + const queryRecordStub = sinon.stub(); + store.queryRecord = queryRecordStub; + controller.model = { organization: { id: 1 } }; + const organizationInvitation = { email: 'test@example.net', lang: 'en', role: 'MEMBER' }; + + // when + controller.sendNewInvitation(organizationInvitation); + + // then + assert.ok( + queryRecordStub.calledWith('organization-invitation', { + ...organizationInvitation, + organizationId: 1, + }), + ); + }); + + test('When an error occurs, it should send a notification error', async function (assert) { + // given + const controller = this.owner.lookup('controller:authenticated/organizations/get/invitations'); + const store = this.owner.lookup('service:store'); + const anError = Symbol('an error'); + store.queryRecord = sinon.stub().rejects(anError); + const notifyStub = sinon.stub(); + class ErrorResponseHandler extends Service { + notify = notifyStub; + } + this.owner.register('service:error-response-handler', ErrorResponseHandler); + const customErrors = Symbol('custom errors'); + controller.CUSTOM_ERROR_MESSAGES = customErrors; + controller.model = { organization: { id: 1 } }; + const organizationInvitation = { email: 'test@example.net', lang: 'en', role: 'MEMBER' }; + + // when + await controller.sendNewInvitation(organizationInvitation); + + // then + assert.ok(notifyStub.calledWithExactly(anError, customErrors)); + }); + }); }); diff --git a/admin/translations/en.json b/admin/translations/en.json index 0ef9e7921f2..daf8f7a209c 100644 --- a/admin/translations/en.json +++ b/admin/translations/en.json @@ -55,6 +55,11 @@ "mandatory": "Champ obligatoire", "mandatory-fields": "The fields marked ''*'' are required" }, + "invitations": { + "send-new": "Resend the invitation", + "send-new-confirm": "An email has been sent to {invitationEmail}", + "send-new-label": "Resend the invitation to {invitationEmail}" + }, "notifications": { "close-button": { "extra-information": "Close notification" diff --git a/admin/translations/fr.json b/admin/translations/fr.json index 45d7fc901c2..0d50062fc33 100644 --- a/admin/translations/fr.json +++ b/admin/translations/fr.json @@ -55,6 +55,11 @@ "mandatory": "Champ obligatoire", "mandatory-fields": "Les champs marqués de ''*'' sont obligatoires" }, + "invitations": { + "send-new": "Renvoyer l'invitation", + "send-new-confirm": "Un email a bien été renvoyé à {invitationEmail}", + "send-new-label": "Renvoyer l'invitation à {invitationEmail}" + }, "notifications": { "close-button": { "extra-information": "Fermer la notification"