From e45e45d8b2f0080a17c25b89dc4a97ea95e3fc78 Mon Sep 17 00:00:00 2001 From: sid0-0 <43578323+sid0-0@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:59:37 +0530 Subject: [PATCH] Prefill Relation Fields with Initiating Object Icon and Name (#7363) feat: #7355 Behaviour implemented: 1. Relation field name field is updated when relation type is updated 2. Icon is only prefilled in the beginning 3. If user manually edits the field name, then no subsequent updates are made to that field upon relation type change. https://github.com/user-attachments/assets/d372b106-8dcb-458d-8374-a76cd130f091 --------- Co-authored-by: sid0-0 Co-authored-by: Lucas Bordeau --- .../SettingsDataModelFieldIconLabelForm.tsx | 38 ++++++++++++++++++- .../SettingsDataModelFieldRelationForm.tsx | 37 ++++++++++++++++-- ...DataModelFieldRelationSettingsFormCard.tsx | 1 + .../useRelationSettingsFormInitialValues.ts | 21 +++++++--- .../SettingsObjectNewFieldConfigure.tsx | 15 +++++--- 5 files changed, 96 insertions(+), 16 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldIconLabelForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldIconLabelForm.tsx index d4771446e5d3..c3b82ccddd8c 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldIconLabelForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldIconLabelForm.tsx @@ -3,10 +3,14 @@ import { Controller, useFormContext } from 'react-hook-form'; import { z } from 'zod'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema'; import { getErrorMessageFromError } from '@/settings/data-model/fields/forms/utils/errorMessages'; +import { RelationType } from '@/settings/data-model/types/RelationType'; import { IconPicker } from '@/ui/input/components/IconPicker'; import { TextInput } from '@/ui/input/components/TextInput'; +import { useEffect, useState } from 'react'; +import { RelationDefinitionType } from '~/generated-metadata/graphql'; export const settingsDataModelFieldIconLabelFormSchema = ( existingOtherLabels: string[] = [], @@ -32,19 +36,47 @@ type SettingsDataModelFieldIconLabelFormProps = { disabled?: boolean; fieldMetadataItem?: FieldMetadataItem; maxLength?: number; + relationObjectMetadataItem?: ObjectMetadataItem; + relationType?: RelationType; }; export const SettingsDataModelFieldIconLabelForm = ({ disabled, fieldMetadataItem, maxLength, + relationObjectMetadataItem, + relationType, }: SettingsDataModelFieldIconLabelFormProps) => { const { control, trigger, formState: { errors }, + setValue, } = useFormContext(); + const [labelEditedManually, setLabelEditedManually] = useState(false); + const [iconEditedManually, setIconEditedManually] = useState(false); + + useEffect(() => { + if (labelEditedManually) return; + const label = [ + RelationDefinitionType.ManyToOne, + RelationDefinitionType.ManyToMany, + ].includes(relationType ?? RelationDefinitionType.OneToMany) + ? relationObjectMetadataItem?.labelPlural + : relationObjectMetadataItem?.labelSingular; + setValue('label', label ?? ''); + + if (iconEditedManually) return; + setValue('icon', relationObjectMetadataItem?.icon ?? 'IconUsers'); + }, [ + labelEditedManually, + iconEditedManually, + relationObjectMetadataItem, + setValue, + relationType, + ]); + return ( onChange(iconKey)} + onChange={({ iconKey }) => { + setIconEditedManually(true); + onChange(iconKey); + }} variant="primary" /> )} @@ -69,6 +104,7 @@ export const SettingsDataModelFieldIconLabelForm = ({ placeholder="Employees" value={value} onChange={(e) => { + setLabelEditedManually(true); onChange(e); trigger('label'); }} diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm.tsx index d6f3fe48be10..9b0968c4f180 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm.tsx @@ -10,11 +10,13 @@ import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fi import { FIELD_NAME_MAXIMUM_LENGTH } from '@/settings/data-model/constants/FieldNameMaximumLength'; import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes'; import { useRelationSettingsFormInitialValues } from '@/settings/data-model/fields/forms/relation/hooks/useRelationSettingsFormInitialValues'; +import { SettingsDataModelFieldPreviewCardProps } from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewCard'; import { RelationType } from '@/settings/data-model/types/RelationType'; import { IconPicker } from '@/ui/input/components/IconPicker'; import { Select } from '@/ui/input/components/Select'; import { TextInput } from '@/ui/input/components/TextInput'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; +import { useEffect, useState } from 'react'; import { RelationDefinitionType } from '~/generated-metadata/graphql'; export const settingsDataModelFieldRelationFormSchema = z.object({ @@ -39,6 +41,7 @@ export type SettingsDataModelFieldRelationFormValues = z.infer< type SettingsDataModelFieldRelationFormProps = { fieldMetadataItem: Pick; + objectMetadataItem: SettingsDataModelFieldPreviewCardProps['objectMetadataItem']; }; const StyledContainer = styled.div` @@ -79,26 +82,49 @@ const RELATION_TYPE_OPTIONS = Object.entries(RELATION_TYPES) export const SettingsDataModelFieldRelationForm = ({ fieldMetadataItem, + objectMetadataItem, }: SettingsDataModelFieldRelationFormProps) => { - const { control, watch: watchFormValue } = - useFormContext(); + const { + control, + watch: watchFormValue, + setValue, + } = useFormContext(); const { getIcon } = useIcons(); const { objectMetadataItems, findObjectMetadataItemById } = useFilteredObjectMetadataItems(); + const [labelEditedManually, setLabelEditedManually] = useState(false); + const { disableFieldEdition, disableRelationEdition, initialRelationFieldMetadataItem, initialRelationObjectMetadataItem, initialRelationType, - } = useRelationSettingsFormInitialValues({ fieldMetadataItem }); + } = useRelationSettingsFormInitialValues({ + fieldMetadataItem, + objectMetadataItem, + }); const selectedObjectMetadataItem = findObjectMetadataItemById( watchFormValue('relation.objectMetadataId'), ); const isMobile = useIsMobile(); + const relationType = watchFormValue('relation.type'); + + useEffect(() => { + if (labelEditedManually) return; + setValue( + 'relation.field.label', + [ + RelationDefinitionType.ManyToMany, + RelationDefinitionType.ManyToOne, + ].includes(relationType) + ? objectMetadataItem.labelPlural + : objectMetadataItem.labelSingular, + ); + }, [labelEditedManually, objectMetadataItem, relationType, setValue]); return ( @@ -169,7 +195,10 @@ export const SettingsDataModelFieldRelationForm = ({ disabled={disableFieldEdition} placeholder="Field name" value={value} - onChange={onChange} + onChange={(newValue) => { + setLabelEditedManually(true); + onChange(newValue); + }} fullWidth maxLength={FIELD_NAME_MAXIMUM_LENGTH} /> diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationSettingsFormCard.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationSettingsFormCard.tsx index db8f46e97216..33509e08e632 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationSettingsFormCard.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationSettingsFormCard.tsx @@ -114,6 +114,7 @@ export const SettingsDataModelFieldRelationSettingsFormCard = ({ form={ } /> diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/hooks/useRelationSettingsFormInitialValues.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/hooks/useRelationSettingsFormInitialValues.ts index bd4b2badd0e2..298c3ad6dc24 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/hooks/useRelationSettingsFormInitialValues.ts +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/relation/hooks/useRelationSettingsFormInitialValues.ts @@ -2,15 +2,17 @@ import { useMemo } from 'react'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation'; +import { SettingsDataModelFieldPreviewCardProps } from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewCard'; import { RelationDefinitionType } from '~/generated-metadata/graphql'; export const useRelationSettingsFormInitialValues = ({ fieldMetadataItem, + objectMetadataItem, }: { fieldMetadataItem?: Pick; + objectMetadataItem?: SettingsDataModelFieldPreviewCardProps['objectMetadataItem']; }) => { const { objectMetadataItems } = useFilteredObjectMetadataItems(); @@ -28,11 +30,13 @@ export const useRelationSettingsFormInitialValues = ({ const initialRelationObjectMetadataItem = useMemo( () => relationObjectMetadataItemFromFieldMetadata ?? - objectMetadataItems.find( - ({ nameSingular }) => nameSingular === CoreObjectNameSingular.Person, - ) ?? + objectMetadataItem ?? objectMetadataItems.filter(isObjectMetadataAvailableForRelation)[0], - [objectMetadataItems, relationObjectMetadataItemFromFieldMetadata], + [ + objectMetadataItem, + objectMetadataItems, + relationObjectMetadataItemFromFieldMetadata, + ], ); const initialRelationType = @@ -44,7 +48,12 @@ export const useRelationSettingsFormInitialValues = ({ disableRelationEdition: !!relationFieldMetadataItem, initialRelationFieldMetadataItem: relationFieldMetadataItem ?? { icon: initialRelationObjectMetadataItem.icon ?? 'IconUsers', - label: '', + label: [ + RelationDefinitionType.ManyToMany, + RelationDefinitionType.ManyToOne, + ].includes(initialRelationType) + ? initialRelationObjectMetadataItem.labelPlural + : initialRelationObjectMetadataItem.labelSingular, }, initialRelationObjectMetadataItem, initialRelationType, diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldConfigure.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldConfigure.tsx index 4d00210c8782..0b27c458859b 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldConfigure.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldConfigure.tsx @@ -2,6 +2,7 @@ import { useCreateOneRelationMetadataItem } from '@/object-metadata/hooks/useCre import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; @@ -47,6 +48,7 @@ export const SettingsObjectNewFieldConfigure = () => { const { findActiveObjectMetadataItemBySlug } = useFilteredObjectMetadataItems(); + const activeObjectMetadataItem = findActiveObjectMetadataItemBySlug(objectSlug); const { createMetadataField } = useFieldMetadataItem(); @@ -67,6 +69,13 @@ export const SettingsObjectNewFieldConfigure = () => { }, }); + const fieldMetadataItem: Pick = + { + icon: formConfig.watch('icon'), + label: formConfig.watch('label') || 'Employees', + type: formConfig.watch('type'), + }; + const [, setObjectViews] = useState([]); const [, setRelationObjectViews] = useState([]); @@ -200,11 +209,7 @@ export const SettingsObjectNewFieldConfigure = () => {