From 90c237a18512501c1a4df36fb1b37084ecdb9b94 Mon Sep 17 00:00:00 2001 From: Guillim Date: Tue, 19 Aug 2025 16:37:26 +0200 Subject: [PATCH] Morph-front-settings (#13872) # Implement Morph Frontend Settings Possiblity to create a morh relation from the Settings ! ## Overview Morph object system with new frontend settings UI for configuring morph relations. ## What Changed - **New Components**: `SettingsDataModelFieldMorphRelationForm`, `SettingsMorphRelationMultiSelect` - **Form System**: React Hook Form + Zod validation for morph relation configuration - **Preview System**: Live preview of field configurations with validation - **State Management**: Enhanced component state handling for complex form interactions ## Technical Details - **Architecture**: Restructured field input/display components for better maintainability - **Types**: Added comprehensive TypeScript types for morph relations (`isFieldMorphRelation`, etc.) - **Validation**: Dynamic validation rules based on field types and relation patterns - **Performance**: Optimized rendering with proper state management --------- Co-authored-by: Charles Bochet --- .../hooks/useFieldMetadataItem.ts | 12 +- .../hooks/useFilteredObjectMetadataItems.ts | 5 +- .../types/FieldMetadataItemRelation.ts | 2 +- ...ct.ts => isFieldMorphRelationManyToOne.ts} | 2 +- ...ts.ts => isFieldMorphRelationOneToMany.ts} | 4 +- .../SettingsMorphRelationMultiSelect.tsx | 269 ++++++++++++++++++ ...ttingsMorphRelationMultiSelect.stories.tsx | 39 +++ ...SettingsDataModelFieldSettingsFormCard.tsx | 19 ++ ...ettingsDataModelFieldMorphRelationForm.tsx | 151 ++++++++++ ...ngsDataModelFieldMorphRelationFormCard.tsx | 173 +++++++++++ ...nSettingsFormDefaultValuesOnDestination.ts | 61 ++++ ...ationSettingsFormInitialTargetMetadatas.ts | 47 +++ .../fieldMetadataItemDisableFieldEdition.ts | 15 + .../fieldMetadataItemHasMorphRelations.ts | 9 + .../fieldMetadataItemInitialRelationType.ts | 12 + .../SettingsDataModelFieldRelationForm.tsx | 10 +- ...gsDataModelFieldRelationPreviewContent.tsx | 20 ++ ...DataModelFieldRelationPreviewImageCard.tsx | 35 +++ ...DataModelFieldRelationSettingsFormCard.tsx | 29 +- .../SettingsDataModelFieldPreviewCard.tsx | 6 +- ...DataModelMorphRelationFieldPreviewCard.tsx | 86 ++++++ .../SettingsDataModelObjectSummary.tsx | 141 +++++++-- ...ettingsDataModelObjectSettingsFormCard.tsx | 6 +- .../data-model/utils/getObjectTypeLabel.ts | 2 +- .../input/components/MultiSelectControl.tsx | 81 ++++++ .../ui/input/components/SelectControl.tsx | 11 +- .../SettingsObjectNewFieldConfigure.tsx | 22 ++ .../field-metadata/dtos/create-field.input.ts | 9 +- .../src/types/RelationCreationPayload.ts | 8 + .../twenty-shared/src/types/RelationType.ts | 4 + packages/twenty-shared/src/types/index.ts | 2 + 31 files changed, 1209 insertions(+), 83 deletions(-) rename packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/{isFieldMorphRelationManyToOneObject.ts => isFieldMorphRelationManyToOne.ts} (91%) rename packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/{isFieldMorphRelationOneToManyObjects.ts => isFieldMorphRelationOneToMany.ts} (79%) create mode 100644 packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx create mode 100644 packages/twenty-front/src/modules/settings/components/__stories__/SettingsMorphRelationMultiSelect.stories.tsx create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldMorphRelationForm.tsx create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldMorphRelationFormCard.tsx create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/morph-relation/hooks/useMorphRelationSettingsFormDefaultValuesOnDestination.ts create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/morph-relation/hooks/useMorphRelationSettingsFormInitialTargetMetadatas.ts create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/morph-relation/utils/fieldMetadataItemDisableFieldEdition.ts create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/morph-relation/utils/fieldMetadataItemHasMorphRelations.ts create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/morph-relation/utils/fieldMetadataItemInitialRelationType.ts create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationPreviewContent.tsx create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationPreviewImageCard.tsx create mode 100644 packages/twenty-front/src/modules/settings/data-model/fields/preview/components/SettingsDataModelMorphRelationFieldPreviewCard.tsx create mode 100644 packages/twenty-front/src/modules/ui/input/components/MultiSelectControl.tsx create mode 100644 packages/twenty-shared/src/types/RelationCreationPayload.ts create mode 100644 packages/twenty-shared/src/types/RelationType.ts diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItem.ts index 598dcd49cd..d62231308a 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItem.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItem.ts @@ -1,8 +1,9 @@ -import { type Field, type RelationType } from '~/generated-metadata/graphql'; +import { type Field } from '~/generated-metadata/graphql'; import { type FieldMetadataItem } from '../types/FieldMetadataItem'; import { formatFieldMetadataItemInput } from '../utils/formatFieldMetadataItemInput'; +import { type RelationCreationPayload } from 'twenty-shared/types'; import { useCreateOneFieldMetadataItem } from './useCreateOneFieldMetadataItem'; import { useDeleteOneFieldMetadataItem } from './useDeleteOneFieldMetadataItem'; import { useUpdateOneFieldMetadataItem } from './useUpdateOneFieldMetadataItem'; @@ -26,12 +27,8 @@ export const useFieldMetadataItem = () => { | 'isLabelSyncedWithName' > & { objectMetadataId: string; - relationCreationPayload?: { - type: RelationType; - targetObjectMetadataId: string; - targetFieldLabel: string; - targetFieldIcon: string; - }; + relationCreationPayload?: RelationCreationPayload; + morphRelationsCreationPayload?: RelationCreationPayload[]; }, ) => { const formattedInput = formatFieldMetadataItemInput(input); @@ -44,6 +41,7 @@ export const useFieldMetadataItem = () => { name: formattedInput.name ?? '', isLabelSyncedWithName: formattedInput.isLabelSyncedWithName ?? true, relationCreationPayload: input.relationCreationPayload, + morphRelationsCreationPayload: input.morphRelationsCreationPayload, }); }; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useFilteredObjectMetadataItems.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useFilteredObjectMetadataItems.ts index f83e42f0bc..ccf519da24 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useFilteredObjectMetadataItems.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useFilteredObjectMetadataItems.ts @@ -15,7 +15,10 @@ export const useFilteredObjectMetadataItems = () => { ); const activeObjectMetadataItems = useMemo( - () => objectMetadataItems.filter(({ isActive }) => isActive), + () => + objectMetadataItems + .filter(({ isActive }) => isActive) + .sort((a, b) => a.labelSingular.localeCompare(b.labelSingular)), [objectMetadataItems], ); diff --git a/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItemRelation.ts b/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItemRelation.ts index 3968c7eef7..99d7d3aab1 100644 --- a/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItemRelation.ts +++ b/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItemRelation.ts @@ -5,7 +5,7 @@ import { type Field, type RelationType } from '~/generated-metadata/graphql'; export type FieldMetadataItemRelation = { type: RelationType; sourceFieldMetadata: Pick; - targetFieldMetadata: Pick; + targetFieldMetadata: Pick; sourceObjectMetadata: Pick< ObjectMetadataItem, 'id' | 'nameSingular' | 'namePlural' diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationManyToOneObject.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationManyToOne.ts similarity index 91% rename from packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationManyToOneObject.ts rename to packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationManyToOne.ts index e2e802d542..2940263dbd 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationManyToOneObject.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationManyToOne.ts @@ -6,7 +6,7 @@ import { type FieldMorphRelationMetadata, } from '../FieldMetadata'; -export const isFieldMorphRelationManyToOneObject = ( +export const isFieldMorphRelationManyToOne = ( field: Pick, 'type' | 'metadata'>, ): field is FieldDefinition => isFieldMorphRelation(field) && diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationOneToManyObjects.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationOneToMany.ts similarity index 79% rename from packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationOneToManyObjects.ts rename to packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationOneToMany.ts index 9934a513bb..a6dba9ce67 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationOneToManyObjects.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/types/guards/isFieldMorphRelationOneToMany.ts @@ -6,8 +6,8 @@ import { type FieldMorphRelationMetadata, } from '../FieldMetadata'; -export const isFieldMorphRelationOneToManyObjects = ( +export const isFieldMorphRelationOneToMany = ( field: Pick, 'type' | 'metadata'>, ): field is FieldDefinition => isFieldMorphRelation(field) && - field.metadata.morphRelations?.[0].type === RelationType.ONE_TO_MANY; + field.metadata.relationType === RelationType.ONE_TO_MANY; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx b/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx new file mode 100644 index 0000000000..60b2216f52 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsMorphRelationMultiSelect.tsx @@ -0,0 +1,269 @@ +import styled from '@emotion/styled'; +import { useMemo, useRef, useState, type MouseEvent } from 'react'; + +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; + +import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation'; +import { MultiSelectControl } from '@/ui/input/components/MultiSelectControl'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { type DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset'; +import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { isDefined } from 'twenty-shared/utils'; +import { IconBox, useIcons, type IconComponent } from 'twenty-ui/display'; +import { MenuItem, MenuItemMultiSelect } from 'twenty-ui/navigation'; + +export type SelectSizeVariant = 'small' | 'default'; + +type CallToActionButton = { + text: string; + onClick: (event: MouseEvent) => void; + Icon?: IconComponent; +}; + +export type SettingsMorphRelationMultiSelectProps = { + className?: string; + disabled?: boolean; + selectSizeVariant?: SelectSizeVariant; + dropdownId: string; + dropdownWidth?: number; + dropdownWidthAuto?: boolean; + fullWidth?: boolean; + label?: string; + description?: string; + onChange?: (value: string[]) => void; + onBlur?: () => void; + selectedObjectMetadataIds: string[]; + withSearchInput?: boolean; + needIconCheck?: boolean; + callToActionButton?: CallToActionButton; + dropdownOffset?: DropdownOffset; + hasRightElement?: boolean; +}; + +const StyledContainer = styled.div<{ fullWidth?: boolean }>` + width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')}; +`; + +const StyledLabel = styled.span` + color: ${({ theme }) => theme.font.color.light}; + display: block; + font-size: ${({ theme }) => theme.font.size.xs}; + font-weight: ${({ theme }) => theme.font.weight.semiBold}; + margin-bottom: ${({ theme }) => theme.spacing(1)}; +`; + +const StyledDescription = styled.span` + color: ${({ theme }) => theme.font.color.light}; + font-size: ${({ theme }) => theme.font.size.sm}; +`; + +export const SettingsMorphRelationMultiSelect = ({ + className, + disabled: disabledFromProps, + selectSizeVariant, + dropdownId, + dropdownWidth = GenericDropdownContentWidth.Medium, + dropdownWidthAuto = false, + fullWidth, + label, + description, + onChange, + onBlur, + selectedObjectMetadataIds, + withSearchInput, + callToActionButton, + dropdownOffset, + hasRightElement, +}: SettingsMorphRelationMultiSelectProps) => { + const selectContainerRef = useRef(null); + + const [searchInputValue, setSearchInputValue] = useState(''); + + const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); + + const { getIcon } = useIcons(); + const options = activeObjectMetadataItems + .filter(isObjectMetadataAvailableForRelation) + .sort((item1, item2) => + item1.labelSingular.localeCompare(item2.labelSingular), + ) + .map((objectMetadataItem) => ({ + label: objectMetadataItem.labelSingular, + Icon: getIcon(objectMetadataItem.icon), + objectMetadataId: objectMetadataItem.id, + })); + + const selectedOptions = options.filter((option) => + selectedObjectMetadataIds.includes(option.objectMetadataId), + ); + + const filteredOptions = useMemo( + () => + searchInputValue + ? options.filter(({ label }) => + label.toLowerCase().includes(searchInputValue.toLowerCase()), + ) + : options, + [options, searchInputValue], + ); + + const isDisabled = + disabledFromProps || + (options.length <= 1 && !isDefined(callToActionButton)); + + const { closeDropdown } = useCloseDropdown(); + + const dropDownMenuWidth = + dropdownWidthAuto && selectContainerRef.current?.clientWidth + ? selectContainerRef.current?.clientWidth + : dropdownWidth; + + const selectableItemIdArray = filteredOptions.map((option) => option.label); + + const selectedItemId = useRecoilComponentValue( + selectedItemIdComponentState, + dropdownId, + ); + + const { setSelectedItemId } = useSelectableList(dropdownId); + + const handleDropdownOpen = () => { + if (selectedOptions && selectedOptions.length > 0 && !searchInputValue) { + setSelectedItemId(selectedOptions[0].label); + } + }; + + const addOrRemoveFromArray = (array: string[], item: string) => { + let newArray = new Set(array); + if (newArray.has(item)) { + newArray.delete(item); + } else { + newArray.add(item); + } + return Array.from(newArray); + }; + + return ( + + {!!label && {label}} + {isDisabled ? ( + + ) : ( + + } + dropdownComponents={ + + {!!withSearchInput && ( + setSearchInputValue(event.target.value)} + /> + )} + {!!withSearchInput && !!filteredOptions.length && ( + + )} + {!!filteredOptions.length && ( + + + {filteredOptions.map((option) => ( + { + const newSelectedObjectMetadataIds = + addOrRemoveFromArray( + selectedObjectMetadataIds, + option.objectMetadataId, + ); + onChange?.(newSelectedObjectMetadataIds); + onBlur?.(); + closeDropdown(dropdownId); + }} + > + + selectedObjectMetadataId === + option.objectMetadataId, + )} + isKeySelected={selectedItemId === option.label} + onSelectChange={() => { + let newSelectedObjectMetadataIds = + addOrRemoveFromArray( + selectedObjectMetadataIds, + option.objectMetadataId, + ); + + onChange?.(newSelectedObjectMetadataIds); + onBlur?.(); + }} + />{' '} + + ))} + + + )} + {!!callToActionButton && !!filteredOptions.length && ( + + )} + {!!callToActionButton && ( + + + + )} + + } + /> + )} + {!!description && {description}} + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/components/__stories__/SettingsMorphRelationMultiSelect.stories.tsx b/packages/twenty-front/src/modules/settings/components/__stories__/SettingsMorphRelationMultiSelect.stories.tsx new file mode 100644 index 0000000000..64657c9886 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/__stories__/SettingsMorphRelationMultiSelect.stories.tsx @@ -0,0 +1,39 @@ +import { SettingsMorphRelationMultiSelect } from '@/settings/components/SettingsMorphRelationMultiSelect'; +import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { type Meta, type StoryObj } from '@storybook/react'; +import { fn } from '@storybook/test'; +import { ComponentDecorator } from 'twenty-ui/testing'; +import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; + +const meta: Meta = { + title: 'Modules/Settings/SettingsMorphRelationMultiSelect', + component: SettingsMorphRelationMultiSelect, + decorators: [ComponentDecorator, ObjectMetadataItemsDecorator], +}; +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + dropdownId: 'test-dropdown', + disabled: false, + selectSizeVariant: 'default', + dropdownWidth: GenericDropdownContentWidth.Medium, + dropdownWidthAuto: true, + fullWidth: true, + label: 'Select objects', + selectedObjectMetadataIds: [ + '4a45f524-b8cb-40e8-8450-28e402b442cf', + '6f3b9df6-57c0-4fe0-b8af-1a5ed20d76bd', + ], + withSearchInput: true, + dropdownOffset: { + x: 0, + y: 0, + }, + hasRightElement: false, + onChange: fn(), + }, + + decorators: [ObjectMetadataItemsDecorator], +}; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard.tsx index 1baf906419..e2621c07aa 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard.tsx @@ -16,6 +16,9 @@ import { settingsDataModelFieldCurrencyFormSchema } from '@/settings/data-model/ import { SettingsDataModelFieldCurrencySettingsFormCard } from '@/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencySettingsFormCard'; import { settingsDataModelFieldDateFormSchema } from '@/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm'; import { SettingsDataModelFieldDateSettingsFormCard } from '@/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateSettingsFormCard'; +import { settingsDataModelFieldMorphRelationFormSchema } from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldMorphRelationForm'; + +import { SettingsDataModelFieldMorphRelationFormCard } from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldMorphRelationFormCard'; import { settingsDataModelFieldNumberFormSchema } from '@/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm'; import { SettingsDataModelFieldNumberSettingsFormCard } from '@/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberSettingsFormCard'; import { settingsDataModelFieldPhonesFormSchema } from '@/settings/data-model/fields/forms/phones/components/SettingsDataModelFieldPhonesForm'; @@ -59,6 +62,10 @@ const relationFieldFormSchema = z .object({ type: z.literal(FieldMetadataType.RELATION) }) .merge(settingsDataModelFieldRelationFormSchema); +const morphRelationFieldFormSchema = z + .object({ type: z.literal(FieldMetadataType.MORPH_RELATION) }) + .merge(settingsDataModelFieldMorphRelationFormSchema); + const selectFieldFormSchema = z .object({ type: z.literal(FieldMetadataType.SELECT) }) .merge(settingsDataModelFieldSelectFormSchema); @@ -94,6 +101,7 @@ const otherFieldsFormSchema = z FieldMetadataType.BOOLEAN, FieldMetadataType.CURRENCY, FieldMetadataType.RELATION, + FieldMetadataType.MORPH_RELATION, FieldMetadataType.SELECT, FieldMetadataType.MULTI_SELECT, FieldMetadataType.DATE, @@ -116,6 +124,7 @@ export const settingsDataModelFieldSettingsFormSchema = z.discriminatedUnion( dateFieldFormSchema, dateTimeFieldFormSchema, relationFieldFormSchema, + morphRelationFieldFormSchema, selectFieldFormSchema, multiSelectFieldFormSchema, numberFieldFormSchema, @@ -154,6 +163,7 @@ const previewableTypes = [ FieldMetadataType.RATING, FieldMetadataType.RAW_JSON, FieldMetadataType.RELATION, + FieldMetadataType.MORPH_RELATION, FieldMetadataType.SELECT, FieldMetadataType.TEXT, FieldMetadataType.UUID, @@ -206,6 +216,15 @@ export const SettingsDataModelFieldSettingsFormCard = ({ ); } + if (fieldMetadataItem.type === FieldMetadataType.MORPH_RELATION) { + return ( + + ); + } + if (fieldMetadataItem.type === FieldMetadataType.NUMBER) { return ( ; + +type SettingsDataModelFieldMorphRelationFormProps = { + fieldMetadataItem: Pick; +}; + +export const SettingsDataModelFieldMorphRelationForm = ({ + fieldMetadataItem, +}: SettingsDataModelFieldMorphRelationFormProps) => { + const { t } = useLingui(); + const { control } = + useFormContext(); + + const morphRelationPreviouslyCreated = + fieldMetadataItemHasMorphRelations(fieldMetadataItem); + + const disableRelationEdition = !!morphRelationPreviouslyCreated; + const disableFieldEdition = + fieldMetadataItemDisableFieldEdition(fieldMetadataItem); + const initialRelationObjectMetadataItems = + useMorphRelationSettingsFormInitialTargetMetadatas({ + fieldMetadataItem, + }); + + const initialRelationType = + fieldMetadataItemInitialRelationType(fieldMetadataItem); + + const { label: defaultLabelOnDestination, icon: defaultIconOnDestination } = + useMorphRelationSettingsFormDefaultValuesOnDestination({ + fieldMetadataItem, + objectMetadataItem: initialRelationObjectMetadataItems[0], + relationType: initialRelationType, + }); + + const initialMorphRelationsObjectMetadataIds = + initialRelationObjectMetadataItems.map( + (relationObjectMetadataItem) => relationObjectMetadataItem.id, + ); + const isMobile = useIsMobile(); + + return ( + + + ( +