From 6fe846d36065cd6fb0b6e323319a62df0e051918 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Wed, 5 Feb 2025 11:58:30 +0100 Subject: [PATCH] refactor(frontend): update label dialogs --- .../src/components/labels/LabelDialog.tsx | 150 ------------------ frontend/src/components/labels/LabelForm.tsx | 131 +++++++++++++++ .../src/components/labels/LabelFormDialog.tsx | 37 +++++ frontend/src/components/labels/index.tsx | 37 ++--- 4 files changed, 184 insertions(+), 171 deletions(-) delete mode 100644 frontend/src/components/labels/LabelDialog.tsx create mode 100644 frontend/src/components/labels/LabelForm.tsx create mode 100644 frontend/src/components/labels/LabelFormDialog.tsx diff --git a/frontend/src/components/labels/LabelDialog.tsx b/frontend/src/components/labels/LabelDialog.tsx deleted file mode 100644 index 3c9b50a5..00000000 --- a/frontend/src/components/labels/LabelDialog.tsx +++ /dev/null @@ -1,150 +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 } from "@mui/material"; -import { FC, useEffect } from "react"; -import { 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 { Input } from "@/app-components/inputs/Input"; -import { useCreate } from "@/hooks/crud/useCreate"; -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 { ILabel, ILabelAttributes } from "@/types/label.types"; -import { slugify } from "@/utils/string"; - -export type LabelDialogProps = DialogControlProps; -export const LabelDialog: FC = ({ - open, - data, - closeDialog, - ...rest -}) => { - const { t } = useTranslate(); - const { toast } = useToast(); - const { mutateAsync: createLabel } = useCreate(EntityType.LABEL, { - onError: () => { - toast.error(t("message.internal_server_error")); - }, - onSuccess() { - closeDialog(); - toast.success(t("message.success_save")); - }, - }); - const { mutateAsync: updateLabel } = useUpdate(EntityType.LABEL, { - onError: () => { - toast.error(t("message.internal_server_error")); - }, - onSuccess() { - closeDialog(); - toast.success(t("message.success_save")); - }, - }); - const { - reset, - register, - setValue, - formState: { errors }, - handleSubmit, - } = useForm({ - defaultValues: { - name: data?.name || "", - title: data?.title || "", - description: data?.description || "", - }, - }); - const validationRules = { - title: { - required: t("message.title_is_required"), - }, - name: {}, - description: {}, - }; - const onSubmitForm = async (params: ILabelAttributes) => { - if (data) { - updateLabel({ id: data.id, params }); - } else { - createLabel(params); - } - }; - - useEffect(() => { - if (open) reset(); - }, [open, reset]); - - useEffect(() => { - if (data) { - reset({ - name: data.name, - title: data.title, - description: data.description, - }); - } else { - reset(); - } - }, [data, reset]); - - return ( - -
- - {data ? t("title.edit_label") : t("title.new_label")} - - - - - { - setValue("title", value); - setValue("name", slugify(value).toUpperCase()); - }, - }} - helperText={errors.title ? errors.title.message : null} - /> - - - - - - - - - - - - -
-
- ); -}; diff --git a/frontend/src/components/labels/LabelForm.tsx b/frontend/src/components/labels/LabelForm.tsx new file mode 100644 index 00000000..d4b4cbf7 --- /dev/null +++ b/frontend/src/components/labels/LabelForm.tsx @@ -0,0 +1,131 @@ +/* + * 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, Fragment, useEffect } from "react"; +import { useForm } from "react-hook-form"; + +import { ContentContainer, ContentItem } from "@/app-components/dialogs/"; +import { Input } from "@/app-components/inputs/Input"; +import { useCreate } from "@/hooks/crud/useCreate"; +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 { ILabel, ILabelAttributes } from "@/types/label.types"; +import { slugify } from "@/utils/string"; + +export const LabelForm: FC> = ({ + data, + Wrapper = Fragment, + WrapperProps, + ...rest +}) => { + const { t } = useTranslate(); + const { toast } = useToast(); + const options = { + onError: (error: Error) => { + rest.onError?.(); + toast.error(error || t("message.internal_server_error")); + }, + onSuccess: () => { + rest.onSuccess?.(); + toast.success(t("message.success_save")); + }, + }; + const { mutate: createLabel } = useCreate(EntityType.LABEL, options); + const { mutate: updateLabel } = useUpdate(EntityType.LABEL, options); + const { + reset, + register, + setValue, + formState: { errors }, + handleSubmit, + } = useForm({ + defaultValues: { + name: data?.name || "", + title: data?.title || "", + description: data?.description || "", + }, + }); + const validationRules = { + title: { + required: t("message.title_is_required"), + }, + name: {}, + description: {}, + }; + const onSubmitForm = (params: ILabelAttributes) => { + if (data) { + updateLabel({ id: data.id, params }); + } else { + createLabel(params); + } + }; + + useEffect(() => { + if (data) { + reset({ + name: data.name, + title: data.title, + description: data.description, + }); + } else { + reset(); + } + }, [data, reset]); + + return ( + +
+ + + { + setValue("title", value); + setValue("name", slugify(value).toUpperCase()); + }, + }} + helperText={errors.title ? errors.title.message : null} + /> + + + + + + + + +
+
+ ); +}; diff --git a/frontend/src/components/labels/LabelFormDialog.tsx b/frontend/src/components/labels/LabelFormDialog.tsx new file mode 100644 index 00000000..16f692b7 --- /dev/null +++ b/frontend/src/components/labels/LabelFormDialog.tsx @@ -0,0 +1,37 @@ +/* + * 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 { ILabel } from "@/types/label.types"; + +import { LabelForm } from "./LabelForm"; + +export const LabelFormDialog: FC> = ({ + payload, + ...rest +}) => { + const { t } = useTranslate(); + + return ( + { + rest.onClose(true); + }} + Wrapper={FormDialog} + WrapperProps={{ + title: payload ? t("title.edit_label") : t("title.new_label"), + ...rest, + }} + /> + ); +}; diff --git a/frontend/src/components/labels/index.tsx b/frontend/src/components/labels/index.tsx index 2f529d96..152f12cb 100644 --- a/frontend/src/components/labels/index.tsx +++ b/frontend/src/components/labels/index.tsx @@ -1,18 +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 { faTags } from "@fortawesome/free-solid-svg-icons"; import AddIcon from "@mui/icons-material/Add"; import { Button, Grid, Paper } from "@mui/material"; import { GridColDef } from "@mui/x-data-grid"; -import React from "react"; -import { DeleteDialog } from "@/app-components/dialogs/DeleteDialog"; +import { ConfirmDialogBody } from "@/app-components/dialogs"; import { FilterTextfield } from "@/app-components/inputs/FilterTextfield"; import { ActionColumnLabel, @@ -22,7 +22,7 @@ import { renderHeader } from "@/app-components/tables/columns/renderHeader"; import { DataGrid } from "@/app-components/tables/DataGrid"; import { useDelete } from "@/hooks/crud/useDelete"; import { useFind } from "@/hooks/crud/useFind"; -import { getDisplayDialogs, useDialog } from "@/hooks/useDialog"; +import { useDialogs } from "@/hooks/useDialogs"; import { useHasPermission } from "@/hooks/useHasPermission"; import { useSearch } from "@/hooks/useSearch"; import { useToast } from "@/hooks/useToast"; @@ -33,14 +33,12 @@ import { ILabel } from "@/types/label.types"; import { PermissionAction } from "@/types/permission.types"; import { getDateTimeFormatter } from "@/utils/date"; -import { LabelDialog } from "./LabelDialog"; +import { LabelFormDialog } from "./LabelFormDialog"; export const Labels = () => { const { t } = useTranslate(); const { toast } = useToast(); - const addDialogCtl = useDialog(false); - const editDialogCtl = useDialog(false); - const deleteDialogCtl = useDialog(false); + const dialogs = useDialogs(); const hasPermission = useHasPermission(); const { onSearch, searchPayload } = useSearch({ $or: ["name", "title"], @@ -51,12 +49,11 @@ export const Labels = () => { params: searchPayload, }, ); - const { mutateAsync: deleteLabel } = useDelete(EntityType.LABEL, { + const { mutate: deleteLabel } = useDelete(EntityType.LABEL, { onError: () => { toast.error(t("message.internal_server_error")); }, onSuccess() { - deleteDialogCtl.closeDialog(); toast.success(t("message.item_delete_success")); }, }); @@ -65,12 +62,18 @@ export const Labels = () => { [ { label: ActionColumnLabel.Edit, - action: (row) => editDialogCtl.openDialog(row), + action: (row) => dialogs.open(LabelFormDialog, row), requires: [PermissionAction.UPDATE], }, { label: ActionColumnLabel.Delete, - action: (row) => deleteDialogCtl.openDialog(row.id), + action: async ({ id }) => { + const isConfirmed = await dialogs.confirm(ConfirmDialogBody); + + if (isConfirmed) { + deleteLabel(id); + } + }, requires: [PermissionAction.DELETE], }, ], @@ -149,14 +152,6 @@ export const Labels = () => { return ( - - - { - if (deleteDialogCtl?.data) deleteLabel(deleteDialogCtl.data); - }} - /> { startIcon={} variant="contained" sx={{ float: "right" }} - onClick={() => addDialogCtl.openDialog()} + onClick={() => dialogs.open(LabelFormDialog, null)} > {t("button.add")}