diff --git a/frontend/src/components/subscribers/EditSubscriberDialog.tsx b/frontend/src/components/subscribers/EditSubscriberDialog.tsx deleted file mode 100644 index 0b2897bf..00000000 --- a/frontend/src/components/subscribers/EditSubscriberDialog.tsx +++ /dev/null @@ -1,146 +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 { - Button, - Dialog, - DialogActions, - DialogContent, - Grid, -} from "@mui/material"; -import Link from "next/link"; -import { useEffect, FC, useState } 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 AutoCompleteEntitySelect from "@/app-components/inputs/AutoCompleteEntitySelect"; -import { Input } from "@/app-components/inputs/Input"; -import { useUpdate } from "@/hooks/crud/useUpdate"; -import { DialogControlProps } from "@/hooks/useDialog"; -import { useToast } from "@/hooks/useToast"; -import { useTranslate } from "@/hooks/useTranslate"; -import { EntityType, Format } from "@/services/types"; -import { ILabel } from "@/types/label.types"; -import { ISubscriber, ISubscriberAttributes } from "@/types/subscriber.types"; - -const getFullName = (val: ISubscriber) => `${val.first_name} ${val.last_name}`; - -export type EditSubscriberDialogProps = DialogControlProps<{ - labels: ILabel[]; - subscriber: ISubscriber; -}>; -export const EditSubscriberDialog: FC = ({ - open, - data, - closeDialog, - ...rest -}) => { - const { t } = useTranslate(); - const { toast } = useToast(); - const [fullName, setFullName] = useState(""); - const { mutateAsync: updateSubscriber } = useUpdate(EntityType.SUBSCRIBER, { - onError: () => { - toast.error(t("message.internal_server_error")); - }, - onSuccess() { - closeDialog(); - toast.success(t("message.success_save")); - }, - }); - const { - reset, - control, - formState: { errors }, - handleSubmit, - } = useForm(); - const validationRules = { - labels: {}, - }; - const onSubmitForm = async (params: ISubscriberAttributes) => { - if (data?.subscriber.id) - updateSubscriber({ id: data?.subscriber.id, params }); - }; - - useEffect(() => { - if (data?.subscriber) setFullName(getFullName(data?.subscriber)); - - if (open) { - reset({ labels: data?.subscriber?.labels }); - } - }, [open, reset, data]); - - return ( - -
- - {t("title.manage_labels")} - - - - - - - - - - { - const { onChange, ...rest } = field; - - return ( - - autoFocus - searchFields={["name"]} - entity={EntityType.LABEL} - format={Format.BASIC} - labelKey="name" - label={t("label.labels")} - multiple - {...field} - error={!!errors.labels} - helperText={ - errors.labels ? errors.labels.message : null - } - onChange={(_e, selected) => - onChange(selected.map(({ id }) => id)) - } - {...rest} - /> - ); - }} - /> - - - - - - - - - - - - - -
-
- ); -}; diff --git a/frontend/src/components/subscribers/SubscriberForm.tsx b/frontend/src/components/subscribers/SubscriberForm.tsx new file mode 100644 index 00000000..aa1e2529 --- /dev/null +++ b/frontend/src/components/subscribers/SubscriberForm.tsx @@ -0,0 +1,124 @@ +/* + * 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 { Button, Grid, Link } from "@mui/material"; +import { FC, Fragment, useEffect } from "react"; +import { Controller, useForm } from "react-hook-form"; + +import { ContentContainer, ContentItem } from "@/app-components/dialogs/"; +import AutoCompleteEntitySelect from "@/app-components/inputs/AutoCompleteEntitySelect"; +import { Input } from "@/app-components/inputs/Input"; +import { useUpdate } from "@/hooks/crud/useUpdate"; +import { useToast } from "@/hooks/useToast"; +import { useTranslate } from "@/hooks/useTranslate"; +import { EntityType, Format } from "@/services/types"; +import { ComponentFormProps } from "@/types/common/dialogs.types"; +import { ILabel } from "@/types/label.types"; +import { ISubscriber, ISubscriberAttributes } from "@/types/subscriber.types"; + +const getFullName = (subscriber: ISubscriber | null) => + `${subscriber?.first_name} ${subscriber?.last_name}`; + +export const SubscriberForm: FC> = ({ + data, + Wrapper = Fragment, + WrapperProps, + ...rest +}) => { + const { t } = useTranslate(); + const { toast } = useToast(); + const { mutate: updateSubscriber } = useUpdate(EntityType.SUBSCRIBER, { + onError: () => { + rest.onError?.(); + toast.error(t("message.internal_server_error")); + }, + onSuccess() { + rest.onSuccess?.(); + toast.success(t("message.success_save")); + }, + }); + const { + reset, + control, + formState: { errors }, + handleSubmit, + } = useForm(); + const onSubmitForm = (params: ISubscriberAttributes) => { + if (data?.id) { + updateSubscriber({ id: data.id, params }); + } + }; + + useEffect(() => { + if (data) { + reset({ labels: data?.labels }); + } + }, [data, reset]); + + return ( + +
+ + + + + + + + { + const { onChange, ...rest } = field; + + return ( + + autoFocus + searchFields={["name"]} + entity={EntityType.LABEL} + format={Format.BASIC} + labelKey="name" + label={t("label.labels")} + multiple + {...field} + error={!!errors.labels} + helperText={ + errors.labels ? errors.labels.message : null + } + onChange={(_e, selected) => + onChange(selected.map(({ id }) => id)) + } + {...rest} + /> + ); + }} + control={control} + /> + + + + + + + + + +
+
+ ); +}; diff --git a/frontend/src/components/subscribers/SubscriberFormDialog.tsx b/frontend/src/components/subscribers/SubscriberFormDialog.tsx new file mode 100644 index 00000000..c234ef2a --- /dev/null +++ b/frontend/src/components/subscribers/SubscriberFormDialog.tsx @@ -0,0 +1,23 @@ +/* + * 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 { GenericFormDialog } from "@/app-components/dialogs"; +import { ComponentFormDialogProps } from "@/types/common/dialogs.types"; +import { ISubscriber } from "@/types/subscriber.types"; + +import { SubscriberForm } from "./SubscriberForm"; + +export const SubscriberFormDialog = ( + props: ComponentFormDialogProps, +) => ( + + Form={SubscriberForm} + editText="title.manage_subscribers" + {...props} + /> +); diff --git a/frontend/src/components/subscribers/index.tsx b/frontend/src/components/subscribers/index.tsx index ebf4daa0..3ffeff8d 100644 --- a/frontend/src/components/subscribers/index.tsx +++ b/frontend/src/components/subscribers/index.tsx @@ -10,7 +10,7 @@ import AccountCircleIcon from "@mui/icons-material/AccountCircle"; import DeleteIcon from "@mui/icons-material/Close"; import { Grid, IconButton, MenuItem, Paper } from "@mui/material"; import { GridColDef } from "@mui/x-data-grid"; -import React, { useState } from "react"; +import { useState } from "react"; import { ChipEntity } from "@/app-components/displays/ChipEntity"; import { FilterTextfield } from "@/app-components/inputs/FilterTextfield"; @@ -23,23 +23,19 @@ import { renderHeader } from "@/app-components/tables/columns/renderHeader"; import { buildRenderPicture } from "@/app-components/tables/columns/renderPicture"; import { DataGrid } from "@/app-components/tables/DataGrid"; import { useFind } from "@/hooks/crud/useFind"; -import { getDisplayDialogs, useDialog } from "@/hooks/useDialog"; +import { useDialogs } from "@/hooks/useDialogs"; import { useSearch } from "@/hooks/useSearch"; import { useTranslate } from "@/hooks/useTranslate"; import { PageHeader } from "@/layout/content/PageHeader"; import { EntityType, Format } from "@/services/types"; -import { ILabel } from "@/types/label.types"; import { ISubscriber } from "@/types/subscriber.types"; import { getDateTimeFormatter } from "@/utils/date"; -import { EditSubscriberDialog } from "./EditSubscriberDialog"; +import { SubscriberFormDialog } from "./SubscriberFormDialog"; export const Subscribers = () => { const { t } = useTranslate(); - const editDialogCtl = useDialog<{ - labels: ILabel[]; - subscriber: ISubscriber; - }>(false); + const dialogs = useDialogs(); const { data: labels } = useFind( { entity: EntityType.LABEL, @@ -155,11 +151,7 @@ export const Subscribers = () => { [ { label: ActionColumnLabel.Manage_Labels, - action: (row) => - editDialogCtl.openDialog({ - labels: labels || [], - subscriber: row, - }), + action: (row) => dialogs.open(SubscriberFormDialog, row), }, ], t("label.operations"), @@ -168,7 +160,6 @@ export const Subscribers = () => { return ( -