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 <a@b.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
sid0-0
2024-10-10 11:59:37 +05:30
committed by GitHub
parent 656ab4ed95
commit e45e45d8b2
5 changed files with 96 additions and 16 deletions

View File

@ -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<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
@ -55,7 +87,10 @@ export const SettingsDataModelFieldIconLabelForm = ({
<IconPicker
disabled={disabled}
selectedIconKey={value ?? ''}
onChange={({ iconKey }) => 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');
}}

View File

@ -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<FieldMetadataItem, 'type'>;
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<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>
@ -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}
/>

View File

@ -114,6 +114,7 @@ export const SettingsDataModelFieldRelationSettingsFormCard = ({
form={
<SettingsDataModelFieldRelationForm
fieldMetadataItem={fieldMetadataItem}
objectMetadataItem={objectMetadataItem}
/>
}
/>

View File

@ -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();
@ -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,

View File

@ -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<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[]>([]);
@ -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>