(app-desk) modal update team

Integrate the modal and the logic to update a team.
This commit is contained in:
Anthony LC
2024-03-21 10:59:26 +01:00
committed by Anthony LC
parent 7347565f8d
commit 8ea7b53286
6 changed files with 237 additions and 56 deletions

View File

@@ -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

View File

@@ -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>
);
};

View File

@@ -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')}&nbsp;
<Text $weight="bold" $display="inline">
{created_at}
</Text>
</Text>
<Text $size="s" $display="inline" as="p">
{t('Last update at')}&nbsp;
<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')}&nbsp;
<Text $weight="bold" $display="inline">
{created_at}
</Text>
</Text>
<Text $size="s" $display="inline" as="p">
{t('Last update at')}&nbsp;
<Text $weight="bold" $display="inline">
{updated_at}
</Text>
</Text>
</Box>
</Card>
</Card>
{isModalUpdateOpen && (
<ModalUpdateTeam
onClose={() => setIsModalUpdateOpen(false)}
team={team}
/>
)}
</>
);
};

View File

@@ -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",

View File

@@ -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();

View File

@@ -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();
});
});