diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index dd9bad2a..f090d8af 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -43,7 +43,9 @@ "account_update_success": "Account has been updated successfully", "account_disabled": "Your account has been either disabled or is pending confirmation.", "success_invitation_sent": "Invitation to join has been successfully sent.", - "item_delete_confirm": "Are you sure you want to delete this item?", + "item_delete_confirm": "Are you sure you want to delete this {{0}} item?", + "items_delete_confirm": "Are you sure you want to delete those {{0}} selected items?", + "selected": "selected", "item_delete_success": "Item has been deleted successfully", "success_save": "Changes has been saved!", "no_result_found": "No result found", diff --git a/frontend/public/locales/fr/translation.json b/frontend/public/locales/fr/translation.json index 7ce2c0dd..7378a7f1 100644 --- a/frontend/public/locales/fr/translation.json +++ b/frontend/public/locales/fr/translation.json @@ -43,7 +43,8 @@ "account_update_success": "Le compte a été mis à jour avec succès", "account_disabled": "Votre compte a été désactivé ou est en attente de confirmation.", "success_invitation_sent": "L'invitation a été envoyée avec succès.", - "item_delete_confirm": "Êtes-vous sûr de bien vouloir supprimer cet élément?", + "item_delete_confirm": "Êtes-vous sûr de bien vouloir supprimer cet élément {{0}}?", + "items_delete_confirm": "Êtes-vous sûr de bien vouloir supprimer ces {{0}} éléments sélectionnés?", "item_delete_success": "L'élément a été supprimé avec succès", "success_save": "Les modifications ont été enregistrées!", "no_result_found": "Aucun résultat trouvé", diff --git a/frontend/src/app-components/buttons/FormButtons.tsx b/frontend/src/app-components/buttons/FormButtons.tsx index 4a86be72..04a9f520 100644 --- a/frontend/src/app-components/buttons/FormButtons.tsx +++ b/frontend/src/app-components/buttons/FormButtons.tsx @@ -6,7 +6,6 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ - import CheckIcon from "@mui/icons-material/Check"; import CloseIcon from "@mui/icons-material/Close"; import { Button, Grid } from "@mui/material"; @@ -22,7 +21,7 @@ export const DialogFormButtons = ({ return ( ({ onSubmit, ...rest }: FormDialogProps) => { + const handleClose = () => rest.onClose?.({}, "backdropClick"); + return ( - rest.onClose?.({}, "backdropClick")}> - {title} - + {title} {children} - - rest.onClose?.({}, "backdropClick")} - onSubmit={onSubmit} - /> + + ); diff --git a/frontend/src/app-components/dialogs/confirm/ConfirmDialog.tsx b/frontend/src/app-components/dialogs/confirm/ConfirmDialog.tsx index 512adeb6..37985cee 100644 --- a/frontend/src/app-components/dialogs/confirm/ConfirmDialog.tsx +++ b/frontend/src/app-components/dialogs/confirm/ConfirmDialog.tsx @@ -6,9 +6,16 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -import { Button, Dialog, DialogActions, DialogContent } from "@mui/material"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + Grid, +} from "@mui/material"; import { FC, ReactNode } from "react"; +import { useTranslate } from "@/hooks/useTranslate"; import { ConfirmOptions, DialogProps } from "@/types/common/dialogs.types"; import { DialogTitle } from "../DialogTitle"; @@ -23,6 +30,7 @@ export interface ConfirmDialogProps extends DialogProps {} export const ConfirmDialog: FC = ({ payload, ...rest }) => { + const { t } = useTranslate(); const cancelButtonProps = useDialogLoadingButton(() => rest.onClose(false)); const okButtonProps = useDialogLoadingButton(() => rest.onClose(true)); @@ -34,22 +42,24 @@ export const ConfirmDialog: FC = ({ payload, ...rest }) => { onClose={() => rest.onClose(false)} > rest.onClose(false)}> - {payload.title} + {payload.title || t("title.warning")} {payload.msg} - - + + + + ); diff --git a/frontend/src/app-components/dialogs/confirm/ConfirmDialogBody.tsx b/frontend/src/app-components/dialogs/confirm/ConfirmDialogBody.tsx index f54f954a..3ac5c9fb 100644 --- a/frontend/src/app-components/dialogs/confirm/ConfirmDialogBody.tsx +++ b/frontend/src/app-components/dialogs/confirm/ConfirmDialogBody.tsx @@ -11,16 +11,30 @@ import { Grid, Typography } from "@mui/material"; import { useTranslate } from "@/hooks/useTranslate"; -export const ConfirmDialogBody = () => { +export const ConfirmDialogBody = ({ + mode = "click", + itemsNumber = 1, +}: { + mode?: "selection" | "click"; + itemsNumber?: number; +}) => { const { t } = useTranslate(); + const DialogBodyText = + itemsNumber === 1 + ? t("message.item_delete_confirm", { + "0": mode === "click" ? "" : t("message.selected"), + }) + : t("message.items_delete_confirm", { + "0": itemsNumber.toString(), + }); return ( - - + + - {t("message.item_delete_confirm")} + {DialogBodyText} ); diff --git a/frontend/src/components/categories/CategoryForm.tsx b/frontend/src/components/categories/CategoryForm.tsx index a2f17f69..47bb9dcb 100644 --- a/frontend/src/components/categories/CategoryForm.tsx +++ b/frontend/src/components/categories/CategoryForm.tsx @@ -21,8 +21,8 @@ import { ComponentFormProps } from "@/types/common/dialogs.types"; export const CategoryForm: FC> = ({ data, - FormWrapper = React.Fragment, - FormWrapperProps, + Wrapper = React.Fragment, + WrapperProps, ...rest }) => { const { t } = useTranslate(); @@ -84,10 +84,10 @@ export const CategoryForm: FC> = ({ }, [data, reset]); return ( -
@@ -102,7 +102,7 @@ export const CategoryForm: FC> = ({
-
+ ); }; diff --git a/frontend/src/components/categories/CategoryFormDialog.tsx b/frontend/src/components/categories/CategoryFormDialog.tsx index b90aba6d..74112ea3 100644 --- a/frontend/src/components/categories/CategoryFormDialog.tsx +++ b/frontend/src/components/categories/CategoryFormDialog.tsx @@ -27,8 +27,8 @@ export const CategoryFormDialog: FC> = ({ onSuccess={() => { rest.onClose(true); }} - FormWrapper={FormDialog} - FormWrapperProps={{ + Wrapper={FormDialog} + WrapperProps={{ title: payload ? t("title.edit_category") : t("title.new_category"), ...rest, }} diff --git a/frontend/src/components/categories/index.tsx b/frontend/src/components/categories/index.tsx index 6c974e08..ea32b1ff 100644 --- a/frontend/src/components/categories/index.tsx +++ b/frontend/src/components/categories/index.tsx @@ -1,11 +1,12 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ + import AddIcon from "@mui/icons-material/Add"; import DeleteIcon from "@mui/icons-material/Delete"; import FolderIcon from "@mui/icons-material/Folder"; @@ -24,7 +25,6 @@ import { DataGrid } from "@/app-components/tables/DataGrid"; import { useDelete } from "@/hooks/crud/useDelete"; import { useDeleteMany } from "@/hooks/crud/useDeleteMany"; import { useFind } from "@/hooks/crud/useFind"; -import { useDialog } from "@/hooks/useDialog"; import { useDialogs } from "@/hooks/useDialogs"; import { useHasPermission } from "@/hooks/useHasPermission"; import { useSearch } from "@/hooks/useSearch"; @@ -42,7 +42,6 @@ import { CategoryFormDialog } from "./CategoryFormDialog"; export const Categories = () => { const { t } = useTranslate(); const { toast } = useToast(); - const deleteDialogCtl = useDialog(false); const hasPermission = useHasPermission(); const { onSearch, searchPayload } = useSearch({ $iLike: ["label"], @@ -53,26 +52,20 @@ export const Categories = () => { params: searchPayload, }, ); - const { mutateAsync: deleteCategory } = useDelete(EntityType.CATEGORY, { - onError: (error) => { + const options = { + onError: (error: Error) => { toast.error(error.message || t("message.internal_server_error")); }, onSuccess: () => { - deleteDialogCtl.closeDialog(); setSelectedCategories([]); toast.success(t("message.item_delete_success")); }, - }); - const { mutateAsync: deleteCategories } = useDeleteMany(EntityType.CATEGORY, { - onError: (error) => { - toast.error(error.message || t("message.internal_server_error")); - }, - onSuccess: () => { - deleteDialogCtl.closeDialog(); - setSelectedCategories([]); - toast.success(t("message.item_delete_success")); - }, - }); + }; + const { mutate: deleteCategory } = useDelete(EntityType.CATEGORY, options); + const { mutate: deleteCategories } = useDeleteMany( + EntityType.CATEGORY, + options, + ); const [selectedCategories, setSelectedCategories] = useState([]); const actionColumns = useActionColumns( EntityType.CATEGORY, @@ -87,14 +80,10 @@ export const Categories = () => { { label: ActionColumnLabel.Delete, action: async ({ id }) => { - const isConfirmed = await dialogs.confirm(, { - title: t("title.warning"), - okText: t("label.yes"), - cancelText: t("label.no"), - }); + const isConfirmed = await dialogs.confirm(); if (isConfirmed) { - await deleteCategory(id); + deleteCategory(id); } }, requires: [PermissionAction.DELETE], @@ -176,16 +165,14 @@ export const Categories = () => { color="error" onClick={async () => { const isConfirmed = await dialogs.confirm( - , - { - title: t("title.warning"), - okText: t("label.yes"), - cancelText: t("label.no"), - }, + , ); if (isConfirmed) { - await deleteCategories(selectedCategories); + deleteCategories(selectedCategories); } }} > diff --git a/frontend/src/types/common/dialogs.types.ts b/frontend/src/types/common/dialogs.types.ts index 5f2a13c8..e4e9b8b2 100644 --- a/frontend/src/types/common/dialogs.types.ts +++ b/frontend/src/types/common/dialogs.types.ts @@ -148,8 +148,8 @@ export type ComponentFormProps = { data: T | null; onError?: () => void; onSuccess?: () => void; - FormWrapper?: React.FC>; - FormWrapperProps?: Partial>; + Wrapper?: React.FC>; + WrapperProps?: Partial>; }; export interface FormButtonsProps {