From 2c13f069f95574df017fa1555747a6c8e77c9a63 Mon Sep 17 00:00:00 2001 From: yassinedorbozgithub Date: Fri, 6 Jun 2025 06:54:51 +0100 Subject: [PATCH] fix(frontend): enhance contentType logic --- .../content-types/ContentTypeForm.tsx | 75 +++++++++++-------- .../content-types/components/FieldInput.tsx | 56 +++++--------- 2 files changed, 61 insertions(+), 70 deletions(-) diff --git a/frontend/src/components/content-types/ContentTypeForm.tsx b/frontend/src/components/content-types/ContentTypeForm.tsx index 7bc201a9..256ff2a9 100644 --- a/frontend/src/components/content-types/ContentTypeForm.tsx +++ b/frontend/src/components/content-types/ContentTypeForm.tsx @@ -25,26 +25,27 @@ import { IContentTypeAttributes, } from "@/types/content-type.types"; import { generateId } from "@/utils/generateId"; +import { slugify } from "@/utils/string"; import { FieldInput } from "./components/FieldInput"; -import { FIELDS_FORM_DEFAULT_VALUES, READ_ONLY_FIELDS } from "./constants"; +import { FIELDS_FORM_DEFAULT_VALUES } from "./constants"; export const ContentTypeForm: FC> = ({ - data: { defaultValues: contentTypeWithoutId }, + data: { defaultValues: contentTypeWithoutUuid }, Wrapper = Fragment, WrapperProps, ...rest }) => { const contentType = useMemo( () => - contentTypeWithoutId && { - ...contentTypeWithoutId, - fields: contentTypeWithoutId?.fields?.map((field) => ({ + contentTypeWithoutUuid && { + ...contentTypeWithoutUuid, + fields: contentTypeWithoutUuid?.fields?.map((field) => ({ ...field, uuid: generateId(), })), }, - [contentTypeWithoutId], + [contentTypeWithoutUuid], ); const { toast } = useToast(); const { t } = useTranslate(); @@ -62,6 +63,27 @@ export const ContentTypeForm: FC> = ({ }); const { append, fields, remove } = useFieldArray({ name: "fields", + rules: { + validate: (value) => { + const labelCounts = value.reduce((acc, field) => { + if (!field.label.trim()) return acc; + acc[field.label] = (acc[field.label] || 0) + 1; + + return acc; + }, {} as Record); + const hasDuplicatedLabels = Object.values(labelCounts).some( + (count: number) => count > 1, + ); + + if (hasDuplicatedLabels) { + toast.error(t("message.duplicate_labels_not_allowed")); + + return false; + } + + return true; + }, + }, control, }); const options = { @@ -83,22 +105,6 @@ export const ContentTypeForm: FC> = ({ options, ); 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( - (count: number) => count > 1, - ); - - if (hasDuplicates) { - toast.error(t("message.duplicate_labels_not_allowed")); - - return; - } - if (contentType?.id) { updateContentType({ id: contentType.id, params }); } else { @@ -122,23 +128,28 @@ export const ContentTypeForm: FC> = ({ autoFocus /> - - {fields.map((f, index) => ( + {fields.map((field, idx) => ( uuid === f.uuid, - )} + onLabelChange={(value) => { + const fieldName = contentType?.fields?.find( + ({ uuid }) => uuid === field.uuid, + )?.name; + + if (!fieldName) { + setValue(`fields.${idx}.name`, value ? slugify(value) : ""); + } + }} + onRemove={() => { + remove(idx); + }} /> ))} diff --git a/frontend/src/components/content-types/components/FieldInput.tsx b/frontend/src/components/content-types/components/FieldInput.tsx index 8041b336..6d07c320 100644 --- a/frontend/src/components/content-types/components/FieldInput.tsx +++ b/frontend/src/components/content-types/components/FieldInput.tsx @@ -8,37 +8,27 @@ import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import { MenuItem } from "@mui/material"; -import { - Control, - Controller, - UseFieldArrayRemove, - UseFormSetValue, -} from "react-hook-form"; +import { useMemo } from "react"; +import { Control, Controller } from "react-hook-form"; import { IconButton } from "@/app-components/buttons/IconButton"; import { Input } from "@/app-components/inputs/Input"; import { useTranslate } from "@/hooks/useTranslate"; -import { - ContentField, - ContentFieldType, - IContentType, -} from "@/types/content-type.types"; -import { slugify } from "@/utils/string"; +import { ContentFieldType, IContentType } from "@/types/content-type.types"; + +import { READ_ONLY_FIELDS } from "../constants"; export const FieldInput = ({ - setValue, - index, - contentTypeField, + idx, ...props }: { - index: number; - disabled?: boolean; - remove: UseFieldArrayRemove; + idx: number; control: Control; - setValue: UseFormSetValue; - contentTypeField?: ContentField; + onRemove?: () => void; + onLabelChange?: (value: string) => void; }) => { const { t } = useTranslate(); + const isDisabled = useMemo(() => idx < READ_ONLY_FIELDS.length, [idx]); return ( <> @@ -46,52 +36,42 @@ export const FieldInput = ({ variant="text" color="error" size="medium" - onClick={() => props.remove(index)} - disabled={props.disabled} + onClick={props.onRemove} + disabled={isDisabled} > - ( { - const currentValue = e.target.value; - - if (!contentTypeField?.label || !contentTypeField?.name) { - setValue( - `fields.${index}.name`, - currentValue ? slugify(currentValue) : "", - ); - } - + props?.onLabelChange?.(e.target.value); field.onChange(e); }} /> )} /> ( )} control={props.control} /> - (