Morph many to one picker (#14155)

This PR follows the multiSelect PR merged previously. It will enable
morph relation Many to One to be handled from the table, using a
singleSelect picker

Main point : I decided to change the singleSelect API to take an array
of **objectMetadataName** instead of only one to deal with both our
usecases.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Guillim
2025-10-07 17:25:11 +02:00
committed by GitHub
parent 9f9c2d85ca
commit f60817a1e1
138 changed files with 2116 additions and 1763 deletions
@@ -9,12 +9,13 @@ import {
import { GRAPHQL_TYPENAME_KEY } from '@/object-record/constants/GraphqlTypenameKey';
import { type FieldActorValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { isFieldActor } from '@/object-record/record-field/ui/types/guards/isFieldActor';
import { isFieldMorphRelation } from '@/object-record/record-field/ui/types/guards/isFieldMorphRelation';
import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation';
import { isFieldUuid } from '@/object-record/record-field/ui/types/guards/isFieldUuid';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { buildOptimisticActorFieldValueFromCurrentWorkspaceMember } from '@/object-record/utils/buildOptimisticActorFieldValueFromCurrentWorkspaceMember';
import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/getForeignKeyNameFromRelationFieldName';
import { isDefined } from 'twenty-shared/utils';
import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils';
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
type ComputeOptimisticCacheRecordInputArgs = {
@@ -39,16 +40,34 @@ export const computeOptimisticRecordFromInput = ({
(field) => field.name === recordKey,
);
const potentialJoinColumnNameFieldMetadataItem =
const potentialRelationJoinColumnNameFieldMetadataItem =
objectMetadataItem.fields.find(
(field) =>
field.type === FieldMetadataType.RELATION &&
field.settings?.joinColumnName === recordKey,
);
const potentialMorphRelationJoinColumnNameFieldMetadataItem =
objectMetadataItem.fields.find((field) => {
if (!isFieldMorphRelation(field)) return false;
return field.morphRelations?.some((morphRelation) => {
const computedFieldName = computeMorphRelationFieldName({
fieldName: field.name,
relationType: morphRelation.type,
targetObjectMetadataNameSingular:
morphRelation.targetObjectMetadata.nameSingular,
targetObjectMetadataNamePlural:
morphRelation.targetObjectMetadata.namePlural,
});
return computedFieldName === recordKey.replace('Id', '');
});
});
const isUnknownField =
!isDefined(correspondingFieldMetadataItem) &&
!isDefined(potentialJoinColumnNameFieldMetadataItem);
!isDefined(potentialRelationJoinColumnNameFieldMetadataItem) &&
!isDefined(potentialMorphRelationJoinColumnNameFieldMetadataItem);
const isTypenameField = recordKey === GRAPHQL_TYPENAME_KEY;
return isUnknownField && !isTypenameField;
@@ -1,7 +1,8 @@
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isFieldMorphRelation } from '@/object-record/record-field/ui/types/guards/isFieldMorphRelation';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isSystemSearchVectorField } from '@/object-record/utils/isSystemSearchVectorField';
import { isDefined } from 'twenty-shared/utils';
import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils';
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
export const sanitizeRecordInput = ({
@@ -27,10 +28,26 @@ export const sanitizeRecordInput = ({
field.type === FieldMetadataType.RELATION &&
field.settings?.joinColumnName === fieldName,
);
const potentialMorphRelationJoinColumnNameFieldMetadataItem =
objectMetadataItem.fields.find((field) => {
if (!isFieldMorphRelation(field)) return false;
return field.morphRelations?.some((morphRelation) => {
const computedFieldName = computeMorphRelationFieldName({
fieldName: field.name,
relationType: morphRelation.type,
targetObjectMetadataNameSingular:
morphRelation.targetObjectMetadata.nameSingular,
targetObjectMetadataNamePlural:
morphRelation.targetObjectMetadata.namePlural,
});
return computedFieldName === fieldName.replace('Id', '');
});
});
if (
!isDefined(fieldMetadataItem) &&
!isDefined(potentialJoinColumnNameFieldMetadataItem)
!isDefined(potentialJoinColumnNameFieldMetadataItem) &&
!isDefined(potentialMorphRelationJoinColumnNameFieldMetadataItem)
) {
return undefined;
}