Skip to content

Commit

Permalink
✨(frontend) add tabs for mail domain page
Browse files Browse the repository at this point in the history
Currently, it is complicated to understand the navigation between mailbox
management and role management for an email domain.
This is why we add tabs with explicit naming
  • Loading branch information
PanchoutNathan committed Oct 22, 2024
1 parent 6a22169 commit f853083
Show file tree
Hide file tree
Showing 14 changed files with 958 additions and 172 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/people.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set services env variables
run: |
make create-env-files
Expand All @@ -175,15 +175,15 @@ jobs:
with:
path: src/frontend/apps/desk/out/
key: build-front-${{ github.run_id }}

- name: Build and Start Docker Servers
env:
DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1
run: |
docker compose build --pull --build-arg BUILDKIT_INLINE_CACHE=1
make run
- name: Apply DRF migrations
run: |
make migrate
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to
- ✨(api) add RELEASE version on config endpoint #459
- ✨(backend) manage roles on domain admin view
- ✨(frontend) show version number in footer #369
- ✨(frontend) add tabs inside #466

### Changed

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { Button } from '@openfun/cunningham-react';
import { useRouter } from 'next/navigation';
import React from 'react';
import { useTranslation } from 'react-i18next';

import { Box, Text } from '@/components';
import { AccessesGrid } from '@/features/mail-domains/access-management/components/AccessesGrid';
import MailDomainsLogo from '@/features/mail-domains/assets/mail-domains-logo.svg';

import { MailDomain, Role } from '../../domains';

Expand All @@ -17,50 +12,6 @@ export const AccessesContent = ({
currentRole: Role;
}) => (
<>
<TopBanner mailDomain={mailDomain} />
<AccessesGrid mailDomain={mailDomain} currentRole={currentRole} />
</>
);

const TopBanner = ({ mailDomain }: { mailDomain: MailDomain }) => {
const router = useRouter();
const { t } = useTranslation();

return (
<Box
$direction="column"
$margin={{ all: 'big', bottom: 'tiny' }}
$gap="1rem"
>
<Box
$direction="row"
$align="center"
$gap="2.25rem"
$justify="space-between"
>
<Box $direction="row" $margin="none" $gap="0.5rem">
<MailDomainsLogo aria-hidden="true" />
<Text $margin="none" as="h3" $size="h3">
{mailDomain?.name}
</Text>
</Box>
</Box>

<Box $direction="row" $justify="flex-end">
<Box $display="flex" $direction="row" $gap="8rem">
{mailDomain?.abilities?.manage_accesses && (
<Button
color="tertiary"
aria-label={t('Manage {{name}} domain mailboxes', {
name: mailDomain?.name,
})}
onClick={() => router.push(`/mail-domains/${mailDomain.slug}/`)}
>
{t('Manage mailboxes')}
</Button>
)}
</Box>
</Box>
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,6 @@ export const AccessesGrid = ({

return (
<Card
$padding={{ bottom: 'small' }}
$margin={{ all: 'big', top: 'none' }}
$overflow="auto"
$css={`
& .c__pagination__goto {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';
import { useRouter } from 'next/navigation';

import { AccessesGrid } from '@/features/mail-domains/access-management';
import { AppWrapper } from '@/tests/utils';

import { MailDomain, Role } from '../../../domains';
Expand Down Expand Up @@ -61,58 +59,9 @@ describe('AccessesContent', () => {
});
});

it('renders the top banner and accesses grid correctly', () => {
it('renders the accesses grid correctly', () => {
renderAccessesContent();

expect(screen.getByText(mockMailDomain.name)).toBeInTheDocument();
expect(screen.getByTestId('mail-domains-logo')).toBeInTheDocument();
expect(screen.getByText('Mock AccessesGrid')).toBeInTheDocument();
});

it('renders the "Manage mailboxes" button when the user has access', () => {
renderAccessesContent();

const manageMailboxesButton = screen.getByRole('button', {
name: /Manage example.com domain mailboxes/,
});

expect(manageMailboxesButton).toBeInTheDocument();

expect(AccessesGrid).toHaveBeenCalledWith(
{ currentRole: Role.ADMIN, mailDomain: mockMailDomain },
{}, // adding this empty object is necessary to load jest context and that AccessesGrid is a mock
);
});

it('does not render the "Manage mailboxes" button if the user lacks manage_accesses ability', () => {
const mailDomainWithoutAccess = {
...mockMailDomain,
abilities: {
...mockMailDomain.abilities,
manage_accesses: false,
},
};

renderAccessesContent(Role.ADMIN, mailDomainWithoutAccess);

expect(
screen.queryByRole('button', {
name: /Manage mailboxes/i,
}),
).not.toBeInTheDocument();
});

it('navigates to the mailboxes management page when "Manage mailboxes" is clicked', async () => {
renderAccessesContent();

const manageMailboxesButton = screen.getByRole('button', {
name: /Manage example.com domain mailboxes/,
});

await userEvent.click(manageMailboxesButton);

await waitFor(() => {
expect(mockRouterPush).toHaveBeenCalledWith(`/mail-domains/example-com/`);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ import {
VariantType,
usePagination,
} from '@openfun/cunningham-react';
import { useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';

import { Box, Card, Text, TextErrors, TextStyled } from '@/components';
import { ModalCreateMailbox } from '@/features/mail-domains/mailboxes';

import { default as MailDomainsLogo } from '../../assets/mail-domains-logo.svg';
import { PAGE_SIZE } from '../../conf';
import { MailDomain } from '../../domains/types';
import { useMailboxes } from '../api/useMailboxes';
Expand Down Expand Up @@ -99,12 +97,7 @@ export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) {
showMailBoxCreationForm={setIsCreateMailboxFormVisible}
/>

<Card
$padding={{ bottom: 'small' }}
$margin={{ all: 'big', top: 'none' }}
$overflow="auto"
aria-label={t('Mailboxes list card')}
>
<Card $overflow="auto" aria-label={t('Mailboxes list card')}>
{error && <TextErrors causes={error.cause} />}

<DataGrid
Expand Down Expand Up @@ -153,47 +146,21 @@ const TopBanner = ({
mailDomain: MailDomain;
showMailBoxCreationForm: (value: boolean) => void;
}) => {
const router = useRouter();
const { t } = useTranslation();

return (
<Box
$direction="column"
$margin={{ all: 'big', bottom: 'tiny' }}
$gap="1rem"
>
<Box $direction="column" $gap="1rem">
<Box $direction="row" $justify="space-between">
<AlertStatus status={mailDomain.status} />
</Box>

<Box
$direction="row"
$justify="flex-end"
$margin={{ bottom: 'small' }}
$align="center"
$gap="2.25rem"
$justify="space-between"
>
<Box $direction="row" $margin="none" $gap="0.5rem">
<MailDomainsLogo aria-hidden="true" />
<Text $margin="none" as="h3" $size="h3">
{mailDomain?.name}
</Text>
</Box>
</Box>

<Box $direction="row" $justify="space-between">
<AlertStatus status={mailDomain.status} />
</Box>
<Box $direction="row" $justify="flex-end">
<Box $display="flex" $direction="row" $gap="0.5rem">
{mailDomain?.abilities?.manage_accesses && (
<Button
color="tertiary"
aria-label={t('Manage {{name}} domain members', {
name: mailDomain?.name,
})}
onClick={() =>
router.push(`/mail-domains/${mailDomain.slug}/accesses/`)
}
>
{t('Manage accesses')}
</Button>
)}
<Box $display="flex" $direction="row">
{mailDomain?.abilities.post && (
<Button
aria-label={t('Create a mailbox in {{name}} domain', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,25 +187,6 @@ describe('MailDomainsContent', () => {
});
});

it('redirects to accesses management page when button is clicked by granted user', async () => {
fetchMock.get('end:/mail-domains/example-com/mailboxes/?page=1', {
count: 0,
results: [],
});

render(<MailDomainsContent mailDomain={mockMailDomain} />, {
wrapper: AppWrapper,
});

await waitFor(async () => {
await userEvent.click(screen.getByText('Manage accesses'));
});

expect(mockPush).toHaveBeenCalledWith(
'/mail-domains/example-com/accesses/',
);
});

it('displays the correct alert based on mail domain status', async () => {
fetchMock.get('end:/mail-domains/example-com/mailboxes/?page=1', {
count: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as React from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
import { useTranslation } from 'react-i18next';

import { Box, Text } from '@/components';
import { AccessesContent } from '@/features/mail-domains/access-management';
import MailDomainsLogo from '@/features/mail-domains/assets/mail-domains-logo.svg';
import { MailDomain, Role } from '@/features/mail-domains/domains';
import { MailDomainsContent } from '@/features/mail-domains/mailboxes';

type Props = {
mailDomain: MailDomain;
};
export const MailDomainView = ({ mailDomain }: Props) => {
const { t } = useTranslation();
const currentRole = mailDomain.abilities.delete
? Role.OWNER
: mailDomain.abilities.manage_accesses
? Role.ADMIN
: Role.VIEWER;

return (
<Box $padding="big">
<Box
$width="100%"
$direction="row"
$align="center"
$gap="2.25rem"
$justify="center"
>
<Box
$direction="row"
$justify="center"
$margin={{ bottom: 'big' }}
$gap="0.5rem"
>
<MailDomainsLogo aria-hidden="true" />
<Text $margin="none" as="h3" $size="h3">
{mailDomain?.name}
</Text>
</Box>
</Box>
<Tabs>
<TabList aria-label="History of Ancient Rome">
<Tab aria-label={t('Go to mailbox management')} id="mails">
<Box $direction="row" $align="center" $gap="5px">
<span className="material-icons" aria-hidden="true">
mail
</span>
{t('Mailbox management')}
</Box>
</Tab>
<Tab aria-label={t('Go to accesses management')} id="accesses">
<Box $direction="row" $align="center" $gap="5px">
<span className="material-icons" aria-hidden="true">
people
</span>
{t('Access management')}
</Box>
</Tab>
</TabList>

<TabPanel id="mails">
<MailDomainsContent mailDomain={mailDomain} />
</TabPanel>
<TabPanel id="accesses">
<AccessesContent mailDomain={mailDomain} currentRole={currentRole} />
</TabPanel>
</Tabs>
</Box>
);
};
4 changes: 4 additions & 0 deletions src/frontend/apps/desk/src/i18n/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@
"Mailboxes list": "Liste des boîtes mail",
"Mailboxes list card": "Carte liste des boîtes mails",
"Manage accesses": "Gérer les accès",
"Access management": "Gestion des rôles",
"Mailbox management": "Gestion des boîtes mails",
"Go to accesses management": "Aller à la gestion des rôles",
"Go to mailbox management": "Aller à la gestion des mails",
"Manage mailboxes": "Gérer les boîtes mails",
"Manage {{name}} domain mailboxes": "Gérer les boîtes mails du domaine {{name}}",
"Manage {{name}} domain members": "Gérer les membres du domaine {{name}}",
Expand Down
1 change: 1 addition & 0 deletions src/frontend/apps/desk/src/pages/globals.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import url('../cunningham/cunningham-style.css');
@import url('../styles/tabs.css');

body {
margin: 0;
Expand Down
Loading

0 comments on commit f853083

Please sign in to comment.