From 83424d883a7b58db1f2295f863aad59afcc5e0d4 Mon Sep 17 00:00:00 2001 From: hexastack Date: Mon, 3 Feb 2025 10:39:06 +0100 Subject: [PATCH 1/6] fix: content edit --- frontend/src/components/contents/ContentDialog.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/contents/ContentDialog.tsx b/frontend/src/components/contents/ContentDialog.tsx index 164e69aa8..199e7cfab 100644 --- a/frontend/src/components/contents/ContentDialog.tsx +++ b/frontend/src/components/contents/ContentDialog.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 LinkIcon from "@mui/icons-material/Link"; import { Dialog, @@ -238,7 +237,9 @@ export const ContentDialog: FC = ({ name={contentField.name} control={control} defaultValue={ - content ? content["dynamicFields"][contentField.name] : null + content && content["dynamicFields"] + ? content["dynamicFields"][contentField.name] + : null } rules={ contentField.name === "title" From 02c50d76a19c92777714694fef348d6afb5e9d61 Mon Sep 17 00:00:00 2001 From: hexastack Date: Tue, 4 Feb 2025 14:51:27 +0100 Subject: [PATCH 2/6] fix: update dynamicfields default values --- api/src/cms/schemas/content.schema.ts | 4 ++-- frontend/src/components/contents/ContentDialog.tsx | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/api/src/cms/schemas/content.schema.ts b/api/src/cms/schemas/content.schema.ts index bcfa79086..9c98d6f38 100644 --- a/api/src/cms/schemas/content.schema.ts +++ b/api/src/cms/schemas/content.schema.ts @@ -1,5 +1,5 @@ /* - * 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. @@ -43,7 +43,7 @@ export class ContentStub extends BaseSchema { @Prop({ type: Boolean, default: true }) status: boolean; - @Prop({ type: mongoose.Schema.Types.Mixed }) + @Prop({ type: mongoose.Schema.Types.Mixed, default: {} }) dynamicFields: Record; @Prop({ type: String }) diff --git a/frontend/src/components/contents/ContentDialog.tsx b/frontend/src/components/contents/ContentDialog.tsx index 199e7cfab..4bd93b071 100644 --- a/frontend/src/components/contents/ContentDialog.tsx +++ b/frontend/src/components/contents/ContentDialog.tsx @@ -237,9 +237,7 @@ export const ContentDialog: FC = ({ name={contentField.name} control={control} defaultValue={ - content && content["dynamicFields"] - ? content["dynamicFields"][contentField.name] - : null + content ? content["dynamicFields"][contentField.name] : null } rules={ contentField.name === "title" From 8112b80f94cb32e742e246619f4b5b07542588b1 Mon Sep 17 00:00:00 2001 From: hexastack Date: Tue, 4 Feb 2025 14:53:22 +0100 Subject: [PATCH 3/6] fix: includes the dynamic fields in creation form --- .../content-types/ContentTypeDialog.tsx | 110 +++++++++++------- 1 file changed, 68 insertions(+), 42 deletions(-) diff --git a/frontend/src/components/content-types/ContentTypeDialog.tsx b/frontend/src/components/content-types/ContentTypeDialog.tsx index ebe33bb75..854e47355 100644 --- a/frontend/src/components/content-types/ContentTypeDialog.tsx +++ b/frontend/src/components/content-types/ContentTypeDialog.tsx @@ -1,14 +1,16 @@ /* - * 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 { Dialog, DialogActions, DialogContent } from "@mui/material"; + +import AddIcon from "@mui/icons-material/Add"; +import { Button, Dialog, DialogActions, DialogContent } from "@mui/material"; import { FC, useEffect } from "react"; -import { useForm } from "react-hook-form"; +import { useFieldArray, useForm } from "react-hook-form"; import DialogButtons from "@/app-components/buttons/DialogButtons"; import { DialogTitle } from "@/app-components/dialogs/DialogTitle"; @@ -21,10 +23,10 @@ import { DialogControlProps } from "@/hooks/useDialog"; import { useToast } from "@/hooks/useToast"; import { useTranslate } from "@/hooks/useTranslate"; import { EntityType } from "@/services/types"; -import { - IContentType, - IContentTypeAttributes, -} from "@/types/content-type.types"; +import { ContentFieldType, IContentType } from "@/types/content-type.types"; + +import { FieldInput } from "./components/FieldInput"; +import { FIELDS_FORM_DEFAULT_VALUES, READ_ONLY_FIELDS } from "./constants"; export type ContentTypeDialogProps = DialogControlProps; export const ContentTypeDialog: FC = ({ @@ -37,15 +39,32 @@ export const ContentTypeDialog: FC = ({ const { handleSubmit, register, + control, reset, + setValue, formState: { errors }, - } = useForm({ - defaultValues: { name: data?.name || "" }, + } = useForm>({ + defaultValues: { + name: data?.name || "", + fields: data?.fields || FIELDS_FORM_DEFAULT_VALUES, + }, }); - const CloseAndReset = () => { - closeDialog(); - reset(); - }; + const { append, fields, remove } = useFieldArray({ + name: "fields", + control, + }); + + useEffect(() => { + if (data) { + reset({ + name: data.name, + fields: data.fields, + }); + } else { + reset(); + } + }, [data, reset]); + const { mutateAsync: createContentType } = useCreate( EntityType.CONTENT_TYPE, { @@ -70,40 +89,18 @@ export const ContentTypeDialog: FC = ({ }, }, ); - const validationRules = { - name: { - required: t("message.name_is_required"), - }, - }; - const onSubmitForm = async (params: IContentTypeAttributes) => { + const onSubmitForm = async (params) => { if (data) { - updateContentType({ - id: data.id, - params, - }); + updateContentType({ id: data.id, params }); } else { - createContentType(params); + createContentType({ ...params, name: params.name || "" }); } }; - useEffect(() => { - if (open) reset(); - }, [open, reset]); - - useEffect(() => { - if (data) { - reset({ - name: data.name, - }); - } else { - reset(); - } - }, [data, reset]); - return ( - +
- + {data ? t("title.edit_content_type") : t("title.new_content_type")} @@ -112,12 +109,41 @@ export const ContentTypeDialog: FC = ({ + + {fields.map((f, index) => ( + + + + ))} + + + From 9e029674e1fa49221946024f223f1f779172ee54 Mon Sep 17 00:00:00 2001 From: hexastack Date: Tue, 4 Feb 2025 16:21:31 +0100 Subject: [PATCH 4/6] fix: reset form --- .../content-types/ContentTypeDialog.tsx | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/content-types/ContentTypeDialog.tsx b/frontend/src/components/content-types/ContentTypeDialog.tsx index 854e47355..ca4bfacc2 100644 --- a/frontend/src/components/content-types/ContentTypeDialog.tsx +++ b/frontend/src/components/content-types/ContentTypeDialog.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 AddIcon from "@mui/icons-material/Add"; import { Button, Dialog, DialogActions, DialogContent } from "@mui/material"; import { FC, useEffect } from "react"; @@ -53,30 +52,34 @@ export const ContentTypeDialog: FC = ({ name: "fields", control, }); + const CloseAndReset = () => { + closeDialog(); + reset(); + }; + + useEffect(() => { + if (open) reset(); + }, [open, reset]); useEffect(() => { if (data) { reset({ name: data.name, - fields: data.fields, }); } else { reset(); } }, [data, reset]); - const { mutateAsync: createContentType } = useCreate( - EntityType.CONTENT_TYPE, - { - onError: (error) => { - toast.error(error); - }, - onSuccess: () => { - closeDialog(); - toast.success(t("message.success_save")); - }, + const { mutate: createContentType } = useCreate(EntityType.CONTENT_TYPE, { + onError: (error) => { + toast.error(error); }, - ); + onSuccess: () => { + closeDialog(); + toast.success(t("message.success_save")); + }, + }); const { mutateAsync: updateContentType } = useUpdate( EntityType.CONTENT_TYPE, { @@ -98,9 +101,9 @@ export const ContentTypeDialog: FC = ({ }; return ( - + - + {data ? t("title.edit_content_type") : t("title.new_content_type")} From 044ce6e7250b78c00c6aee942ad1cbf9cd2465da Mon Sep 17 00:00:00 2001 From: hexastack Date: Tue, 4 Feb 2025 17:07:13 +0100 Subject: [PATCH 5/6] fix: revert accidentally removed snippet --- frontend/src/components/content-types/ContentTypeDialog.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/content-types/ContentTypeDialog.tsx b/frontend/src/components/content-types/ContentTypeDialog.tsx index ca4bfacc2..c8d057037 100644 --- a/frontend/src/components/content-types/ContentTypeDialog.tsx +++ b/frontend/src/components/content-types/ContentTypeDialog.tsx @@ -115,6 +115,7 @@ export const ContentTypeDialog: FC = ({ {...register("name", { required: t("message.name_is_required"), })} + helperText={errors.name ? errors.name.message : null} required autoFocus /> From fe13e43b6693ecf951cb77e6461e4043e6219809 Mon Sep 17 00:00:00 2001 From: hexastack Date: Tue, 4 Feb 2025 18:46:16 +0100 Subject: [PATCH 6/6] fix: minor --- .../content-types/ContentTypeDialog.tsx | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/content-types/ContentTypeDialog.tsx b/frontend/src/components/content-types/ContentTypeDialog.tsx index c8d057037..c53a093e0 100644 --- a/frontend/src/components/content-types/ContentTypeDialog.tsx +++ b/frontend/src/components/content-types/ContentTypeDialog.tsx @@ -56,21 +56,6 @@ export const ContentTypeDialog: FC = ({ closeDialog(); reset(); }; - - useEffect(() => { - if (open) reset(); - }, [open, reset]); - - useEffect(() => { - if (data) { - reset({ - name: data.name, - }); - } else { - reset(); - } - }, [data, reset]); - const { mutate: createContentType } = useCreate(EntityType.CONTENT_TYPE, { onError: (error) => { toast.error(error); @@ -96,10 +81,24 @@ export const ContentTypeDialog: FC = ({ if (data) { updateContentType({ id: data.id, params }); } else { - createContentType({ ...params, name: params.name || "" }); + createContentType(params); } }; + useEffect(() => { + if (open) reset(); + }, [open, reset]); + + useEffect(() => { + if (data) { + reset({ + name: data.name, + }); + } else { + reset(); + } + }, [data, reset]); + return (