diff --git a/CHANGELOG.md b/CHANGELOG.md index 835e1854e..56794903e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to ### Added +- ✨(frontend) add support email field on domain creation form - ✨(domains) add support email field on MailDomain ## [1.11.0] - 2025-02-07 diff --git a/src/frontend/apps/desk/src/features/mail-domains/access-management/__tests__/accesses.test.tsx b/src/frontend/apps/desk/src/features/mail-domains/access-management/__tests__/accesses.test.tsx index 600f66552..9e485f0ae 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/access-management/__tests__/accesses.test.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/access-management/__tests__/accesses.test.tsx @@ -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, diff --git a/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessAction.test.tsx b/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessAction.test.tsx index 15007364f..50d627794 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessAction.test.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessAction.test.tsx @@ -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, diff --git a/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessesContent.test.tsx b/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessesContent.test.tsx index 51ddb0c7b..17998d240 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessesContent.test.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessesContent.test.tsx @@ -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, diff --git a/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessesGrid.test.tsx b/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessesGrid.test.tsx index 68e3775d8..31ac23fe4 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessesGrid.test.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/AccessesGrid.test.tsx @@ -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, diff --git a/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/ModalDelete.test.tsx b/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/ModalDelete.test.tsx index e24d169db..571516640 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/ModalDelete.test.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/access-management/components/__tests__/ModalDelete.test.tsx @@ -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, diff --git a/src/frontend/apps/desk/src/features/mail-domains/domains/__tests__/ModalAddMailDomain.test.tsx b/src/frontend/apps/desk/src/features/mail-domains/domains/__tests__/ModalAddMailDomain.test.tsx index f09c6156d..d45868a1b 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/domains/__tests__/ModalAddMailDomain.test.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/domains/__tests__/ModalAddMailDomain.test.tsx @@ -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(, { 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(, { 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(, { 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(, { 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,8 @@ describe('ModalAddMailDomain', () => { expect(inputName).toHaveFocus(); await user.type(inputName, 'domain2.fr'); + //await user.type(inputSupportEmail, 'support@domain.fr'); + expect(buttonSubmit).toBeEnabled(); }); @@ -190,9 +179,10 @@ describe('ModalAddMailDomain', () => { render(, { 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 +210,10 @@ describe('ModalAddMailDomain', () => { render(, { 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); diff --git a/src/frontend/apps/desk/src/features/mail-domains/domains/api/useAddMailDomain.tsx b/src/frontend/apps/desk/src/features/mail-domains/domains/api/useAddMailDomain.tsx index c76d3b648..1c73e6bba 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/domains/api/useAddMailDomain.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/domains/api/useAddMailDomain.tsx @@ -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 => { +export const addMailDomain = async ({ + name, + supportEmail, +}: AddMailDomainParams): Promise => { 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({ + return useMutation({ mutationFn: addMailDomain, onSuccess: (data) => { void queryClient.invalidateQueries({ diff --git a/src/frontend/apps/desk/src/features/mail-domains/domains/components/ModalAddMailDomain.tsx b/src/frontend/apps/desk/src/features/mail-domains/domains/components/ModalAddMailDomain.tsx index 62f07c4e9..20dc24fb6 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/domains/components/ModalAddMailDomain.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/domains/components/ModalAddMailDomain.tsx @@ -2,10 +2,16 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Button, Input, Loader, ModalSize } from '@openfun/cunningham-react'; import { useRouter } from 'next/navigation'; import React, { useState } from 'react'; -import { Controller, FormProvider, useForm } from 'react-hook-form'; +import { + Controller, + FormProvider, + UseFormReturn, + 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 +29,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 +47,7 @@ export const ModalAddMailDomain = () => { onSuccess: (mailDomain) => { router.push(`/mail-domains/${mailDomain.slug}`); }, - onError: (error) => { + onError: (error: APIError) => { const unhandledCauses = parseAPIError({ error, errorParams: [ @@ -87,8 +95,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 +175,27 @@ export const ModalAddMailDomain = () => { /> )} /> + + ( + + )} + /> + {isPending && ( diff --git a/src/frontend/apps/desk/src/features/mail-domains/domains/types.ts b/src/frontend/apps/desk/src/features/mail-domains/domains/types.ts index 8c877bbc0..4f9489b45 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/domains/types.ts +++ b/src/frontend/apps/desk/src/features/mail-domains/domains/types.ts @@ -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; diff --git a/src/frontend/apps/desk/src/features/mail-domains/mailboxes/components/__tests__/MailDomainsContent.test.tsx b/src/frontend/apps/desk/src/features/mail-domains/mailboxes/components/__tests__/MailDomainsContent.test.tsx index e4e1a4137..2cc3e1a86 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/mailboxes/components/__tests__/MailDomainsContent.test.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/mailboxes/components/__tests__/MailDomainsContent.test.tsx @@ -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, diff --git a/src/frontend/apps/desk/src/features/mail-domains/mailboxes/components/__tests__/ModalCreateMailbox.test.tsx b/src/frontend/apps/desk/src/features/mail-domains/mailboxes/components/__tests__/ModalCreateMailbox.test.tsx index 51659aad4..6898468ea 100644 --- a/src/frontend/apps/desk/src/features/mail-domains/mailboxes/components/__tests__/ModalCreateMailbox.test.tsx +++ b/src/frontend/apps/desk/src/features/mail-domains/mailboxes/components/__tests__/ModalCreateMailbox.test.tsx @@ -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, diff --git a/src/frontend/apps/e2e/__tests__/app-desk/mail-domains-add.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/mail-domains-add.spec.ts index beb194e07..f57e32acb 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/mail-domains-add.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/mail-domains-add.spec.ts @@ -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}/`);