feat: find duplicate objects init (#4038)
* feat: find duplicate objects backend init * refactor: move duplicate criteria to constants * fix: correct constant usage after type change * feat: skip query generation in case its not necessary * feat: filter out existing duplicate * feat: FE queries and hooks * feat: show duplicates on FE * refactor: should-skip-query moved to workspace utils * refactor: naming improvements * refactor: current record typings/parsing improvements * refactor: throw error if existing record not found * fix: domain -> domainName duplicate criteria * refactor: fieldNames -> columnNames * docs: add explanation to duplicate criteria collection * feat: add person linkedinLinkUrl as duplicate criteria * feat: throw early when bot id and data are empty * refactor: trying to improve readability of filter criteria query * refactor: naming improvements * refactor: remove shouldSkipQuery * feat: resolve empty array in case of empty filter * feat: hide whole section in case of no duplicates * feat: FE display list the same way as relations * test: basic unit test coverage * Refactor Record detail section front * Use Create as input argument of findDuplicates * Improve coverage * Fix --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -0,0 +1,79 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { useMapConnectionToRecords } from '@/object-record/hooks/useMapConnectionToRecords';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { ObjectRecordQueryResult } from '../types/ObjectRecordQueryResult';
|
||||
|
||||
export const useFindDuplicateRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
objectRecordId = '',
|
||||
objectNameSingular,
|
||||
onCompleted,
|
||||
depth,
|
||||
}: ObjectMetadataItemIdentifier & {
|
||||
objectRecordId: string | undefined;
|
||||
onCompleted?: (data: ObjectRecordConnection<T>) => void;
|
||||
skip?: boolean;
|
||||
depth?: number;
|
||||
}) => {
|
||||
const findDuplicateQueryStateIdentifier = objectNameSingular;
|
||||
|
||||
const { objectMetadataItem, findDuplicateRecordsQuery } =
|
||||
useObjectMetadataItem({ objectNameSingular }, depth);
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const { data, loading, error } = useQuery<ObjectRecordQueryResult<T>>(
|
||||
findDuplicateRecordsQuery,
|
||||
{
|
||||
variables: {
|
||||
id: objectRecordId,
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
onCompleted?.(data[objectMetadataItem.nameSingular]);
|
||||
},
|
||||
onError: (error) => {
|
||||
logError(
|
||||
`useFindDuplicateRecords for "${objectMetadataItem.nameSingular}" error : ` +
|
||||
error,
|
||||
);
|
||||
enqueueSnackBar(
|
||||
`Error during useFindDuplicateRecords for "${objectMetadataItem.nameSingular}", ${error.message}`,
|
||||
{
|
||||
variant: 'error',
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const objectRecordConnection =
|
||||
data?.[`${objectMetadataItem.nameSingular}Duplicates`];
|
||||
|
||||
const mapConnectionToRecords = useMapConnectionToRecords();
|
||||
|
||||
const records = useMemo(
|
||||
() =>
|
||||
mapConnectionToRecords({
|
||||
objectRecordConnection,
|
||||
objectNameSingular,
|
||||
depth: 5,
|
||||
}) as T[],
|
||||
[mapConnectionToRecords, objectRecordConnection, objectNameSingular],
|
||||
);
|
||||
|
||||
return {
|
||||
objectMetadataItem,
|
||||
records,
|
||||
totalCount: objectRecordConnection?.totalCount || 0,
|
||||
loading,
|
||||
error,
|
||||
queryStateIdentifier: findDuplicateQueryStateIdentifier,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,42 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useGenerateFindDuplicateRecordsQuery = () => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
return ({
|
||||
objectMetadataItem,
|
||||
depth,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
depth?: number;
|
||||
}) => gql`
|
||||
query FindDuplicate${capitalize(objectMetadataItem.nameSingular)}($id: ID) {
|
||||
${objectMetadataItem.nameSingular}Duplicates(id: $id){
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
${objectMetadataItem.fields
|
||||
.map((field) =>
|
||||
mapFieldMetadataToGraphQLQuery({
|
||||
field,
|
||||
maxDepthForRelations: depth,
|
||||
}),
|
||||
)
|
||||
.join('\n')}
|
||||
}
|
||||
cursor
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
};
|
||||
Reference in New Issue
Block a user