From 74beb9426c1e361ee6ed13f44d067f2045042606 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Thu, 5 Jun 2025 15:42:00 +0100 Subject: [PATCH] fix(frontend): apply feedback --- .../content-types/ContentTypeForm.tsx | 75 ++++++++++--------- .../content-types/components/FieldInput.tsx | 42 ++++++----- frontend/src/types/content-type.types.ts | 3 +- 3 files changed, 62 insertions(+), 58 deletions(-) diff --git a/frontend/src/components/content-types/ContentTypeForm.tsx b/frontend/src/components/content-types/ContentTypeForm.tsx index da74b069..d91bbb8c 100644 --- a/frontend/src/components/content-types/ContentTypeForm.tsx +++ b/frontend/src/components/content-types/ContentTypeForm.tsx @@ -8,7 +8,7 @@ import AddIcon from "@mui/icons-material/Add"; import { Button } from "@mui/material"; -import { FC, Fragment, useEffect } from "react"; +import { FC, Fragment, useMemo } from "react"; import { useFieldArray, useForm } from "react-hook-form"; import { ContentContainer, ContentItem } from "@/app-components/dialogs"; @@ -19,30 +19,45 @@ import { useToast } from "@/hooks/useToast"; import { useTranslate } from "@/hooks/useTranslate"; import { EntityType } from "@/services/types"; import { ComponentFormProps } from "@/types/common/dialogs.types"; -import { ContentFieldType, IContentType } from "@/types/content-type.types"; +import { + ContentFieldType, + IContentType, + IContentTypeAttributes, +} from "@/types/content-type.types"; +import { generateId } from "@/utils/generateId"; import { FieldInput } from "./components/FieldInput"; import { FIELDS_FORM_DEFAULT_VALUES, READ_ONLY_FIELDS } from "./constants"; export const ContentTypeForm: FC> = ({ - data: { defaultValues: contentType }, + data: { defaultValues: contentTypeWithoutId }, Wrapper = Fragment, WrapperProps, ...rest }) => { + const contentType = useMemo( + () => + contentTypeWithoutId && { + ...contentTypeWithoutId, + fields: contentTypeWithoutId?.fields?.map((field) => ({ + ...field, + uuid: generateId(), + })), + }, + [contentTypeWithoutId], + ); const { toast } = useToast(); const { t } = useTranslate(); const { - reset, control, register, setValue, formState: { errors }, handleSubmit, - } = useForm>({ - defaultValues: { - name: contentType?.name || "", - fields: contentType?.fields || FIELDS_FORM_DEFAULT_VALUES, + } = useForm({ + defaultValues: contentType || { + name: "", + fields: FIELDS_FORM_DEFAULT_VALUES, }, }); const { append, fields, remove } = useFieldArray({ @@ -67,17 +82,14 @@ export const ContentTypeForm: FC> = ({ EntityType.CONTENT_TYPE, options, ); - const onSubmitForm = (params) => { - const labelCounts: Record = params.fields.reduce( - (acc, field) => { - if (!field.label.trim()) return acc; - acc[field.label] = (acc[field.label] || 0) + 1; + const onSubmitForm = (params: IContentTypeAttributes) => { + const labelCounts = params.fields?.reduce((acc, field) => { + if (!field.label.trim()) return acc; + acc[field.label] = (acc[field.label] || 0) + 1; - return acc; - }, - {} as Record, - ); - const hasDuplicates = Object.values(labelCounts).some( + return acc; + }, {} as Record); + const hasDuplicates = Object.values(labelCounts || {}).some( (count: number) => count > 1, ); @@ -87,24 +99,13 @@ export const ContentTypeForm: FC> = ({ return; } - if (contentType) { + if (contentType?.id) { updateContentType({ id: contentType.id, params }); } else { createContentType(params); } }; - useEffect(() => { - if (contentType) { - reset({ - name: contentType.name, - fields: contentType.fields || FIELDS_FORM_DEFAULT_VALUES, - }); - } else { - reset({ name: "", fields: FIELDS_FORM_DEFAULT_VALUES }); - } - }, [contentType, reset]); - return (
@@ -130,12 +131,8 @@ export const ContentTypeForm: FC> = ({ gap={2} > @@ -145,7 +142,11 @@ export const ContentTypeForm: FC> = ({ startIcon={} variant="contained" onClick={() => - append({ label: "", name: "", type: ContentFieldType.TEXT }) + append({ + label: "", + name: "", + type: ContentFieldType.TEXT, + }) } > {t("button.add")} diff --git a/frontend/src/components/content-types/components/FieldInput.tsx b/frontend/src/components/content-types/components/FieldInput.tsx index 06d519af..4c07603c 100644 --- a/frontend/src/components/content-types/components/FieldInput.tsx +++ b/frontend/src/components/content-types/components/FieldInput.tsx @@ -8,13 +8,11 @@ import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import { MenuItem } from "@mui/material"; -import { useEffect } from "react"; import { Control, Controller, UseFieldArrayRemove, UseFormSetValue, - useWatch, } from "react-hook-form"; import { IconButton } from "@/app-components/buttons/IconButton"; @@ -26,31 +24,17 @@ import { slugify } from "@/utils/string"; export const FieldInput = ({ setValue, index, - defaultLabel, - defaultName, + uuid, ...props }: { index: number; disabled?: boolean; remove: UseFieldArrayRemove; - control: Control>; - setValue: UseFormSetValue>; - defaultLabel?: string; - defaultName?: string; + control: Control; + setValue: UseFormSetValue; + uuid?: string; }) => { const { t } = useTranslate(); - const label = useWatch({ - control: props.control, - name: `fields.${index}.label`, - }); - - useEffect(() => { - if (defaultLabel && defaultName !== slugify(defaultLabel)) { - defaultName && setValue(`fields.${index}.name`, defaultName); - } else { - setValue(`fields.${index}.name`, label ? slugify(label) : ""); - } - }, [label, setValue, index]); return ( <> @@ -75,6 +59,24 @@ export const FieldInput = ({ label={t("label.label")} error={!!fieldState.error} helperText={fieldState.error?.message} + onChange={(e) => { + const currentValue = e.target.value; + const { label, name } = + props.control._defaultValues.fields?.find( + (field) => field?.uuid === uuid, + ) || {}; + + if (label && name !== slugify(label)) { + name && setValue(`fields.${index}.name`, name); + } else { + setValue( + `fields.${index}.name`, + currentValue ? slugify(currentValue) : "", + ); + } + + field.onChange(e); + }} /> )} /> diff --git a/frontend/src/types/content-type.types.ts b/frontend/src/types/content-type.types.ts index ee15b4c2..83d988b9 100644 --- a/frontend/src/types/content-type.types.ts +++ b/frontend/src/types/content-type.types.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. @@ -23,6 +23,7 @@ export type ContentField = { name: string; label: string; type: ContentFieldType; + uuid?: string; }; export interface IContentTypeAttributes {