Skip to content

Commit

Permalink
✨(frontend) disable mailbox and allow to create pending mailbox
Browse files Browse the repository at this point in the history
  • Loading branch information
PanchoutNathan authored and sdemagny committed Dec 17, 2024
1 parent bcdc579 commit c1761d2
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';

import { APIError, errorCauses, fetchAPI } from '@/api';

import { KEY_LIST_MAILBOX } from './useMailboxes';

export interface DisableMailboxParams {
mailDomainSlug: string;
mailboxId: string;
isEnabled: boolean;
}

export const disableMailbox = async ({
mailDomainSlug,
mailboxId,
isEnabled,
}: DisableMailboxParams): Promise<void> => {
const response = await fetchAPI(
`mail-domains/${mailDomainSlug}/mailboxes/${mailboxId}/${
isEnabled ? 'enable' : 'disable'
}/`,
{
method: 'POST',
},
);

if (!response.ok) {
throw new APIError(
'Failed to disable the mailbox',
await errorCauses(response),
);
}
};

export const useUpdateMailboxStatus = () => {
const queryClient = useQueryClient();
return useMutation<void, APIError, DisableMailboxParams>({
mutationFn: disableMailbox,
onSuccess: (_data, variables) => {
void queryClient.invalidateQueries({
queryKey: [
KEY_LIST_MAILBOX,
{ mailDomainSlug: variables.mailDomainSlug },
],
});
},
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Button } from '@openfun/cunningham-react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';

import { Box, DropButton, IconOptions, Text } from '@/components';

import { MailDomain } from '../../domains/types';
import { useUpdateMailboxStatus } from '../api/useUpdateMailboxStatus';
import { MailDomainMailbox } from '../types';

interface MailDomainsActionsProps {
mailbox: MailDomainMailbox;
mailDomain: MailDomain;
}

export const MailDomainsActions = ({
mailDomain,
mailbox,
}: MailDomainsActionsProps) => {
const { t } = useTranslation();
const [isDropOpen, setIsDropOpen] = useState(false);
const isEnabled = mailbox.status === 'enabled';

const { mutate: updateMailboxStatus } = useUpdateMailboxStatus();

const handleUpdateMailboxStatus = () => {
updateMailboxStatus({
mailDomainSlug: mailDomain.slug,
mailboxId: mailbox.id,
isEnabled: !isEnabled,
});
};

if (mailbox.status === 'pending' || mailbox.status === 'failed') {
return null;
}

return (
<>
<DropButton
button={
<IconOptions
isOpen={isDropOpen}
aria-label={t('Open the access options modal')}
/>
}
onOpenChange={(isOpen) => setIsDropOpen(isOpen)}
isOpen={isDropOpen}
>
<Box>
<Button
aria-label={t('Open the modal to update the role of this access')}
onClick={() => {
setIsDropOpen(false);
handleUpdateMailboxStatus();
}}
fullWidth
color="primary-text"
icon={
<span className="material-icons" aria-hidden="true">
{isEnabled ? 'lock' : 'lock_open'}
</span>
}
>
<Text $theme="primary">
{isEnabled ? t('Disable mailbox') : t('Enable mailbox')}
</Text>
</Button>
</Box>
</DropButton>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
VariantType,
usePagination,
} from '@openfun/cunningham-react';
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';

import { Box, Card, Text, TextErrors, TextStyled } from '@/components';
Expand All @@ -20,10 +20,14 @@ import { MailDomain } from '../../domains/types';
import { useMailboxes } from '../api/useMailboxes';
import { MailDomainMailbox } from '../types';

import { MailDomainsActions } from './MailDomainsActions';

export type ViewMailbox = {
name: string;
email: string;
id: UUID;
status: MailDomainMailbox['status'];
mailbox: MailDomainMailbox;
};

// FIXME : ask Cunningham to export this type
Expand Down Expand Up @@ -72,6 +76,8 @@ export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) {
email: `${mailbox.local_part}@${mailDomain.name}`,
name: `${mailbox.first_name} ${mailbox.last_name}`,
id: mailbox.id,
status: mailbox.status,
mailbox,
}))
: [];

Expand All @@ -97,7 +103,16 @@ export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) {
showMailBoxCreationForm={setIsCreateMailboxFormVisible}
/>

<Card $overflow="auto" aria-label={t('Mailboxes list card')}>
<Card
$overflow="auto"
aria-label={t('Mailboxes list card')}
$css={`
& table td:last-child {
text-align: right;
}
`}
>
{error && <TextErrors causes={error.cause} />}

<DataGrid
Expand All @@ -119,6 +134,19 @@ export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) {
field: 'email',
headerName: t('Emails'),
},
{
field: 'status',
headerName: t('Status'),
},
{
id: 'column-actions',
renderCell: ({ row }) => (
<MailDomainsActions
mailbox={row.mailbox}
mailDomain={mailDomain}
/>
),
},
]}
rows={viewMailboxes}
isLoading={isLoading}
Expand Down Expand Up @@ -147,6 +175,8 @@ const TopBanner = ({
showMailBoxCreationForm: (value: boolean) => void;
}) => {
const { t } = useTranslation();
const canCreateMailbox =
mailDomain.status === 'enabled' || mailDomain.status === 'pending';

return (
<Box $direction="column" $gap="1rem">
Expand All @@ -164,7 +194,7 @@ const TopBanner = ({
aria-label={t('Create a mailbox in {{name}} domain', {
name: mailDomain?.name,
})}
disabled={mailDomain?.status !== 'enabled'}
disabled={!canCreateMailbox}
onClick={() => showMailBoxCreationForm(true)}
>
{t('Create a mailbox')}
Expand All @@ -181,14 +211,6 @@ const AlertStatus = ({ status }: { status: MailDomain['status'] }) => {

const getStatusAlertProps = (status?: string) => {
switch (status) {
case 'pending':
return {
variant: VariantType.WARNING,
message: t(
'Your domain name is being validated. ' +
'You will not be able to create mailboxes until your domain name has been validated by our team.',
),
};
case 'disabled':
return {
variant: VariantType.NEUTRAL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,11 @@ export interface MailDomainMailbox {
first_name: string;
last_name: string;
secondary_email: string;
status: MailDomainMailboxStatus;
}

export type MailDomainMailboxStatus =
| 'enabled'
| 'disabled'
| 'pending'
| 'failed';
Original file line number Diff line number Diff line change
Expand Up @@ -85,38 +85,41 @@ const mailboxesFixtures = {
},
};

const interceptCommonApiRequests = (page: Page) => {
const interceptCommonApiRequests = (page: Page, mailDomains?: MailDomain[]) => {
const mailDomainsToUse = mailDomains ?? mailDomainsFixtures;
void page.route('**/api/v1.0/mail-domains/?page=*', (route) => {
void route.fulfill({
json: {
count: mailDomainsFixtures.length,
count: mailDomainsToUse.length,
next: null,
previous: null,
results: mailDomainsFixtures,
results: mailDomainsToUse,
},
});
});

void page.route('**/api/v1.0/mail-domains/domainfr/', (route) => {
void route.fulfill({
json: mailDomainDomainFrFixture,
});
});

void page.route(
'**/api/v1.0/mail-domains/domainfr/mailboxes/?page=1**',
(route) => {
mailDomainsToUse.forEach((mailDomain) => {
void page.route(`**/api/v1.0/mail-domains/${mailDomain.slug}/`, (route) => {
void route.fulfill({
json: {
count: mailboxesFixtures.domainFr.page1.length,
next: null,
previous: null,
results: mailboxesFixtures.domainFr.page1,
},
json: mailDomain,
});
},
{ times: 1 },
);
});

void page.route(
`**/api/v1.0/mail-domains/${mailDomain.slug}/mailboxes/?page=1**`,
(route) => {
void route.fulfill({
json: {
count: mailboxesFixtures.domainFr.page1.length,
next: null,
previous: null,
results: mailboxesFixtures.domainFr.page1,
},
});
},
{ times: 1 },
);
});
};

const navigateToMailboxCreationFormForMailDomainFr = async (
Expand All @@ -134,6 +137,57 @@ test.describe('Mail domain create mailbox', () => {
await keyCloakSignIn(page, browserName, 'mail-member');
});

test('checks create mailbox button is visible or not', async ({ page }) => {
const domains = [...mailDomainsFixtures];
domains[0].status = 'enabled';
domains[1].status = 'pending';
domains[2].status = 'disabled';
domains[3].status = 'failed';
void interceptCommonApiRequests(page, domains);

await page
.locator('menu')
.first()
.getByLabel(`Mail Domains button`)
.click();
const domainFr = page.getByRole('listbox').first().getByText('domain.fr');
const mailsFr = page.getByRole('listbox').first().getByText('mails.fr');
const versaillesNet = page
.getByRole('listbox')
.first()
.getByText('versailles.net');
const parisFr = page.getByRole('listbox').first().getByText('paris.fr');

await expect(domainFr).toBeVisible();
await expect(mailsFr).toBeVisible();
await expect(versaillesNet).toBeVisible();
await expect(parisFr).toBeVisible();

// Check that the button is enabled when the domain is enabled
await domainFr.click();
await expect(
page.getByRole('button', { name: 'Create a mailbox' }),
).toBeEnabled();

// Check that the button is enabled when the domain is pending
await mailsFr.click();
await expect(
page.getByRole('button', { name: 'Create a mailbox' }),
).toBeEnabled();

// Check that the button is disabled when the domain is disabled
await versaillesNet.click();
await expect(
page.getByRole('button', { name: 'Create a mailbox' }),
).toBeDisabled();

// Check that the button is disabled when the domain is failed
await parisFr.click();
await expect(
page.getByRole('button', { name: 'Create a mailbox' }),
).toBeDisabled();
});

test('checks user can create a mailbox when he has post ability', async ({
page,
}) => {
Expand Down Expand Up @@ -196,7 +250,7 @@ test.describe('Mail domain create mailbox', () => {
});

void interceptRequests(page);

expect(true).toBeTruthy();
await navigateToMailboxCreationFormForMailDomainFr(page);

await page.getByRole('button', { name: 'Cancel' }).click();
Expand Down
Loading

0 comments on commit c1761d2

Please sign in to comment.