89 lines
2.3 KiB
TypeScript
89 lines
2.3 KiB
TypeScript
|
|
/**
|
||
|
|
* Deduplication / merge hooks for contacts (Task 5.23).
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||
|
|
import { apiPost, apiGet } from './client';
|
||
|
|
|
||
|
|
export interface DuplicateContactBrief {
|
||
|
|
id: string;
|
||
|
|
type: string;
|
||
|
|
displayname: string;
|
||
|
|
name: string | null;
|
||
|
|
firstname: string | null;
|
||
|
|
surname: string | null;
|
||
|
|
email_1: string | null;
|
||
|
|
email_2: string | null;
|
||
|
|
phone_1: string | null;
|
||
|
|
phone_2: string | null;
|
||
|
|
mailing_city: string | null;
|
||
|
|
mailing_postalcode: string | null;
|
||
|
|
created_at: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface DuplicatePair {
|
||
|
|
source_contact: DuplicateContactBrief;
|
||
|
|
target_contact: DuplicateContactBrief;
|
||
|
|
similarity_score: number;
|
||
|
|
match_reasons: string[];
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MergeHistoryItem {
|
||
|
|
id: string;
|
||
|
|
source_contact_id: string;
|
||
|
|
target_contact_id: string;
|
||
|
|
merged_fields: Record<string, unknown>;
|
||
|
|
merged_by: string | null;
|
||
|
|
note: string | null;
|
||
|
|
created_at: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MergeHistoryResponse {
|
||
|
|
items: MergeHistoryItem[];
|
||
|
|
total: number;
|
||
|
|
page: number;
|
||
|
|
page_size: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MergeRequest {
|
||
|
|
source_contact_id: string;
|
||
|
|
target_contact_id: string;
|
||
|
|
field_overrides?: Record<string, unknown>;
|
||
|
|
note?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useFindDuplicates() {
|
||
|
|
const queryClient = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: (params: { threshold?: number; limit?: number }) =>
|
||
|
|
apiPost<DuplicatePair[]>('/contacts/duplicates', {
|
||
|
|
threshold: params.threshold ?? 0.7,
|
||
|
|
limit: params.limit ?? 50,
|
||
|
|
}),
|
||
|
|
onSuccess: () => {
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['duplicates'] });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useMergeContacts() {
|
||
|
|
const queryClient = useQueryClient();
|
||
|
|
return useMutation({
|
||
|
|
mutationFn: (data: MergeRequest) =>
|
||
|
|
apiPost('/contacts/merge', data),
|
||
|
|
onSuccess: () => {
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['contacts'] });
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['duplicates'] });
|
||
|
|
queryClient.invalidateQueries({ queryKey: ['mergeHistory'] });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useMergeHistory(page = 1, pageSize = 20) {
|
||
|
|
return useQuery({
|
||
|
|
queryKey: ['mergeHistory', page, pageSize],
|
||
|
|
queryFn: () =>
|
||
|
|
apiGet<MergeHistoryResponse>(`/contacts/merge-history?page=${page}&page_size=${pageSize}`),
|
||
|
|
});
|
||
|
|
}
|