(frontend) add support email field on domain creation form

Add new field to give email of support to manage actions
required on domain.
This commit is contained in:
Sabrina Demagny
2025-02-04 14:42:57 +01:00
parent 418db6194a
commit d7957547f8
12 changed files with 83 additions and 45 deletions

View File

@@ -99,6 +99,7 @@ describe('MailDomainAccessesPage', () => {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: 'support@example.com',
abilities: {
get: true,
patch: true,

View File

@@ -25,6 +25,7 @@ describe('AccessAction', () => {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: 'support@example.com',
abilities: {
get: true,
patch: true,

View File

@@ -31,6 +31,7 @@ describe('AccessesContent', () => {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: 'support@example.com',
abilities: {
get: true,
patch: true,

View File

@@ -26,6 +26,7 @@ const mockMailDomain: MailDomain = {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: 'support@example.com',
abilities: {
manage_accesses: true,
get: true,

View File

@@ -36,6 +36,7 @@ describe('ModalDelete', () => {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: 'support@example.com',
abilities: {
get: true,
patch: true,

View File

@@ -19,6 +19,7 @@ describe('ModalAddMailDomain', () => {
modalElement: screen.getByText('Add a mail domain'),
formTag: screen.getByTitle('Mail domain addition form'),
inputName: screen.getByLabelText(/Domain name/i),
inputSupportEmail: screen.getByLabelText(/Support email address/i),
buttonCancel: screen.getByRole('button', { name: /Cancel/i, hidden: true }),
buttonSubmit: screen.getByRole('button', {
name: /Add the domain/i,
@@ -37,12 +38,19 @@ describe('ModalAddMailDomain', () => {
it('renders all the elements', () => {
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
const { modalElement, formTag, inputName, buttonCancel, buttonSubmit } =
getElements();
const {
modalElement,
formTag,
inputName,
inputSupportEmail,
buttonCancel,
buttonSubmit,
} = getElements();
expect(modalElement).toBeVisible();
expect(formTag).toBeVisible();
expect(inputName).toBeVisible();
expect(inputSupportEmail).toBeVisible();
expect(screen.getByText('Example: saint-laurent.fr')).toBeVisible();
expect(buttonCancel).toBeVisible();
expect(buttonSubmit).toBeVisible();
@@ -104,9 +112,10 @@ describe('ModalAddMailDomain', () => {
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
const { inputName, buttonSubmit } = getElements();
const { inputName, inputSupportEmail, buttonSubmit } = getElements();
await user.type(inputName, 'domain.fr');
await user.type(inputSupportEmail, 'support@domain.fr');
await user.click(buttonSubmit);
@@ -114,6 +123,7 @@ describe('ModalAddMailDomain', () => {
expect(fetchMock.lastOptions()).toEqual({
body: JSON.stringify({
name: 'domain.fr',
support_email: 'support@domain.fr',
}),
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
@@ -123,29 +133,6 @@ describe('ModalAddMailDomain', () => {
expect(mockPush).toHaveBeenCalledWith(`/mail-domains/domainfr`);
});
it('submits the form on key enter press', async () => {
fetchMock.mock(`end:mail-domains/`, 201);
const user = userEvent.setup();
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
const { inputName } = getElements();
await user.type(inputName, 'domain.fr');
await user.type(inputName, '{enter}');
expect(fetchMock.lastUrl()).toContain('/mail-domains/');
expect(fetchMock.lastOptions()).toEqual({
body: JSON.stringify({
name: 'domain.fr',
}),
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
method: 'POST',
});
});
it('displays right error message error when maildomain name is already used', async () => {
fetchMock.mock(`end:mail-domains/`, {
status: 400,
@@ -158,10 +145,10 @@ describe('ModalAddMailDomain', () => {
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
const { inputName, buttonSubmit } = getElements();
const { inputName, inputSupportEmail, buttonSubmit } = getElements();
await user.type(inputName, 'domain.fr');
await user.type(inputSupportEmail, 'support@domain.fr');
await user.click(buttonSubmit);
await waitFor(() => {
@@ -175,6 +162,7 @@ describe('ModalAddMailDomain', () => {
expect(inputName).toHaveFocus();
await user.type(inputName, 'domain2.fr');
expect(buttonSubmit).toBeEnabled();
});
@@ -190,9 +178,10 @@ describe('ModalAddMailDomain', () => {
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
const { inputName, buttonSubmit } = getElements();
const { inputName, inputSupportEmail, buttonSubmit } = getElements();
await user.type(inputName, 'domainfr');
await user.type(inputSupportEmail, 'support@domain.fr');
await user.click(buttonSubmit);
@@ -220,9 +209,10 @@ describe('ModalAddMailDomain', () => {
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
const { inputName, buttonSubmit } = getElements();
const { inputName, inputSupportEmail, buttonSubmit } = getElements();
await user.type(inputName, 'domain.fr');
await user.type(inputSupportEmail, 'support@domain.fr');
await user.click(buttonSubmit);

View File

@@ -8,16 +8,16 @@ import { KEY_LIST_MAIL_DOMAIN } from './useMailDomains';
export interface AddMailDomainParams {
name: string;
supportEmail: string;
}
export const addMailDomain = async (
name: AddMailDomainParams['name'],
): Promise<MailDomain> => {
export const addMailDomain = async ({
name,
supportEmail,
}: AddMailDomainParams): Promise<MailDomain> => {
const response = await fetchAPI(`mail-domains/`, {
method: 'POST',
body: JSON.stringify({
name,
}),
body: JSON.stringify({ name, support_email: supportEmail }),
});
if (!response.ok) {
@@ -38,7 +38,7 @@ export const useAddMailDomain = ({
onError: (error: APIError) => void;
}) => {
const queryClient = useQueryClient();
return useMutation<MailDomain, APIError, string>({
return useMutation<MailDomain, APIError, AddMailDomainParams>({
mutationFn: addMailDomain,
onSuccess: (data) => {
void queryClient.invalidateQueries({

View File

@@ -6,6 +6,7 @@ import { Controller, FormProvider, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import { APIError } from '@/api';
import { parseAPIError } from '@/api/parseAPIError';
import { Box, Text, TextErrors } from '@/components';
import { Modal } from '@/components/Modal';
@@ -23,12 +24,14 @@ export const ModalAddMailDomain = () => {
const addMailDomainValidationSchema = z.object({
name: z.string().min(1, t('Example: saint-laurent.fr')),
supportEmail: z.string().email(t('Please enter a valid email address')),
});
const methods = useForm<{ name: string }>({
const methods = useForm<{ name: string; supportEmail: string }>({
delayError: 0,
defaultValues: {
name: '',
supportEmail: '',
},
mode: 'onChange',
reValidateMode: 'onChange',
@@ -39,7 +42,7 @@ export const ModalAddMailDomain = () => {
onSuccess: (mailDomain) => {
router.push(`/mail-domains/${mailDomain.slug}`);
},
onError: (error) => {
onError: (error: APIError) => {
const unhandledCauses = parseAPIError({
error,
errorParams: [
@@ -87,8 +90,8 @@ export const ModalAddMailDomain = () => {
const onSubmitCallback = (event: React.FormEvent) => {
event.preventDefault();
void methods.handleSubmit(({ name }) => {
void addMailDomain(name);
void methods.handleSubmit(({ name, supportEmail }) => {
void addMailDomain({ name, supportEmail });
})();
};
@@ -167,6 +170,27 @@ export const ModalAddMailDomain = () => {
/>
)}
/>
<Box $margin={{ vertical: '10px' }}>
<Controller
control={methods.control}
name="supportEmail"
render={({ fieldState }) => (
<Input
aria-invalid={!!fieldState.error}
aria-required
required
label={t('Support email address')}
state={fieldState.error ? 'error' : 'default'}
text={
fieldState?.error?.message
? fieldState.error.message
: t('E.g. : support@saint-laurent.fr')
}
{...methods.register('supportEmail')}
/>
)}
/>
</Box>
</form>
{isPending && (

View File

@@ -7,6 +7,7 @@ export interface MailDomain {
updated_at: string;
slug: string;
status: 'pending' | 'enabled' | 'failed' | 'disabled';
support_email: string;
abilities: {
get: boolean;
patch: boolean;

View File

@@ -14,6 +14,7 @@ const mockMailDomain: MailDomain = {
name: 'example.com',
slug: 'example-com',
status: 'enabled',
support_email: 'support@example.com',
abilities: {
get: true,
patch: true,
@@ -31,6 +32,7 @@ const mockMailDomainAsViewer: MailDomain = {
name: 'example.com',
slug: 'example-com',
status: 'enabled',
support_email: 'support@example.com',
abilities: {
get: true,
patch: false,

View File

@@ -14,6 +14,7 @@ const mockMailDomain: MailDomain = {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: 'support@example.com',
abilities: {
get: true,
patch: true,

View File

@@ -9,6 +9,7 @@ const getElements = (page: Page) => {
});
const form = page.locator('form');
const inputName = form.getByLabel('Domain name');
const inputSupportEmail = form.getByLabel('Support email address');
const buttonSubmit = page.getByRole('button', {
name: 'Add the domain',
});
@@ -22,6 +23,7 @@ const getElements = (page: Page) => {
linkIndexPageAddDomain,
form,
inputName,
inputSupportEmail,
buttonCancel,
buttonSubmit,
};
@@ -50,6 +52,7 @@ test.describe('Add Mail Domains', () => {
}),
).toBeVisible();
await expect(inputName).toBeVisible();
await expect(inputName).toBeVisible();
await expect(page.getByText('Example: saint-laurent.fr')).toBeVisible();
@@ -82,12 +85,17 @@ test.describe('Add Mail Domains', () => {
test('checks form invalid status', async ({ page }) => {
await page.goto('/mail-domains/');
const { linkIndexPageAddDomain, inputName, buttonSubmit } =
getElements(page);
const {
linkIndexPageAddDomain,
inputName,
inputSupportEmail,
buttonSubmit,
} = getElements(page);
await linkIndexPageAddDomain.click();
await expect(inputName).toBeVisible();
await expect(inputSupportEmail).toBeVisible();
await expect(page.getByText('Example: saint-laurent.fr')).toBeVisible();
await expect(
@@ -111,16 +119,23 @@ test.describe('Add Mail Domains', () => {
browserName,
}) => {
const mailDomainName = randomName('versailles.fr', browserName, 1)[0];
const mailDomainSupportMail = 'support@'.concat(mailDomainName);
const mailDomainSlug = mailDomainName.replace('.', '');
await page.goto('/mail-domains/');
const { linkIndexPageAddDomain, inputName, buttonSubmit } =
getElements(page);
const {
linkIndexPageAddDomain,
inputName,
inputSupportEmail,
buttonSubmit,
} = getElements(page);
await linkIndexPageAddDomain.click();
await inputName.fill(mailDomainName);
await inputSupportEmail.fill(mailDomainSupportMail);
await buttonSubmit.click();
await expect(page).toHaveURL(`/mail-domains/${mailDomainSlug}/`);