diff --git a/frontend/src/components/categories/CategoryDialog.tsx b/frontend/src/components/categories/CategoryDialog.tsx index f6baa5e..8a2b69e 100644 --- a/frontend/src/components/categories/CategoryDialog.tsx +++ b/frontend/src/components/categories/CategoryDialog.tsx @@ -34,8 +34,8 @@ export const CategoryDialog: FC = ({ const { t } = useTranslate(); const { toast } = useToast(); const { mutateAsync: createCategory } = useCreate(EntityType.CATEGORY, { - onError: () => { - toast.error(t("message.internal_server_error")); + onError: (error) => { + toast.error(error); }, onSuccess: () => { closeDialog(); diff --git a/frontend/src/components/content-types/ContentTypeDialog.tsx b/frontend/src/components/content-types/ContentTypeDialog.tsx index f1e5ec0..ebe33bb 100644 --- a/frontend/src/components/content-types/ContentTypeDialog.tsx +++ b/frontend/src/components/content-types/ContentTypeDialog.tsx @@ -49,8 +49,8 @@ export const ContentTypeDialog: FC = ({ const { mutateAsync: createContentType } = useCreate( EntityType.CONTENT_TYPE, { - onError: () => { - toast.error(t("message.internal_server_error")); + onError: (error) => { + toast.error(error); }, onSuccess: () => { closeDialog(); diff --git a/frontend/src/components/contents/ContentDialog.tsx b/frontend/src/components/contents/ContentDialog.tsx index 4d1b27d..22c7b3d 100644 --- a/frontend/src/components/contents/ContentDialog.tsx +++ b/frontend/src/components/contents/ContentDialog.tsx @@ -178,8 +178,8 @@ export const ContentDialog: FC = ({ createContent( { ...params, entity: contentType.id }, { - onError: () => { - toast.error(t("message.internal_server_error")); + onError: (error) => { + toast.error(error); }, onSuccess: () => { closeDialog(); diff --git a/frontend/src/components/context-vars/ContextVarDialog.tsx b/frontend/src/components/context-vars/ContextVarDialog.tsx index 05f5e0f..a1257ba 100644 --- a/frontend/src/components/context-vars/ContextVarDialog.tsx +++ b/frontend/src/components/context-vars/ContextVarDialog.tsx @@ -41,8 +41,8 @@ export const ContextVarDialog: FC = ({ const { t } = useTranslate(); const { toast } = useToast(); const { mutateAsync: createContextVar } = useCreate(EntityType.CONTEXT_VAR, { - onError: () => { - toast.error(t("message.internal_server_error")); + onError: (error) => { + toast.error(error); }, onSuccess() { closeDialog(); diff --git a/frontend/src/components/roles/RoleDialog.tsx b/frontend/src/components/roles/RoleDialog.tsx index 798d19c..144c138 100644 --- a/frontend/src/components/roles/RoleDialog.tsx +++ b/frontend/src/components/roles/RoleDialog.tsx @@ -33,8 +33,8 @@ export const RoleDialog: FC = ({ const { t } = useTranslate(); const { toast } = useToast(); const { mutateAsync: createRole } = useCreate(EntityType.ROLE, { - onError: () => { - toast.error(t("message.internal_server_error")); + onError: (error) => { + toast.error(error); }, onSuccess() { closeDialog(); diff --git a/frontend/src/components/roles/index.tsx b/frontend/src/components/roles/index.tsx index e790bf7..f23655e 100644 --- a/frontend/src/components/roles/index.tsx +++ b/frontend/src/components/roles/index.tsx @@ -27,7 +27,6 @@ import { useHasPermission } from "@/hooks/useHasPermission"; import { useSearch } from "@/hooks/useSearch"; import { useToast } from "@/hooks/useToast"; import { useTranslate } from "@/hooks/useTranslate"; -import { TTranslationKeys } from "@/i18n/i18n.types"; import { PageHeader } from "@/layout/content/PageHeader"; import { EntityType } from "@/services/types"; import { PermissionAction } from "@/types/permission.types"; @@ -58,12 +57,7 @@ export const Roles = () => { ); const { mutateAsync: deleteRole } = useDelete(EntityType.ROLE, { onError: (error) => { - toast.error( - t( - (error.message as TTranslationKeys) || - "message.internal_server_error", - ), - ); + toast.error(error); }, onSuccess() { deleteDialogCtl.closeDialog(); diff --git a/frontend/src/components/translations/index.tsx b/frontend/src/components/translations/index.tsx index a4e3bb0..08e6cca 100644 --- a/frontend/src/components/translations/index.tsx +++ b/frontend/src/components/translations/index.tsx @@ -55,8 +55,8 @@ export const Translations = () => { }, ); const { mutateAsync: deleteTranslation } = useDelete(EntityType.TRANSLATION, { - onError: () => { - toast.error(t("message.internal_server_error")); + onError: (error) => { + toast.error(error); }, onSuccess() { deleteDialogCtl.closeDialog(); diff --git a/frontend/src/hooks/useToast.ts b/frontend/src/hooks/useToast.ts index e86ccfd..1f8f82c 100644 --- a/frontend/src/hooks/useToast.ts +++ b/frontend/src/hooks/useToast.ts @@ -5,13 +5,14 @@ * 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 { OptionsObject, enqueueSnackbar, SnackbarProvider as ToastProvider, } from "notistack"; +import { useTranslation } from "react-i18next"; + export { ToastProvider }; const TOAST_COMMON_STYLE = { @@ -38,25 +39,39 @@ const TOAST_WARNING_STYLE = { backgroundColor: "#fdf6ec", }; -export const useToast = () => ({ - toast: { - error: (message: string, options?: OptionsObject<"error">) => - enqueueSnackbar(message, { - variant: "error", - ...options, - style: { ...TOAST_ERROR_STYLE, ...options?.style }, - }), - success: (message: string, options?: OptionsObject<"success">) => - enqueueSnackbar(message, { - variant: "success", - ...options, - style: { ...TOAST_SUCCESS_STYLE, ...options?.style }, - }), - warning: (message: string, options?: OptionsObject<"warning">) => - enqueueSnackbar(message, { - variant: "warning", - ...options, - style: { ...TOAST_WARNING_STYLE, ...options?.style }, - }), - }, -}); +export const useToast = () => { + const { t } = useTranslation(); + + const extractErrorMessage = (error: any) => { + if (error?.statusCode == 409) { + return t("message.duplicate_error"); + } + + return error?.message || t("message.internal_server_error"); + }; + + return { + toast: { + error: (error: any, options?: OptionsObject<"error">) => { + const errorMessage = extractErrorMessage(error); + enqueueSnackbar(errorMessage, { + variant: "error", + ...options, + style: { ...TOAST_ERROR_STYLE, ...options?.style }, + }); + }, + success: (message: string, options?: OptionsObject<"success">) => + enqueueSnackbar(message, { + variant: "success", + ...options, + style: { ...TOAST_SUCCESS_STYLE, ...options?.style }, + }), + warning: (message: string, options?: OptionsObject<"warning">) => + enqueueSnackbar(message, { + variant: "warning", + ...options, + style: { ...TOAST_WARNING_STYLE, ...options?.style }, + }), + }, + }; +}; diff --git a/frontend/src/i18n/en/translation.json b/frontend/src/i18n/en/translation.json index a6ccc9d..13198b4 100644 --- a/frontend/src/i18n/en/translation.json +++ b/frontend/src/i18n/en/translation.json @@ -1,5 +1,6 @@ { "message": { + "duplicate_error": "Duplicate entry. Please choose a unique value.", "bad_request": "400 BAD REQUEST", "unable_to_process_request": "Unable to process request", "not_found": "404 NOT FOUND", diff --git a/frontend/src/i18n/fr/translation.json b/frontend/src/i18n/fr/translation.json index ad20c43..97cc201 100644 --- a/frontend/src/i18n/fr/translation.json +++ b/frontend/src/i18n/fr/translation.json @@ -1,5 +1,6 @@ { "message": { + "duplicate_error" : "Entrée en double. Veuillez choisir une valeur unique.", "bad_request": "400 MAUVAISE REQUÊTE", "unable_to_process_request": "Impossible de traiter la requête", "not_found": "404 RESSOURCE INTROUVABLE",