Morph-settings-relation-and-morph-merge (#15062)
🎯 Merge Settings Relation and Morph Relation Forms In the settings, we unify morph and relation into a single form. The form now automatically creates - a `RELATION` field when 1 destination object is selected - or a `MORPH_RELATION` field when 2+ objects are selected. Better UI labels (showing object name for single selection, "X Objects" for multiple), proper field editing controls on destination objects, and capitalized field labels. We still make sure the isMorphRelationEnabled feature flag prevents current users from accessing this feature Fixes https://github.com/twentyhq/core-team-issues/issues/1589
This commit is contained in:
+11
-24
@@ -15,15 +15,11 @@ 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 { settingsDataModelFieldMorphRelationFormSchema } from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldRelationForm';
|
||||
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';
|
||||
import { SettingsDataModelFieldPhonesSettingsFormCard } from '@/settings/data-model/fields/forms/phones/components/SettingsDataModelFieldPhonesSettingsFormCard';
|
||||
import { settingsDataModelFieldRelationFormSchema } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm';
|
||||
import { SettingsDataModelFieldRelationSettingsFormCard } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationSettingsFormCard';
|
||||
import {
|
||||
settingsDataModelFieldMultiSelectFormSchema,
|
||||
settingsDataModelFieldSelectFormSchema,
|
||||
@@ -31,7 +27,9 @@ import {
|
||||
import { SettingsDataModelFieldSelectSettingsFormCard } from '@/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectSettingsFormCard';
|
||||
import { settingsDataModelFieldMaxValuesSchema } from '@/settings/data-model/fields/forms/utils/settingsDataModelFieldMaxValuesSchema';
|
||||
import { SettingsDataModelFieldPreviewWidget } from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewWidget';
|
||||
|
||||
import { Separator } from '@/settings/components/Separator';
|
||||
import { SettingsDataModelFieldRelationFormCard } from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldRelationFormCard';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { type SettingsDataModelFieldEditFormValues } from '~/pages/settings/data-model/SettingsObjectFieldEdit';
|
||||
@@ -58,12 +56,10 @@ const dateTimeFieldFormSchema = z
|
||||
.extend(settingsDataModelFieldDateFormSchema.shape)
|
||||
.extend(isUniqueFieldFormSchema.shape);
|
||||
|
||||
const relationFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.RELATION) })
|
||||
.extend(settingsDataModelFieldRelationFormSchema.shape);
|
||||
|
||||
const morphRelationFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.MORPH_RELATION) })
|
||||
.object({
|
||||
type: z.literal(FieldMetadataType.MORPH_RELATION),
|
||||
})
|
||||
.extend(settingsDataModelFieldMorphRelationFormSchema.shape);
|
||||
|
||||
const selectFieldFormSchema = z
|
||||
@@ -115,7 +111,6 @@ const otherFieldsFormSchema = z
|
||||
omit(SETTINGS_FIELD_TYPE_CONFIGS, [
|
||||
FieldMetadataType.BOOLEAN,
|
||||
FieldMetadataType.CURRENCY,
|
||||
FieldMetadataType.RELATION,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
@@ -141,7 +136,6 @@ export const settingsDataModelFieldSettingsFormSchema = z.discriminatedUnion(
|
||||
currencyFieldFormSchema,
|
||||
dateFieldFormSchema,
|
||||
dateTimeFieldFormSchema,
|
||||
relationFieldFormSchema,
|
||||
morphRelationFieldFormSchema,
|
||||
selectFieldFormSchema,
|
||||
multiSelectFieldFormSchema,
|
||||
@@ -231,19 +225,12 @@ export const SettingsDataModelFieldSettingsFormCard = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (fieldType === FieldMetadataType.RELATION) {
|
||||
if (
|
||||
fieldType === FieldMetadataType.RELATION ||
|
||||
fieldType === FieldMetadataType.MORPH_RELATION
|
||||
) {
|
||||
return (
|
||||
<SettingsDataModelFieldRelationSettingsFormCard
|
||||
existingFieldMetadataId={existingFieldMetadataId}
|
||||
objectNameSingular={objectNameSingular}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (fieldType === FieldMetadataType.MORPH_RELATION) {
|
||||
return (
|
||||
<SettingsDataModelFieldMorphRelationFormCard
|
||||
<SettingsDataModelFieldRelationFormCard
|
||||
existingFieldMetadataId={existingFieldMetadataId}
|
||||
objectNameSingular={objectNameSingular}
|
||||
disabled={disabled}
|
||||
|
||||
+13
-11
@@ -10,7 +10,6 @@ import { useSelectSettingsFormInitialValues } from '@/settings/data-model/fields
|
||||
import { type FieldType } from '@/settings/data-model/types/FieldType';
|
||||
import { type SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType';
|
||||
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
@@ -22,7 +21,6 @@ import { getSettingsPath } from 'twenty-shared/utils';
|
||||
import { H2Title, IconSearch } from 'twenty-ui/display';
|
||||
import { UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { type SettingsDataModelFieldTypeFormValues } from '~/pages/settings/data-model/new-field/SettingsObjectNewFieldSelect';
|
||||
|
||||
type SettingsObjectNewFieldSelectorProps = {
|
||||
@@ -105,9 +103,7 @@ export const SettingsObjectNewFieldSelector = ({
|
||||
break;
|
||||
}
|
||||
};
|
||||
const isMorphRelationEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_MORPH_RELATION_ENABLED,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{' '}
|
||||
@@ -136,12 +132,18 @@ export const SettingsObjectNewFieldSelector = ({
|
||||
<StyledContainer>
|
||||
{fieldTypeConfigs
|
||||
.filter(([, config]) => config.category === category)
|
||||
.filter(([key]) => {
|
||||
return (
|
||||
key !== FieldMetadataType.MORPH_RELATION ||
|
||||
isMorphRelationEnabled
|
||||
);
|
||||
})
|
||||
// by default, we hide the relation type and create only the morph relation type
|
||||
// on submit the new field, we choose the relation type based on the amount of target object
|
||||
.filter(([key]) => key !== FieldMetadataType.RELATION)
|
||||
.map(
|
||||
([key, config]) =>
|
||||
[
|
||||
key,
|
||||
key === FieldMetadataType.MORPH_RELATION
|
||||
? { ...config, label: 'Relation' }
|
||||
: config,
|
||||
] as [string, SettingsFieldTypeConfig<any>],
|
||||
)
|
||||
.map(([key, config]) => (
|
||||
<StyledCardContainer key={key}>
|
||||
<UndecoratedLink
|
||||
|
||||
+88
-31
@@ -5,27 +5,55 @@ import { StyledContainer } from '@/keyboard-shortcut-menu/components/KeyboardSho
|
||||
import { SettingsMorphRelationMultiSelect } from '@/settings/components/SettingsMorphRelationMultiSelect';
|
||||
import { FIELD_NAME_MAXIMUM_LENGTH } from '@/settings/data-model/constants/FieldNameMaximumLength';
|
||||
import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes';
|
||||
import { useMorphRelationSettingsFormDefaultValuesOnDestination } from '@/settings/data-model/fields/forms/morph-relation/hooks/useMorphRelationSettingsFormDefaultValuesOnDestination';
|
||||
import { useMorphRelationSettingsFormInitialTargetMetadatas } from '@/settings/data-model/fields/forms/morph-relation/hooks/useMorphRelationSettingsFormInitialTargetMetadatas';
|
||||
import { fieldMetadataItemDisableFieldEdition } from '@/settings/data-model/fields/forms/morph-relation/utils/fieldMetadataItemDisableFieldEdition';
|
||||
import { useRelationSettingsFormDefaultValuesTargetFieldMetadata } from '@/settings/data-model/fields/forms/morph-relation/hooks/useRelationSettingsFormDefaultValuesTargetFieldMetadata';
|
||||
import { useRelationSettingsFormInitialTargetObjectMetadatas } from '@/settings/data-model/fields/forms/morph-relation/hooks/useRelationSettingsFormInitialTargetObjectMetadatas';
|
||||
import { useFieldMetadataItemDisableFieldEdition } from '@/settings/data-model/fields/forms/morph-relation/utils/fieldMetadataItemDisableFieldEdition';
|
||||
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import {
|
||||
RELATION_TYPE_OPTIONS,
|
||||
StyledInputsContainer,
|
||||
StyledInputsLabel,
|
||||
StyledSelectsContainer,
|
||||
} from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm';
|
||||
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { RelationType } from '~/generated-metadata/graphql';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { FeatureFlagKey, RelationType } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledSelectsContainer = styled.div<{ isMobile: boolean }>`
|
||||
display: grid;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
grid-template-columns: ${({ isMobile }) => (isMobile ? '1fr' : '1fr 1fr')};
|
||||
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
const StyledInputsLabel = 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 StyledInputsContainer = styled.div`
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const RELATION_TYPE_OPTIONS = Object.entries(RELATION_TYPES).map(
|
||||
([value, { label, Icon }]) => ({
|
||||
label,
|
||||
value: value as RelationType,
|
||||
Icon,
|
||||
}),
|
||||
);
|
||||
|
||||
export const settingsDataModelFieldMorphRelationFormSchema = z.object({
|
||||
morphRelationObjectMetadataIds: z.array(z.uuid()).min(2),
|
||||
morphRelationObjectMetadataIds: z.array(z.uuid()).min(1),
|
||||
relationType: z.enum(
|
||||
Object.keys(RELATION_TYPES) as [RelationType, ...RelationType[]],
|
||||
),
|
||||
@@ -37,29 +65,29 @@ export type SettingsDataModelFieldMorphRelationFormValues = z.infer<
|
||||
typeof settingsDataModelFieldMorphRelationFormSchema
|
||||
>;
|
||||
|
||||
type SettingsDataModelFieldMorphRelationFormProps = {
|
||||
type SettingsDataModelFieldRelationFormProps = {
|
||||
existingFieldMetadataId: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SettingsDataModelFieldMorphRelationForm = ({
|
||||
export const SettingsDataModelFieldRelationForm = ({
|
||||
existingFieldMetadataId,
|
||||
disabled = false,
|
||||
}: SettingsDataModelFieldMorphRelationFormProps) => {
|
||||
}: SettingsDataModelFieldRelationFormProps) => {
|
||||
const { t } = useLingui();
|
||||
const { control } =
|
||||
useFormContext<SettingsDataModelFieldMorphRelationFormValues>();
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
const { fieldMetadataItem: existingFieldMetadataItem } =
|
||||
useFieldMetadataItemById(existingFieldMetadataId);
|
||||
|
||||
const disableRelationEdition = isDefined(existingFieldMetadataItem);
|
||||
const disableFieldEdition = isDefined(existingFieldMetadataItem)
|
||||
? fieldMetadataItemDisableFieldEdition(existingFieldMetadataItem)
|
||||
: false;
|
||||
const disableFieldEdition = useFieldMetadataItemDisableFieldEdition(
|
||||
existingFieldMetadataItem,
|
||||
);
|
||||
|
||||
const initialRelationObjectMetadataItems =
|
||||
useMorphRelationSettingsFormInitialTargetMetadatas({
|
||||
useRelationSettingsFormInitialTargetObjectMetadatas({
|
||||
fieldMetadataItem: existingFieldMetadataItem,
|
||||
});
|
||||
|
||||
@@ -68,7 +96,7 @@ export const SettingsDataModelFieldMorphRelationForm = ({
|
||||
RelationType.ONE_TO_MANY;
|
||||
|
||||
const { label: defaultLabelOnDestination, icon: defaultIconOnDestination } =
|
||||
useMorphRelationSettingsFormDefaultValuesOnDestination({
|
||||
useRelationSettingsFormDefaultValuesTargetFieldMetadata({
|
||||
fieldMetadataItem: existingFieldMetadataItem,
|
||||
objectMetadataItem: initialRelationObjectMetadataItems[0],
|
||||
relationType: initialRelationType,
|
||||
@@ -80,6 +108,12 @@ export const SettingsDataModelFieldMorphRelationForm = ({
|
||||
);
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const isMorphRelationEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_MORPH_RELATION_ENABLED,
|
||||
);
|
||||
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledSelectsContainer isMobile={isMobile}>
|
||||
@@ -104,17 +138,40 @@ export const SettingsDataModelFieldMorphRelationForm = ({
|
||||
name="morphRelationObjectMetadataIds"
|
||||
control={control}
|
||||
defaultValue={initialMorphRelationsObjectMetadataIds}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<SettingsMorphRelationMultiSelect
|
||||
label={t`Object destination`}
|
||||
dropdownId="object-destination-select"
|
||||
fullWidth
|
||||
disabled={disableRelationEdition}
|
||||
selectedObjectMetadataIds={value}
|
||||
withSearchInput={true}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
render={({ field: { onChange, value } }) =>
|
||||
isMorphRelationEnabled ? (
|
||||
<SettingsMorphRelationMultiSelect
|
||||
label={t`Object destination`}
|
||||
dropdownId="object-destination-select"
|
||||
fullWidth
|
||||
disabled={disableRelationEdition}
|
||||
selectedObjectMetadataIds={value}
|
||||
withSearchInput={true}
|
||||
onChange={onChange}
|
||||
/>
|
||||
) : (
|
||||
<Select
|
||||
label={t`Object destination`}
|
||||
dropdownId="object-destination-select"
|
||||
fullWidth
|
||||
disabled={disableRelationEdition}
|
||||
value={value[0]}
|
||||
options={activeObjectMetadataItems
|
||||
.filter(isObjectMetadataAvailableForRelation)
|
||||
.sort((item1, item2) =>
|
||||
item1.labelSingular.localeCompare(item2.labelSingular),
|
||||
)
|
||||
.map((objectMetadataItem) => ({
|
||||
label: objectMetadataItem.labelPlural,
|
||||
value: objectMetadataItem.id,
|
||||
Icon: getIcon(objectMetadataItem.icon),
|
||||
}))}
|
||||
onChange={(value) => {
|
||||
onChange([value]);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</StyledSelectsContainer>
|
||||
<StyledInputsLabel>{t`Field on destination`}</StyledInputsLabel>
|
||||
+9
-9
@@ -4,28 +4,28 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes';
|
||||
import {
|
||||
SettingsDataModelFieldMorphRelationForm,
|
||||
SettingsDataModelFieldRelationForm,
|
||||
type SettingsDataModelFieldMorphRelationFormValues,
|
||||
} from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldMorphRelationForm';
|
||||
import { SettingsDataModelFieldRelationPreviewContent } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationPreviewContent';
|
||||
import { SettingsDataModelRelationPreviewImage } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationPreviewImageCard';
|
||||
} from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldRelationForm';
|
||||
import { SettingsDataModelFieldRelationPreviewContent } from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldRelationPreviewContent';
|
||||
import { SettingsDataModelRelationPreviewImage } from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldRelationPreviewImageCard';
|
||||
import { SettingsDataModelRelationFieldPreviewSubWidget } from '@/settings/data-model/fields/preview/components/SettingsDataModelRelationFieldPreviewSubWidget';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
||||
import { type SettingsDataModelFieldEditFormValues } from '~/pages/settings/data-model/SettingsObjectFieldEdit';
|
||||
|
||||
type SettingsDataModelFieldMorphRelationFormCardProps = {
|
||||
type SettingsDataModelFieldRelationFormCardProps = {
|
||||
existingFieldMetadataId: string;
|
||||
objectNameSingular: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SettingsDataModelFieldMorphRelationFormCard = ({
|
||||
export const SettingsDataModelFieldRelationFormCard = ({
|
||||
existingFieldMetadataId,
|
||||
objectNameSingular,
|
||||
disabled = false,
|
||||
}: SettingsDataModelFieldMorphRelationFormCardProps) => {
|
||||
}: SettingsDataModelFieldRelationFormCardProps) => {
|
||||
const { watch } = useFormContext<
|
||||
SettingsDataModelFieldMorphRelationFormValues &
|
||||
SettingsDataModelFieldEditFormValues
|
||||
@@ -67,7 +67,7 @@ export const SettingsDataModelFieldMorphRelationFormCard = ({
|
||||
fieldMetadataItem={{
|
||||
icon: watch('icon'),
|
||||
label: watch('label'),
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
type: FieldMetadataType.RELATION,
|
||||
settings: {
|
||||
relationType,
|
||||
},
|
||||
@@ -107,7 +107,7 @@ export const SettingsDataModelFieldMorphRelationFormCard = ({
|
||||
</SettingsDataModelFieldRelationPreviewContent>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldMorphRelationForm
|
||||
<SettingsDataModelFieldRelationForm
|
||||
existingFieldMetadataId={existingFieldMetadataId}
|
||||
disabled={disabled}
|
||||
/>
|
||||
+36
-7
@@ -1,16 +1,20 @@
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getFieldMetadataItemById } from '@/object-metadata/utils/getFieldMetadataItemById';
|
||||
import { fieldMetadataItemHasMorphRelations } from '@/settings/data-model/fields/forms/morph-relation/utils/fieldMetadataItemHasMorphRelations';
|
||||
import { RelationType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType, RelationType } from 'twenty-shared/types';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useMorphRelationSettingsFormDefaultValuesOnDestination = ({
|
||||
export const useRelationSettingsFormDefaultValuesTargetFieldMetadata = ({
|
||||
fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
relationType,
|
||||
}: {
|
||||
fieldMetadataItem?: Pick<FieldMetadataItem, 'type' | 'morphRelations'>;
|
||||
fieldMetadataItem?: Pick<
|
||||
FieldMetadataItem,
|
||||
'type' | 'morphRelations' | 'relation'
|
||||
>;
|
||||
objectMetadataItem?: Pick<
|
||||
ObjectMetadataItem,
|
||||
'id' | 'namePlural' | 'nameSingular' | 'icon'
|
||||
@@ -22,8 +26,15 @@ export const useMorphRelationSettingsFormDefaultValuesOnDestination = ({
|
||||
} => {
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
|
||||
if (!isDefined(fieldMetadataItem)) {
|
||||
return {
|
||||
icon: 'IconUsers',
|
||||
label: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(fieldMetadataItem) &&
|
||||
fieldMetadataItem.type === FieldMetadataType.MORPH_RELATION &&
|
||||
fieldMetadataItemHasMorphRelations(fieldMetadataItem)
|
||||
) {
|
||||
const firstMorphRelation = fieldMetadataItem.morphRelations?.[0];
|
||||
@@ -38,11 +49,29 @@ export const useMorphRelationSettingsFormDefaultValuesOnDestination = ({
|
||||
|
||||
return {
|
||||
icon: targetFieldMetadata?.icon ?? 'IconUsers',
|
||||
label: targetFieldMetadata?.label ?? '',
|
||||
label: capitalize(targetFieldMetadata?.label ?? ''),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
fieldMetadataItem.type === FieldMetadataType.RELATION &&
|
||||
isDefined(fieldMetadataItem.relation?.targetObjectMetadata.id)
|
||||
) {
|
||||
const { fieldMetadataItem: targetFieldMetadata } = getFieldMetadataItemById(
|
||||
{
|
||||
fieldMetadataId: fieldMetadataItem.relation?.targetFieldMetadata.id,
|
||||
objectMetadataItems: activeObjectMetadataItems,
|
||||
},
|
||||
);
|
||||
return {
|
||||
icon: targetFieldMetadata?.icon ?? 'IconUsers',
|
||||
label: capitalize(
|
||||
fieldMetadataItem.relation?.targetFieldMetadata?.name ?? '',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (isDefined(objectMetadataItem)) {
|
||||
const label = [RelationType.MANY_TO_ONE].includes(relationType)
|
||||
? objectMetadataItem.namePlural
|
||||
@@ -50,7 +79,7 @@ export const useMorphRelationSettingsFormDefaultValuesOnDestination = ({
|
||||
|
||||
return {
|
||||
icon: objectMetadataItem.icon ?? 'IconUsers',
|
||||
label,
|
||||
label: capitalize(label),
|
||||
};
|
||||
}
|
||||
|
||||
+10
-9
@@ -4,10 +4,13 @@ import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/is
|
||||
import { fieldMetadataItemHasMorphRelations } from '@/settings/data-model/fields/forms/morph-relation/utils/fieldMetadataItemHasMorphRelations';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useMorphRelationSettingsFormInitialTargetMetadatas = ({
|
||||
export const useRelationSettingsFormInitialTargetObjectMetadatas = ({
|
||||
fieldMetadataItem,
|
||||
}: {
|
||||
fieldMetadataItem?: Pick<FieldMetadataItem, 'type' | 'morphRelations'>;
|
||||
fieldMetadataItem?: Pick<
|
||||
FieldMetadataItem,
|
||||
'type' | 'morphRelations' | 'relation'
|
||||
>;
|
||||
}) => {
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
|
||||
@@ -27,6 +30,10 @@ export const useMorphRelationSettingsFormInitialTargetMetadatas = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isDefined(fieldMetadataItem) && isDefined(fieldMetadataItem.relation)) {
|
||||
return [fieldMetadataItem.relation.targetObjectMetadata];
|
||||
}
|
||||
|
||||
const availableItems = activeObjectMetadataItems
|
||||
.filter(isObjectMetadataAvailableForRelation)
|
||||
.sort((a, b) => a.labelSingular.localeCompare(b.labelSingular));
|
||||
@@ -36,12 +43,6 @@ export const useMorphRelationSettingsFormInitialTargetMetadatas = ({
|
||||
'Relation Form initialization error: invariant violated – no valid object available for relation (this should never happen).',
|
||||
);
|
||||
}
|
||||
const secondInitialObjectCandidate = availableItems[1];
|
||||
if (!isDefined(secondInitialObjectCandidate)) {
|
||||
throw new Error(
|
||||
'Relation Form initialization error: invariant violated – no valid object available for relation (this should never happen).',
|
||||
);
|
||||
}
|
||||
|
||||
return [firstInitialObjectCandidate, secondInitialObjectCandidate];
|
||||
return [firstInitialObjectCandidate];
|
||||
};
|
||||
+20
-3
@@ -1,12 +1,29 @@
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const fieldMetadataItemDisableFieldEdition = (
|
||||
fieldMetadataItem: Pick<FieldMetadataItem, 'morphRelations'>,
|
||||
export const useFieldMetadataItemDisableFieldEdition = (
|
||||
fieldMetadataItem:
|
||||
| Pick<FieldMetadataItem, 'morphRelations' | 'type' | 'id'>
|
||||
| undefined,
|
||||
) => {
|
||||
const { fieldMetadataItem: fieldMetadataItemWithIsCustom } =
|
||||
useFieldMetadataItemById(fieldMetadataItem?.id ?? '');
|
||||
|
||||
if (!isDefined(fieldMetadataItem)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const morphRelations = fieldMetadataItem?.morphRelations;
|
||||
|
||||
// TODO: @guillim to be updated with isCustom when available
|
||||
if (isFieldRelation(fieldMetadataItem)) {
|
||||
return (
|
||||
isDefined(fieldMetadataItemWithIsCustom) &&
|
||||
fieldMetadataItemWithIsCustom?.isCustom === true
|
||||
);
|
||||
}
|
||||
|
||||
return isDefined(morphRelations) && morphRelations.length > 0
|
||||
? morphRelations.every(
|
||||
(morphRelation) => !morphRelation.targetFieldMetadata.isCustom,
|
||||
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
||||
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
|
||||
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 { IconPicker } from '@/ui/input/components/IconPicker';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { RelationType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const settingsDataModelFieldRelationFormSchema = z.object({
|
||||
relation: z.object({
|
||||
field: fieldMetadataItemSchema()
|
||||
.pick({
|
||||
icon: true,
|
||||
label: true,
|
||||
})
|
||||
// NOT SURE IF THIS IS CORRECT
|
||||
.extend(
|
||||
fieldMetadataItemSchema()
|
||||
.pick({
|
||||
name: true,
|
||||
isLabelSyncedWithName: true,
|
||||
})
|
||||
.partial().shape,
|
||||
),
|
||||
objectMetadataId: z.uuid(),
|
||||
type: z.enum(
|
||||
Object.keys(RELATION_TYPES) as [RelationType, ...RelationType[]],
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
export type SettingsDataModelFieldRelationFormValues = z.infer<
|
||||
typeof settingsDataModelFieldRelationFormSchema
|
||||
>;
|
||||
|
||||
type SettingsDataModelFieldRelationFormProps = {
|
||||
existingFieldMetadataId: string;
|
||||
objectMetadataItem?: ObjectMetadataItem;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const StyledContainer = styled.div`
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
export const StyledSelectsContainer = styled.div<{ isMobile: boolean }>`
|
||||
display: grid;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
grid-template-columns: ${({ isMobile }) => (isMobile ? '1fr' : '1fr 1fr')};
|
||||
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
export const StyledInputsLabel = 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)};
|
||||
`;
|
||||
|
||||
export const StyledInputsContainer = styled.div`
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const RELATION_TYPE_OPTIONS = Object.entries(RELATION_TYPES).map(
|
||||
([value, { label, Icon }]) => ({
|
||||
label,
|
||||
value: value as RelationType,
|
||||
Icon,
|
||||
}),
|
||||
);
|
||||
|
||||
export const SettingsDataModelFieldRelationForm = ({
|
||||
existingFieldMetadataId,
|
||||
objectMetadataItem,
|
||||
disabled,
|
||||
}: SettingsDataModelFieldRelationFormProps) => {
|
||||
const { t } = useLingui();
|
||||
const { control, watch: watchFormValue } =
|
||||
useFormContext<SettingsDataModelFieldRelationFormValues>();
|
||||
const { getIcon } = useIcons();
|
||||
const { activeObjectMetadataItems, findObjectMetadataItemById } =
|
||||
useFilteredObjectMetadataItems();
|
||||
|
||||
const {
|
||||
disableFieldEdition,
|
||||
disableRelationEdition,
|
||||
initialRelationFieldMetadataItem,
|
||||
initialRelationObjectMetadataItem,
|
||||
initialRelationType,
|
||||
} = useRelationSettingsFormInitialValues({
|
||||
existingFieldMetadataId,
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const selectedObjectMetadataItem = findObjectMetadataItemById(
|
||||
watchFormValue(
|
||||
'relation.objectMetadataId',
|
||||
initialRelationObjectMetadataItem?.id,
|
||||
),
|
||||
);
|
||||
|
||||
const selectedRelationType = watchFormValue(
|
||||
'relation.type',
|
||||
initialRelationType,
|
||||
);
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledSelectsContainer isMobile={isMobile}>
|
||||
<Controller
|
||||
name="relation.type"
|
||||
control={control}
|
||||
defaultValue={initialRelationType}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Select
|
||||
label={t`Relation type`}
|
||||
dropdownId="relation-type-select"
|
||||
fullWidth
|
||||
disabled={disabled || disableRelationEdition}
|
||||
value={value}
|
||||
options={RELATION_TYPE_OPTIONS}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="relation.objectMetadataId"
|
||||
control={control}
|
||||
defaultValue={initialRelationObjectMetadataItem.id}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Select
|
||||
label={t`Object destination`}
|
||||
dropdownId="object-destination-select"
|
||||
fullWidth
|
||||
disabled={disabled || disableRelationEdition}
|
||||
value={value}
|
||||
options={activeObjectMetadataItems
|
||||
.filter(isObjectMetadataAvailableForRelation)
|
||||
.sort((item1, item2) =>
|
||||
item1.labelPlural.localeCompare(item2.labelPlural),
|
||||
)
|
||||
.map((objectMetadataItem) => ({
|
||||
label: objectMetadataItem.labelPlural,
|
||||
value: objectMetadataItem.id,
|
||||
Icon: getIcon(objectMetadataItem.icon),
|
||||
}))}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</StyledSelectsContainer>
|
||||
<StyledInputsLabel>
|
||||
Field on{' '}
|
||||
{selectedRelationType === RelationType.MANY_TO_ONE
|
||||
? selectedObjectMetadataItem?.labelSingular
|
||||
: selectedObjectMetadataItem?.labelPlural}
|
||||
</StyledInputsLabel>
|
||||
<StyledInputsContainer>
|
||||
<Controller
|
||||
name="relation.field.icon"
|
||||
control={control}
|
||||
defaultValue={initialRelationFieldMetadataItem.icon}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<IconPicker
|
||||
disabled={disabled || disableFieldEdition}
|
||||
dropdownId="field-destination-icon-picker"
|
||||
selectedIconKey={value ?? undefined}
|
||||
onChange={({ iconKey }) => onChange(iconKey)}
|
||||
variant="primary"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="relation.field.label"
|
||||
control={control}
|
||||
defaultValue={initialRelationFieldMetadataItem.label}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<SettingsTextInput
|
||||
instanceId="relation-field-label"
|
||||
disabled={disabled || disableFieldEdition}
|
||||
placeholder={t`Field name`}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
fullWidth
|
||||
maxLength={FIELD_NAME_MAXIMUM_LENGTH}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</StyledInputsContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes';
|
||||
import {
|
||||
SettingsDataModelFieldRelationForm,
|
||||
type SettingsDataModelFieldRelationFormValues,
|
||||
} from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm';
|
||||
import { SettingsDataModelFieldRelationPreviewContent } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationPreviewContent';
|
||||
import { SettingsDataModelRelationPreviewImage } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationPreviewImageCard';
|
||||
import { SettingsDataModelRelationFieldPreviewSubWidget } from '@/settings/data-model/fields/preview/components/SettingsDataModelRelationFieldPreviewSubWidget';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
||||
import { type SettingsDataModelFieldEditFormValues } from '~/pages/settings/data-model/SettingsObjectFieldEdit';
|
||||
|
||||
type SettingsDataModelFieldRelationSettingsFormCardProps = {
|
||||
existingFieldMetadataId: string;
|
||||
objectNameSingular: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SettingsDataModelFieldRelationSettingsFormCard = ({
|
||||
existingFieldMetadataId,
|
||||
objectNameSingular,
|
||||
disabled = false,
|
||||
}: SettingsDataModelFieldRelationSettingsFormCardProps) => {
|
||||
const { watch } = useFormContext<
|
||||
SettingsDataModelFieldRelationFormValues &
|
||||
SettingsDataModelFieldEditFormValues
|
||||
>();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const relationObjectMetadataId = watch('relation.objectMetadataId');
|
||||
const relationObjectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === relationObjectMetadataId,
|
||||
);
|
||||
const relationTargetField = watch('relation.field');
|
||||
|
||||
const relationType: RelationType = watch('relation.type');
|
||||
const relationTypeConfig = RELATION_TYPES[relationType];
|
||||
|
||||
const oppositeRelationType =
|
||||
relationType === RelationType.MANY_TO_ONE
|
||||
? RelationType.ONE_TO_MANY
|
||||
: RelationType.MANY_TO_ONE;
|
||||
|
||||
return (
|
||||
<SettingsDataModelPreviewFormCard
|
||||
preview={
|
||||
<SettingsDataModelFieldRelationPreviewContent isMobile={isMobile}>
|
||||
<SettingsDataModelRelationFieldPreviewSubWidget
|
||||
fieldMetadataItem={{
|
||||
icon: watch('icon'),
|
||||
label: watch('label'),
|
||||
type: FieldMetadataType.RELATION,
|
||||
settings: {
|
||||
relationType: relationType,
|
||||
},
|
||||
}}
|
||||
shrink
|
||||
objectNameSingulars={[objectNameSingular]}
|
||||
fieldPreviewTargetObjectNameSingular={
|
||||
relationObjectMetadataItem?.nameSingular ?? 'company'
|
||||
}
|
||||
pluralizeLabel={relationType === RelationType.ONE_TO_MANY}
|
||||
/>
|
||||
<SettingsDataModelRelationPreviewImage
|
||||
src={relationTypeConfig?.imageSrc}
|
||||
flip={relationTypeConfig?.isImageFlipped}
|
||||
alt={relationTypeConfig?.label}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
<SettingsDataModelRelationFieldPreviewSubWidget
|
||||
fieldMetadataItem={{
|
||||
icon: relationTargetField?.icon,
|
||||
label: relationTargetField?.label,
|
||||
type: FieldMetadataType.RELATION,
|
||||
settings: {
|
||||
relationType: oppositeRelationType,
|
||||
},
|
||||
}}
|
||||
shrink
|
||||
objectNameSingulars={[
|
||||
relationObjectMetadataItem?.nameSingular ??
|
||||
CoreObjectNameSingular.Company,
|
||||
]}
|
||||
fieldPreviewTargetObjectNameSingular={objectNameSingular}
|
||||
pluralizeLabel={oppositeRelationType === RelationType.ONE_TO_MANY}
|
||||
/>
|
||||
</SettingsDataModelFieldRelationPreviewContent>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldRelationForm
|
||||
existingFieldMetadataId={existingFieldMetadataId}
|
||||
objectMetadataItem={relationObjectMetadataItem}
|
||||
disabled={disabled}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { RelationType } from '~/generated-metadata/graphql';
|
||||
|
||||
type UseRelationSettingsFormInitialValuesProps = {
|
||||
existingFieldMetadataId: string;
|
||||
objectMetadataItem?: Pick<
|
||||
ObjectMetadataItem,
|
||||
'id' | 'icon' | 'labelSingular' | 'labelPlural'
|
||||
>;
|
||||
};
|
||||
|
||||
export const useRelationSettingsFormInitialValues = ({
|
||||
existingFieldMetadataId,
|
||||
objectMetadataItem,
|
||||
}: UseRelationSettingsFormInitialValuesProps) => {
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
const { fieldMetadataItem } = useFieldMetadataItemById(
|
||||
existingFieldMetadataId,
|
||||
);
|
||||
|
||||
const getRelationMetadata = useGetRelationMetadata();
|
||||
const {
|
||||
relationFieldMetadataItem,
|
||||
relationObjectMetadataItem: relationObjectMetadataItemFromFieldMetadata,
|
||||
relationType: relationTypeFromFieldMetadata,
|
||||
} = useMemo(
|
||||
() =>
|
||||
fieldMetadataItem ? getRelationMetadata({ fieldMetadataItem }) : null,
|
||||
[fieldMetadataItem, getRelationMetadata],
|
||||
) ?? {};
|
||||
|
||||
const initialRelationObjectMetadataItem = useMemo(() => {
|
||||
const availableItems = activeObjectMetadataItems.filter(
|
||||
isObjectMetadataAvailableForRelation,
|
||||
);
|
||||
const initialObjectCandidate =
|
||||
relationObjectMetadataItemFromFieldMetadata ??
|
||||
objectMetadataItem ??
|
||||
availableItems[0];
|
||||
if (!isDefined(initialObjectCandidate)) {
|
||||
throw new Error(
|
||||
'Relation Form initialization error: invariant violated – no valid object available for relation (this should never happen).',
|
||||
);
|
||||
}
|
||||
return initialObjectCandidate;
|
||||
}, [
|
||||
objectMetadataItem,
|
||||
activeObjectMetadataItems,
|
||||
relationObjectMetadataItemFromFieldMetadata,
|
||||
]);
|
||||
|
||||
const initialRelationType =
|
||||
relationTypeFromFieldMetadata ?? RelationType.ONE_TO_MANY;
|
||||
|
||||
return {
|
||||
disableFieldEdition:
|
||||
isDefined(relationFieldMetadataItem) &&
|
||||
relationFieldMetadataItem?.isCustom === true,
|
||||
disableRelationEdition: !!relationFieldMetadataItem,
|
||||
initialRelationFieldMetadataItem: relationFieldMetadataItem ?? {
|
||||
icon: initialRelationObjectMetadataItem.icon ?? 'IconUsers',
|
||||
label: [RelationType.MANY_TO_ONE].includes(initialRelationType)
|
||||
? initialRelationObjectMetadataItem.labelPlural
|
||||
: initialRelationObjectMetadataItem.labelSingular,
|
||||
},
|
||||
initialRelationObjectMetadataItem,
|
||||
initialRelationType,
|
||||
};
|
||||
};
|
||||
+6
@@ -74,6 +74,7 @@ export const SettingsDataModelRelationFieldPreview = ({
|
||||
|
||||
const recordId = `${relationTargetObjectNameSingular}-${fieldName}-preview`;
|
||||
|
||||
const isRelation = fieldMetadataItem.type === FieldMetadataType.RELATION;
|
||||
const metadata = {
|
||||
fieldName,
|
||||
objectMetadataNameSingular: CoreObjectNameSingular.Company,
|
||||
@@ -107,6 +108,11 @@ export const SettingsDataModelRelationFieldPreview = ({
|
||||
} satisfies FieldMetadataItemRelation,
|
||||
]
|
||||
: [],
|
||||
relationFieldMetadataId: isRelation ? '' : v4(),
|
||||
relationObjectMetadataId: isRelation ? '' : v4(),
|
||||
relationObjectMetadataNamePlural: isRelation
|
||||
? ''
|
||||
: relationTargetObjectMetadataItem.namePlural,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user