From 16a24c5f0c13d1a0abaef0c11e992c482cd0f87b Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Mon, 15 Jan 2024 12:07:23 +0100 Subject: [PATCH] Rework relations (#3431) * Rework relations * Fix tests --- .../src/generated-metadata/gql.ts | 4 +- .../src/generated-metadata/graphql.ts | 38 ++- .../object-metadata/graphql/queries.ts | 2 + .../useMapFieldMetadataToGraphQLQuery.ts | 20 ++ .../types/FieldMetadataItem.ts | 4 +- .../types/StandardObjectNameSingular.ts | 5 - .../isObjectMetadataAvailableForRelation.ts | 11 + .../object-metadata/utils/isStandardObject.ts | 11 - .../components/RecordShowPage.tsx | 25 +- .../components/RelationFieldDisplay.tsx | 4 +- .../field/meta-types/hooks/useChipField.ts | 4 +- .../RecordRelationFieldCardSection.tsx | 105 ++++---- .../SingleEntitySelect.stories.tsx | 7 +- .../hooks/useEntitySelectSearch.ts | 4 +- .../SettingsObjectFieldRelationForm.tsx | 13 +- .../components/ShowPageRightContainer.tsx | 17 +- .../testing/mock-data/objectMetadataItems.ts | 6 + .../__tests__/filter-input.factory.spec.ts | 2 +- .../__tests__/order-by-input.factory.spec.ts | 2 +- .../utils/fields.utils.ts | 5 +- .../quick-actions/quick-actions.service.ts | 35 ++- .../src/database/typeorm/typeorm.service.ts | 2 - .../currency.composite-type.ts | 3 + .../full-name.composite-type.ts | 3 + .../composite-types/link.composite-type.ts | 3 + .../field-metadata/field-metadata.service.ts | 5 +- .../interfaces/object-metadata.interface.ts | 3 + .../object-metadata.service.ts | 44 ++-- .../relation-metadata.service.ts | 17 +- .../clean-inactive-workspace.job.ts | 3 +- .../utils/compute-object-target-table.util.ts | 9 + .../object-metadata-health.service.ts | 35 +-- .../workspace-health.service.ts | 3 +- .../pg-graphql-query-builder.spec.ts | 237 ------------------ .../factories/create-many-query.factory.ts | 7 +- .../factories/delete-many-query.factory.ts | 7 +- .../factories/delete-one-query.factory.ts | 6 +- .../factories/find-many-query.factory.ts | 3 +- .../factories/find-one-query.factory.ts | 4 +- .../factories/relation-field-alias.factory.ts | 15 +- .../factories/update-many-query.factory.ts | 3 +- .../factories/update-one-query.factory.ts | 7 +- ...rkspace-query-builder-options.interface.ts | 2 +- .../query-runner-optionts.interface.ts | 2 +- .../jobs/call-webhook-jobs.job.ts | 5 +- .../utils/parse-result.util.ts | 2 + .../workspace-query-runner.service.ts | 53 ++-- .../factories/create-many-resolver.factory.ts | 2 +- .../factories/create-one-resolver.factory.ts | 2 +- .../factories/delete-many-resolver.factory.ts | 2 +- .../factories/delete-one-resolver.factory.ts | 2 +- ...te-quick-action-on-one-resolver.factory.ts | 6 +- .../factories/find-many-resolver.factory.ts | 2 +- .../factories/find-one-resolver.factory.ts | 2 +- .../factories/update-many-resolver.factory.ts | 2 +- .../factories/update-one-resolver.factory.ts | 2 +- .../workspace-resolver.factory.ts | 4 +- ...kspace-schema-builder-context.interface.ts | 2 +- .../decorators/object-metadata.decorator.ts | 2 +- .../workspace-sync.metadata.service.ts | 18 +- 60 files changed, 392 insertions(+), 463 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-metadata/types/StandardObjectNameSingular.ts create mode 100644 packages/twenty-front/src/modules/object-metadata/utils/isObjectMetadataAvailableForRelation.ts delete mode 100644 packages/twenty-front/src/modules/object-metadata/utils/isStandardObject.ts create mode 100644 packages/twenty-server/src/workspace/utils/compute-object-target-table.util.ts delete mode 100644 packages/twenty-server/src/workspace/workspace-query-builder/__tests__/pg-graphql-query-builder.spec.ts diff --git a/packages/twenty-front/src/generated-metadata/gql.ts b/packages/twenty-front/src/generated-metadata/gql.ts index fd3b42a26..00721794e 100644 --- a/packages/twenty-front/src/generated-metadata/gql.ts +++ b/packages/twenty-front/src/generated-metadata/gql.ts @@ -20,7 +20,7 @@ const documents = { "\n mutation UpdateOneObjectMetadataItem(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n }\n }\n": types.UpdateOneObjectMetadataItemDocument, "\n mutation DeleteOneObjectMetadataItem($idToDelete: ID!) {\n deleteOneObject(input: { id: $idToDelete }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n }\n }\n": types.DeleteOneObjectMetadataItemDocument, "\n mutation DeleteOneFieldMetadataItem($idToDelete: ID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n": types.DeleteOneFieldMetadataItemDocument, - "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n": types.ObjectMetadataItemsDocument, + "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n": types.ObjectMetadataItemsDocument, }; /** @@ -68,7 +68,7 @@ export function graphql(source: "\n mutation DeleteOneFieldMetadataItem($idToDe /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"): (typeof documents)["\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"]; +export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"): (typeof documents)["\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n toFieldMetadataId\n }\n toRelationMetadata {\n id\n relationType\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n isSystem\n }\n fromFieldMetadataId\n }\n defaultValue\n options\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"]; export function graphql(source: string) { return (documents as any)[source] ?? {}; diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 1bf3601ee..c647e4a64 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -20,6 +20,8 @@ export type Scalars = { DateTime: { input: any; output: any; } /** The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */ JSON: { input: any; output: any; } + /** The `Upload` scalar type represents a file upload. */ + Upload: { input: any; output: any; } }; export type AuthProviders = { @@ -182,6 +184,13 @@ export enum FieldMetadataType { Uuid = 'UUID' } +export enum FileFolder { + Attachment = 'Attachment', + PersonPicture = 'PersonPicture', + ProfilePicture = 'ProfilePicture', + WorkspaceLogo = 'WorkspaceLogo' +} + export type FullName = { __typename?: 'FullName'; firstName: Scalars['String']['output']; @@ -213,8 +222,12 @@ export type Mutation = { deleteOneField: FieldDeleteResponse; deleteOneObject: ObjectDeleteResponse; deleteOneRelation: RelationDeleteResponse; + deleteUser: User; updateOneField: Field; updateOneObject: Object; + uploadFile: Scalars['String']['output']; + uploadImage: Scalars['String']['output']; + uploadProfilePicture: Scalars['String']['output']; }; @@ -257,6 +270,23 @@ export type MutationUpdateOneObjectArgs = { input: UpdateOneObjectInput; }; + +export type MutationUploadFileArgs = { + file: Scalars['Upload']['input']; + fileFolder?: InputMaybe; +}; + + +export type MutationUploadImageArgs = { + file: Scalars['Upload']['input']; + fileFolder?: InputMaybe; +}; + + +export type MutationUploadProfilePictureArgs = { + file: Scalars['Upload']['input']; +}; + export type ObjectConnection = { __typename?: 'ObjectConnection'; /** Array of edges. */ @@ -310,6 +340,7 @@ export type PageInfo = { export type Query = { __typename?: 'Query'; + currentUser: User; field: Field; fields: FieldConnection; object: Object; @@ -397,7 +428,7 @@ export enum RelationMetadataType { export type Sentry = { __typename?: 'Sentry'; - dsn: Scalars['String']['output']; + dsn?: Maybe; }; export type Support = { @@ -464,6 +495,7 @@ export type User = { id: Scalars['ID']['output']; lastName: Scalars['String']['output']; passwordHash?: Maybe; + supportUserHash?: Maybe; updatedAt: Scalars['DateTime']['output']; workspaceMember: WorkspaceMember; }; @@ -666,7 +698,7 @@ export type ObjectMetadataItemsQueryVariables = Exact<{ }>; -export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', totalCount: number, edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, fields: { __typename?: 'ObjectFieldsConnection', totalCount: number, edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: string, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, fromRelationMetadata?: { __typename?: 'relation', id: string, relationType: RelationMetadataType, toFieldMetadataId: string, toObjectMetadata: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string } } | null, toRelationMetadata?: { __typename?: 'relation', id: string, relationType: RelationMetadataType, fromFieldMetadataId: string, fromObjectMetadata: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } }; +export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', totalCount: number, edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, fields: { __typename?: 'ObjectFieldsConnection', totalCount: number, edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: string, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, fromRelationMetadata?: { __typename?: 'relation', id: string, relationType: RelationMetadataType, toFieldMetadataId: string, toObjectMetadata: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string, isSystem: boolean } } | null, toRelationMetadata?: { __typename?: 'relation', id: string, relationType: RelationMetadataType, fromFieldMetadataId: string, fromObjectMetadata: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string, isSystem: boolean } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } }; export const CreateOneObjectMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneObjectMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateOneObjectInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}}]}}]}}]} as unknown as DocumentNode; @@ -676,4 +708,4 @@ export const UpdateOneFieldMetadataItemDocument = {"kind":"Document","definition export const UpdateOneObjectMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneObjectMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateObjectInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"update"},"value":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}}]}}]}}]} as unknown as DocumentNode; export const DeleteOneObjectMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneObjectMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}}]}}]}}]} as unknown as DocumentNode; export const DeleteOneFieldMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneFieldMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode; -export const ObjectMetadataItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ObjectMetadataItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"objectFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"fromRelationMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"relationType"}},{"kind":"Field","name":{"kind":"Name","value":"toObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toFieldMetadataId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toRelationMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"relationType"}},{"kind":"Field","name":{"kind":"Name","value":"fromObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fromFieldMetadataId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const ObjectMetadataItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ObjectMetadataItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"objectFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"fromRelationMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"relationType"}},{"kind":"Field","name":{"kind":"Name","value":"toObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toFieldMetadataId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toRelationMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"relationType"}},{"kind":"Field","name":{"kind":"Name","value":"fromObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fromFieldMetadataId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/packages/twenty-front/src/modules/object-metadata/graphql/queries.ts b/packages/twenty-front/src/modules/object-metadata/graphql/queries.ts index e702d3d16..1c1b34da6 100644 --- a/packages/twenty-front/src/modules/object-metadata/graphql/queries.ts +++ b/packages/twenty-front/src/modules/object-metadata/graphql/queries.ts @@ -46,6 +46,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql` dataSourceId nameSingular namePlural + isSystem } toFieldMetadataId } @@ -57,6 +58,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql` dataSourceId nameSingular namePlural + isSystem } fromFieldMetadataId } diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts index a7c551df1..abf182aaa 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts @@ -54,6 +54,26 @@ export const useMapFieldMetadataToGraphQLQuery = () => { ) .join('\n')} }`; + } else if ( + fieldType === 'RELATION' && + field.toRelationMetadata?.relationType === 'ONE_TO_ONE' + ) { + const relationMetadataItem = objectMetadataItems.find( + (objectMetadataItem) => + objectMetadataItem.id === + (field.toRelationMetadata as any)?.fromObjectMetadata?.id, + ); + + return `${field.name} + { + id + ${(relationMetadataItem?.fields ?? []) + .filter((field) => field.type !== 'RELATION') + .map((field) => + mapFieldMetadataToGraphQLQuery(field, maxDepthForRelations - 1), + ) + .join('\n')} + }`; } else if ( fieldType === 'RELATION' && field.fromRelationMetadata?.relationType === 'ONE_TO_MANY' diff --git a/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItem.ts index 2116e008f..ea4ac9ddf 100644 --- a/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItem.ts +++ b/packages/twenty-front/src/modules/object-metadata/types/FieldMetadataItem.ts @@ -9,7 +9,7 @@ export type FieldMetadataItem = Omit< | (Pick & { toObjectMetadata: Pick< Relation['toObjectMetadata'], - 'id' | 'nameSingular' | 'namePlural' + 'id' | 'nameSingular' | 'namePlural' | 'isSystem' >; }) | null; @@ -17,7 +17,7 @@ export type FieldMetadataItem = Omit< | (Pick & { fromObjectMetadata: Pick< Relation['fromObjectMetadata'], - 'id' | 'nameSingular' | 'namePlural' + 'id' | 'nameSingular' | 'namePlural' | 'isSystem' >; }) | null; diff --git a/packages/twenty-front/src/modules/object-metadata/types/StandardObjectNameSingular.ts b/packages/twenty-front/src/modules/object-metadata/types/StandardObjectNameSingular.ts deleted file mode 100644 index 3d60d3c76..000000000 --- a/packages/twenty-front/src/modules/object-metadata/types/StandardObjectNameSingular.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum StandardObjectNameSingular { - Company = 'company', - Person = 'person', - Opportunity = 'opportunity', -} diff --git a/packages/twenty-front/src/modules/object-metadata/utils/isObjectMetadataAvailableForRelation.ts b/packages/twenty-front/src/modules/object-metadata/utils/isObjectMetadataAvailableForRelation.ts new file mode 100644 index 000000000..e399e09ba --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/isObjectMetadataAvailableForRelation.ts @@ -0,0 +1,11 @@ +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; + +export const isObjectMetadataAvailableForRelation = ( + objectMetadataItem: Pick, +) => { + return ( + !objectMetadataItem.isSystem || + objectMetadataItem.nameSingular === CoreObjectNameSingular.WorkspaceMember + ); +}; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/isStandardObject.ts b/packages/twenty-front/src/modules/object-metadata/utils/isStandardObject.ts deleted file mode 100644 index d5c1e78a2..000000000 --- a/packages/twenty-front/src/modules/object-metadata/utils/isStandardObject.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { StandardObjectNameSingular } from '@/object-metadata/types/StandardObjectNameSingular'; - -export const isStandardObject = (objectNameSingular: string) => { - const standardObjectNames = [ - StandardObjectNameSingular.Company, - StandardObjectNameSingular.Person, - StandardObjectNameSingular.Opportunity, - ] as string[]; - - return standardObjectNames.includes(objectNameSingular); -}; diff --git a/packages/twenty-front/src/modules/object-record/components/RecordShowPage.tsx b/packages/twenty-front/src/modules/object-record/components/RecordShowPage.tsx index 269d32339..79904eaa4 100644 --- a/packages/twenty-front/src/modules/object-record/components/RecordShowPage.tsx +++ b/packages/twenty-front/src/modules/object-record/components/RecordShowPage.tsx @@ -1,9 +1,10 @@ import { useParams } from 'react-router-dom'; -import { useRecoilState } from 'recoil'; +import { useSetRecoilState } from 'recoil'; import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition'; +import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation'; import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType'; import { parseFieldType } from '@/object-metadata/utils/parseFieldType'; import { @@ -62,7 +63,7 @@ export const RecordShowPage = () => { const { favorites, createFavorite, deleteFavorite } = useFavorites(); - const [, setEntityFields] = useRecoilState( + const setEntityFields = useSetRecoilState( entityFieldsFamilyState(objectRecordId ?? ''), ); @@ -274,8 +275,21 @@ export const RecordShowPage = () => { )} {isRelationFieldCardEnabled && - relationFieldMetadataItems.map( - (fieldMetadataItem, index) => ( + relationFieldMetadataItems + .filter((item) => { + const relationObjectMetadataItem = + item.toRelationMetadata + ? item.toRelationMetadata.fromObjectMetadata + : item.fromRelationMetadata?.toObjectMetadata; + + if (!relationObjectMetadataItem) { + return false; + } + return isObjectMetadataAvailableForRelation( + relationObjectMetadataItem, + ); + }) + .map((fieldMetadataItem, index) => ( { > - ), - )} + ))} )} diff --git a/packages/twenty-front/src/modules/object-record/field/meta-types/display/components/RelationFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/field/meta-types/display/components/RelationFieldDisplay.tsx index 09a918151..3db283f15 100644 --- a/packages/twenty-front/src/modules/object-record/field/meta-types/display/components/RelationFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/field/meta-types/display/components/RelationFieldDisplay.tsx @@ -6,7 +6,9 @@ import { useRelationField } from '../../hooks/useRelationField'; export const RelationFieldDisplay = () => { const { fieldValue, fieldDefinition } = useRelationField(); - const { identifiersMapper } = useRelationPicker(); + const { identifiersMapper } = useRelationPicker({ + relationPickerScopeId: 'relation-picker', + }); if (!fieldValue || !fieldDefinition || !identifiersMapper) { return <>; diff --git a/packages/twenty-front/src/modules/object-record/field/meta-types/hooks/useChipField.ts b/packages/twenty-front/src/modules/object-record/field/meta-types/hooks/useChipField.ts index 3e6b093ff..f5da173ee 100644 --- a/packages/twenty-front/src/modules/object-record/field/meta-types/hooks/useChipField.ts +++ b/packages/twenty-front/src/modules/object-record/field/meta-types/hooks/useChipField.ts @@ -19,7 +19,9 @@ export const useChipField = () => { const record = useRecoilValue(entityFieldsFamilyState(entityId)); - const { identifiersMapper } = useRelationPicker(); + const { identifiersMapper } = useRelationPicker({ + relationPickerScopeId: 'relation-picker', + }); return { basePathToShowPage, diff --git a/packages/twenty-front/src/modules/object-record/record-relation-card/components/RecordRelationFieldCardSection.tsx b/packages/twenty-front/src/modules/object-record/record-relation-card/components/RecordRelationFieldCardSection.tsx index 8650ed97c..0df52e980 100644 --- a/packages/twenty-front/src/modules/object-record/record-relation-card/components/RecordRelationFieldCardSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-relation-card/components/RecordRelationFieldCardSection.tsx @@ -19,6 +19,7 @@ import { useUpsertRecordFromState } from '@/object-record/hooks/useUpsertRecordF import { RecordRelationFieldCardContent } from '@/object-record/record-relation-card/components/RecordRelationFieldCardContent'; import { SingleEntitySelectMenuItemsWithSearch } from '@/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch'; import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker'; +import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; import { IconForbid, IconPlus } from '@/ui/display/icon'; @@ -153,12 +154,10 @@ export const RecordRelationFieldCardSection = () => { const { closeDropdown, isDropdownOpen } = useDropdown(dropdownId); - const { - identifiersMapper, - relationPickerSearchFilter, - searchQuery, - setRelationPickerSearchFilter, - } = useRelationPicker(); + const { relationPickerSearchFilter, setRelationPickerSearchFilter } = + useRelationPicker({ relationPickerScopeId: dropdownId }); + + const { identifiersMapper, searchQuery } = useRelationPicker(); const entities = useFilteredSearchEntityQuery({ filters: [ @@ -225,53 +224,55 @@ export const RecordRelationFieldCardSection = () => { return (
- - - {fieldDefinition.label} - {parseFieldRelationType(relationFieldMetadataItem) === - 'TO_ONE_OBJECT' && ( - - All ({relationRecords.length}) - - )} - - - - } - dropdownComponents={ - - } - dropdownHotkeyScope={{ - scope: dropdownId, - }} - /> - - - {!!relationRecords.length && ( - - {relationRecords.slice(0, 5).map((relationRecord, index) => ( - + + + {fieldDefinition.label} + {parseFieldRelationType(relationFieldMetadataItem) === + 'TO_ONE_OBJECT' && ( + + All ({relationRecords.length}) + + )} + + + + } + dropdownComponents={ + + } + dropdownHotkeyScope={{ + scope: dropdownId, + }} /> - ))} - - )} + + + {!!relationRecords.length && ( + + {relationRecords.slice(0, 5).map((relationRecord, index) => ( + + ))} + + )} +
); }; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx index b8efb9036..e9b4f029e 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx @@ -4,6 +4,7 @@ import { expect, userEvent, within } from '@storybook/test'; import { IconUserCircle } from '@/ui/display/icon'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator'; +import { RelationPickerDecorator } from '~/testing/decorators/RelationPickerDecorator'; import { mockedPeopleData } from '~/testing/mock-data/people'; import { sleep } from '~/testing/sleep'; @@ -19,7 +20,11 @@ const entities = mockedPeopleData.map((person) => ({ const meta: Meta = { title: 'UI/Input/RelationPicker/SingleEntitySelect', component: SingleEntitySelect, - decorators: [ComponentDecorator, ComponentWithRecoilScopeDecorator], + decorators: [ + ComponentDecorator, + ComponentWithRecoilScopeDecorator, + RelationPickerDecorator, + ], argTypes: { selectedEntity: { options: entities.map(({ name }) => name), diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useEntitySelectSearch.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useEntitySelectSearch.ts index 1862a615e..379ef7bdb 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useEntitySelectSearch.ts +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useEntitySelectSearch.ts @@ -7,9 +7,7 @@ export const useEntitySelectSearch = () => { setRelationPickerPreselectedId, relationPickerSearchFilter, setRelationPickerSearchFilter, - } = useRelationPicker({ - relationPickerScopeId: 'relation-picker', - }); + } = useRelationPicker(); const debouncedSetSearchFilter = debounce( setRelationPickerSearchFilter, diff --git a/packages/twenty-front/src/modules/settings/data-model/components/SettingsObjectFieldRelationForm.tsx b/packages/twenty-front/src/modules/settings/data-model/components/SettingsObjectFieldRelationForm.tsx index 2504daf9e..ab7bdc403 100644 --- a/packages/twenty-front/src/modules/settings/data-model/components/SettingsObjectFieldRelationForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/components/SettingsObjectFieldRelationForm.tsx @@ -1,6 +1,7 @@ import styled from '@emotion/styled'; import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings'; +import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation'; import { validateMetadataLabel } from '@/object-metadata/utils/validateMetadataLabel'; import { useIcons } from '@/ui/display/icon/hooks/useIcons'; import { IconPicker } from '@/ui/input/components/IconPicker'; @@ -74,13 +75,13 @@ export const SettingsObjectFieldRelationForm = ({ fullWidth disabled={disableRelationEdition} value={values.type} - options={Object.entries(relationTypes).map( - ([value, { label, Icon }]) => ({ + options={Object.entries(relationTypes) + .filter(([value]) => 'ONE_TO_ONE' !== value) + .map(([value, { label, Icon }]) => ({ label, value: value as RelationType, Icon, - }), - )} + }))} onChange={(value) => onChange({ type: value })} />