From e2a50e9dcf468f486d483dd9b4572be47df5aac6 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 5 Feb 2025 10:53:19 +0100 Subject: [PATCH 1/3] refactor(frontend): update translation dialogs --- .../translations/EditTranslationDialog.tsx | 107 ---------------- .../translations/TranslationForm.tsx | 115 ++++++++++++++++++ .../translations/TranslationFormDialog.tsx | 36 ++++++ .../translations/TranslationInput.tsx | 42 ------- .../src/components/translations/index.tsx | 35 +++--- 5 files changed, 167 insertions(+), 168 deletions(-) delete mode 100644 frontend/src/components/translations/EditTranslationDialog.tsx create mode 100644 frontend/src/components/translations/TranslationForm.tsx create mode 100644 frontend/src/components/translations/TranslationFormDialog.tsx delete mode 100644 frontend/src/components/translations/TranslationInput.tsx diff --git a/frontend/src/components/translations/EditTranslationDialog.tsx b/frontend/src/components/translations/EditTranslationDialog.tsx deleted file mode 100644 index f3d3fcbe8..000000000 --- a/frontend/src/components/translations/EditTranslationDialog.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright © 2024 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 { - Dialog, - DialogActions, - DialogContent, - FormLabel, - Typography, -} from "@mui/material"; -import { FC, useEffect } from "react"; -import { Controller, useForm } from "react-hook-form"; - -import DialogButtons from "@/app-components/buttons/DialogButtons"; -import { DialogTitle } from "@/app-components/dialogs/DialogTitle"; -import { ContentContainer } from "@/app-components/dialogs/layouts/ContentContainer"; -import { ContentItem } from "@/app-components/dialogs/layouts/ContentItem"; -import { useFind } from "@/hooks/crud/useFind"; -import { useUpdate } from "@/hooks/crud/useUpdate"; -import { DialogControlProps } from "@/hooks/useDialog"; -import { useToast } from "@/hooks/useToast"; -import { useTranslate } from "@/hooks/useTranslate"; -import { EntityType } from "@/services/types"; -import { - ITranslation, - ITranslationAttributes, - ITranslations, -} from "@/types/translation.types"; - -import TranslationInput from "./TranslationInput"; - -export type EditTranslationDialogProps = DialogControlProps; -export const EditTranslationDialog: FC = ({ - open, - data, - closeDialog, - ...rest -}) => { - const { data: languages } = useFind( - { entity: EntityType.LANGUAGE }, - { - hasCount: false, - }, - ); - const { t } = useTranslate(); - const { toast } = useToast(); - const { mutateAsync: updateTranslation } = useUpdate(EntityType.TRANSLATION, { - onError: () => { - toast.error(t("message.internal_server_error")); - }, - onSuccess() { - closeDialog(); - toast.success(t("message.success_save")); - }, - }); - const { reset, control, handleSubmit } = useForm({ - defaultValues: data, - }); - const onSubmitForm = async (params: ITranslationAttributes) => { - if (data?.id) updateTranslation({ id: data.id, params }); - }; - - useEffect(() => { - if (open) reset(data); - }, [open, reset, data]); - - return ( - -
- - {t("title.update_translation")} - - - - {t("label.original_text")} - {data?.str} - - - {languages - .filter(({ isDefault }) => !isDefault) - .map((language) => ( - - ( - - )} - /> - - ))} - - - - - -
-
- ); -}; diff --git a/frontend/src/components/translations/TranslationForm.tsx b/frontend/src/components/translations/TranslationForm.tsx new file mode 100644 index 000000000..c4fb1dcc4 --- /dev/null +++ b/frontend/src/components/translations/TranslationForm.tsx @@ -0,0 +1,115 @@ +/* + * 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 { FormLabel, Grid, Typography } from "@mui/material"; +import { FC, Fragment } from "react"; +import { Controller, ControllerRenderProps, useForm } from "react-hook-form"; + +import { ContentContainer, ContentItem } from "@/app-components/dialogs"; +import { Input } from "@/app-components/inputs/Input"; +import { useFind } from "@/hooks/crud/useFind"; +import { useUpdate } from "@/hooks/crud/useUpdate"; +import { useToast } from "@/hooks/useToast"; +import { useTranslate } from "@/hooks/useTranslate"; +import { EntityType } from "@/services/types"; +import { ComponentFormProps } from "@/types/common/dialogs.types"; +import { ILanguage } from "@/types/language.types"; +import { + ITranslation, + ITranslationAttributes, + ITranslations, +} from "@/types/translation.types"; + +interface TranslationInputProps { + field: ControllerRenderProps; + language: ILanguage; +} + +const TranslationInput: React.FC = ({ + field, + language: { isRTL, title }, +}) => ( + + {title} + + } + minRows={3} + inputRef={field.ref} + multiline={true} + {...field} + /> +); + +export const TranslationForm: FC> = ({ + data, + Wrapper = Fragment, + WrapperProps, + ...rest +}) => { + const { t } = useTranslate(); + const { toast } = useToast(); + const { data: languages } = useFind( + { entity: EntityType.LANGUAGE }, + { + hasCount: false, + }, + ); + const { mutate: updateTranslation } = useUpdate(EntityType.TRANSLATION, { + onError: (error: Error) => { + rest.onError?.(); + toast.error(error || t("message.internal_server_error")); + }, + onSuccess() { + rest.onSuccess?.(); + toast.success(t("message.success_save")); + }, + }); + const { control, handleSubmit } = useForm({ + defaultValues: { + translations: data?.translations, + }, + }); + const onSubmitForm = (params: ITranslationAttributes) => { + if (data?.id) updateTranslation({ id: data.id, params }); + }; + + return ( + +
+ + {t("label.original_text")} + {data?.str} + + + {languages + .filter(({ isDefault }) => !isDefault) + .map((language) => ( + + ( + + )} + /> + + ))} + +
+
+ ); +}; + +TranslationForm.displayName = TranslationForm.name; diff --git a/frontend/src/components/translations/TranslationFormDialog.tsx b/frontend/src/components/translations/TranslationFormDialog.tsx new file mode 100644 index 000000000..aa1c75efe --- /dev/null +++ b/frontend/src/components/translations/TranslationFormDialog.tsx @@ -0,0 +1,36 @@ +/* + * 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 { FC } from "react"; + +import { FormDialog } from "@/app-components/dialogs"; +import { useTranslate } from "@/hooks/useTranslate"; +import { ComponentFormDialogProps } from "@/types/common/dialogs.types"; +import { ITranslation } from "@/types/translation.types"; + +import { TranslationForm } from "./TranslationForm"; + +export const TranslationFormDialog: FC< + ComponentFormDialogProps +> = ({ payload, ...rest }) => { + const { t } = useTranslate(); + + return ( + { + rest.onClose(true); + }} + Wrapper={FormDialog} + WrapperProps={{ + title: t("title.update_translation"), + ...rest, + }} + /> + ); +}; diff --git a/frontend/src/components/translations/TranslationInput.tsx b/frontend/src/components/translations/TranslationInput.tsx deleted file mode 100644 index 060098458..000000000 --- a/frontend/src/components/translations/TranslationInput.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2024 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 { Grid } from "@mui/material"; -import React from "react"; -import { ControllerRenderProps } from "react-hook-form"; - -import { Input } from "@/app-components/inputs/Input"; -import { ILanguage } from "@/types/language.types"; -import { ITranslationAttributes } from "@/types/translation.types"; - -interface RenderTranslationInputProps { - language: ILanguage; - field: ControllerRenderProps; -} - -const TranslationInput: React.FC = ({ - language, - field, -}) => ( - - {language.title} - - } - multiline={true} - minRows={3} - {...field} - /> -); - -TranslationInput.displayName = "TranslationInput"; - -export default TranslationInput; diff --git a/frontend/src/components/translations/index.tsx b/frontend/src/components/translations/index.tsx index 08e6cca4e..dcbb32494 100644 --- a/frontend/src/components/translations/index.tsx +++ b/frontend/src/components/translations/index.tsx @@ -1,17 +1,18 @@ /* - * 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 { faLanguage } from "@fortawesome/free-solid-svg-icons"; import AutorenewIcon from "@mui/icons-material/Autorenew"; import { Button, Chip, Grid, Paper, Stack } from "@mui/material"; import { GridColDef } from "@mui/x-data-grid"; -import { DeleteDialog } from "@/app-components/dialogs"; +import { ConfirmDialogBody } from "@/app-components/dialogs"; import { FilterTextfield } from "@/app-components/inputs/FilterTextfield"; import { ActionColumnLabel, @@ -21,7 +22,7 @@ import { DataGrid } from "@/app-components/tables/DataGrid"; import { useDelete } from "@/hooks/crud/useDelete"; import { useFind } from "@/hooks/crud/useFind"; import { useRefreshTranslations } from "@/hooks/entities/translation-hooks"; -import { getDisplayDialogs, useDialog } from "@/hooks/useDialog"; +import { useDialogs } from "@/hooks/useDialogs"; import { useSearch } from "@/hooks/useSearch"; import { useToast } from "@/hooks/useToast"; import { useTranslate } from "@/hooks/useTranslate"; @@ -32,19 +33,18 @@ import { PermissionAction } from "@/types/permission.types"; import { ITranslation } from "@/types/translation.types"; import { getDateTimeFormatter } from "@/utils/date"; -import { EditTranslationDialog } from "./EditTranslationDialog"; +import { TranslationFormDialog } from "./TranslationFormDialog"; export const Translations = () => { const { t } = useTranslate(); const { toast } = useToast(); + const dialogs = useDialogs(); const { data: languages } = useFind( { entity: EntityType.LANGUAGE }, { hasCount: false, }, ); - const editDialogCtl = useDialog(false); - const deleteDialogCtl = useDialog(false); const { onSearch, searchPayload } = useSearch({ $iLike: ["str"], }); @@ -54,16 +54,15 @@ export const Translations = () => { params: searchPayload, }, ); - const { mutateAsync: deleteTranslation } = useDelete(EntityType.TRANSLATION, { + const { mutate: deleteTranslation } = useDelete(EntityType.TRANSLATION, { onError: (error) => { toast.error(error); }, onSuccess() { - deleteDialogCtl.closeDialog(); toast.success(t("message.item_delete_success")); }, }); - const { mutateAsync: checkRefreshTranslations, isLoading } = + const { mutate: checkRefreshTranslations, isLoading } = useRefreshTranslations({ onError: () => { toast.error(t("message.internal_server_error")); @@ -78,12 +77,18 @@ export const Translations = () => { [ { label: ActionColumnLabel.Edit, - action: (row) => editDialogCtl.openDialog(row), + action: (row) => dialogs.open(TranslationFormDialog, row), requires: [PermissionAction.UPDATE], }, { label: ActionColumnLabel.Delete, - action: (row) => deleteDialogCtl.openDialog(row.id), + action: async ({ id }) => { + const isConfirmed = await dialogs.confirm(ConfirmDialogBody); + + if (isConfirmed) { + deleteTranslation(id); + } + }, requires: [PermissionAction.DELETE], }, ], @@ -164,14 +169,6 @@ export const Translations = () => { - - { - if (deleteDialogCtl?.data) - deleteTranslation(deleteDialogCtl.data); - }} - /> From 0a77bc87ac50e57d0a2bbedba9cdbb5f46dbf106 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 5 Feb 2025 10:57:27 +0100 Subject: [PATCH 2/3] fix(frontend): remove extra line --- frontend/src/components/translations/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/translations/index.tsx b/frontend/src/components/translations/index.tsx index dcbb32494..d190e8b31 100644 --- a/frontend/src/components/translations/index.tsx +++ b/frontend/src/components/translations/index.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 { faLanguage } from "@fortawesome/free-solid-svg-icons"; import AutorenewIcon from "@mui/icons-material/Autorenew"; import { Button, Chip, Grid, Paper, Stack } from "@mui/material"; From c520fd8f36f536e975a7f337f6c95e99056f5e6e Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 5 Feb 2025 15:07:49 +0100 Subject: [PATCH 3/3] fix(frontend): update onError toast error message --- frontend/src/components/translations/TranslationForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/translations/TranslationForm.tsx b/frontend/src/components/translations/TranslationForm.tsx index c4fb1dcc4..a796c94bd 100644 --- a/frontend/src/components/translations/TranslationForm.tsx +++ b/frontend/src/components/translations/TranslationForm.tsx @@ -65,7 +65,7 @@ export const TranslationForm: FC> = ({ const { mutate: updateTranslation } = useUpdate(EntityType.TRANSLATION, { onError: (error: Error) => { rest.onError?.(); - toast.error(error || t("message.internal_server_error")); + toast.error(error.message || t("message.internal_server_error")); }, onSuccess() { rest.onSuccess?.();