✨(app-desk) create invitation
Invite the selected members to the team. To have a successful invitation: - none user has this email - an invitation is not pending for this email and this team
This commit is contained in:
@@ -1,12 +1,20 @@
|
||||
import { Button, Modal, ModalSize } from '@openfun/cunningham-react';
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalSize,
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
import { APIError } from '@/api';
|
||||
import { Box, Text } from '@/components';
|
||||
import { Team } from '@/features/teams';
|
||||
|
||||
import { Role } from '../types';
|
||||
import { useCreateInvitation } from '../api';
|
||||
import { Invitation, Role } from '../types';
|
||||
|
||||
import { ChooseRole } from './ChooseRole';
|
||||
import { OptionSelect, SearchMembers } from './SearchMembers';
|
||||
@@ -30,7 +38,51 @@ export const ModalAddMembers = ({
|
||||
}: ModalAddMembersProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [selectedMembers, setSelectedMembers] = useState<OptionSelect>([]);
|
||||
const [, setSelectedRole] = useState<Role>(Role.MEMBER);
|
||||
const [selectedRole, setSelectedRole] = useState<Role>(Role.MEMBER);
|
||||
const { toast } = useToastProvider();
|
||||
const { mutateAsync: createInvitation } = useCreateInvitation();
|
||||
|
||||
const handleValidate = async () => {
|
||||
const promisesMembers = selectedMembers.map((selectedMember) => {
|
||||
return createInvitation({
|
||||
email: selectedMember.value.email,
|
||||
role: selectedRole,
|
||||
teamId: team.id,
|
||||
});
|
||||
});
|
||||
|
||||
const promises = await Promise.allSettled<Invitation>(promisesMembers);
|
||||
|
||||
onClose();
|
||||
promises.forEach((promise) => {
|
||||
switch (promise.status) {
|
||||
case 'rejected':
|
||||
const apiError = promise.reason as APIError<string>;
|
||||
toast(
|
||||
t(`Failed to create the invitation for {{email}}`, {
|
||||
email: apiError.data,
|
||||
}),
|
||||
VariantType.ERROR,
|
||||
{
|
||||
duration: 4000,
|
||||
},
|
||||
);
|
||||
break;
|
||||
|
||||
case 'fulfilled':
|
||||
toast(
|
||||
t('Invitation sent to {{email}}', {
|
||||
email: promise.value.email,
|
||||
}),
|
||||
VariantType.SUCCESS,
|
||||
{
|
||||
duration: 4000,
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -48,7 +100,7 @@ export const ModalAddMembers = ({
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={!selectedMembers.length}
|
||||
onClick={() => {}}
|
||||
onClick={() => void handleValidate()}
|
||||
>
|
||||
{t('Validate')}
|
||||
</Button>
|
||||
|
||||
@@ -27,10 +27,12 @@
|
||||
"Empty teams icon": "Icône de groupe vide",
|
||||
"Enter the new name of the selected team": "Entrez le nouveau nom du groupe sélectionné",
|
||||
"Enter the new team name": "Entrez le nouveau nom de groupe",
|
||||
"Failed to create the invitation for {{email}}": "Échec de la création de l'invitation pour {{email}}",
|
||||
"Favorite": "Favoris",
|
||||
"Find a member to add to the team": "Trouver un membre à ajouter au groupe",
|
||||
"Freedom Equality Fraternity Logo": "Logo Liberté Égalité Fraternité",
|
||||
"Groups": "Groupes",
|
||||
"Invitation sent to {{email}}": "Invitation envoyée à {{email}}",
|
||||
"Invite new members to {{teamName}}": "Invitez de nouveaux membres à rejoindre {{teamName}}",
|
||||
"Language": "Langue",
|
||||
"Language Icon": "Icône de langue",
|
||||
|
||||
@@ -18,13 +18,9 @@ export const keyCloakSignIn = async (page: Page, browserName: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const randomTeamsName = (
|
||||
teamName: string,
|
||||
browserName: string,
|
||||
length: number,
|
||||
) =>
|
||||
export const randomName = (name: string, browserName: string, length: number) =>
|
||||
Array.from({ length }, (_el, index) => {
|
||||
return `${teamName}-${browserName}-${Math.floor(Math.random() * 10000)}-${index}`;
|
||||
return `${browserName}-${Math.floor(Math.random() * 10000)}-${index}-${name}`;
|
||||
});
|
||||
|
||||
export const createTeam = async (
|
||||
@@ -36,7 +32,7 @@ export const createTeam = async (
|
||||
const panel = page.getByLabel('Teams panel').first();
|
||||
const buttonCreate = page.getByRole('button', { name: 'Create the team' });
|
||||
|
||||
const randomTeams = randomTeamsName(teamName, browserName, length);
|
||||
const randomTeams = randomName(teamName, browserName, length);
|
||||
|
||||
for (let i = 0; i < randomTeams.length; i++) {
|
||||
await panel.getByRole('button', { name: 'Add a team' }).click();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { createTeam, keyCloakSignIn } from './common';
|
||||
import { createTeam, keyCloakSignIn, randomName } from './common';
|
||||
|
||||
test.beforeEach(async ({ page, browserName }) => {
|
||||
await page.goto('/');
|
||||
@@ -60,16 +60,31 @@ test.describe('Members Create', () => {
|
||||
await expect(page.getByRole('radio', { name: 'Admin' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('it selects non existing email', async ({ page, browserName }) => {
|
||||
await createTeam(page, 'member-modal-search-user', browserName, 1);
|
||||
test('it sends an invitation', async ({ page, browserName }) => {
|
||||
await createTeam(page, 'member-invitation', browserName, 1);
|
||||
|
||||
await page.getByLabel('Add members to the team').click();
|
||||
|
||||
const inputSearch = page.getByLabel(/Find a member to add to the team/);
|
||||
await inputSearch.fill('test@test.fr');
|
||||
await page.getByRole('option', { name: 'test@test.fr' }).click();
|
||||
|
||||
await expect(page.getByText('test@test.fr', { exact: true })).toBeVisible();
|
||||
await expect(page.getByLabel(`Remove test@test.fr`)).toBeVisible();
|
||||
const email = randomName('test@test.fr', browserName, 1)[0];
|
||||
await inputSearch.fill(email);
|
||||
await page.getByRole('option', { name: email }).click();
|
||||
|
||||
await expect(page.getByText(email, { exact: true })).toBeVisible();
|
||||
await expect(page.getByLabel(`Remove ${email}`)).toBeVisible();
|
||||
|
||||
await page.getByRole('radio', { name: 'Owner' }).click();
|
||||
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/invitations/') && response.status() === 201,
|
||||
);
|
||||
|
||||
await page.getByRole('button', { name: 'Validate' }).click();
|
||||
await expect(page.getByText(`Invitation sent to ${email}`)).toBeVisible();
|
||||
|
||||
const response = await responsePromise;
|
||||
expect(response.ok()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { createTeam, keyCloakSignIn, randomTeamsName } from './common';
|
||||
import { createTeam, keyCloakSignIn, randomName } from './common';
|
||||
|
||||
test.beforeEach(async ({ page, browserName }) => {
|
||||
await page.goto('/');
|
||||
@@ -46,7 +46,7 @@ test.describe('Team', () => {
|
||||
|
||||
await page.getByLabel(`Open the team options modal`).click();
|
||||
|
||||
const teamName = randomTeamsName('new-team-update-name', browserName, 1)[0];
|
||||
const teamName = randomName('new-team-update-name', browserName, 1)[0];
|
||||
await page.getByText('New name...', { exact: true }).fill(teamName);
|
||||
|
||||
await page
|
||||
|
||||
Reference in New Issue
Block a user