import { useState, useEffect } from "react"; import { Merge, CircleX, AlertTriangle, ArrowDown } from "lucide-react"; import { useDataProvider, useRecordContext, useGetList, useGetManyReference, required, Form, useNotify, useRedirect, useTranslate, } from "ra-core"; import type { Identifier } from "ra-core"; import { useMutation } from "@tanstack/react-query"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@/components/ds/ui/dialog"; import { Button } from "@/components/ds/ui/button"; import { ReferenceInput } from "@/components/ds/admin/reference-input"; import { AutocompleteInput } from "@/components/ds/admin/autocomplete-input"; import { Alert, AlertDescription, AlertTitle } from "@/components/ds/ui/alert"; import type { Contact } from "../types"; import { contactOptionText } from "../misc/ContactOption"; export const ContactMergeButton = () => { const [mergeDialogOpen, setMergeDialogOpen] = useState(false); const translate = useTranslate(); return ( <> setMergeDialogOpen(false)} /> ); }; interface ContactMergeDialogProps { open: boolean; onClose: () => void; } const ContactMergeDialog = ({ open, onClose }: ContactMergeDialogProps) => { const loserContact = useRecordContext(); const notify = useNotify(); const redirect = useRedirect(); const dataProvider = useDataProvider(); const translate = useTranslate(); const [winnerId, setWinnerId] = useState(null); const [suggestedWinnerId, setSuggestedWinnerId] = useState( null, ); const [isMerging, setIsMerging] = useState(false); const { mutateAsync } = useMutation({ mutationKey: ["contacts", "merge", { loserId: loserContact?.id, winnerId }], mutationFn: async () => { return dataProvider.mergeContacts(loserContact?.id, winnerId); }, }); // Find potential contacts with matching first and last name const { data: matchingContacts } = useGetList( "contacts", { filter: { first_name: loserContact?.first_name, last_name: loserContact?.last_name, "id@neq": `${loserContact?.id}`, // Exclude current contact }, pagination: { page: 1, perPage: 10 }, }, { enabled: open && !!loserContact }, ); // Get counts of items to be merged const canFetchCounts = open && !!loserContact && !!winnerId; const { total: tasksCount } = useGetManyReference( "tasks", { target: "contact_id", id: loserContact?.id, pagination: { page: 1, perPage: 1 }, }, { enabled: canFetchCounts }, ); const { total: notesCount } = useGetManyReference( "contactNotes", { target: "contact_id", id: loserContact?.id, pagination: { page: 1, perPage: 1 }, }, { enabled: canFetchCounts }, ); const { total: dealsCount } = useGetList( "deals", { filter: { "contact_ids@cs": `{${loserContact?.id}}` }, pagination: { page: 1, perPage: 1 }, }, { enabled: canFetchCounts }, ); useEffect(() => { if (matchingContacts && matchingContacts.length > 0) { const suggestedWinnerId = matchingContacts[0].id; setSuggestedWinnerId(suggestedWinnerId); setWinnerId(suggestedWinnerId); } }, [matchingContacts]); const handleMerge = async () => { if (!winnerId || !loserContact) { notify(translate("crm.contact.merge.select_contact"), { type: "warning", }); return; } try { setIsMerging(true); await mutateAsync(); setIsMerging(false); notify(translate("crm.contact.merge.success"), { type: "success" }); redirect(`/contacts/${winnerId}/show`); onClose(); } catch (error) { setIsMerging(false); notify(translate("crm.contact.merge.error"), { type: "error" }); console.error("Merge failed:", error); } }; if (!loserContact) return null; return ( {translate("crm.contact.merge.title")} {translate("crm.contact.merge.description")}

{translate("crm.contact.merge.current_contact")}

{contactOptionText}

{translate("crm.contact.merge.target_contact")}

{winnerId && ( <>

{translate("crm.contact.merge.what_will_be_merged")}

    {notesCount != null && notesCount > 0 && (
  • •{" "} {translate("crm.contact.merge.notes_to_merge", { smart_count: notesCount, })}
  • )} {tasksCount != null && tasksCount > 0 && (
  • •{" "} {translate("crm.contact.merge.tasks_to_merge", { smart_count: tasksCount, })}
  • )} {dealsCount != null && dealsCount > 0 && (
  • •{" "} {translate("crm.contact.merge.deals_to_merge", { smart_count: dealsCount, })}
  • )} {loserContact.email_jsonb?.length > 0 && (
  • •{" "} {translate("crm.contact.merge.emails_to_merge", { smart_count: loserContact.email_jsonb.length, })}
  • )} {loserContact.phone_jsonb?.length > 0 && (
  • •{" "} {translate("crm.contact.merge.phones_to_merge", { smart_count: loserContact.phone_jsonb.length, })}
  • )} {!notesCount && !tasksCount && !dealsCount && !loserContact.email_jsonb?.length && !loserContact.phone_jsonb?.length && (
  • {translate("crm.contact.merge.no_data")}
  • )}
{translate("crm.contact.merge.warning_title")} {translate("crm.contact.merge.warning_message")} )}
); };