From 6febde0d9337d03ef2933ed8ba8d29fe5172ee39 Mon Sep 17 00:00:00 2001 From: leo <leopold@lchappuis.fr> Date: Wed, 12 Feb 2025 08:12:36 -0500 Subject: [PATCH] admin-panel: require confirmation before deleting accounts Admins must now confirm their intent before deleting accounts. Change-Id: I3e75bedfc636db087c76ff648650e6b5d50b2164 --- .../AdminSettings/AccountsOverview.tsx | 17 +++ .../AdminSettings/ValidationRequiredModal.tsx | 113 ++++++++++++++++++ server/locale/en/translation.json | 3 + 3 files changed, 133 insertions(+) create mode 100644 client/src/components/AdminSettings/ValidationRequiredModal.tsx diff --git a/client/src/components/AdminSettings/AccountsOverview.tsx b/client/src/components/AdminSettings/AccountsOverview.tsx index fb69e5e9..8ce4acc5 100644 --- a/client/src/components/AdminSettings/AccountsOverview.tsx +++ b/client/src/components/AdminSettings/AccountsOverview.tsx @@ -40,6 +40,7 @@ import { ChangeEvent, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDeleteAccounts, useGetAllAccounts } from '../../services/adminQueries' +import ValidationRequiredModal from './ValidationRequiredModal' function descendingComparator<T>(a: T, b: T, orderBy: keyof T) { if (b[orderBy] < a[orderBy]) { @@ -200,6 +201,7 @@ export default function AccountsOverview() { const [rowsPerPage, setRowsPerPage] = useState(rowPerPagesOptions[0]) const getAllAccounts = useGetAllAccounts() const accountDeletionMutation = useDeleteAccounts() + const [modalOpen, setModalOpen] = useState(false) const rows = useMemo(() => { if (getAllAccounts.data) { @@ -240,6 +242,10 @@ export default function AccountsOverview() { } const handleSubmitDeletion = () => { + setModalOpen(true) + } + + const handleSubmitDeletionValidation = () => { const accountsToDelete: AccountOverview[] = [] for (const account of rows) { if (selected.includes(account.accountUri)) { @@ -248,6 +254,7 @@ export default function AccountsOverview() { } accountDeletionMutation.mutate(accountsToDelete) setSelected([]) + setModalOpen(false) } const handleChangePage = (event: unknown, newPage: number) => { @@ -344,6 +351,16 @@ export default function AccountsOverview() { /> </Paper> </Box> + {modalOpen && ( + <ValidationRequiredModal + info={t('remove_account_info')} + title={t('validation_required')} + buttonText={t('remove_button')} + open={modalOpen} + onClose={() => setModalOpen(false)} + onValidation={handleSubmitDeletionValidation} + /> + )} </Box> ) } diff --git a/client/src/components/AdminSettings/ValidationRequiredModal.tsx b/client/src/components/AdminSettings/ValidationRequiredModal.tsx new file mode 100644 index 00000000..45f845e0 --- /dev/null +++ b/client/src/components/AdminSettings/ValidationRequiredModal.tsx @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2022-2025 Savoir-faire Linux Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +import { Box, Button, Card, CardContent, CardHeader, Modal, useTheme } from '@mui/material' +import { useTranslation } from 'react-i18next' + +interface ValidationRequiredModalProps { + open: boolean + onClose: () => void + onValidation: () => void + title: string + buttonText: string + info?: string +} + +export default function ValidationRequiredModal({ + open, + onClose, + onValidation, + title, + buttonText, + info, +}: ValidationRequiredModalProps) { + const theme = useTheme() + const { t } = useTranslation() + + return ( + <Modal + open={open} + onClose={() => { + onClose() + }} + sx={{ + width: '100%', + height: '100%', + display: 'flex', + justifyContent: 'center', + alignContent: 'center', + alignItems: 'center', + }} + > + <Card + sx={{ + width: '350px', + height: '350px', + backgroundColor: theme.SettingsNeedPassword.backgroundColor, + borderRadius: '6px', + border: 'none', + }} + > + <CardHeader title={title} /> + <CardContent sx={{ height: '100%' }}> + <Box + sx={{ + fontSize: '14px', + display: 'flex', + color: 'grey', + maxWidth: '100%', + whiteSpace: 'pre-wrap', + wordWrap: 'break-word', + overflowWrap: 'break-word', + overflow: 'hidden', + textOverflow: 'ellipsis', + margin: 'auto', + textAlign: 'start', + marginTop: '-14px', + }} + > + {info} + </Box> + + <Box + sx={{ + height: '45%', + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + flexDirection: 'column', + }} + ></Box> + <Box sx={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}> + <Button variant="contained" onClick={onClose}> + {t('dialog_cancel')} + </Button> + <Button + variant="contained" + onClick={() => { + onValidation() + }} + > + {buttonText} + </Button> + </Box> + </CardContent> + </Card> + </Modal> + ) +} diff --git a/server/locale/en/translation.json b/server/locale/en/translation.json index 1d2ea652..05cc3a56 100644 --- a/server/locale/en/translation.json +++ b/server/locale/en/translation.json @@ -238,6 +238,8 @@ "registration_success": "Account registered successfully. Logging in…", "resetting_display_name_success_alert": "Display name reset successfully.", "resetting_display_name_error_alert": "An error occurred while resetting the display name.", + "remove_account_info": "You are about to delete the selected accounts.\n This action is irreversible.\n Are you sure you want to proceed?", + "remove_button": "Remove", "remove_jams_server": "Remove", "removing_jams_server_success": "JAMS server address removed successfully.", "removing_jams_server_error": "An error occurred while removing the JAMS server.", @@ -316,6 +318,7 @@ "username_rule_3": "The username may contain hyphens (-).", "username_rule_4": "The username may contain underscores (_).", "username_rules_dialog_title": "Username rules", + "validation_required": "Validation required", "version": "Version", "welcome_text": "Welcome to Jami", "welcome_to_text": "Welcome to ", -- GitLab