✨(app-desk) integrate teams panel design
- Integrate teams panel design based from the mockup. - List teams from the API.
This commit is contained in:
@@ -3,6 +3,7 @@ const config = {
|
|||||||
default: {
|
default: {
|
||||||
theme: {
|
theme: {
|
||||||
colors: {
|
colors: {
|
||||||
|
'primary-bg': '#FAFAFA',
|
||||||
'primary-100': '#EDF5FA',
|
'primary-100': '#EDF5FA',
|
||||||
'primary-150': '#E5EEFA',
|
'primary-150': '#E5EEFA',
|
||||||
'info-150': '#E5EEFA',
|
'info-150': '#E5EEFA',
|
||||||
@@ -259,6 +260,16 @@ const config = {
|
|||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
'border-radius': '2px',
|
'border-radius': '2px',
|
||||||
|
primary: {
|
||||||
|
background: {
|
||||||
|
color: 'var(--c--theme--colors--primary-text)',
|
||||||
|
'color-hover': 'var(--c--theme--colors--primary-700)',
|
||||||
|
'color-active': 'var(--c--theme--colors--primary-900)',
|
||||||
|
},
|
||||||
|
color: '#ffffff',
|
||||||
|
'color-hover': '#ffffff',
|
||||||
|
'color-active': '#ffffff',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'forms-checkbox': {
|
'forms-checkbox': {
|
||||||
'border-radius': '0',
|
'border-radius': '0',
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ describe('Page', () => {
|
|||||||
|
|
||||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||||
|
|
||||||
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(
|
expect(
|
||||||
'Hello Desk !',
|
screen.getByRole('button', {
|
||||||
);
|
name: /Create a new team/i,
|
||||||
|
}),
|
||||||
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,17 +1,32 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { Button } from '@openfun/cunningham-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { Box } from '@/components';
|
import { Box } from '@/components';
|
||||||
import { Teams } from '@/features';
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
|
import { Panel } from '@/features';
|
||||||
|
|
||||||
|
const StyledButton = styled(Button)`
|
||||||
|
width: fit-content;
|
||||||
|
`;
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className="p-b">
|
<Box $height="inherit" $direction="row">
|
||||||
<h1>{t('Hello Desk !')}</h1>
|
<Panel />
|
||||||
<Teams />
|
<Box
|
||||||
|
$background={colorsTokens()['primary-bg']}
|
||||||
|
$justify="center"
|
||||||
|
$align="center"
|
||||||
|
$width="100%"
|
||||||
|
>
|
||||||
|
<StyledButton>{t('Create a new group')}</StyledButton>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1020 B After Width: | Height: | Size: 1020 B |
@@ -14,6 +14,7 @@ export interface TextProps extends BoxProps {
|
|||||||
'p' | 'span' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
|
'p' | 'span' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
|
||||||
>;
|
>;
|
||||||
$weight?: CSSProperties['fontWeight'];
|
$weight?: CSSProperties['fontWeight'];
|
||||||
|
$textAlign?: CSSProperties['textAlign'];
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
$size?: TextSizes | (string & {});
|
$size?: TextSizes | (string & {});
|
||||||
$theme?:
|
$theme?:
|
||||||
@@ -38,6 +39,7 @@ export interface TextProps extends BoxProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TextStyled = styled(Box)<TextProps>`
|
export const TextStyled = styled(Box)<TextProps>`
|
||||||
|
${({ $textAlign }) => $textAlign && `text-align: ${$textAlign};`}
|
||||||
${({ $weight }) => $weight && `font-weight: ${$weight};`}
|
${({ $weight }) => $weight && `font-weight: ${$weight};`}
|
||||||
${({ $size }) =>
|
${({ $size }) =>
|
||||||
$size &&
|
$size &&
|
||||||
|
|||||||
@@ -272,6 +272,13 @@ input:-webkit-autofill:focus {
|
|||||||
color: var(--c--components--button--primary--color);
|
color: var(--c--components--button--primary--color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c__button--primary:hover {
|
||||||
|
background-color: var(
|
||||||
|
--c--components--button--primary--background--color-hover
|
||||||
|
);
|
||||||
|
color: var(--c--components--button--primary--color-hover);
|
||||||
|
}
|
||||||
|
|
||||||
.c__button--primary:active,
|
.c__button--primary:active,
|
||||||
.c__button--primary.c__button--active {
|
.c__button--primary.c__button--active {
|
||||||
background-color: var(
|
background-color: var(
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
--c--theme--colors--success-text: var(--c--theme--colors--greyscale-000);
|
--c--theme--colors--success-text: var(--c--theme--colors--greyscale-000);
|
||||||
--c--theme--colors--warning-text: var(--c--theme--colors--greyscale-000);
|
--c--theme--colors--warning-text: var(--c--theme--colors--greyscale-000);
|
||||||
--c--theme--colors--danger-text: var(--c--theme--colors--greyscale-000);
|
--c--theme--colors--danger-text: var(--c--theme--colors--greyscale-000);
|
||||||
|
--c--theme--colors--primary-bg: #fafafa;
|
||||||
--c--theme--colors--primary-150: #e5eefa;
|
--c--theme--colors--primary-150: #e5eefa;
|
||||||
--c--theme--colors--info-150: #e5eefa;
|
--c--theme--colors--info-150: #e5eefa;
|
||||||
--c--theme--font--sizes--h1: 2.2rem;
|
--c--theme--font--sizes--h1: 2.2rem;
|
||||||
@@ -387,6 +388,18 @@
|
|||||||
--c--theme--font--families--base: marianne;
|
--c--theme--font--families--base: marianne;
|
||||||
--c--components--alert--border-radius: 0;
|
--c--components--alert--border-radius: 0;
|
||||||
--c--components--button--border-radius: 2px;
|
--c--components--button--border-radius: 2px;
|
||||||
|
--c--components--button--primary--background--color: var(
|
||||||
|
--c--theme--colors--primary-text
|
||||||
|
);
|
||||||
|
--c--components--button--primary--background--color-hover: var(
|
||||||
|
--c--theme--colors--primary-700
|
||||||
|
);
|
||||||
|
--c--components--button--primary--background--color-active: var(
|
||||||
|
--c--theme--colors--primary-900
|
||||||
|
);
|
||||||
|
--c--components--button--primary--color: #fff;
|
||||||
|
--c--components--button--primary--color-hover: #fff;
|
||||||
|
--c--components--button--primary--color-active: #fff;
|
||||||
--c--components--forms-checkbox--border-radius: 0;
|
--c--components--forms-checkbox--border-radius: 0;
|
||||||
--c--components--forms-switch--handle-border-radius: 2px;
|
--c--components--forms-switch--handle-border-radius: 2px;
|
||||||
--c--components--forms-switch--rail-border-radius: 4px;
|
--c--components--forms-switch--rail-border-radius: 4px;
|
||||||
@@ -677,6 +690,10 @@
|
|||||||
color: var(--c--theme--colors--danger-text);
|
color: var(--c--theme--colors--danger-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clr-primary-bg {
|
||||||
|
color: var(--c--theme--colors--primary-bg);
|
||||||
|
}
|
||||||
|
|
||||||
.clr-primary-150 {
|
.clr-primary-150 {
|
||||||
color: var(--c--theme--colors--primary-150);
|
color: var(--c--theme--colors--primary-150);
|
||||||
}
|
}
|
||||||
@@ -965,6 +982,10 @@
|
|||||||
background-color: var(--c--theme--colors--danger-text);
|
background-color: var(--c--theme--colors--danger-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-primary-bg {
|
||||||
|
background-color: var(--c--theme--colors--primary-bg);
|
||||||
|
}
|
||||||
|
|
||||||
.bg-primary-150 {
|
.bg-primary-150 {
|
||||||
background-color: var(--c--theme--colors--primary-150);
|
background-color: var(--c--theme--colors--primary-150);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export const tokens = {
|
|||||||
'success-text': '#FFFFFF',
|
'success-text': '#FFFFFF',
|
||||||
'warning-text': '#FFFFFF',
|
'warning-text': '#FFFFFF',
|
||||||
'danger-text': '#FFFFFF',
|
'danger-text': '#FFFFFF',
|
||||||
|
'primary-bg': '#FAFAFA',
|
||||||
'primary-150': '#E5EEFA',
|
'primary-150': '#E5EEFA',
|
||||||
'info-150': '#E5EEFA',
|
'info-150': '#E5EEFA',
|
||||||
},
|
},
|
||||||
@@ -396,7 +397,19 @@ export const tokens = {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
alert: { 'border-radius': '0' },
|
alert: { 'border-radius': '0' },
|
||||||
button: { 'border-radius': '2px' },
|
button: {
|
||||||
|
'border-radius': '2px',
|
||||||
|
primary: {
|
||||||
|
background: {
|
||||||
|
color: 'var(--c--theme--colors--primary-text)',
|
||||||
|
'color-hover': 'var(--c--theme--colors--primary-700)',
|
||||||
|
'color-active': 'var(--c--theme--colors--primary-900)',
|
||||||
|
},
|
||||||
|
color: '#ffffff',
|
||||||
|
'color-hover': '#ffffff',
|
||||||
|
'color-active': '#ffffff',
|
||||||
|
},
|
||||||
|
},
|
||||||
'forms-checkbox': { 'border-radius': '0' },
|
'forms-checkbox': { 'border-radius': '0' },
|
||||||
'forms-switch': {
|
'forms-switch': {
|
||||||
'handle-border-radius': '2px',
|
'handle-border-radius': '2px',
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import IconGroup from '@/assets/icons/icon-group.svg';
|
||||||
import { Box } from '@/components/';
|
import { Box } from '@/components/';
|
||||||
import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
|
import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
|
||||||
|
|
||||||
import MenuItem from './MenuItems';
|
import MenuItem from './MenuItems';
|
||||||
import IconRecent from './assets/icon-clock.svg';
|
import IconRecent from './assets/icon-clock.svg';
|
||||||
import IconContacts from './assets/icon-contacts.svg';
|
import IconContacts from './assets/icon-contacts.svg';
|
||||||
import IconGroup from './assets/icon-group.svg';
|
|
||||||
import IconSearch from './assets/icon-search.svg';
|
import IconSearch from './assets/icon-search.svg';
|
||||||
import IconFavorite from './assets/icon-stars.svg';
|
import IconFavorite from './assets/icon-stars.svg';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import '@testing-library/jest-dom';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import fetchMock from 'fetch-mock';
|
||||||
|
|
||||||
|
import { AppWrapper } from '@/tests/utils';
|
||||||
|
|
||||||
|
import { PanelTeams } from '../components/PanelTeams';
|
||||||
|
|
||||||
|
describe('PanelTeams', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
fetchMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders with no team to display', async () => {
|
||||||
|
fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, {
|
||||||
|
count: 0,
|
||||||
|
results: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<PanelTeams />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
|
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText(
|
||||||
|
'Create your first team by clicking on the "Create a new team" button.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders with empty team to display', async () => {
|
||||||
|
fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, {
|
||||||
|
count: 1,
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
name: 'Team 1',
|
||||||
|
accesses: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<PanelTeams />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
|
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(await screen.findByLabelText('Empty team icon')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders with not team to display', async () => {
|
||||||
|
fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, {
|
||||||
|
count: 1,
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
name: 'Team 1',
|
||||||
|
accesses: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
role: 'admin',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<PanelTeams />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
|
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(await screen.findByLabelText('Team icon')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the error', async () => {
|
||||||
|
fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<PanelTeams />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
|
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await screen.findByText(
|
||||||
|
'Something bad happens, please refresh the page.',
|
||||||
|
),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import '@testing-library/jest-dom';
|
|
||||||
import { render, screen } from '@testing-library/react';
|
|
||||||
import fetchMock from 'fetch-mock';
|
|
||||||
|
|
||||||
import { AppWrapper } from '@/tests/utils';
|
|
||||||
|
|
||||||
import { Teams } from '..';
|
|
||||||
|
|
||||||
describe('Teams', () => {
|
|
||||||
afterEach(() => {
|
|
||||||
fetchMock.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('checks Teams rendering', async () => {
|
|
||||||
fetchMock.mock(`/api/teams/`, {
|
|
||||||
results: [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
name: 'Team 1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
name: 'Team 2',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
render(<Teams />, { wrapper: AppWrapper });
|
|
||||||
|
|
||||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await screen.findByRole('button', {
|
|
||||||
name: 'Create Team',
|
|
||||||
}),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
|
|
||||||
expect(screen.getByText(/Team 1/)).toBeInTheDocument();
|
|
||||||
expect(screen.getByText(/Team 2/)).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -2,9 +2,22 @@ import { UseQueryOptions, useQuery } from '@tanstack/react-query';
|
|||||||
|
|
||||||
import { APIList, fetchAPI } from '@/api';
|
import { APIList, fetchAPI } from '@/api';
|
||||||
|
|
||||||
|
enum Role {
|
||||||
|
MEMBER = 'member',
|
||||||
|
ADMIN = 'administrator',
|
||||||
|
OWNER = 'owner',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Access {
|
||||||
|
id: string;
|
||||||
|
role: Role;
|
||||||
|
user: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface TeamResponse {
|
interface TeamResponse {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
accesses: Access[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type TeamsResponse = APIList<TeamResponse>;
|
type TeamsResponse = APIList<TeamResponse>;
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<svg
|
||||||
|
width="30"
|
||||||
|
height="30"
|
||||||
|
viewBox="0 0 30 30"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0_178_17838)">
|
||||||
|
<path
|
||||||
|
d="M16.25 8.75H13.75V13.75H8.75V16.25H13.75V21.25H16.25V16.25H21.25V13.75H16.25V8.75ZM15 2.5C8.1 2.5 2.5 8.1 2.5 15C2.5 21.9 8.1 27.5 15 27.5C21.9 27.5 27.5 21.9 27.5 15C27.5 8.1 21.9 2.5 15 2.5ZM15 25C9.4875 25 5 20.5125 5 15C5 9.4875 9.4875 5 15 5C20.5125 5 25 9.4875 25 15C25 20.5125 20.5125 25 15 25Z"
|
||||||
|
fill="#000091"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_178_17838">
|
||||||
|
<rect width="30" height="30" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 633 B |
@@ -0,0 +1,13 @@
|
|||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_508_5524)">
|
||||||
|
<path
|
||||||
|
d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM4 12C4 7.58 7.58 4 12 4C13.85 4 15.55 4.63 16.9 5.69L5.69 16.9C4.63 15.55 4 13.85 4 12ZM12 20C10.15 20 8.45 19.37 7.1 18.31L18.31 7.1C19.37 8.45 20 10.15 20 12C20 16.42 16.42 20 12 20Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_508_5524">
|
||||||
|
<rect width="24" height="24" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 578 B |
@@ -0,0 +1,19 @@
|
|||||||
|
<svg
|
||||||
|
width="30"
|
||||||
|
height="30"
|
||||||
|
viewBox="0 0 30 30"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0_178_17837)">
|
||||||
|
<path
|
||||||
|
d="M11.25 3.75L6.25 8.7375H10V17.5H12.5V8.7375H16.25L11.25 3.75ZM20 21.2625V12.5H17.5V21.2625H13.75L18.75 26.25L23.75 21.2625H20Z"
|
||||||
|
fill="#000091"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_178_17837">
|
||||||
|
<rect width="30" height="30" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 458 B |
@@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Box, Text } from '@/components';
|
||||||
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
|
|
||||||
|
import { PanelActions } from './PanelActions';
|
||||||
|
import { PanelTeams } from './PanelTeams';
|
||||||
|
|
||||||
|
export const Panel = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
$width="28rem"
|
||||||
|
$css={`
|
||||||
|
border-right: 1px solid ${colorsTokens()['primary-300']};
|
||||||
|
`}
|
||||||
|
$height="inherit"
|
||||||
|
aria-label="Teams panel"
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
className="p-s"
|
||||||
|
$direction="row"
|
||||||
|
$align="center"
|
||||||
|
$justify="space-between"
|
||||||
|
$css={`
|
||||||
|
border-bottom: 1px solid ${colorsTokens()['primary-300']};
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<Text $weight="bold" $size="1.25rem">
|
||||||
|
{t('Recents')}
|
||||||
|
</Text>
|
||||||
|
<PanelActions />
|
||||||
|
</Box>
|
||||||
|
<PanelTeams />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { Button } from '@openfun/cunningham-react';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { Box } from '@/components';
|
||||||
|
|
||||||
|
import { default as IconAdd } from '../assets/icon-add.svg?url';
|
||||||
|
import { default as IconSort } from '../assets/icon-sort.svg?url';
|
||||||
|
|
||||||
|
export const PanelActions = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
$direction="row"
|
||||||
|
$gap="1rem"
|
||||||
|
$css={`
|
||||||
|
& > button {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
aria-label={t('Sort the teams')}
|
||||||
|
icon={<Image priority src={IconSort} alt={t('Sort teams icon')} />}
|
||||||
|
color="tertiary"
|
||||||
|
className="c__button-no-bg p-0 m-0"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
aria-label={t('Add a team')}
|
||||||
|
icon={<Image priority src={IconAdd} alt={t('Add team icon')} />}
|
||||||
|
color="tertiary"
|
||||||
|
className="c__button-no-bg p-0 m-0"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { Loader } from '@openfun/cunningham-react';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import IconGroup from '@/assets/icons/icon-group.svg';
|
||||||
|
import { Box, Text } from '@/components';
|
||||||
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
|
|
||||||
|
import { useTeams } from '../api/useTeams';
|
||||||
|
import IconNone from '../assets/icon-none.svg';
|
||||||
|
|
||||||
|
export const PanelTeams = () => {
|
||||||
|
const { data, isPending, isError } = useTeams();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
|
if (isPending) {
|
||||||
|
return (
|
||||||
|
<Box $align="center" className="m-l">
|
||||||
|
<Loader />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Box $justify="center" className="m-b">
|
||||||
|
<Text $theme="danger" $align="center" $textAlign="center">
|
||||||
|
{t('Something bad happens, please refresh the page.')}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.count) {
|
||||||
|
return (
|
||||||
|
<Box $justify="center" className="m-b">
|
||||||
|
<Text as="p" className="mb-0 mt-0" $theme="greyscale" $variation="500">
|
||||||
|
{t('0 group to display.')}
|
||||||
|
</Text>
|
||||||
|
<Text as="p" $theme="greyscale" $variation="500">
|
||||||
|
{t(
|
||||||
|
'Create your first team by clicking on the "Create a new team" button.',
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box as="ul" $gap="1rem" className="p-s mt-t">
|
||||||
|
{data?.results.map((team) => (
|
||||||
|
<Box
|
||||||
|
as="li"
|
||||||
|
key={team.id}
|
||||||
|
$direction="row"
|
||||||
|
$align="center"
|
||||||
|
$gap="0.5rem"
|
||||||
|
>
|
||||||
|
{team.accesses.length ? (
|
||||||
|
<IconGroup
|
||||||
|
className="p-t"
|
||||||
|
width={36}
|
||||||
|
aria-label={t(`Team icon`)}
|
||||||
|
color={colorsTokens()['primary-500']}
|
||||||
|
style={{
|
||||||
|
borderRadius: '10px',
|
||||||
|
border: `1px solid ${colorsTokens()['primary-300']}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IconNone
|
||||||
|
className="p-t"
|
||||||
|
width={36}
|
||||||
|
aria-label={t(`Empty team icon`)}
|
||||||
|
color={colorsTokens()['greyscale-500']}
|
||||||
|
style={{
|
||||||
|
borderRadius: '10px',
|
||||||
|
border: `1px solid ${colorsTokens()['greyscale-300']}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Text $weight="bold">{team.name}</Text>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './Panel';
|
||||||
@@ -1,49 +1 @@
|
|||||||
import { Button, Field, Input, Loader } from '@openfun/cunningham-react';
|
export * from './components/Panel';
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { useCreateTeam } from './api/useCreateTeam';
|
|
||||||
import { useTeams } from './api/useTeams';
|
|
||||||
|
|
||||||
export const Teams = () => {
|
|
||||||
const { data, isPending } = useTeams();
|
|
||||||
const { mutate: createTeam } = useCreateTeam();
|
|
||||||
const [teamName, setTeamName] = useState('');
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
if (isPending) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Field>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
label={t('Team name')}
|
|
||||||
onChange={(e) => setTeamName(e.target.value)}
|
|
||||||
/>
|
|
||||||
<Button fullWidth onClick={() => createTeam(teamName)} className="mt-s">
|
|
||||||
{t('Create Team')}
|
|
||||||
</Button>
|
|
||||||
</Field>
|
|
||||||
<section>
|
|
||||||
<ul>
|
|
||||||
{data?.results.map((post, index) => (
|
|
||||||
<li key={post.id}>
|
|
||||||
<div>
|
|
||||||
<span>
|
|
||||||
{index + 1}. {post.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
"{{label}} button": "Bouton {{label}}",
|
"{{label}} button": "Bouton {{label}}",
|
||||||
"{{label}} icon": "Icône {{label}}",
|
"{{label}} icon": "Icône {{label}}",
|
||||||
"Team name": "Nom de l’équipe",
|
"Team name": "Nom de l’équipe",
|
||||||
"Create Team": "Créer une équipe"
|
"Create Team": "Créer une équipe",
|
||||||
|
"Create a new team": "Créer un nouveau groupe"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
|
||||||
|
|
||||||
import { keyCloakSignIn } from './common';
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
await keyCloakSignIn(page);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.describe('App', () => {
|
|
||||||
test('should display the main elements', async ({ page }) => {
|
|
||||||
await expect(page.locator('header').first()).toContainText('Desk');
|
|
||||||
await expect(page.getByLabel('Team name')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('creates 2 teams and displayed them', async ({ page }) => {
|
|
||||||
await page.getByLabel('Team name').fill('My new team');
|
|
||||||
await page.click('button:has-text("Create Team")');
|
|
||||||
await page.getByLabel('Team name').fill('My second new team');
|
|
||||||
await page.click('button:has-text("Create Team")');
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.locator('li').getByText('My new team').first(),
|
|
||||||
).toBeVisible();
|
|
||||||
await expect(
|
|
||||||
page.locator('li').getByText('My second new team').first(),
|
|
||||||
).toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -24,7 +24,7 @@ test.describe('Header', () => {
|
|||||||
);
|
);
|
||||||
await expect(header.locator('h2').getByText('Desk')).toHaveCSS(
|
await expect(header.locator('h2').getByText('Desk')).toHaveCSS(
|
||||||
'font-family',
|
'font-family',
|
||||||
'marianne',
|
/Marianne/i,
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ test.beforeEach(async ({ page }) => {
|
|||||||
test.describe('Language', () => {
|
test.describe('Language', () => {
|
||||||
test('checks the language picker', async ({ page }) => {
|
test('checks the language picker', async ({ page }) => {
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('h1').first().getByText('Hello Desk !'),
|
page.getByRole('button', {
|
||||||
|
name: 'Create a new team',
|
||||||
|
}),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
const header = page.locator('header').first();
|
const header = page.locator('header').first();
|
||||||
@@ -19,7 +21,9 @@ test.describe('Language', () => {
|
|||||||
await expect(header.getByRole('combobox').getByText('FR')).toBeVisible();
|
await expect(header.getByRole('combobox').getByText('FR')).toBeVisible();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('h1').first().getByText('Bonjour Desk !'),
|
page.getByRole('button', {
|
||||||
|
name: 'Créer un nouveau groupe',
|
||||||
|
}),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ test.describe('Menu', () => {
|
|||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('h1').first().getByText('Hello Desk !'),
|
page.getByRole('button', {
|
||||||
|
name: 'Create a new team',
|
||||||
|
}),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
const menu = page.locator('menu').first();
|
const menu = page.locator('menu').first();
|
||||||
@@ -57,11 +59,15 @@ test.describe('Menu', () => {
|
|||||||
// eslint-disable-next-line playwright/no-conditional-in-test
|
// eslint-disable-next-line playwright/no-conditional-in-test
|
||||||
if (isDefault) {
|
if (isDefault) {
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('h1').first().getByText('Hello Desk !'),
|
page.getByRole('button', {
|
||||||
|
name: 'Create a new team',
|
||||||
|
}),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
} else {
|
} else {
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('h1').first().getByText('Hello Desk !'),
|
page.getByRole('button', {
|
||||||
|
name: 'Create a new team',
|
||||||
|
}),
|
||||||
).toBeHidden();
|
).toBeHidden();
|
||||||
|
|
||||||
const reg = new RegExp(name.toLowerCase());
|
const reg = new RegExp(name.toLowerCase());
|
||||||
|
|||||||
34
src/frontend/apps/e2e/__tests__/app-desk/teams.spec.ts
Normal file
34
src/frontend/apps/e2e/__tests__/app-desk/teams.spec.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
import { keyCloakSignIn } from "./common";
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
await keyCloakSignIn(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe("Teams", () => {
|
||||||
|
test("checks all the elements are visible", async ({ page }) => {
|
||||||
|
const panel = page.getByLabel("Teams panel").first();
|
||||||
|
|
||||||
|
await expect(panel.getByText("Recents")).toBeVisible();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
panel.getByRole("button", {
|
||||||
|
name: "Sort the teams",
|
||||||
|
}),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
panel.getByRole("button", {
|
||||||
|
name: "Add a team",
|
||||||
|
}),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
panel.getByText(
|
||||||
|
'Create your first team by clicking on the "Create a new team" button.',
|
||||||
|
),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user