From 003b04e9aed7dfe867c14a381852002f3e9639e8 Mon Sep 17 00:00:00 2001 From: Harshit Singh <73997189+harshit078@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:03:24 +0530 Subject: [PATCH] fix: removeuseMergeRecordRelatationship and simplify dry run response (#15486) ## Description - This PR addresses issue https://github.com/twentyhq/twenty/issues/15201 - Removed [useMergeRecordRelationships.ts](https://github.com/twentyhq/twenty/compare/main...harshit078:fix-merge-frontend?expand=1#diff-5d0366f814ff0c0e8961f40120690206a7acd3f7b66135cfb8c4ad8d3f6bc681) - now backend returns populated result and frontend just shows it without refetching relations. - to be merged after PR- https://github.com/twentyhq/twenty/pull/15484 ## Visual Appearance https://github.com/user-attachments/assets/09d7548a-74a9-4742-af49-e98b4174f68c --------- Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> --- .../components/MergePreviewTab.tsx | 4 +- .../record-merge/hooks/useMergePreview.ts | 19 +-- .../hooks/useMergeRecordRelationships.ts | 159 ------------------ .../common-merge-many-query-runner.service.ts | 20 ++- 4 files changed, 28 insertions(+), 174 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergeRecordRelationships.ts diff --git a/packages/twenty-front/src/modules/object-record/record-merge/components/MergePreviewTab.tsx b/packages/twenty-front/src/modules/object-record/record-merge/components/MergePreviewTab.tsx index 728b8c1b4c..af5c92a154 100644 --- a/packages/twenty-front/src/modules/object-record/record-merge/components/MergePreviewTab.tsx +++ b/packages/twenty-front/src/modules/object-record/record-merge/components/MergePreviewTab.tsx @@ -18,11 +18,11 @@ export const MergePreviewTab = ({ objectNameSingular, }); - if (!isDefined(mergePreviewRecord) && !isGeneratingPreview) { + if (!isDefined(mergePreviewRecord) || isGeneratingPreview) { return null; } - const recordId = mergePreviewRecord?.id ?? 'merge-preview-loading'; + const recordId = mergePreviewRecord.id; return ( { const fetchPreview = async () => { if (selectedRecords.length < 2 || isMergeInProgress || isInitialized) @@ -67,8 +61,13 @@ export const useMergePreview = ({ setMergePreviewRecord(null); return; } - setMergePreviewRecord(previewRecord); - upsertRecordsInStore([previewRecord]); + + const transformPreviewRecord = getRecordFromRecordNode({ + recordNode: previewRecord, + }); + + setMergePreviewRecord(transformPreviewRecord); + upsertRecordsInStore([transformPreviewRecord]); } catch { setMergePreviewRecord(null); } finally { @@ -92,6 +91,6 @@ export const useMergePreview = ({ return { selectedRecords, mergePreviewRecord, - isGeneratingPreview: isGeneratingPreview || isLoadingRelationships, + isGeneratingPreview: isGeneratingPreview, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergeRecordRelationships.ts b/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergeRecordRelationships.ts deleted file mode 100644 index 3f876f5a68..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-merge/hooks/useMergeRecordRelationships.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { useLazyQuery } from '@apollo/client'; -import { useEffect, useMemo, useState } from 'react'; -import { useRecoilCallback } from 'recoil'; - -import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection'; -import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult'; -import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery'; -import { mergeRecordRelationshipData } from '@/object-record/record-merge/utils/mergeRelationshipData'; -import { buildFindOneRecordForShowPageOperationSignature } from '@/object-record/record-show/graphql/operations/factories/findOneRecordForShowPageOperationSignatureFactory'; -import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; -import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; - -type UseMergeRecordRelationshipsProps = { - objectNameSingular: string; - previewRecordId: string; - selectedRecords: ObjectRecord[]; -}; - -type MergedRelationshipData = Record< - string, - ObjectRecord[] | ObjectRecord | null ->; - -type UseMergeRecordRelationshipsResult = { - isLoading: boolean; - mergedRelationshipData: MergedRelationshipData; - error?: Error; -}; - -export const useMergeRecordRelationships = ({ - objectNameSingular, - previewRecordId, - selectedRecords, -}: UseMergeRecordRelationshipsProps): UseMergeRecordRelationshipsResult => { - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular, - }); - const { objectMetadataItems } = useObjectMetadataItems(); - const [completeRecords, setCompleteRecords] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(); - - const recordGqlFields = useMemo(() => { - return buildFindOneRecordForShowPageOperationSignature({ - objectMetadataItem, - objectMetadataItems, - }).fields; - }, [objectMetadataItem, objectMetadataItems]); - - const apolloCoreClient = useApolloCoreClient(); - - const { findManyRecordsQuery } = useFindManyRecordsQuery({ - objectNameSingular, - recordGqlFields, - }); - - const [findManyRecords] = useLazyQuery( - findManyRecordsQuery, - { - fetchPolicy: 'cache-first', - client: apolloCoreClient, - }, - ); - - useEffect(() => { - if (selectedRecords.length === 0 || !previewRecordId) { - return; - } - - const fetchCompleteRecords = async () => { - setIsLoading(true); - setError(undefined); - - try { - const result = await findManyRecords({ - variables: { - filter: { - id: { - in: selectedRecords.map((record) => record.id), - }, - }, - }, - }); - - const records = getRecordsFromRecordConnection({ - recordConnection: { - edges: result?.data?.[objectMetadataItem.namePlural]?.edges ?? [], - pageInfo: result?.data?.[objectMetadataItem.namePlural] - ?.pageInfo ?? { - hasNextPage: false, - hasPreviousPage: false, - startCursor: '', - endCursor: '', - }, - }, - }); - - setCompleteRecords(records || []); - } catch (fetchError) { - const errorMessage = - fetchError instanceof Error - ? fetchError.message - : 'Failed to fetch complete records'; - setError( - new Error(`Failed to merge record relationships: ${errorMessage}`), - ); - } finally { - setIsLoading(false); - } - }; - - fetchCompleteRecords(); - }, [ - selectedRecords, - findManyRecords, - previewRecordId, - objectMetadataItem.namePlural, - ]); - - const mergedRelationshipData = useMemo((): MergedRelationshipData => { - return mergeRecordRelationshipData( - completeRecords, - objectMetadataItem.fields, - isLoading, - ); - }, [completeRecords, objectMetadataItem.fields, isLoading]); - - const updatePreviewRecordStore = useRecoilCallback( - ({ set }) => - async (relationshipData: MergedRelationshipData) => { - if (Object.keys(relationshipData).length === 0) return; - - set(recordStoreFamilyState(previewRecordId), (prevRecord) => { - if (!prevRecord) return prevRecord; - - return { - ...prevRecord, - ...relationshipData, - }; - }); - }, - [previewRecordId], - ); - - useEffect(() => { - if (!isLoading && Object.keys(mergedRelationshipData).length > 0) { - updatePreviewRecordStore(mergedRelationshipData); - } - }, [isLoading, mergedRelationshipData, updatePreviewRecordStore]); - - return { - isLoading, - mergedRelationshipData, - error, - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts index c713ac7c80..33059e504e 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts @@ -134,9 +134,6 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ const recordsToMerge = await context.repository.find({ where: { id: In(args.ids) }, select: columnsToSelect, - ...(args.dryRun - ? { relations: args.selectedFieldsResult.relations } - : {}), }); if (recordsToMerge.length !== args.ids.length) { @@ -146,6 +143,23 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ ); } + if (args.dryRun && args.selectedFieldsResult.relations) { + await this.processNestedRelationsHelper.processNestedRelations({ + objectMetadataMaps: context.objectMetadataMaps, + parentObjectMetadataItem: context.objectMetadataItemWithFieldMaps, + parentObjectRecords: recordsToMerge as ObjectRecord[], + relations: args.selectedFieldsResult.relations as Record< + string, + FindOptionsRelations + >, + limit: QUERY_MAX_RECORDS, + authContext: context.authContext, + workspaceDataSource: context.workspaceDataSource, + rolePermissionConfig: context.rolePermissionConfig, + selectedFields: args.selectedFieldsResult.select, + }); + } + return recordsToMerge as ObjectRecord[]; }