✨(app-desk) modal update team
Integrate the modal and the logic to update a team.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M6 34.5002V42.0002H13.5L35.62 19.8802L28.12 12.3802L6 34.5002ZM42.82 12.6802L35.32 5.18018L30.26 10.2602L37.76 17.7602L42.82 12.6802Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 259 B |
@@ -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 (
|
||||
<Modal
|
||||
isOpen
|
||||
closeOnClickOutside
|
||||
hideCloseButton
|
||||
leftActions={
|
||||
<Button
|
||||
aria-label={t('Close the modal')}
|
||||
color="secondary"
|
||||
fullWidth
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
}
|
||||
onClose={() => onClose()}
|
||||
rightActions={
|
||||
<Button
|
||||
aria-label={t('Validate the modification')}
|
||||
color="primary"
|
||||
fullWidth
|
||||
onClick={() =>
|
||||
updateTeam({
|
||||
name: newTeamName,
|
||||
id: team.id,
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('Validate the modification')}
|
||||
</Button>
|
||||
}
|
||||
size={ModalSize.MEDIUM}
|
||||
title={
|
||||
<Box $align="center" $gap="1rem">
|
||||
<IconEdit width={48} color={colorsTokens()['primary-text']} />
|
||||
<Text $size="h3" className="m-0">
|
||||
{t('Update team {{teamName}}', { teamName: team.name })}
|
||||
</Text>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Box className="mb-xl" aria-label={t('Content modal to update the team')}>
|
||||
<Text as="p" className="mb-b">
|
||||
{t('Enter the new name of the selected team')}
|
||||
</Text>
|
||||
|
||||
<Input
|
||||
fullWidth
|
||||
type="text"
|
||||
label={t('New name...')}
|
||||
defaultValue={team.name}
|
||||
onChange={(e) => {
|
||||
setNewTeamName(e.target.value);
|
||||
setIsShowingError(false);
|
||||
}}
|
||||
rightIcon={<span className="material-icons">edit</span>}
|
||||
state={isShowingError ? 'error' : undefined}
|
||||
/>
|
||||
{isError && error && <TextErrors causes={error.cause} />}
|
||||
{isPending && (
|
||||
<Box $align="center">
|
||||
<Loader />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -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 (
|
||||
<Card className="m-b" style={{ paddingBottom: 0 }}>
|
||||
<Box className="m-b" $direction="row" $align="center" $gap="1.5rem">
|
||||
<IconGroup
|
||||
width={44}
|
||||
color={colorsTokens()['primary-text']}
|
||||
aria-label={t('icon group')}
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
alignSelf: 'start',
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<Text as="h3" $weight="bold" $size="1.25rem" className="mt-0">
|
||||
{t('Members of “{{teamName}}“', {
|
||||
teamName: team.name,
|
||||
})}
|
||||
<>
|
||||
<Card className="m-b" style={{ paddingBottom: 0 }}>
|
||||
<Box $css="align-self: flex-end;" className="m-t" $position="absolute">
|
||||
<BoxButton
|
||||
onClick={() => {
|
||||
setIsModalUpdateOpen(true);
|
||||
}}
|
||||
>
|
||||
<IconOptions
|
||||
isOpen={isModalUpdateOpen}
|
||||
aria-label={t('Open the team options modal')}
|
||||
/>
|
||||
</BoxButton>
|
||||
</Box>
|
||||
<Box className="m-b" $direction="row" $align="center" $gap="1.5rem">
|
||||
<IconGroup
|
||||
width={44}
|
||||
color={colorsTokens()['primary-text']}
|
||||
aria-label={t('icon group')}
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
alignSelf: 'start',
|
||||
}}
|
||||
/>
|
||||
<Box>
|
||||
<Text as="h3" $weight="bold" $size="1.25rem" className="mt-0">
|
||||
{t('Members of “{{teamName}}“', {
|
||||
teamName: team.name,
|
||||
})}
|
||||
</Text>
|
||||
<Text $size="m">
|
||||
{t('Add people to the “{{teamName}}“ group.', {
|
||||
teamName: team.name,
|
||||
})}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
className="p-s"
|
||||
$gap="2rem"
|
||||
$direction="row"
|
||||
$justify="start"
|
||||
$css={`
|
||||
border-top: 1px solid ${colorsTokens()['card-border']};
|
||||
padding-left: 1.5rem;
|
||||
@media (min-width: 768px) {
|
||||
padding-left: 6rem;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Text $size="s" as="p">
|
||||
{t('{{count}} member', { count: team.accesses.length })}
|
||||
</Text>
|
||||
<Text $size="m">
|
||||
{t('Add people to the “{{teamName}}“ group.', {
|
||||
teamName: team.name,
|
||||
})}
|
||||
<Text $size="s" $display="inline" as="p">
|
||||
{t('Created at')}
|
||||
<Text $weight="bold" $display="inline">
|
||||
{created_at}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text $size="s" $display="inline" as="p">
|
||||
{t('Last update at')}
|
||||
<Text $weight="bold" $display="inline">
|
||||
{updated_at}
|
||||
</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
className="p-s"
|
||||
$gap="2rem"
|
||||
$direction="row"
|
||||
$justify="start"
|
||||
$css={`
|
||||
border-top: 1px solid ${colorsTokens()['card-border']};
|
||||
padding-left: 1.5rem;
|
||||
@media (min-width: 768px) {
|
||||
padding-left: 6rem;
|
||||
}`}
|
||||
>
|
||||
<Text $size="s" as="p">
|
||||
{t('{{count}} member', { count: team.accesses.length })}
|
||||
</Text>
|
||||
<Text $size="s" $display="inline" as="p">
|
||||
{t('Created at')}
|
||||
<Text $weight="bold" $display="inline">
|
||||
{created_at}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text $size="s" $display="inline" as="p">
|
||||
{t('Last update at')}
|
||||
<Text $weight="bold" $display="inline">
|
||||
{updated_at}
|
||||
</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
</Card>
|
||||
</Card>
|
||||
{isModalUpdateOpen && (
|
||||
<ModalUpdateTeam
|
||||
onClose={() => setIsModalUpdateOpen(false)}
|
||||
team={team}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user