Skip to content

Commit

Permalink
Prefill Relation Fields with Initiating Object Icon and Name (twentyh…
Browse files Browse the repository at this point in the history
…q#7363)

feat: twentyhq#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 <[email protected]>
Co-authored-by: Lucas Bordeau <[email protected]>
  • Loading branch information
3 people authored Oct 10, 2024
1 parent 656ab4e commit e45e45d
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [],
Expand All @@ -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<SettingsDataModelFieldIconLabelFormValues>();

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 (
<StyledInputsContainer>
<Controller
Expand All @@ -55,7 +87,10 @@ export const SettingsDataModelFieldIconLabelForm = ({
<IconPicker
disabled={disabled}
selectedIconKey={value ?? ''}
onChange={({ iconKey }) => onChange(iconKey)}
onChange={({ iconKey }) => {
setIconEditedManually(true);
onChange(iconKey);
}}
variant="primary"
/>
)}
Expand All @@ -69,6 +104,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
placeholder="Employees"
value={value}
onChange={(e) => {
setLabelEditedManually(true);
onChange(e);
trigger('label');
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -39,6 +41,7 @@ export type SettingsDataModelFieldRelationFormValues = z.infer<

type SettingsDataModelFieldRelationFormProps = {
fieldMetadataItem: Pick<FieldMetadataItem, 'type'>;
objectMetadataItem: SettingsDataModelFieldPreviewCardProps['objectMetadataItem'];
};

const StyledContainer = styled.div`
Expand Down Expand Up @@ -79,26 +82,49 @@ const RELATION_TYPE_OPTIONS = Object.entries(RELATION_TYPES)

export const SettingsDataModelFieldRelationForm = ({
fieldMetadataItem,
objectMetadataItem,
}: SettingsDataModelFieldRelationFormProps) => {
const { control, watch: watchFormValue } =
useFormContext<SettingsDataModelFieldRelationFormValues>();
const {
control,
watch: watchFormValue,
setValue,
} = useFormContext<SettingsDataModelFieldRelationFormValues>();
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 (
<StyledContainer>
Expand Down Expand Up @@ -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}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export const SettingsDataModelFieldRelationSettingsFormCard = ({
form={
<SettingsDataModelFieldRelationForm
fieldMetadataItem={fieldMetadataItem}
objectMetadataItem={objectMetadataItem}
/>
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<FieldMetadataItem, 'type' | 'relationDefinition'>;
objectMetadataItem?: SettingsDataModelFieldPreviewCardProps['objectMetadataItem'];
}) => {
const { objectMetadataItems } = useFilteredObjectMetadataItems();

Expand All @@ -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 =
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -47,6 +48,7 @@ export const SettingsObjectNewFieldConfigure = () => {

const { findActiveObjectMetadataItemBySlug } =
useFilteredObjectMetadataItems();

const activeObjectMetadataItem =
findActiveObjectMetadataItemBySlug(objectSlug);
const { createMetadataField } = useFieldMetadataItem();
Expand All @@ -67,6 +69,13 @@ export const SettingsObjectNewFieldConfigure = () => {
},
});

const fieldMetadataItem: Pick<FieldMetadataItem, 'icon' | 'label' | 'type'> =
{
icon: formConfig.watch('icon'),
label: formConfig.watch('label') || 'Employees',
type: formConfig.watch('type'),
};

const [, setObjectViews] = useState<View[]>([]);
const [, setRelationObjectViews] = useState<View[]>([]);

Expand Down Expand Up @@ -200,11 +209,7 @@ export const SettingsObjectNewFieldConfigure = () => {
<H2Title title="Values" description="The values of this field" />
<SettingsDataModelFieldSettingsFormCard
isCreatingField
fieldMetadataItem={{
icon: formConfig.watch('icon'),
label: formConfig.watch('label') || 'New Field',
type: fieldType as FieldMetadataType,
}}
fieldMetadataItem={fieldMetadataItem}
objectMetadataItem={activeObjectMetadataItem}
/>
</Section>
Expand Down

0 comments on commit e45e45d

Please sign in to comment.