Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a591c64169 | |||
| c629a5e149 |
@@ -10,6 +10,7 @@ and this project adheres to
|
||||
|
||||
### Added
|
||||
|
||||
- ⚡️(fix) fix button add mail domain + add modal view information mailbox
|
||||
- ⚡️(fix) add error when mailbox create failed
|
||||
- ✨(mailbox) allow to reset password on mailboxes #834
|
||||
|
||||
|
||||
@@ -243,6 +243,10 @@ input:-webkit-autofill:focus {
|
||||
border-bottom: 1px var(--c--theme--colors--greyscale-100) solid;
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table tbody tr:hover {
|
||||
background-color: var(--c--theme--colors--greyscale-050);
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table tbody {
|
||||
background-color: var(--c--components--datagrid--body--background-color);
|
||||
color: var(--c--theme--colors--greyscale-900);
|
||||
@@ -259,7 +263,7 @@ input:-webkit-autofill:focus {
|
||||
|
||||
.c__datagrid__table__container > table th:first-child,
|
||||
.c__datagrid__table__container > table td:first-child {
|
||||
padding-left: 0;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.c__datagrid__table__container > table tr:last-child {
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ export const PanelActions = () => {
|
||||
$css={`
|
||||
& button {
|
||||
padding: 0;
|
||||
justify-content: start;
|
||||
|
||||
svg {
|
||||
padding: 0.1rem;
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './useCreateMailbox';
|
||||
export * from './useMailboxes';
|
||||
export * from './useUpdateMailbox';
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
UseMutationOptions,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
|
||||
import { KEY_LIST_MAILBOX } from './useMailboxes';
|
||||
|
||||
export interface UpdateMailboxParams {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
secondary_email: string;
|
||||
mailDomainSlug: string;
|
||||
}
|
||||
|
||||
export const updateMailbox = async ({
|
||||
mailDomainSlug,
|
||||
mailboxId,
|
||||
...data
|
||||
}: UpdateMailboxParams & { mailboxId: string }): Promise<void> => {
|
||||
const response = await fetchAPI(
|
||||
`mail-domains/${mailDomainSlug}/mailboxes/${mailboxId}/`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(data),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await errorCauses(response);
|
||||
console.log('Error data:', errorData);
|
||||
throw new APIError('Failed to update the mailbox', {
|
||||
status: errorData.status,
|
||||
cause: errorData.cause as string[],
|
||||
data: errorData.data,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
type UseUpdateMailboxParams = {
|
||||
mailDomainSlug: string;
|
||||
mailboxId: string;
|
||||
} & UseMutationOptions<void, APIError, UpdateMailboxParams>;
|
||||
|
||||
export const useUpdateMailbox = (options: UseUpdateMailboxParams) => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, APIError, UpdateMailboxParams>({
|
||||
mutationFn: (data) =>
|
||||
updateMailbox({ ...data, mailboxId: options.mailboxId }),
|
||||
onSuccess: (data, variables, context) => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [
|
||||
KEY_LIST_MAILBOX,
|
||||
{ mailDomainSlug: variables.mailDomainSlug },
|
||||
],
|
||||
});
|
||||
if (options?.onSuccess) {
|
||||
options.onSuccess(data, variables, context);
|
||||
}
|
||||
},
|
||||
onError: (error, variables, context) => {
|
||||
if (options?.onError) {
|
||||
options.onError(error, variables, context);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import {
|
||||
Button,
|
||||
Loader,
|
||||
ModalSize,
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import React, { useState } from 'react';
|
||||
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { APIError } from '@/api/APIError';
|
||||
import { parseAPIError } from '@/api/parseAPIError';
|
||||
import {
|
||||
Box,
|
||||
HorizontalSeparator,
|
||||
Input,
|
||||
Text,
|
||||
TextErrors,
|
||||
} from '@/components';
|
||||
import { CustomModal } from '@/components/modal/CustomModal';
|
||||
|
||||
import { MailDomain } from '../../domains/types';
|
||||
import { useUpdateMailbox } from '../api/useUpdateMailbox';
|
||||
import { Step, ViewMailbox } from '../types';
|
||||
|
||||
const FORM_ID = 'form-update-mailbox';
|
||||
|
||||
interface ModalUpdateMailboxProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
mailDomain: MailDomain;
|
||||
mailbox: ViewMailbox;
|
||||
}
|
||||
|
||||
export const ModalUpdateMailbox = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
mailDomain,
|
||||
mailbox,
|
||||
}: ModalUpdateMailboxProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToastProvider();
|
||||
const [errorCauses, setErrorCauses] = useState<string[]>([]);
|
||||
const [step] = useState(0);
|
||||
|
||||
const updateMailboxValidationSchema = z.object({
|
||||
first_name: z.string().min(1, t('Please enter your first name')),
|
||||
last_name: z.string().min(1, t('Please enter your last name')),
|
||||
secondary_email: z.string().email(t('Please enter a valid email address')),
|
||||
});
|
||||
|
||||
const methods = useForm({
|
||||
resolver: zodResolver(updateMailboxValidationSchema),
|
||||
defaultValues: {
|
||||
first_name: mailbox?.first_name || '',
|
||||
last_name: mailbox?.last_name || '',
|
||||
secondary_email: mailbox?.secondary_email || '',
|
||||
},
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
const { mutate: updateMailbox, isPending } = useUpdateMailbox({
|
||||
mailDomainSlug: mailDomain.slug,
|
||||
mailboxId: mailbox?.id || '',
|
||||
onSuccess: () => {
|
||||
toast(t('Mailbox updated!'), VariantType.SUCCESS, { duration: 4000 });
|
||||
onClose();
|
||||
},
|
||||
onError: (error: APIError) => {
|
||||
const causes: string[] =
|
||||
parseAPIError({
|
||||
error,
|
||||
errorParams: [
|
||||
[
|
||||
['Invalid format'],
|
||||
t('Invalid format for the email address.'),
|
||||
undefined,
|
||||
],
|
||||
],
|
||||
serverErrorParams: [
|
||||
t(
|
||||
'An error occurred while updating the mailbox. Please try again.',
|
||||
),
|
||||
undefined,
|
||||
],
|
||||
}) || [];
|
||||
if (causes.length > 0) {
|
||||
causes.forEach((cause) => {
|
||||
toast(cause, VariantType.ERROR, { duration: 4000 });
|
||||
});
|
||||
} else {
|
||||
toast(t('Mailbox update failed!'), VariantType.ERROR, {
|
||||
duration: 4000,
|
||||
});
|
||||
}
|
||||
setErrorCauses(causes);
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmitCallback = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!mailbox?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
void methods.handleSubmit((data) =>
|
||||
updateMailbox({ ...data, mailDomainSlug: mailDomain.slug }),
|
||||
)();
|
||||
};
|
||||
|
||||
if (!mailbox) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const steps: Step[] = [
|
||||
{
|
||||
title: t('Set up account'),
|
||||
content: (
|
||||
<FormProvider {...methods}>
|
||||
{!!errorCauses.length && <TextErrors causes={errorCauses} />}
|
||||
<form id={FORM_ID} onSubmit={onSubmitCallback}>
|
||||
<Box $padding={{ top: 'sm', horizontal: 'md' }} $gap="4px">
|
||||
<Text $size="md" $weight="bold">
|
||||
{t('Personal informations')}
|
||||
</Text>
|
||||
<Text $theme="greyscale" $variation="600">
|
||||
{t('Update the user information.')}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box $padding={{ horizontal: 'md' }}>
|
||||
<Box $margin={{ top: 'base' }}>
|
||||
<Controller
|
||||
name="first_name"
|
||||
control={methods.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Input
|
||||
{...field}
|
||||
label={t('First name')}
|
||||
placeholder={t('First name')}
|
||||
required
|
||||
error={fieldState.error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<Box $margin={{ top: 'base' }}>
|
||||
<Controller
|
||||
name="last_name"
|
||||
control={methods.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Input
|
||||
{...field}
|
||||
label={t('Last name')}
|
||||
placeholder={t('Last name')}
|
||||
required
|
||||
error={fieldState.error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<Box $margin={{ top: 'base' }}>
|
||||
<Controller
|
||||
name="secondary_email"
|
||||
control={methods.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Input
|
||||
{...field}
|
||||
label={t('Personal email address')}
|
||||
placeholder={t('john.appleseed@free.fr')}
|
||||
required
|
||||
error={fieldState.error?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Text $theme="greyscale" $variation="600">
|
||||
{t(
|
||||
'The person will receive an email at this address to set up their account.',
|
||||
)}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<HorizontalSeparator $withPadding={true} />
|
||||
|
||||
<Box $padding={{ top: 'base', horizontal: 'md' }}>
|
||||
<Text $size="md" $weight="bold">
|
||||
{t('Email address')}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box $padding="md">
|
||||
<Text>
|
||||
{mailbox.local_part}@{mailDomain.name}
|
||||
</Text>
|
||||
</Box>
|
||||
</form>
|
||||
</FormProvider>
|
||||
),
|
||||
leftAction: (
|
||||
<Button color="secondary" onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
),
|
||||
// rightAction: (
|
||||
// <Button
|
||||
// type="submit"
|
||||
// form={FORM_ID}
|
||||
// disabled={!methods.formState.isValid || isPending}
|
||||
// >
|
||||
// {t('Update')}
|
||||
// </Button>
|
||||
// ),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div id="modal-update-mailbox">
|
||||
<CustomModal
|
||||
isOpen={isOpen}
|
||||
hideCloseButton
|
||||
step={step}
|
||||
totalSteps={steps.length}
|
||||
leftActions={steps[step].leftAction}
|
||||
rightActions={steps[step].rightAction}
|
||||
size={ModalSize.MEDIUM}
|
||||
title={steps[step].title}
|
||||
onClose={onClose}
|
||||
closeOnEsc
|
||||
closeOnClickOutside
|
||||
>
|
||||
{steps[step].content}
|
||||
{isPending && (
|
||||
<Box $align="center">
|
||||
<Loader />
|
||||
</Box>
|
||||
)}
|
||||
</CustomModal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './ModalCreateMailbox';
|
||||
export * from './ModalUpdateMailbox';
|
||||
export * from './MailBoxesView';
|
||||
export * from './panel';
|
||||
|
||||
+14
-15
@@ -6,7 +6,7 @@ import { Box, Tag, Text, TextErrors } from '@/components';
|
||||
import { MailDomain } from '@/features/mail-domains/domains';
|
||||
import {
|
||||
MailDomainMailbox,
|
||||
MailDomainMailboxStatus,
|
||||
ViewMailbox,
|
||||
} from '@/features/mail-domains/mailboxes/types';
|
||||
|
||||
import { PAGE_SIZE } from '../../../conf';
|
||||
@@ -28,13 +28,6 @@ function formatSortModel(sortModel: SortModelItem) {
|
||||
return sortModel.sort === 'desc' ? `-${sortModel.field}` : sortModel.field;
|
||||
}
|
||||
|
||||
export type ViewMailbox = {
|
||||
name: string;
|
||||
id: string;
|
||||
email: string;
|
||||
status: MailDomainMailboxStatus;
|
||||
};
|
||||
|
||||
export function MailBoxesListView({
|
||||
mailDomain,
|
||||
querySearch,
|
||||
@@ -63,11 +56,12 @@ export function MailBoxesListView({
|
||||
}
|
||||
|
||||
return data.results.map((mailbox: MailDomainMailbox) => ({
|
||||
email: `${mailbox.local_part}@${mailDomain.name}`,
|
||||
name: `${mailbox.first_name} ${mailbox.last_name}`,
|
||||
id: mailbox.id,
|
||||
first_name: mailbox.first_name,
|
||||
last_name: mailbox.last_name,
|
||||
local_part: mailbox.local_part,
|
||||
secondary_email: mailbox.secondary_email,
|
||||
status: mailbox.status,
|
||||
mailbox,
|
||||
}));
|
||||
}, [data?.results, mailDomain]);
|
||||
|
||||
@@ -79,7 +73,7 @@ export function MailBoxesListView({
|
||||
return (
|
||||
(mailboxes &&
|
||||
mailboxes.filter((mailbox) =>
|
||||
mailbox.email.toLowerCase().includes(lowerCaseSearch),
|
||||
mailbox.local_part.toLowerCase().includes(lowerCaseSearch),
|
||||
)) ||
|
||||
[]
|
||||
);
|
||||
@@ -95,11 +89,16 @@ export function MailBoxesListView({
|
||||
rows={filteredMailboxes}
|
||||
columns={[
|
||||
{
|
||||
field: 'email',
|
||||
field: 'local_part',
|
||||
headerName: `${t('Address')} • ${filteredMailboxes.length}`,
|
||||
renderCell: ({ row }) => (
|
||||
<Text>
|
||||
{row.local_part}@{mailDomain.name}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
field: 'first_name',
|
||||
headerName: t('User'),
|
||||
enableSorting: true,
|
||||
renderCell: ({ row }) => (
|
||||
@@ -108,7 +107,7 @@ export function MailBoxesListView({
|
||||
$theme="greyscale"
|
||||
$css="text-transform: capitalize;"
|
||||
>
|
||||
{row.name}
|
||||
{row.first_name} {row.last_name}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
|
||||
+31
-3
@@ -11,7 +11,10 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, DropButton, IconOptions, Text } from '@/components';
|
||||
import { MailDomain } from '@/features/mail-domains/domains';
|
||||
import { ViewMailbox } from '@/features/mail-domains/mailboxes';
|
||||
import {
|
||||
ModalUpdateMailbox,
|
||||
ViewMailbox,
|
||||
} from '@/features/mail-domains/mailboxes';
|
||||
|
||||
import {
|
||||
useResetPassword,
|
||||
@@ -28,6 +31,7 @@ export const PanelActions = ({ mailDomain, mailbox }: PanelActionsProps) => {
|
||||
const [isDropOpen, setIsDropOpen] = useState(false);
|
||||
const isEnabled = mailbox.status === 'enabled';
|
||||
const disableModal = useModal();
|
||||
const updateModal = useModal();
|
||||
const { toast } = useToastProvider();
|
||||
|
||||
const { mutate: updateMailboxStatus } = useUpdateMailboxStatus();
|
||||
@@ -83,6 +87,25 @@ export const PanelActions = ({ mailDomain, mailbox }: PanelActionsProps) => {
|
||||
isOpen={isDropOpen}
|
||||
>
|
||||
<Box>
|
||||
<Button
|
||||
aria-label={t('Open the modal to update the role of this access')}
|
||||
onClick={() => {
|
||||
setIsDropOpen(false);
|
||||
if (isEnabled) {
|
||||
updateModal.open();
|
||||
} else {
|
||||
handleUpdateMailboxStatus();
|
||||
}
|
||||
}}
|
||||
color="primary-text"
|
||||
icon={
|
||||
<span className="material-icons" aria-hidden="true">
|
||||
settings
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Text $theme="primary">{t('Informations mailbox')}</Text>
|
||||
</Button>
|
||||
<Button
|
||||
aria-label={t('Open the modal to update the role of this access')}
|
||||
onClick={() => {
|
||||
@@ -93,7 +116,6 @@ export const PanelActions = ({ mailDomain, mailbox }: PanelActionsProps) => {
|
||||
handleUpdateMailboxStatus();
|
||||
}
|
||||
}}
|
||||
fullWidth
|
||||
color="primary-text"
|
||||
icon={
|
||||
<span className="material-icons" aria-hidden="true">
|
||||
@@ -120,11 +142,17 @@ export const PanelActions = ({ mailDomain, mailbox }: PanelActionsProps) => {
|
||||
}
|
||||
>
|
||||
<Text $theme={isEnabled ? 'primary' : 'greyscale'}>
|
||||
{isEnabled ? t('Reset password') : t('Reset password')}
|
||||
{t('Reset password')}
|
||||
</Text>
|
||||
</Button>
|
||||
</Box>
|
||||
</DropButton>
|
||||
<ModalUpdateMailbox
|
||||
isOpen={updateModal.isOpen}
|
||||
onClose={updateModal.close}
|
||||
mailDomain={mailDomain}
|
||||
mailbox={mailbox}
|
||||
/>
|
||||
<Modal
|
||||
isOpen={disableModal.isOpen}
|
||||
onClose={disableModal.close}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { UUID } from 'crypto';
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface MailDomainMailbox {
|
||||
id: UUID;
|
||||
local_part: string;
|
||||
@@ -14,3 +16,19 @@ export type MailDomainMailboxStatus =
|
||||
| 'disabled'
|
||||
| 'pending'
|
||||
| 'failed';
|
||||
|
||||
export interface ViewMailbox {
|
||||
id: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
local_part: string;
|
||||
secondary_email: string;
|
||||
status: MailDomainMailboxStatus;
|
||||
}
|
||||
|
||||
export interface Step {
|
||||
title: string;
|
||||
content: ReactNode;
|
||||
leftAction: ReactNode;
|
||||
rightAction?: ReactNode;
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Loader } from '@openfun/cunningham-react';
|
||||
import { useRouter as useNavigate } from 'next/navigation';
|
||||
import { useRouter } from 'next/router';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { TextErrors } from '@/components/TextErrors';
|
||||
import {
|
||||
MailDomainsLayout,
|
||||
useMailDomain,
|
||||
} from '@/features/mail-domains/domains';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const MailDomainAccessesPage: NextPageWithLayout = () => {
|
||||
const router = useRouter();
|
||||
|
||||
if (router?.query?.slug && typeof router.query.slug !== 'string') {
|
||||
throw new Error('Invalid mail domain slug');
|
||||
}
|
||||
|
||||
const { slug } = router.query;
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { error, isError, isLoading } = useMailDomain({ slug: String(slug) });
|
||||
|
||||
if (error?.status === 404) {
|
||||
navigate.replace(`/404`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isError && error) {
|
||||
return <TextErrors causes={error?.cause} />;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Box $align="center" $justify="center" $height="100%">
|
||||
<Loader />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
MailDomainAccessesPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <MailDomainsLayout>{page}</MailDomainsLayout>;
|
||||
};
|
||||
|
||||
export default MailDomainAccessesPage;
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Button, Input, Tooltip } from '@openfun/cunningham-react';
|
||||
import { Button, Input } from '@openfun/cunningham-react';
|
||||
import React, { ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, Text } from '@/components';
|
||||
import { useAuthStore } from '@/core/auth';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { ModalAddMailDomain } from '@/features/mail-domains/domains/components/ModalAddMailDomain';
|
||||
import { MailDomainsListView } from '@/features/mail-domains/domains/components/panel/MailDomainsListView';
|
||||
@@ -12,8 +11,6 @@ import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
const { t } = useTranslation();
|
||||
const { userData } = useAuthStore();
|
||||
const can_create = userData?.abilities?.mailboxes.can_create;
|
||||
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
||||
const [searchValue, setSearchValue] = React.useState('');
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
@@ -106,32 +103,12 @@ const Page: NextPageWithLayout = () => {
|
||||
<Box className="block md:hidden" $css="margin-bottom: 10px;"></Box>
|
||||
|
||||
<Box>
|
||||
{can_create ? (
|
||||
<Button data-testid="button-new-domain" onClick={openModal}>
|
||||
{t('Add a mail domain')}
|
||||
</Button>
|
||||
) : (
|
||||
<Tooltip content={t("You don't have the correct access right")}>
|
||||
<div>
|
||||
<Button
|
||||
data-testid="button-new-domain"
|
||||
onClick={openModal}
|
||||
disabled={!can_create}
|
||||
>
|
||||
{t('Add a mail domain')}
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Button data-testid="button-new-domain" onClick={openModal}>
|
||||
{t('Add a mail domain')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{!can_create && (
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
{t('Click on mailbox to view details')}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<MailDomainsListView querySearch={searchValue} />
|
||||
{isModalOpen && <ModalAddMailDomain closeModal={closeModal} />}
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user