(app-desk) add team page

- add the team page, you can access to
the team page with the id of the team.
- Add link to the panel team to access to the
team page.
This commit is contained in:
Anthony LC
2024-02-13 11:33:22 +01:00
committed by Anthony LC
parent d0562029e8
commit 8b0c20dbdc
6 changed files with 162 additions and 60 deletions

View File

@@ -12,7 +12,7 @@ export interface CreateTeamResponseError {
detail: string; detail: string;
} }
export const createTeam = async (name: string) => { export const createTeam = async (name: string): Promise<CreateTeamResponse> => {
const response = await fetchAPI(`teams/`, { const response = await fetchAPI(`teams/`, {
method: 'POST', method: 'POST',
body: JSON.stringify({ body: JSON.stringify({
@@ -24,17 +24,22 @@ export const createTeam = async (name: string) => {
throw new Error(`Couldn't create team: ${response.statusText}`); throw new Error(`Couldn't create team: ${response.statusText}`);
} }
return response.json(); return response.json() as Promise<CreateTeamResponse>;
}; };
export function useCreateTeam() { interface CreateTeamProps {
onSuccess: (data: CreateTeamResponse) => void;
}
export function useCreateTeam({ onSuccess }: CreateTeamProps) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<CreateTeamResponse, CreateTeamResponseError, string>({ return useMutation<CreateTeamResponse, CreateTeamResponseError, string>({
mutationFn: createTeam, mutationFn: createTeam,
onSuccess: () => { onSuccess: (data) => {
void queryClient.invalidateQueries({ void queryClient.invalidateQueries({
queryKey: [KEY_LIST_TEAM], queryKey: [KEY_LIST_TEAM],
}); });
onSuccess(data);
}, },
}); });
} }

View File

@@ -2,10 +2,10 @@ import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import IconGroup from '@/assets/icons/icon-group.svg'; import IconGroup from '@/assets/icons/icon-group.svg';
import { Box, Text } from '@/components'; import { Box, StyledLink, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham'; import { useCunninghamTheme } from '@/cunningham';
import { TeamResponse } from '../api/useTeams'; import { TeamResponse } from '../api/types';
import IconNone from '../assets/icon-none.svg'; import IconNone from '../assets/icon-none.svg';
interface TeamProps { interface TeamProps {
@@ -29,29 +29,33 @@ export const PanelTeam = ({ team }: TeamProps) => {
}; };
return ( return (
<Box as="li" $direction="row" $align="center" $gap="0.5rem"> <Box as="li">
{hasMembers ? ( <StyledLink href={`/teams/${team.id}`}>
<IconGroup <Box $align="center" $direction="row" $gap="0.5rem">
aria-label={t(`Teams icon`)} {hasMembers ? (
color={colorsTokens()['primary-500']} <IconGroup
{...commonProps} aria-label={t(`Teams icon`)}
style={{ color={colorsTokens()['primary-500']}
...commonProps.style, {...commonProps}
border: `1px solid ${colorsTokens()['primary-300']}`, style={{
}} ...commonProps.style,
/> border: `1px solid ${colorsTokens()['primary-300']}`,
) : ( }}
<IconNone />
aria-label={t(`Empty teams icon`)} ) : (
color={colorsTokens()['greyscale-500']} <IconNone
{...commonProps} aria-label={t(`Empty teams icon`)}
style={{ color={colorsTokens()['greyscale-500']}
...commonProps.style, {...commonProps}
border: `1px solid ${colorsTokens()['greyscale-300']}`, style={{
}} ...commonProps.style,
/> border: `1px solid ${colorsTokens()['greyscale-300']}`,
)} }}
<Text $weight="bold">{team.name}</Text> />
)}
<Text $weight="bold">{team.name}</Text>
</Box>
</StyledLink>
</Box> </Box>
); );
}; };

View File

@@ -0,0 +1,65 @@
import { Loader } from '@openfun/cunningham-react';
import { useRouter } from 'next/router';
import { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, Text } from '@/components';
import { useTeam } from '@/features/teams/api/useTeam';
import { NextPageWithLayout } from '@/types/next';
import TeamLayout from './TeamLayout';
const Page: NextPageWithLayout = () => {
const {
query: { id },
} = useRouter();
if (typeof id !== 'string') {
throw new Error('Invalid team id');
}
return <Team id={id} />;
};
interface TeamProps {
id: string;
}
const Team = ({ id }: TeamProps) => {
const { t } = useTranslation();
const { data: team, isLoading, isError } = useTeam({ id });
if (isError) {
return (
<Text
$align="center"
$justify="center"
$height="100%"
$theme="danger"
$textAlign="center"
>
{t('Something bad happens, please retry.')}
</Text>
);
}
if (isLoading) {
return (
<Box $align="center" $justify="center" $height="100%">
<Loader />
</Box>
);
}
return (
<Text as="h3" $textAlign="center">
Teams: {team?.name}
</Text>
);
};
Page.getLayout = function getLayout(page: ReactElement) {
return <TeamLayout>{page}</TeamLayout>;
};
export default Page;

View File

@@ -1,4 +1,5 @@
import { Button, Input, Loader } from '@openfun/cunningham-react'; import { Button, Input, Loader } from '@openfun/cunningham-react';
import { useRouter } from 'next/navigation';
import { ReactElement, useState } from 'react'; import { ReactElement, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -12,7 +13,16 @@ import TeamLayout from './TeamLayout';
const Page: NextPageWithLayout = () => { const Page: NextPageWithLayout = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { mutate: createTeam, isError, isPending } = useCreateTeam(); const router = useRouter();
const {
mutate: createTeam,
isError,
isPending,
} = useCreateTeam({
onSuccess: (team) => {
router.push(`/teams/${team.id}`);
},
});
const [teamName, setTeamName] = useState(''); const [teamName, setTeamName] = useState('');
const { colorsTokens } = useCunninghamTheme(); const { colorsTokens } = useCunninghamTheme();

View File

@@ -58,4 +58,26 @@ test.describe('Teams', () => {
await expect(buttonCreateHomepage).toBeVisible(); await expect(buttonCreateHomepage).toBeVisible();
}); });
test('checks the routing on new team created', async ({
page,
browserName,
}) => {
const panel = page.getByLabel('Teams panel').first();
await panel.getByRole('button', { name: 'Add a team' }).click();
const teamName = `My routing team ${browserName}-${Math.floor(Math.random() * 1000)}`;
await page.getByText('Team name').fill(teamName);
await page.getByRole('button', { name: 'Create the team' }).click();
const elTeam = page.getByText(`Teams: ${teamName}`);
await expect(elTeam).toBeVisible();
await panel.getByRole('button', { name: 'Add a team' }).click();
await expect(elTeam).toBeHidden();
await panel.locator('li').getByText(teamName).click();
await expect(elTeam).toBeVisible();
});
}); });

View File

@@ -27,28 +27,23 @@ test.describe('Teams Panel', () => {
name: 'Add a team', name: 'Add a team',
}), }),
).toBeVisible(); ).toBeVisible();
await expect(
panel.getByText(
'Create your first team by clicking on the "Create a new team" button.',
),
).toBeVisible();
}); });
test('002 - checks the sort button', async ({ page, browserName }) => { test('002 - checks the sort button', async ({ page, browserName }) => {
const panel = page.getByLabel('Teams panel').first(); const panel = page.getByLabel('Teams panel').first();
await panel.getByRole('button', { name: 'Add a team' }).click(); const buttonCreate = page.getByRole('button', { name: 'Create the team' });
const randomTeams = Array.from({ length: 3 }, (_el, index) => {
const randomTeams = Array.from({ length: 3 }, () => { return `team-sort-${browserName}-${Math.floor(Math.random() * 1000)}-${index}`;
return `team-sort-${browserName}-${Math.floor(Math.random() * 1000)}`;
}); });
for (let i = 0; i < 3; i++) { for (let i = 0; i < randomTeams.length; i++) {
await page.getByText('Team name').fill(`${randomTeams[i]}-${i}`); await panel.getByRole('button', { name: 'Add a team' }).click();
await page.getByRole('button', { name: 'Create the team' }).click(); await page.getByText('Team name').fill(randomTeams[i]);
await expect(buttonCreate).toBeEnabled();
await buttonCreate.click();
await expect( await expect(
panel.locator('li').nth(0).getByText(`${randomTeams[i]}-${i}`), panel.locator('li').nth(0).getByText(randomTeams[i]),
).toBeVisible(); ).toBeVisible();
} }
@@ -58,32 +53,33 @@ test.describe('Teams Panel', () => {
}) })
.click(); .click();
for (let i = 0; i < 3; i++) { await expect(panel.locator('li').getByText(randomTeams[1])).toBeVisible();
await expect(
panel.locator('li').nth(i).getByText(`${randomTeams[i]}-${i}`), const allTeams = await panel.locator('li').allTextContents();
).toBeVisible(); const sortedTeamTexts = allTeams.filter((team) =>
} randomTeams.some((randomTeam) => team.includes(randomTeam)),
);
expect(sortedTeamTexts).toStrictEqual(randomTeams);
}); });
test('003 - checks the infinite scrool', async ({ page, browserName }) => { test('003 - checks the infinite scrool', async ({ page, browserName }) => {
test.setTimeout(90000); test.setTimeout(90000);
const panel = page.getByLabel('Teams panel').first(); const panel = page.getByLabel('Teams panel').first();
await panel.getByRole('button', { name: 'Add a team' }).click(); const buttonCreate = page.getByRole('button', { name: 'Create the team' });
const randomTeams = Array.from({ length: 40 }, (_el, index) => {
const randomTeams = Array.from({ length: 40 }, () => { return `team-infinite-${browserName}-${Math.floor(Math.random() * 10000)}-${index}`;
return `team-infinite-${browserName}-${Math.floor(Math.random() * 10000)}`;
}); });
for (let i = 0; i < 40; i++) { for (let i = 0; i < randomTeams.length; i++) {
await page.getByText('Team name').fill(`${randomTeams[i]}-${i}`); await panel.getByRole('button', { name: 'Add a team' }).click();
await page.getByRole('button', { name: 'Create the team' }).click(); await page.getByText('Team name').fill(randomTeams[i]);
await expect( await expect(buttonCreate).toBeEnabled();
panel.locator('li').getByText(`${randomTeams[i]}-${i}`), await buttonCreate.click();
).toBeVisible(); await expect(panel.locator('li').getByText(randomTeams[i])).toBeVisible();
} }
await expect(panel.locator('li')).toHaveCount(20); await expect(panel.locator('li')).toHaveCount(20);
await panel.getByText(`${randomTeams[24]}-${24}`).click(); await panel.getByText(randomTeams[24]).click();
await waitForElementCount(panel.locator('li'), 21, 10000); await waitForElementCount(panel.locator('li'), 21, 10000);
expect(await panel.locator('li').count()).toBeGreaterThan(20); expect(await panel.locator('li').count()).toBeGreaterThan(20);