✨(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:
@@ -99,6 +99,7 @@ describe('MailDomainAccessesPage', () => {
|
|||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
|
support_email: 'support@example.com',
|
||||||
abilities: {
|
abilities: {
|
||||||
get: true,
|
get: true,
|
||||||
patch: true,
|
patch: true,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ describe('AccessAction', () => {
|
|||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
|
support_email: 'support@example.com',
|
||||||
abilities: {
|
abilities: {
|
||||||
get: true,
|
get: true,
|
||||||
patch: true,
|
patch: true,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ describe('AccessesContent', () => {
|
|||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
|
support_email: 'support@example.com',
|
||||||
abilities: {
|
abilities: {
|
||||||
get: true,
|
get: true,
|
||||||
patch: true,
|
patch: true,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const mockMailDomain: MailDomain = {
|
|||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
|
support_email: 'support@example.com',
|
||||||
abilities: {
|
abilities: {
|
||||||
manage_accesses: true,
|
manage_accesses: true,
|
||||||
get: true,
|
get: true,
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ describe('ModalDelete', () => {
|
|||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
|
support_email: 'support@example.com',
|
||||||
abilities: {
|
abilities: {
|
||||||
get: true,
|
get: true,
|
||||||
patch: true,
|
patch: true,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ describe('ModalAddMailDomain', () => {
|
|||||||
modalElement: screen.getByText('Add a mail domain'),
|
modalElement: screen.getByText('Add a mail domain'),
|
||||||
formTag: screen.getByTitle('Mail domain addition form'),
|
formTag: screen.getByTitle('Mail domain addition form'),
|
||||||
inputName: screen.getByLabelText(/Domain name/i),
|
inputName: screen.getByLabelText(/Domain name/i),
|
||||||
|
inputSupportEmail: screen.getByLabelText(/Support email address/i),
|
||||||
buttonCancel: screen.getByRole('button', { name: /Cancel/i, hidden: true }),
|
buttonCancel: screen.getByRole('button', { name: /Cancel/i, hidden: true }),
|
||||||
buttonSubmit: screen.getByRole('button', {
|
buttonSubmit: screen.getByRole('button', {
|
||||||
name: /Add the domain/i,
|
name: /Add the domain/i,
|
||||||
@@ -37,12 +38,19 @@ describe('ModalAddMailDomain', () => {
|
|||||||
it('renders all the elements', () => {
|
it('renders all the elements', () => {
|
||||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
const { modalElement, formTag, inputName, buttonCancel, buttonSubmit } =
|
const {
|
||||||
getElements();
|
modalElement,
|
||||||
|
formTag,
|
||||||
|
inputName,
|
||||||
|
inputSupportEmail,
|
||||||
|
buttonCancel,
|
||||||
|
buttonSubmit,
|
||||||
|
} = getElements();
|
||||||
|
|
||||||
expect(modalElement).toBeVisible();
|
expect(modalElement).toBeVisible();
|
||||||
expect(formTag).toBeVisible();
|
expect(formTag).toBeVisible();
|
||||||
expect(inputName).toBeVisible();
|
expect(inputName).toBeVisible();
|
||||||
|
expect(inputSupportEmail).toBeVisible();
|
||||||
expect(screen.getByText('Example: saint-laurent.fr')).toBeVisible();
|
expect(screen.getByText('Example: saint-laurent.fr')).toBeVisible();
|
||||||
expect(buttonCancel).toBeVisible();
|
expect(buttonCancel).toBeVisible();
|
||||||
expect(buttonSubmit).toBeVisible();
|
expect(buttonSubmit).toBeVisible();
|
||||||
@@ -104,9 +112,10 @@ describe('ModalAddMailDomain', () => {
|
|||||||
|
|
||||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
const { inputName, buttonSubmit } = getElements();
|
const { inputName, inputSupportEmail, buttonSubmit } = getElements();
|
||||||
|
|
||||||
await user.type(inputName, 'domain.fr');
|
await user.type(inputName, 'domain.fr');
|
||||||
|
await user.type(inputSupportEmail, 'support@domain.fr');
|
||||||
|
|
||||||
await user.click(buttonSubmit);
|
await user.click(buttonSubmit);
|
||||||
|
|
||||||
@@ -114,6 +123,7 @@ describe('ModalAddMailDomain', () => {
|
|||||||
expect(fetchMock.lastOptions()).toEqual({
|
expect(fetchMock.lastOptions()).toEqual({
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name: 'domain.fr',
|
name: 'domain.fr',
|
||||||
|
support_email: 'support@domain.fr',
|
||||||
}),
|
}),
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -123,29 +133,6 @@ describe('ModalAddMailDomain', () => {
|
|||||||
expect(mockPush).toHaveBeenCalledWith(`/mail-domains/domainfr`);
|
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 () => {
|
it('displays right error message error when maildomain name is already used', async () => {
|
||||||
fetchMock.mock(`end:mail-domains/`, {
|
fetchMock.mock(`end:mail-domains/`, {
|
||||||
status: 400,
|
status: 400,
|
||||||
@@ -158,10 +145,10 @@ describe('ModalAddMailDomain', () => {
|
|||||||
|
|
||||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
const { inputName, buttonSubmit } = getElements();
|
const { inputName, inputSupportEmail, buttonSubmit } = getElements();
|
||||||
|
|
||||||
await user.type(inputName, 'domain.fr');
|
await user.type(inputName, 'domain.fr');
|
||||||
|
await user.type(inputSupportEmail, 'support@domain.fr');
|
||||||
await user.click(buttonSubmit);
|
await user.click(buttonSubmit);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
@@ -175,6 +162,7 @@ describe('ModalAddMailDomain', () => {
|
|||||||
expect(inputName).toHaveFocus();
|
expect(inputName).toHaveFocus();
|
||||||
|
|
||||||
await user.type(inputName, 'domain2.fr');
|
await user.type(inputName, 'domain2.fr');
|
||||||
|
|
||||||
expect(buttonSubmit).toBeEnabled();
|
expect(buttonSubmit).toBeEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -190,9 +178,10 @@ describe('ModalAddMailDomain', () => {
|
|||||||
|
|
||||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
const { inputName, buttonSubmit } = getElements();
|
const { inputName, inputSupportEmail, buttonSubmit } = getElements();
|
||||||
|
|
||||||
await user.type(inputName, 'domainfr');
|
await user.type(inputName, 'domainfr');
|
||||||
|
await user.type(inputSupportEmail, 'support@domain.fr');
|
||||||
|
|
||||||
await user.click(buttonSubmit);
|
await user.click(buttonSubmit);
|
||||||
|
|
||||||
@@ -220,9 +209,10 @@ describe('ModalAddMailDomain', () => {
|
|||||||
|
|
||||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||||
|
|
||||||
const { inputName, buttonSubmit } = getElements();
|
const { inputName, inputSupportEmail, buttonSubmit } = getElements();
|
||||||
|
|
||||||
await user.type(inputName, 'domain.fr');
|
await user.type(inputName, 'domain.fr');
|
||||||
|
await user.type(inputSupportEmail, 'support@domain.fr');
|
||||||
|
|
||||||
await user.click(buttonSubmit);
|
await user.click(buttonSubmit);
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import { KEY_LIST_MAIL_DOMAIN } from './useMailDomains';
|
|||||||
|
|
||||||
export interface AddMailDomainParams {
|
export interface AddMailDomainParams {
|
||||||
name: string;
|
name: string;
|
||||||
|
supportEmail: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addMailDomain = async (
|
export const addMailDomain = async ({
|
||||||
name: AddMailDomainParams['name'],
|
name,
|
||||||
): Promise<MailDomain> => {
|
supportEmail,
|
||||||
|
}: AddMailDomainParams): Promise<MailDomain> => {
|
||||||
const response = await fetchAPI(`mail-domains/`, {
|
const response = await fetchAPI(`mail-domains/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({ name, support_email: supportEmail }),
|
||||||
name,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -38,7 +38,7 @@ export const useAddMailDomain = ({
|
|||||||
onError: (error: APIError) => void;
|
onError: (error: APIError) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation<MailDomain, APIError, string>({
|
return useMutation<MailDomain, APIError, AddMailDomainParams>({
|
||||||
mutationFn: addMailDomain,
|
mutationFn: addMailDomain,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
void queryClient.invalidateQueries({
|
void queryClient.invalidateQueries({
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Controller, FormProvider, useForm } from 'react-hook-form';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { APIError } from '@/api';
|
||||||
import { parseAPIError } from '@/api/parseAPIError';
|
import { parseAPIError } from '@/api/parseAPIError';
|
||||||
import { Box, Text, TextErrors } from '@/components';
|
import { Box, Text, TextErrors } from '@/components';
|
||||||
import { Modal } from '@/components/Modal';
|
import { Modal } from '@/components/Modal';
|
||||||
@@ -23,12 +24,14 @@ export const ModalAddMailDomain = () => {
|
|||||||
|
|
||||||
const addMailDomainValidationSchema = z.object({
|
const addMailDomainValidationSchema = z.object({
|
||||||
name: z.string().min(1, t('Example: saint-laurent.fr')),
|
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,
|
delayError: 0,
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: '',
|
name: '',
|
||||||
|
supportEmail: '',
|
||||||
},
|
},
|
||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
reValidateMode: 'onChange',
|
reValidateMode: 'onChange',
|
||||||
@@ -39,7 +42,7 @@ export const ModalAddMailDomain = () => {
|
|||||||
onSuccess: (mailDomain) => {
|
onSuccess: (mailDomain) => {
|
||||||
router.push(`/mail-domains/${mailDomain.slug}`);
|
router.push(`/mail-domains/${mailDomain.slug}`);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error: APIError) => {
|
||||||
const unhandledCauses = parseAPIError({
|
const unhandledCauses = parseAPIError({
|
||||||
error,
|
error,
|
||||||
errorParams: [
|
errorParams: [
|
||||||
@@ -87,8 +90,8 @@ export const ModalAddMailDomain = () => {
|
|||||||
const onSubmitCallback = (event: React.FormEvent) => {
|
const onSubmitCallback = (event: React.FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
void methods.handleSubmit(({ name }) => {
|
void methods.handleSubmit(({ name, supportEmail }) => {
|
||||||
void addMailDomain(name);
|
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>
|
</form>
|
||||||
|
|
||||||
{isPending && (
|
{isPending && (
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export interface MailDomain {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
status: 'pending' | 'enabled' | 'failed' | 'disabled';
|
status: 'pending' | 'enabled' | 'failed' | 'disabled';
|
||||||
|
support_email: string;
|
||||||
abilities: {
|
abilities: {
|
||||||
get: boolean;
|
get: boolean;
|
||||||
patch: boolean;
|
patch: boolean;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const mockMailDomain: MailDomain = {
|
|||||||
name: 'example.com',
|
name: 'example.com',
|
||||||
slug: 'example-com',
|
slug: 'example-com',
|
||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
|
support_email: 'support@example.com',
|
||||||
abilities: {
|
abilities: {
|
||||||
get: true,
|
get: true,
|
||||||
patch: true,
|
patch: true,
|
||||||
@@ -31,6 +32,7 @@ const mockMailDomainAsViewer: MailDomain = {
|
|||||||
name: 'example.com',
|
name: 'example.com',
|
||||||
slug: 'example-com',
|
slug: 'example-com',
|
||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
|
support_email: 'support@example.com',
|
||||||
abilities: {
|
abilities: {
|
||||||
get: true,
|
get: true,
|
||||||
patch: false,
|
patch: false,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const mockMailDomain: MailDomain = {
|
|||||||
status: 'enabled',
|
status: 'enabled',
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString(),
|
updated_at: new Date().toISOString(),
|
||||||
|
support_email: 'support@example.com',
|
||||||
abilities: {
|
abilities: {
|
||||||
get: true,
|
get: true,
|
||||||
patch: true,
|
patch: true,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const getElements = (page: Page) => {
|
|||||||
});
|
});
|
||||||
const form = page.locator('form');
|
const form = page.locator('form');
|
||||||
const inputName = form.getByLabel('Domain name');
|
const inputName = form.getByLabel('Domain name');
|
||||||
|
const inputSupportEmail = form.getByLabel('Support email address');
|
||||||
const buttonSubmit = page.getByRole('button', {
|
const buttonSubmit = page.getByRole('button', {
|
||||||
name: 'Add the domain',
|
name: 'Add the domain',
|
||||||
});
|
});
|
||||||
@@ -22,6 +23,7 @@ const getElements = (page: Page) => {
|
|||||||
linkIndexPageAddDomain,
|
linkIndexPageAddDomain,
|
||||||
form,
|
form,
|
||||||
inputName,
|
inputName,
|
||||||
|
inputSupportEmail,
|
||||||
buttonCancel,
|
buttonCancel,
|
||||||
buttonSubmit,
|
buttonSubmit,
|
||||||
};
|
};
|
||||||
@@ -50,6 +52,7 @@ test.describe('Add Mail Domains', () => {
|
|||||||
}),
|
}),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
|
await expect(inputName).toBeVisible();
|
||||||
await expect(inputName).toBeVisible();
|
await expect(inputName).toBeVisible();
|
||||||
|
|
||||||
await expect(page.getByText('Example: saint-laurent.fr')).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 }) => {
|
test('checks form invalid status', async ({ page }) => {
|
||||||
await page.goto('/mail-domains/');
|
await page.goto('/mail-domains/');
|
||||||
|
|
||||||
const { linkIndexPageAddDomain, inputName, buttonSubmit } =
|
const {
|
||||||
getElements(page);
|
linkIndexPageAddDomain,
|
||||||
|
inputName,
|
||||||
|
inputSupportEmail,
|
||||||
|
buttonSubmit,
|
||||||
|
} = getElements(page);
|
||||||
|
|
||||||
await linkIndexPageAddDomain.click();
|
await linkIndexPageAddDomain.click();
|
||||||
|
|
||||||
await expect(inputName).toBeVisible();
|
await expect(inputName).toBeVisible();
|
||||||
|
await expect(inputSupportEmail).toBeVisible();
|
||||||
await expect(page.getByText('Example: saint-laurent.fr')).toBeVisible();
|
await expect(page.getByText('Example: saint-laurent.fr')).toBeVisible();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@@ -111,16 +119,23 @@ test.describe('Add Mail Domains', () => {
|
|||||||
browserName,
|
browserName,
|
||||||
}) => {
|
}) => {
|
||||||
const mailDomainName = randomName('versailles.fr', browserName, 1)[0];
|
const mailDomainName = randomName('versailles.fr', browserName, 1)[0];
|
||||||
|
const mailDomainSupportMail = 'support@'.concat(mailDomainName);
|
||||||
const mailDomainSlug = mailDomainName.replace('.', '');
|
const mailDomainSlug = mailDomainName.replace('.', '');
|
||||||
|
|
||||||
await page.goto('/mail-domains/');
|
await page.goto('/mail-domains/');
|
||||||
|
|
||||||
const { linkIndexPageAddDomain, inputName, buttonSubmit } =
|
const {
|
||||||
getElements(page);
|
linkIndexPageAddDomain,
|
||||||
|
inputName,
|
||||||
|
inputSupportEmail,
|
||||||
|
buttonSubmit,
|
||||||
|
} = getElements(page);
|
||||||
|
|
||||||
await linkIndexPageAddDomain.click();
|
await linkIndexPageAddDomain.click();
|
||||||
|
|
||||||
await inputName.fill(mailDomainName);
|
await inputName.fill(mailDomainName);
|
||||||
|
await inputSupportEmail.fill(mailDomainSupportMail);
|
||||||
|
|
||||||
await buttonSubmit.click();
|
await buttonSubmit.click();
|
||||||
|
|
||||||
await expect(page).toHaveURL(`/mail-domains/${mailDomainSlug}/`);
|
await expect(page).toHaveURL(`/mail-domains/${mailDomainSlug}/`);
|
||||||
|
|||||||
Reference in New Issue
Block a user