diff --git a/src/frontend/apps/desk/src/features/teams/assets/icon-edit.svg b/src/frontend/apps/desk/src/features/teams/assets/icon-edit.svg
new file mode 100644
index 0000000..5e31621
--- /dev/null
+++ b/src/frontend/apps/desk/src/features/teams/assets/icon-edit.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/frontend/apps/desk/src/features/teams/components/ModalUpdateTeam.tsx b/src/frontend/apps/desk/src/features/teams/components/ModalUpdateTeam.tsx
new file mode 100644
index 0000000..529270e
--- /dev/null
+++ b/src/frontend/apps/desk/src/features/teams/components/ModalUpdateTeam.tsx
@@ -0,0 +1,117 @@
+import {
+ Button,
+ Input,
+ Loader,
+ Modal,
+ ModalSize,
+ VariantType,
+ useToastProvider,
+} from '@openfun/cunningham-react';
+import { t } from 'i18next';
+import { useEffect, useState } from 'react';
+
+import { Box, Text, TextErrors } from '@/components';
+import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
+
+import { Team, useUpdateTeam } from '../api';
+import IconEdit from '../assets/icon-edit.svg';
+
+interface ModalUpdateTeamProps {
+ onClose: () => void;
+ team: Team;
+}
+
+export const ModalUpdateTeam = ({ onClose, team }: ModalUpdateTeamProps) => {
+ const { colorsTokens } = useCunninghamTheme();
+ const [newTeamName, setNewTeamName] = useState(team.name);
+ const [isShowingError, setIsShowingError] = useState(false);
+ const { toast } = useToastProvider();
+
+ const {
+ mutate: updateTeam,
+ isError,
+ isPending,
+ error,
+ } = useUpdateTeam({
+ onSuccess: () => {
+ toast(t('The team has been updated.'), VariantType.SUCCESS, {
+ duration: 4000,
+ });
+ onClose();
+ },
+ });
+
+ useEffect(() => {
+ if (isError) {
+ setIsShowingError(true);
+ }
+ }, [isError]);
+
+ return (
+ onClose()}
+ >
+ {t('Cancel')}
+
+ }
+ onClose={() => onClose()}
+ rightActions={
+
+ }
+ size={ModalSize.MEDIUM}
+ title={
+
+
+
+ {t('Update team {{teamName}}', { teamName: team.name })}
+
+
+ }
+ >
+
+
+ {t('Enter the new name of the selected team')}
+
+
+ {
+ setNewTeamName(e.target.value);
+ setIsShowingError(false);
+ }}
+ rightIcon={edit}
+ state={isShowingError ? 'error' : undefined}
+ />
+ {isError && error && }
+ {isPending && (
+
+
+
+ )}
+
+
+ );
+};
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 0b1cffd..3fc504c 100644
--- a/src/frontend/apps/desk/src/features/teams/components/TeamInfo.tsx
+++ b/src/frontend/apps/desk/src/features/teams/components/TeamInfo.tsx
@@ -1,13 +1,15 @@
import { DateTime, DateTimeFormatOptions } from 'luxon';
-import React from 'react';
+import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import IconGroup from '@/assets/icons/icon-group2.svg';
-import { Box, Card, Text } from '@/components';
+import { Box, BoxButton, Card, IconOptions, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Team } from '../api/types';
+import { ModalUpdateTeam } from './ModalUpdateTeam';
+
const format: DateTimeFormatOptions = {
month: '2-digit',
day: '2-digit',
@@ -22,6 +24,7 @@ export const TeamInfo = ({ team }: TeamInfoProps) => {
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const { i18n } = useTranslation();
+ const [isModalUpdateOpen, setIsModalUpdateOpen] = useState(false);
const created_at = DateTime.fromISO(team.created_at)
.setLocale(i18n.language)
@@ -32,58 +35,79 @@ export const TeamInfo = ({ team }: TeamInfoProps) => {
.toLocaleString(format);
return (
-
-
-
-
-
- {t('Members of “{{teamName}}“', {
- teamName: team.name,
- })}
+ <>
+
+
+ {
+ setIsModalUpdateOpen(true);
+ }}
+ >
+
+
+
+
+
+
+
+ {t('Members of “{{teamName}}“', {
+ teamName: team.name,
+ })}
+
+
+ {t('Add people to the “{{teamName}}“ group.', {
+ teamName: team.name,
+ })}
+
+
+
+
+
+ {t('{{count}} member', { count: team.accesses.length })}
-
- {t('Add people to the “{{teamName}}“ group.', {
- teamName: team.name,
- })}
+
+ {t('Created at')}
+
+ {created_at}
+
+
+
+ {t('Last update at')}
+
+ {updated_at}
+
-
-
-
- {t('{{count}} member', { count: team.accesses.length })}
-
-
- {t('Created at')}
-
- {created_at}
-
-
-
- {t('Last update at')}
-
- {updated_at}
-
-
-
-
+
+ {isModalUpdateOpen && (
+ setIsModalUpdateOpen(false)}
+ team={team}
+ />
+ )}
+ >
);
};
diff --git a/src/frontend/apps/desk/src/i18n/translations.json b/src/frontend/apps/desk/src/i18n/translations.json
index 5b07949..9e77fd4 100644
--- a/src/frontend/apps/desk/src/i18n/translations.json
+++ b/src/frontend/apps/desk/src/i18n/translations.json
@@ -13,7 +13,9 @@
"Cancel": "Annuler",
"Cells icon": "Icône Cellules",
"Choose a role": "Choisissez un rôle",
+ "Close the modal": "Fermer la modale",
"Contacts": "Contacts",
+ "Content modal to update the team": "Contenu modal pour mettre à jour le groupe",
"Create a new team": "Créer un nouveau groupe",
"Create new team card": "Carte créer une nouvelle équipe",
"Create the team": "Créer le groupe",
@@ -23,6 +25,8 @@
"Desk Logo": "Logo Desk",
"Emails": "Emails",
"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",
"Favorite": "Favoris",
"Find a member to add to the team": "Trouver un membre à ajouter au groupe",
"Freedom Equality Fraternity Logo": "Logo Liberté Égalité Fraternité",
@@ -38,7 +42,9 @@
"Members of “{{teamName}}“": "Membres de “{{teamName}}“",
"Name the team": "Nommer le groupe",
"Names": "Noms",
+ "New name...": "Nouveau nom...",
"Open the member options modal": "Ouvrir les options de membre dans la fenêtre modale",
+ "Open the team options modal": "Ouvrir les options de groupe dans la fenêtre modale",
"Owner": "Propriétaire",
"People": "People",
"People Description": "Description de People",
@@ -56,8 +62,11 @@
"Team name": "Nom du groupe",
"Teams icon": "Icône de groupe",
"The role has been updated": "Le rôle a bien été mis à jour",
+ "The team has been updated.": "Le groupe a été mise à jour.",
+ "Update team {{teamName}}": "Modification du groupe {{teamName}}",
"Update the role": "Mettre à jour ce rôle",
"Validate": "Valider",
+ "Validate the modification": "Valider la modification",
"You are the last owner, you cannot change your role.": "Vous êtes le dernier propriétaire, vous ne pouvez pas changer votre rôle.",
"You cannot update the role of other owner.": "Vous ne pouvez pas mettre à jour les rôles d'autre propriétaire.",
"icon group": "icône groupe",
diff --git a/src/frontend/apps/e2e/__tests__/app-desk/common.ts b/src/frontend/apps/e2e/__tests__/app-desk/common.ts
index bc34179..08203d0 100644
--- a/src/frontend/apps/e2e/__tests__/app-desk/common.ts
+++ b/src/frontend/apps/e2e/__tests__/app-desk/common.ts
@@ -18,6 +18,15 @@ export const keyCloakSignIn = async (page: Page, browserName: string) => {
}
};
+export const randomTeamsName = (
+ teamName: string,
+ browserName: string,
+ length: number,
+) =>
+ Array.from({ length }, (_el, index) => {
+ return `${teamName}-${browserName}-${Math.floor(Math.random() * 10000)}-${index}`;
+ });
+
export const createTeam = async (
page: Page,
teamName: string,
@@ -27,9 +36,7 @@ export const createTeam = async (
const panel = page.getByLabel('Teams panel').first();
const buttonCreate = page.getByRole('button', { name: 'Create the team' });
- const randomTeams = Array.from({ length }, (_el, index) => {
- return `${teamName}-${browserName}-${Math.floor(Math.random() * 10000)}-${index}`;
- });
+ const randomTeams = randomTeamsName(teamName, browserName, length);
for (let i = 0; i < randomTeams.length; i++) {
await panel.getByRole('button', { name: 'Add a team' }).click();
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 b732251..fd3370f 100644
--- a/src/frontend/apps/e2e/__tests__/app-desk/team.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-desk/team.spec.ts
@@ -1,6 +1,6 @@
import { expect, test } from '@playwright/test';
-import { createTeam, keyCloakSignIn } from './common';
+import { createTeam, keyCloakSignIn, randomTeamsName } from './common';
test.beforeEach(async ({ page, browserName }) => {
await page.goto('/');
@@ -40,4 +40,22 @@ test.describe('Team', () => {
page.getByText(`Last update at ${todayFormated}`),
).toBeVisible();
});
+
+ 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();
+
+ const teamName = randomTeamsName('new-team-update-name', browserName, 1)[0];
+ await page.getByText('New name...', { exact: true }).fill(teamName);
+
+ await page
+ .getByRole('button', { name: 'Validate the modification' })
+ .click();
+
+ await expect(page.getByText('The team has been updated.')).toBeVisible();
+ await expect(
+ page.getByText(`Add people to the “${teamName}“ group.`),
+ ).toBeVisible();
+ });
});