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:
@ -3,10 +3,14 @@ import { Controller, useFormContext } from 'react-hook-form';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
|
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
|
||||||
import { getErrorMessageFromError } from '@/settings/data-model/fields/forms/utils/errorMessages';
|
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 { IconPicker } from '@/ui/input/components/IconPicker';
|
||||||
import { TextInput } from '@/ui/input/components/TextInput';
|
import { TextInput } from '@/ui/input/components/TextInput';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
export const settingsDataModelFieldIconLabelFormSchema = (
|
export const settingsDataModelFieldIconLabelFormSchema = (
|
||||||
existingOtherLabels: string[] = [],
|
existingOtherLabels: string[] = [],
|
||||||
@ -32,19 +36,47 @@ type SettingsDataModelFieldIconLabelFormProps = {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
fieldMetadataItem?: FieldMetadataItem;
|
fieldMetadataItem?: FieldMetadataItem;
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
|
relationObjectMetadataItem?: ObjectMetadataItem;
|
||||||
|
relationType?: RelationType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsDataModelFieldIconLabelForm = ({
|
export const SettingsDataModelFieldIconLabelForm = ({
|
||||||
disabled,
|
disabled,
|
||||||
fieldMetadataItem,
|
fieldMetadataItem,
|
||||||
maxLength,
|
maxLength,
|
||||||
|
relationObjectMetadataItem,
|
||||||
|
relationType,
|
||||||
}: SettingsDataModelFieldIconLabelFormProps) => {
|
}: SettingsDataModelFieldIconLabelFormProps) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
trigger,
|
trigger,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
} = useFormContext<SettingsDataModelFieldIconLabelFormValues>();
|
} = 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 (
|
return (
|
||||||
<StyledInputsContainer>
|
<StyledInputsContainer>
|
||||||
<Controller
|
<Controller
|
||||||
@ -55,7 +87,10 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
|||||||
<IconPicker
|
<IconPicker
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
selectedIconKey={value ?? ''}
|
selectedIconKey={value ?? ''}
|
||||||
onChange={({ iconKey }) => onChange(iconKey)}
|
onChange={({ iconKey }) => {
|
||||||
|
setIconEditedManually(true);
|
||||||
|
onChange(iconKey);
|
||||||
|
}}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -69,6 +104,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
|||||||
placeholder="Employees"
|
placeholder="Employees"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
setLabelEditedManually(true);
|
||||||
onChange(e);
|
onChange(e);
|
||||||
trigger('label');
|
trigger('label');
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -10,11 +10,13 @@ import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fi
|
|||||||
import { FIELD_NAME_MAXIMUM_LENGTH } from '@/settings/data-model/constants/FieldNameMaximumLength';
|
import { FIELD_NAME_MAXIMUM_LENGTH } from '@/settings/data-model/constants/FieldNameMaximumLength';
|
||||||
import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes';
|
import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes';
|
||||||
import { useRelationSettingsFormInitialValues } from '@/settings/data-model/fields/forms/relation/hooks/useRelationSettingsFormInitialValues';
|
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 { RelationType } from '@/settings/data-model/types/RelationType';
|
||||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||||
import { Select } from '@/ui/input/components/Select';
|
import { Select } from '@/ui/input/components/Select';
|
||||||
import { TextInput } from '@/ui/input/components/TextInput';
|
import { TextInput } from '@/ui/input/components/TextInput';
|
||||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
export const settingsDataModelFieldRelationFormSchema = z.object({
|
export const settingsDataModelFieldRelationFormSchema = z.object({
|
||||||
@ -39,6 +41,7 @@ export type SettingsDataModelFieldRelationFormValues = z.infer<
|
|||||||
|
|
||||||
type SettingsDataModelFieldRelationFormProps = {
|
type SettingsDataModelFieldRelationFormProps = {
|
||||||
fieldMetadataItem: Pick<FieldMetadataItem, 'type'>;
|
fieldMetadataItem: Pick<FieldMetadataItem, 'type'>;
|
||||||
|
objectMetadataItem: SettingsDataModelFieldPreviewCardProps['objectMetadataItem'];
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -79,26 +82,49 @@ const RELATION_TYPE_OPTIONS = Object.entries(RELATION_TYPES)
|
|||||||
|
|
||||||
export const SettingsDataModelFieldRelationForm = ({
|
export const SettingsDataModelFieldRelationForm = ({
|
||||||
fieldMetadataItem,
|
fieldMetadataItem,
|
||||||
|
objectMetadataItem,
|
||||||
}: SettingsDataModelFieldRelationFormProps) => {
|
}: SettingsDataModelFieldRelationFormProps) => {
|
||||||
const { control, watch: watchFormValue } =
|
const {
|
||||||
useFormContext<SettingsDataModelFieldRelationFormValues>();
|
control,
|
||||||
|
watch: watchFormValue,
|
||||||
|
setValue,
|
||||||
|
} = useFormContext<SettingsDataModelFieldRelationFormValues>();
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
const { objectMetadataItems, findObjectMetadataItemById } =
|
const { objectMetadataItems, findObjectMetadataItemById } =
|
||||||
useFilteredObjectMetadataItems();
|
useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
|
const [labelEditedManually, setLabelEditedManually] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
disableFieldEdition,
|
disableFieldEdition,
|
||||||
disableRelationEdition,
|
disableRelationEdition,
|
||||||
initialRelationFieldMetadataItem,
|
initialRelationFieldMetadataItem,
|
||||||
initialRelationObjectMetadataItem,
|
initialRelationObjectMetadataItem,
|
||||||
initialRelationType,
|
initialRelationType,
|
||||||
} = useRelationSettingsFormInitialValues({ fieldMetadataItem });
|
} = useRelationSettingsFormInitialValues({
|
||||||
|
fieldMetadataItem,
|
||||||
|
objectMetadataItem,
|
||||||
|
});
|
||||||
|
|
||||||
const selectedObjectMetadataItem = findObjectMetadataItemById(
|
const selectedObjectMetadataItem = findObjectMetadataItemById(
|
||||||
watchFormValue('relation.objectMetadataId'),
|
watchFormValue('relation.objectMetadataId'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
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 (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
@ -169,7 +195,10 @@ export const SettingsDataModelFieldRelationForm = ({
|
|||||||
disabled={disableFieldEdition}
|
disabled={disableFieldEdition}
|
||||||
placeholder="Field name"
|
placeholder="Field name"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={(newValue) => {
|
||||||
|
setLabelEditedManually(true);
|
||||||
|
onChange(newValue);
|
||||||
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
maxLength={FIELD_NAME_MAXIMUM_LENGTH}
|
maxLength={FIELD_NAME_MAXIMUM_LENGTH}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -114,6 +114,7 @@ export const SettingsDataModelFieldRelationSettingsFormCard = ({
|
|||||||
form={
|
form={
|
||||||
<SettingsDataModelFieldRelationForm
|
<SettingsDataModelFieldRelationForm
|
||||||
fieldMetadataItem={fieldMetadataItem}
|
fieldMetadataItem={fieldMetadataItem}
|
||||||
|
objectMetadataItem={objectMetadataItem}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,15 +2,17 @@ import { useMemo } from 'react';
|
|||||||
|
|
||||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
||||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
||||||
|
import { SettingsDataModelFieldPreviewCardProps } from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewCard';
|
||||||
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
export const useRelationSettingsFormInitialValues = ({
|
export const useRelationSettingsFormInitialValues = ({
|
||||||
fieldMetadataItem,
|
fieldMetadataItem,
|
||||||
|
objectMetadataItem,
|
||||||
}: {
|
}: {
|
||||||
fieldMetadataItem?: Pick<FieldMetadataItem, 'type' | 'relationDefinition'>;
|
fieldMetadataItem?: Pick<FieldMetadataItem, 'type' | 'relationDefinition'>;
|
||||||
|
objectMetadataItem?: SettingsDataModelFieldPreviewCardProps['objectMetadataItem'];
|
||||||
}) => {
|
}) => {
|
||||||
const { objectMetadataItems } = useFilteredObjectMetadataItems();
|
const { objectMetadataItems } = useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
@ -28,11 +30,13 @@ export const useRelationSettingsFormInitialValues = ({
|
|||||||
const initialRelationObjectMetadataItem = useMemo(
|
const initialRelationObjectMetadataItem = useMemo(
|
||||||
() =>
|
() =>
|
||||||
relationObjectMetadataItemFromFieldMetadata ??
|
relationObjectMetadataItemFromFieldMetadata ??
|
||||||
objectMetadataItems.find(
|
objectMetadataItem ??
|
||||||
({ nameSingular }) => nameSingular === CoreObjectNameSingular.Person,
|
|
||||||
) ??
|
|
||||||
objectMetadataItems.filter(isObjectMetadataAvailableForRelation)[0],
|
objectMetadataItems.filter(isObjectMetadataAvailableForRelation)[0],
|
||||||
[objectMetadataItems, relationObjectMetadataItemFromFieldMetadata],
|
[
|
||||||
|
objectMetadataItem,
|
||||||
|
objectMetadataItems,
|
||||||
|
relationObjectMetadataItemFromFieldMetadata,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const initialRelationType =
|
const initialRelationType =
|
||||||
@ -44,7 +48,12 @@ export const useRelationSettingsFormInitialValues = ({
|
|||||||
disableRelationEdition: !!relationFieldMetadataItem,
|
disableRelationEdition: !!relationFieldMetadataItem,
|
||||||
initialRelationFieldMetadataItem: relationFieldMetadataItem ?? {
|
initialRelationFieldMetadataItem: relationFieldMetadataItem ?? {
|
||||||
icon: initialRelationObjectMetadataItem.icon ?? 'IconUsers',
|
icon: initialRelationObjectMetadataItem.icon ?? 'IconUsers',
|
||||||
label: '',
|
label: [
|
||||||
|
RelationDefinitionType.ManyToMany,
|
||||||
|
RelationDefinitionType.ManyToOne,
|
||||||
|
].includes(initialRelationType)
|
||||||
|
? initialRelationObjectMetadataItem.labelPlural
|
||||||
|
: initialRelationObjectMetadataItem.labelSingular,
|
||||||
},
|
},
|
||||||
initialRelationObjectMetadataItem,
|
initialRelationObjectMetadataItem,
|
||||||
initialRelationType,
|
initialRelationType,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useCreateOneRelationMetadataItem } from '@/object-metadata/hooks/useCre
|
|||||||
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
||||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
|
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||||
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||||
@ -47,6 +48,7 @@ export const SettingsObjectNewFieldConfigure = () => {
|
|||||||
|
|
||||||
const { findActiveObjectMetadataItemBySlug } =
|
const { findActiveObjectMetadataItemBySlug } =
|
||||||
useFilteredObjectMetadataItems();
|
useFilteredObjectMetadataItems();
|
||||||
|
|
||||||
const activeObjectMetadataItem =
|
const activeObjectMetadataItem =
|
||||||
findActiveObjectMetadataItemBySlug(objectSlug);
|
findActiveObjectMetadataItemBySlug(objectSlug);
|
||||||
const { createMetadataField } = useFieldMetadataItem();
|
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 [, setObjectViews] = useState<View[]>([]);
|
||||||
const [, setRelationObjectViews] = useState<View[]>([]);
|
const [, setRelationObjectViews] = useState<View[]>([]);
|
||||||
|
|
||||||
@ -200,11 +209,7 @@ export const SettingsObjectNewFieldConfigure = () => {
|
|||||||
<H2Title title="Values" description="The values of this field" />
|
<H2Title title="Values" description="The values of this field" />
|
||||||
<SettingsDataModelFieldSettingsFormCard
|
<SettingsDataModelFieldSettingsFormCard
|
||||||
isCreatingField
|
isCreatingField
|
||||||
fieldMetadataItem={{
|
fieldMetadataItem={fieldMetadataItem}
|
||||||
icon: formConfig.watch('icon'),
|
|
||||||
label: formConfig.watch('label') || 'New Field',
|
|
||||||
type: fieldType as FieldMetadataType,
|
|
||||||
}}
|
|
||||||
objectMetadataItem={activeObjectMetadataItem}
|
objectMetadataItem={activeObjectMetadataItem}
|
||||||
/>
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
|
|||||||
Reference in New Issue
Block a user