(frontend) use user abilities to show or not the features

use the abilities to show or not the Teams / Mailbox features as well as the
object creation button
This commit is contained in:
Nathan Panchout
2024-11-12 10:22:49 +01:00
committed by BEY Quentin
parent 6be1b63277
commit 6123e11dd4
7 changed files with 205 additions and 24 deletions

View File

@@ -4,6 +4,7 @@ import { render, screen } from '@testing-library/react';
import { AppWrapper } from '@/tests/utils';
import { MainLayout } from '../MainLayout';
import { useAuthStore } from '../auth';
import { useConfigStore } from '../config';
jest.mock('next/navigation', () => ({
@@ -19,6 +20,19 @@ describe('MainLayout', () => {
useConfigStore.setState({
config: { RELEASE: '1.0.0', FEATURES: { TEAMS: true }, LANGUAGES: [] },
});
useAuthStore.setState({
authenticated: true,
userData: {
id: '1',
email: 'test@example.com',
name: 'Test User',
abilities: {
contacts: { can_view: true },
teams: { can_view: true },
mailboxes: { can_view: true },
},
},
});
render(<MainLayout />, { wrapper: AppWrapper });
@@ -35,10 +49,57 @@ describe('MainLayout', () => {
).toBeInTheDocument();
});
it('checks menu rendering with no abilities', () => {
useConfigStore.setState({
config: { RELEASE: '1.0.0', FEATURES: { TEAMS: true }, LANGUAGES: [] },
});
useAuthStore.setState({
authenticated: true,
userData: {
id: '1',
email: 'test@example.com',
name: 'Test User',
abilities: {
contacts: { can_view: false },
teams: { can_view: false },
mailboxes: { can_view: false },
},
},
});
render(<MainLayout />, { wrapper: AppWrapper });
expect(
screen.queryByRole('button', {
// Changé de getByRole à queryByRole
name: /Teams button/i,
}),
).not.toBeInTheDocument(); //
expect(
screen.queryByRole('button', {
name: /Mail Domains button/i,
}),
).not.toBeInTheDocument();
});
it('checks menu rendering without team feature', () => {
useConfigStore.setState({
config: { RELEASE: '1.0.0', FEATURES: { TEAMS: false }, LANGUAGES: [] },
});
useAuthStore.setState({
authenticated: true,
userData: {
id: '1',
email: 'test@example.com',
name: 'Test User',
abilities: {
contacts: { can_view: true },
teams: { can_view: true },
mailboxes: { can_view: true },
},
},
});
render(<MainLayout />, { wrapper: AppWrapper });

View File

@@ -9,4 +9,14 @@ export interface User {
id: string;
email: string;
name?: string;
abilities?: {
mailboxes: UserAbilities;
contacts: UserAbilities;
teams: UserAbilities;
};
}
export type UserAbilities = {
can_view?: boolean;
can_create?: boolean;
};

View File

@@ -35,7 +35,11 @@ describe('getMailDomainAccesses', () => {
{
id: '2',
role: Role.VIEWER,
user: { id: '12', name: 'username2', email: 'user2@test.com' },
user: {
id: '12',
name: 'username2',
email: 'user2@test.com',
},
can_set_role_to: [Role.VIEWER],
},
],

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import IconGroup from '@/assets/icons/icon-group.svg';
import { Box } from '@/components/';
import { useAuthStore } from '@/core/auth';
import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
import MenuItem from './MenuItems';
@@ -10,6 +10,9 @@ import IconMailDomains from './assets/icon-mails.svg';
export const Menu = () => {
const { colorsTokens } = useCunninghamTheme();
const { userData } = useAuthStore();
console.log(userData);
const { t } = useTranslation();
return (
@@ -22,17 +25,21 @@ export const Menu = () => {
$margin="none"
>
<Box $padding={{ top: 'large' }} $direction="column" $gap="0.8rem">
<MenuItem
Icon={IconGroup}
label={t('Teams')}
href="/teams"
alias={['/teams']}
/>
<MenuItem
Icon={IconMailDomains}
label={t('Mail Domains')}
href="/mail-domains"
/>
{userData?.abilities?.teams.can_view && (
<MenuItem
Icon={IconGroup}
label={t('Teams')}
href="/teams"
alias={['/teams']}
/>
)}
{userData?.abilities?.mailboxes.can_view && (
<MenuItem
Icon={IconMailDomains}
label={t('Mail Domains')}
href="/mail-domains"
/>
)}
</Box>
</Box>
);

View File

@@ -4,7 +4,8 @@ import type { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { Box } from '@/components';
import { Box, Text } from '@/components';
import { useAuthStore } from '@/core/auth';
import { MailDomainsLayout } from '@/features/mail-domains/domains';
import { NextPageWithLayout } from '@/types/next';
@@ -14,14 +15,18 @@ const StyledButton = styled(Button)`
const Page: NextPageWithLayout = () => {
const { t } = useTranslation();
const { userData } = useAuthStore();
const can_create = userData?.abilities?.mailboxes.can_create;
const router = useRouter();
return (
<Box $align="center" $justify="center" $height="inherit">
<StyledButton onClick={() => void router.push('/mail-domains/add')}>
{t('Add a mail domain')}
</StyledButton>
{can_create && (
<StyledButton onClick={() => void router.push('/mail-domains/add')}>
{t('Add a mail domain')}
</StyledButton>
)}
{!can_create && <Text>{t('Click on mailbox to view details')}</Text>}
</Box>
);
};

View File

@@ -1,10 +1,11 @@
import { Button } from '@openfun/cunningham-react';
import { useRouter as useNavigate } from 'next/navigation';
import type { ReactElement } from 'react';
import { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { Box } from '@/components';
import { Box, Text } from '@/components';
import { useAuthStore } from '@/core/auth';
import { TeamLayout } from '@/features/teams/team-management';
import { NextPageWithLayout } from '@/types/next';
@@ -15,12 +16,17 @@ const StyledButton = styled(Button)`
const Page: NextPageWithLayout = () => {
const { t } = useTranslation();
const router = useNavigate();
const { userData } = useAuthStore();
const can_create = userData?.abilities?.teams.can_create ?? false;
return (
<Box $align="center" $justify="center" $height="inherit">
<StyledButton onClick={() => void router.push('/teams/create')}>
{t('Create a new team')}
</StyledButton>
{can_create && (
<StyledButton onClick={() => void router.push('/teams/create')}>
{t('Create a new team')}
</StyledButton>
)}
{!can_create && <Text>{t('Click on team to view details')}</Text>}
</Box>
);
};

View File

@@ -13,7 +13,7 @@ test.describe('Menu', () => {
name: 'Teams',
isDefault: true,
expectedUrl: '/teams/',
expectedText: 'Create a new team',
expectedText: 'Teams',
},
{
name: 'Mail Domains',
@@ -62,6 +62,94 @@ test.describe('Menu', () => {
});
}
test(`it checks that the menu is not displaying when no abilities`, async ({
page,
}) => {
await page.route('**/api/v1.0/users/me/', async (route) => {
await route.fulfill({
json: {
id: '52de4dcf-5ca0-4b7f-9841-3a18e8cb6a95',
email: 'user@chromium.e2e',
language: 'en-us',
name: 'E2E Chromium',
timezone: 'UTC',
is_device: false,
is_staff: false,
abilities: {
contacts: {
can_view: true,
can_create: true,
},
teams: {
can_view: true,
can_create: false,
},
mailboxes: {
can_view: true,
can_create: false,
},
},
},
});
});
const menu = page.locator('menu').first();
let buttonMenu = menu.getByLabel(`Teams button`);
await buttonMenu.click();
await expect(
page.getByText('Click on team to view details').first(),
).toBeVisible();
buttonMenu = menu.getByLabel(`Mail Domains`);
await buttonMenu.click();
await expect(
page.getByText('Click on mailbox to view details').first(),
).toBeVisible();
});
test(`it checks that the menu is not displaying when all abilities`, async ({
page,
}) => {
await page.route('**/api/v1.0/users/me/', async (route) => {
await route.fulfill({
json: {
id: '52de4dcf-5ca0-4b7f-9841-3a18e8cb6a95',
email: 'user@chromium.e2e',
language: 'en-us',
name: 'E2E ChromiumMM',
timezone: 'UTC',
is_device: false,
is_staff: false,
abilities: {
contacts: {
can_view: true,
can_create: true,
},
teams: {
can_view: true,
can_create: true,
},
mailboxes: {
can_view: true,
can_create: true,
},
},
},
});
});
const menu = page.locator('menu').first();
let buttonMenu = menu.getByLabel(`Teams button`);
await buttonMenu.click();
await expect(page.getByText('Create a new team').first()).toBeVisible();
buttonMenu = menu.getByLabel(`Mail Domains`);
await buttonMenu.click();
await expect(page.getByText('Add a mail domain').first()).toBeVisible();
});
test(`it checks that the sub menu is still highlighted`, async ({
page,
browserName,