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>
This commit is contained in:
Harshit Singh
2025-11-05 15:03:24 +05:30
committed by GitHub
parent 642e0c882a
commit 003b04e9ae
4 changed files with 28 additions and 174 deletions
@@ -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 (
<LayoutRenderingProvider
@@ -1,7 +1,7 @@
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode';
import { useMergeManyRecords } from '@/object-record/hooks/useMergeManyRecords';
import { useMergeRecordRelationships } from '@/object-record/record-merge/hooks/useMergeRecordRelationships';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { recordStoreRecordsSelector } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelector';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
@@ -45,12 +45,6 @@ export const useMergePreview = ({
const { upsertRecordsInStore } = useUpsertRecordsInStore();
const { isLoading: isLoadingRelationships } = useMergeRecordRelationships({
objectNameSingular,
previewRecordId: mergePreviewRecord?.id || '',
selectedRecords: selectedRecords,
});
useEffect(() => {
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<ObjectRecord>({
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,
};
};
@@ -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<ObjectRecord[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | undefined>();
const recordGqlFields = useMemo(() => {
return buildFindOneRecordForShowPageOperationSignature({
objectMetadataItem,
objectMetadataItems,
}).fields;
}, [objectMetadataItem, objectMetadataItems]);
const apolloCoreClient = useApolloCoreClient();
const { findManyRecordsQuery } = useFindManyRecordsQuery({
objectNameSingular,
recordGqlFields,
});
const [findManyRecords] = useLazyQuery<RecordGqlOperationFindManyResult>(
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,
};
};
@@ -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<ObjectLiteral>
>,
limit: QUERY_MAX_RECORDS,
authContext: context.authContext,
workspaceDataSource: context.workspaceDataSource,
rolePermissionConfig: context.rolePermissionConfig,
selectedFields: args.selectedFieldsResult.select,
});
}
return recordsToMerge as ObjectRecord[];
}