diff --git a/src/frontend/apps/desk/src/features/teams/components/TeamActions.tsx b/src/frontend/apps/desk/src/features/teams/components/TeamActions.tsx
new file mode 100644
index 0000000..d29c65a
--- /dev/null
+++ b/src/frontend/apps/desk/src/features/teams/components/TeamActions.tsx
@@ -0,0 +1,78 @@
+import { Button } from '@openfun/cunningham-react';
+import React, { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { Box, DropButton, IconOptions, Text } from '@/components';
+
+import { Role, Team } from '../types';
+
+import { ModalRemoveTeam } from './ModalRemoveTeam';
+import { ModalUpdateTeam } from './ModalUpdateTeam';
+
+interface TeamActionsProps {
+ currentRole: Role;
+ team: Team;
+}
+
+export const TeamActions = ({ currentRole, team }: TeamActionsProps) => {
+ const { t } = useTranslation();
+ const [isModalUpdateOpen, setIsModalUpdateOpen] = useState(false);
+ const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
+ const [isDropOpen, setIsDropOpen] = useState(false);
+
+ if (currentRole === Role.MEMBER) {
+ return null;
+ }
+
+ return (
+ <>
+
+ }
+ onOpenChange={(isOpen) => setIsDropOpen(isOpen)}
+ isOpen={isDropOpen}
+ >
+
+
+ {currentRole === Role.OWNER && (
+
+ )}
+
+
+ {isModalUpdateOpen && (
+ setIsModalUpdateOpen(false)}
+ team={team}
+ />
+ )}
+ {isModalRemoveOpen && (
+ setIsModalRemoveOpen(false)}
+ team={team}
+ />
+ )}
+ >
+ );
+};
diff --git a/src/frontend/apps/desk/src/features/teams/components/TeamInfo.tsx b/src/frontend/apps/desk/src/features/teams/components/TeamInfo.tsx
index 3fc504c..5dcb6a2 100644
--- a/src/frontend/apps/desk/src/features/teams/components/TeamInfo.tsx
+++ b/src/frontend/apps/desk/src/features/teams/components/TeamInfo.tsx
@@ -3,12 +3,13 @@ import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import IconGroup from '@/assets/icons/icon-group2.svg';
-import { Box, BoxButton, Card, IconOptions, Text } from '@/components';
+import { Box, Card, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
-import { Team } from '../api/types';
+import { Role, Team } from '../types';
import { ModalUpdateTeam } from './ModalUpdateTeam';
+import { TeamActions } from './TeamActions';
const format: DateTimeFormatOptions = {
month: '2-digit',
@@ -18,9 +19,10 @@ const format: DateTimeFormatOptions = {
interface TeamInfoProps {
team: Team;
+ currentRole: Role;
}
-export const TeamInfo = ({ team }: TeamInfoProps) => {
+export const TeamInfo = ({ team, currentRole }: TeamInfoProps) => {
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const { i18n } = useTranslation();
@@ -38,16 +40,7 @@ export const TeamInfo = ({ team }: TeamInfoProps) => {
<>
- {
- setIsModalUpdateOpen(true);
- }}
- >
-
-
+
{
return (
<>
-
+
>
);
diff --git a/src/frontend/apps/e2e/__tests__/app-desk/common.ts b/src/frontend/apps/e2e/__tests__/app-desk/common.ts
index d231814..ae77bcb 100644
--- a/src/frontend/apps/e2e/__tests__/app-desk/common.ts
+++ b/src/frontend/apps/e2e/__tests__/app-desk/common.ts
@@ -46,3 +46,44 @@ export const createTeam = async (
return randomTeams;
};
+
+export const addNewMember = async (
+ page: Page,
+ index: number,
+ role: 'Admin' | 'Owner' | 'Member',
+ fillText: string = 'test',
+) => {
+ const responsePromiseSearchUser = page.waitForResponse(
+ (response) =>
+ response.url().includes(`/users/?q=${fillText}`) &&
+ response.status() === 200,
+ );
+ await page.getByLabel('Add members to the team').click();
+ const inputSearch = page.getByLabel(/Find a member to add to the team/);
+
+ // Select a new user
+ await inputSearch.fill(fillText);
+
+ // Intercept response
+ const responseSearchUser = await responsePromiseSearchUser;
+ const users = (await responseSearchUser.json()).results as {
+ name: string;
+ }[];
+
+ // Choose user
+ await page.getByRole('option', { name: users[index].name }).click();
+
+ // Choose a role
+ await page.getByRole('radio', { name: role }).click();
+
+ await page.getByRole('button', { name: 'Validate' }).click();
+
+ const table = page.getByLabel('List members card').getByRole('table');
+
+ await expect(table.getByText(users[index].name)).toBeVisible();
+ await expect(
+ page.getByText(`Member ${users[index].name} added to the team`),
+ ).toBeVisible();
+
+ return users[index].name;
+};
diff --git a/src/frontend/apps/e2e/__tests__/app-desk/team.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/team.spec.ts
index 6d287e9..60e4371 100644
--- a/src/frontend/apps/e2e/__tests__/app-desk/team.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-desk/team.spec.ts
@@ -44,7 +44,8 @@ test.describe('Team', () => {
test('it updates the team name', async ({ page, browserName }) => {
await createTeam(page, 'team-update-name', browserName, 1);
- await page.getByLabel(`Open the team options modal`).click();
+ await page.getByLabel(`Open the team options`).click();
+ await page.getByRole('button', { name: `Update the team` }).click();
const teamName = randomName('new-team-update-name', browserName, 1)[0];
await page.getByText('New name...', { exact: true }).fill(teamName);
diff --git a/src/frontend/apps/e2e/__tests__/app-desk/teams-delete.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/teams-delete.spec.ts
new file mode 100644
index 0000000..65fbe34
--- /dev/null
+++ b/src/frontend/apps/e2e/__tests__/app-desk/teams-delete.spec.ts
@@ -0,0 +1,78 @@
+import { expect, test } from '@playwright/test';
+
+import { addNewMember, createTeam, keyCloakSignIn } from './common';
+
+test.beforeEach(async ({ page, browserName }) => {
+ await page.goto('/');
+ await keyCloakSignIn(page, browserName);
+});
+
+test.describe('Teams Delete', () => {
+ test('it deletes the team when we are owner', async ({
+ page,
+ browserName,
+ }) => {
+ await createTeam(page, 'team-update-name', browserName, 1);
+
+ await page.getByLabel(`Open the team options`).click();
+ await page.getByRole('button', { name: `Delete the team` }).click();
+ await page.getByRole('button', { name: `Confirm deletion` }).click();
+ await expect(page.getByText(`The team has been removed.`)).toBeVisible();
+ await expect(
+ page.getByRole('button', { name: `Create a new team` }),
+ ).toBeVisible();
+ });
+
+ test('it cannot delete the team when we are admin', async ({
+ page,
+ browserName,
+ }) => {
+ await createTeam(page, 'team-update-name', browserName, 1);
+
+ await addNewMember(page, 0, 'Owner');
+
+ // Change role to Admin
+ const table = page.getByLabel('List members card').getByRole('table');
+ const myCells = table
+ .getByRole('row')
+ .filter({ hasText: new RegExp(`E2E ${browserName}`, 'i') })
+ .getByRole('cell');
+ await myCells.nth(4).getByLabel('Member options').click();
+
+ await page.getByText('Update the role').click();
+ const radioGroup = page.getByLabel('Radio buttons to update the roles');
+ await radioGroup.getByRole('radio', { name: 'Admin' }).click();
+ await page.getByRole('button', { name: 'Validate' }).click();
+
+ // Delete the team button should be hidden
+ await page.getByLabel(`Open the team options`).click();
+ await expect(
+ page.getByRole('button', { name: `Delete the team` }),
+ ).toBeHidden();
+ });
+
+ test('it cannot delete the team when we are member', async ({
+ page,
+ browserName,
+ }) => {
+ await createTeam(page, 'team-update-name', browserName, 1);
+
+ await addNewMember(page, 0, 'Owner');
+
+ // Change role to Admin
+ const table = page.getByLabel('List members card').getByRole('table');
+ const myCells = table
+ .getByRole('row')
+ .filter({ hasText: new RegExp(`E2E ${browserName}`, 'i') })
+ .getByRole('cell');
+ await myCells.nth(4).getByLabel('Member options').click();
+
+ await page.getByText('Update the role').click();
+ const radioGroup = page.getByLabel('Radio buttons to update the roles');
+ await radioGroup.getByRole('radio', { name: 'Member' }).click();
+ await page.getByRole('button', { name: 'Validate' }).click();
+
+ // Option button should be hidden
+ await expect(page.getByLabel(`Open the team options`)).toBeHidden();
+ });
+});
diff --git a/src/frontend/packages/eslint-config-people/playwright.js b/src/frontend/packages/eslint-config-people/playwright.js
index ae963e2..0bbfdf9 100644
--- a/src/frontend/packages/eslint-config-people/playwright.js
+++ b/src/frontend/packages/eslint-config-people/playwright.js
@@ -15,6 +15,12 @@ module.exports = {
rules: { ...common.globalRules, '@next/next/no-html-link-for-pages': 'off' },
overrides: [
...common.eslintTS,
+ {
+ files: ['**/*.ts'],
+ rules: {
+ '@typescript-eslint/no-unsafe-member-access': 'off',
+ },
+ },
{
files: ['*.spec.*', '*.test.*', '**/__mock__/**/*'],
extends: ['plugin:playwright/recommended'],