Skip to content

Commit baf3dbd

Browse files
committed
feat(zimbra): add deletion of multiple selected email accounts
ref: #PRDCOL-219 Signed-off-by: Atef ZAAFOURI <[email protected]>
1 parent 8bd027b commit baf3dbd

File tree

8 files changed

+493
-80
lines changed

8 files changed

+493
-80
lines changed

packages/manager/apps/zimbra/public/translations/accounts/Messages_fr_FR.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"zimbra_account_account_add": "Créer un compte",
1010
"zimbra_account_account_order": "Commander un compte",
1111
"zimbra_account_account_migrate": "Migrer un compte",
12+
"zimbra_account_delete_all": "Supprimer les comptes ({{ count }})",
1213
"zimbra_account_tooltip_need_domain": "Veuillez d'abord configurer un domaine.",
1314
"zimbra_account_tooltip_need_slot": "Aucun compte disponible, veuillez en commander.",
1415
"zimbra_account_cancel_modal_content": "Le compte email <strong>{{ email }}</strong> et toutes ses données seront définitivement supprimées à la fin de la période d'engagement du service.",
@@ -19,6 +20,9 @@
1920
"zimbra_slot_modal_renew_date": "Fin de l'engagement: {{ renewDate }}",
2021
"zimbra_account_delete_modal_content_step1": "Êtes-vous sûr(e) de vouloir supprimer le compte email <strong>{{ email }}</strong> ?",
2122
"zimbra_account_delete_modal_content_step2": "Êtes-vous vraiment sûr(e) de vouloir supprimer ce compte email <strong>{{ email }}</strong> ?",
23+
"zimbra_account_delete_all_modal_content_step1": "Êtes-vous sûr(e) de vouloir supprimer les comptes suivants ? <br/><strong>{{ emails }}</strong>",
24+
"zimbra_account_delete_all_modal_content_step2": "Êtes-vous vraiment sûr(e) de vouloir supprimer ces comptes email suivants ? <br/><strong>{{ emails }}</strong>",
25+
"zimbra_account_delete_all_confirm_label": "Saisissez <strong>{{ label }}</strong> pour confirmer la suppression: ",
2226
"zimbra_account_delete_modal_warn_message": "Attention cette action est irréversible et entraîne la suppression des données.",
2327
"zimbra_account_upgrade_cta_back": "Retour vers mes comptes emails",
2428
"zimbra_account_upgrade_new_offer": "Nous lançons en version bêta notre <Link href=\"{{ zimbra_emails_link }}\">nouvelle offre Zimbra Pro</Link>.",

packages/manager/apps/zimbra/public/translations/common/Messages_fr_FR.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"add_email_account": "Créer un compte email",
4848
"email_account_settings": "Paramètres du compte",
4949
"delete_email_account": "Supprimer le compte",
50+
"delete_email_accounts": "Supprimer les comptes",
5051
"order_zimbra_accounts": "Commander des comptes Zimbra",
5152
"select_slot": "Sélectionner une offre",
5253
"form_at_least_one_digit": "Doit contenir au minimum un chiffre",

packages/manager/apps/zimbra/src/pages/dashboard/emailAccounts/DatagridTopBar.component.tsx

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import {
1515
import { OdsText, OdsTooltip } from '@ovhcloud/ods-components/react';
1616

1717
import { ManagerButton } from '@ovh-ux/manager-react-components';
18-
import { ButtonType, PageLocation, useOvhTracking } from '@ovh-ux/manager-react-shell-client';
18+
import {
19+
ButtonType,
20+
PageLocation,
21+
useOvhTracking,
22+
} from '@ovh-ux/manager-react-shell-client';
1923

2024
import { useDomains, usePlatform } from '@/data/hooks';
2125
import { GUIDES_LIST } from '@/guides.constants';
@@ -27,7 +31,15 @@ import {
2731
} from '@/tracking.constants';
2832
import { IAM_ACTIONS } from '@/utils/iamAction.constants';
2933

30-
export const DatagridTopbar = () => {
34+
import { EmailAccountItem } from './EmailAccounts.types';
35+
36+
export type DatagridTopbarProps = {
37+
selectedRows?: EmailAccountItem[];
38+
};
39+
40+
export const DatagridTopbar: React.FC<DatagridTopbarProps> = ({
41+
selectedRows,
42+
}: DatagridTopbarProps) => {
3143
const { t } = useTranslation(['accounts', 'common']);
3244
const { trackClick } = useOvhTracking();
3345
const navigate = useNavigate();
@@ -36,6 +48,10 @@ export const DatagridTopbar = () => {
3648

3749
const hrefAddEmailAccount = useGenerateUrl('./add', 'path');
3850
const hrefOrderEmailAccount = useGenerateUrl('./order', 'path');
51+
const hrefDeleteSelectedEmailAccounts = useGenerateUrl(
52+
'./delete_all',
53+
'path',
54+
);
3955

4056
const { data: domains, isLoading: isLoadingDomains } = useDomains();
4157

@@ -47,7 +63,8 @@ export const DatagridTopbar = () => {
4763
.some(Boolean);
4864
}, [accountsStatistics]);
4965

50-
const canCreateAccount = !isLoadingDomains && !!domains?.length && hasAvailableAccounts;
66+
const canCreateAccount =
67+
!isLoadingDomains && !!domains?.length && hasAvailableAccounts;
5168

5269
const handleAddEmailAccountClick = () => {
5370
trackClick({
@@ -76,6 +93,16 @@ export const DatagridTopbar = () => {
7693
});
7794
window.open(GUIDES_LIST.ovh_mail_migrator.url.DEFAULT, '_blank');
7895
};
96+
const handleSelectEmailAccounts = () => {
97+
navigate(hrefDeleteSelectedEmailAccounts, {
98+
state: {
99+
selectedEmailAccounts: selectedRows.map((row) => ({
100+
id: row?.id,
101+
email: row?.email,
102+
})),
103+
},
104+
});
105+
};
79106

80107
return (
81108
<div className="flex gap-6">
@@ -125,6 +152,17 @@ export const DatagridTopbar = () => {
125152
iconAlignment={ODS_BUTTON_ICON_ALIGNMENT.right}
126153
icon={ODS_ICON_NAME.externalLink}
127154
/>
155+
{!!selectedRows?.length && (
156+
<ManagerButton
157+
id="ovh-mail-delete-selected-btn"
158+
color={ODS_BUTTON_COLOR.critical}
159+
size={ODS_BUTTON_SIZE.sm}
160+
onClick={handleSelectEmailAccounts}
161+
label={t('zimbra_account_delete_all', { count: selectedRows.length })}
162+
iconAlignment={ODS_BUTTON_ICON_ALIGNMENT.right}
163+
icon={ODS_ICON_NAME.trash}
164+
/>
165+
)}
128166
</div>
129167
);
130168
};

packages/manager/apps/zimbra/src/pages/dashboard/emailAccounts/EmailAccountsDatagrid.component.tsx

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
1-
import { useEffect, useMemo, useState } from 'react';
1+
import { useCallback, useEffect, useMemo, useState } from 'react';
2+
3+
import { useLocation } from 'react-router-dom';
24

35
import { useTranslation } from 'react-i18next';
46

57
import { ODS_TEXT_PRESET } from '@ovhcloud/ods-components';
68
import { OdsText } from '@ovhcloud/ods-components/react';
79

810
import { NAMESPACES } from '@ovh-ux/manager-common-translations';
9-
import { Datagrid, DatagridColumn, useBytes } from '@ovh-ux/manager-react-components';
11+
import {
12+
Datagrid,
13+
DatagridColumn,
14+
useBytes,
15+
} from '@ovh-ux/manager-react-components';
1016

1117
import { AccountStatusBadge, BillingStateBadge, LabelChip } from '@/components';
12-
import { AccountType, ResourceStatus, getZimbraPlatformListQueryKey } from '@/data/api';
18+
import {
19+
AccountType,
20+
ResourceStatus,
21+
getZimbraPlatformListQueryKey,
22+
} from '@/data/api';
1323
import { useAccounts, useSlotServices } from '@/data/hooks';
1424
import { useDebouncedValue, useOverridePage } from '@/hooks';
1525
import queryClient from '@/queryClient';
@@ -21,12 +31,20 @@ import { EmailAccountItem } from './EmailAccounts.types';
2131

2232
export const EmailAccountsDatagrid = () => {
2333
const { t } = useTranslation(['accounts', 'common', NAMESPACES.STATUS]);
34+
const [rowSelection, setRowSelection] = useState({});
35+
const [selectedRows, setSelectedRows] = useState([]);
2436
const [hasADeletingAccount, setHasADeletingAccount] = useState(false);
2537
const isOverridedPage = useOverridePage();
2638
const { formatBytes } = useBytes();
39+
const location = useLocation();
40+
const { clearSelectedEmailAccounts } = location.state || {};
2741

28-
const [searchInput, setSearchInput, debouncedSearchInput, setDebouncedSearchInput] =
29-
useDebouncedValue('');
42+
const [
43+
searchInput,
44+
setSearchInput,
45+
debouncedSearchInput,
46+
setDebouncedSearchInput,
47+
] = useDebouncedValue('');
3048

3149
const {
3250
data: accounts,
@@ -48,14 +66,23 @@ export const EmailAccountsDatagrid = () => {
4866
enabled: !isOverridedPage,
4967
});
5068

69+
useEffect(() => {
70+
if (clearSelectedEmailAccounts) {
71+
setRowSelection({});
72+
setSelectedRows([]);
73+
}
74+
}, [clearSelectedEmailAccounts]);
75+
5176
/* This is necessary to enable back the "Create account" button when your
5277
* slots are full and you delete an account and the account goes
5378
* from "DELETING" state to actually being deleted, because we invalidate
5479
* the cache when sending the delete request of the account but when it is
5580
* in "DELETING" state the slot is not yet freed, to me it should be
5681
* but it requires changes on backend side */
5782
useEffect(() => {
58-
const current = accounts?.some((account) => account.resourceStatus === ResourceStatus.DELETING);
83+
const current = accounts?.some(
84+
(account) => account.resourceStatus === ResourceStatus.DELETING,
85+
);
5986
if (hasADeletingAccount !== current) {
6087
if (!current) {
6188
queryClient.invalidateQueries({
@@ -84,29 +111,43 @@ export const EmailAccountsDatagrid = () => {
84111
);
85112
}, [accounts, services]);
86113

114+
const isRowSelectable = useCallback(
115+
(item: EmailAccountItem) => item.status === ResourceStatus.READY,
116+
[],
117+
);
118+
87119
const columns: DatagridColumn<EmailAccountItem>[] = useMemo(
88120
() => [
89121
{
90122
id: 'email_account',
91-
cell: (item) => <OdsText preset={ODS_TEXT_PRESET.paragraph}>{item.email}</OdsText>,
123+
cell: (item) => (
124+
<OdsText preset={ODS_TEXT_PRESET.paragraph}>{item.email}</OdsText>
125+
),
92126
label: 'common:email_account',
93127
isSearchable: true,
94128
},
95129
{
96130
id: 'organization',
97-
cell: (item) => <LabelChip id={item.organizationId}>{item.organizationLabel}</LabelChip>,
131+
cell: (item) => (
132+
<LabelChip id={item.organizationId}>
133+
{item.organizationLabel}
134+
</LabelChip>
135+
),
98136
label: 'common:organization',
99137
},
100138
{
101139
id: 'offer',
102-
cell: (item) => <OdsText preset={ODS_TEXT_PRESET.paragraph}>{item.offer}</OdsText>,
140+
cell: (item) => (
141+
<OdsText preset={ODS_TEXT_PRESET.paragraph}>{item.offer}</OdsText>
142+
),
103143
label: 'zimbra_account_datagrid_offer_label',
104144
},
105145
{
106146
id: 'quota',
107147
cell: (item) => (
108148
<OdsText preset={ODS_TEXT_PRESET.paragraph}>
109-
{formatBytes(item.used, 2, 1024)} / {formatBytes(item.available, 0, 1024)}
149+
{formatBytes(item.used, 2, 1024)} /{' '}
150+
{formatBytes(item.available, 0, 1024)}
110151
</OdsText>
111152
),
112153
label: 'zimbra_account_datagrid_quota',
@@ -123,7 +164,9 @@ export const EmailAccountsDatagrid = () => {
123164
},
124165
{
125166
id: 'tooltip',
126-
cell: (item: EmailAccountItem) => <ActionButtonEmailAccount item={item} />,
167+
cell: (item: EmailAccountItem) => (
168+
<ActionButtonEmailAccount item={item} />
169+
),
127170
label: '',
128171
},
129172
],
@@ -132,12 +175,18 @@ export const EmailAccountsDatagrid = () => {
132175

133176
return (
134177
<Datagrid
135-
topbar={<DatagridTopbar />}
178+
topbar={<DatagridTopbar selectedRows={selectedRows} />}
136179
search={{
137180
searchInput,
138181
setSearchInput,
139182
onSearch: (search) => setDebouncedSearchInput(search),
140183
}}
184+
rowSelection={{
185+
rowSelection,
186+
setRowSelection,
187+
enableRowSelection: ({ original: item }) => isRowSelectable(item),
188+
onRowSelectionChange: setSelectedRows,
189+
}}
141190
columns={columns.map((column) => ({
142191
...column,
143192
label: t(column.label),

0 commit comments

Comments
 (0)