"use client" import { useMemo, useState } from "react" import { toast } from "sonner" import { Button } from "@/components/ui/button" import { useContactsStore } from "@/lib/contacts/contacts-store" import { useContactsList } from "@/lib/contacts/use-contacts-list" import { useMergeContactPair } from "@/lib/api/hooks/use-contact-mutations" import { findDuplicatePairs, mergePairKey, type DuplicateMatchReason } from "@/lib/contacts/duplicate-detection" import { useVisibleEnrichmentSuggestions } from "@/lib/api/hooks/use-contact-discovery" import { fullContactDisplayName, type MergeSuggestion } from "@/lib/contacts/types" import { ContactAvatar } from "@/components/gmail/contacts/contact-avatar" import { AddCoordinatesView } from "./add-coordinates-view" import { DiscoveryCardsMasonry, DiscoveryCardsMasonryItem, } from "@/components/gmail/contacts-page/discovery-cards-masonry" import { CONTACTS_HEADING_TEXT, CONTACTS_MUTED_TEXT, CONTACTS_PAGE_CARD_CLASS, CONTACTS_PAGE_INFO_BANNER_CLASS, CONTACTS_PAGE_INFO_BANNER_ICON_CLASS, CONTACTS_PAGE_LINK_BTN_CLASS, CONTACTS_PAGE_SECTION_TITLE_CLASS, CONTACTS_PAGE_TAB_ACTIVE_CLASS, CONTACTS_PAGE_TAB_INACTIVE_CLASS, CONTACTS_PRIMARY_BTN_CLASS, } from "@/lib/contacts-chrome-classes" import { cn } from "@/lib/utils" type SubView = "merge" | "coordinates" const REASON_LABELS: Record = { email: "Même adresse e-mail", phone: "Même numéro de téléphone", name: "Nom similaire", } export function MergeDuplicatesView() { const [subView, setSubView] = useState("merge") const { contacts, bookId } = useContactsList() const ignoredMergePairs = useContactsStore((s) => s.ignoredMergePairs) const ignoreMergePair = useContactsStore((s) => s.ignoreMergePair) const mergeContactPairMutation = useMergeContactPair() const mergeSuggestions = useMemo( () => findDuplicatePairs(contacts, new Set(ignoredMergePairs)), [contacts, ignoredMergePairs] ) const { suggestions: coordinateSuggestions } = useVisibleEnrichmentSuggestions() const [mergingAll, setMergingAll] = useState(false) const [mergingPairKey, setMergingPairKey] = useState(null) function handleMerge(suggestion: MergeSuggestion) { if (!bookId) { toast.error("Carnet de contacts introuvable") return } if (!suggestion.contactA.etag) { toast.error("Impossible de fusionner : version du contact inconnue. Rechargez la liste.") return } const pairKey = mergePairKey(suggestion.contactA.id, suggestion.contactB.id) setMergingPairKey(pairKey) mergeContactPairMutation.mutate( { bookId, contactA: suggestion.contactA, contactB: suggestion.contactB, }, { onSuccess: () => toast.success("Contacts fusionnés"), onError: (err) => { const msg = err instanceof Error && err.message ? err.message : "Impossible de fusionner ces contacts" toast.error(msg) }, onSettled: () => setMergingPairKey(null), }, ) } function handleIgnore(suggestion: MergeSuggestion) { ignoreMergePair(suggestion.contactA.id, suggestion.contactB.id) } async function handleMergeAll() { if (!bookId) { toast.error("Carnet de contacts introuvable") return } if (mergeSuggestions.length === 0) return setMergingAll(true) try { for (const suggestion of mergeSuggestions) { if (!suggestion.contactA.etag) continue await mergeContactPairMutation.mutateAsync({ bookId, contactA: suggestion.contactA, contactB: suggestion.contactB, }) } toast.success("Doublons fusionnés") } catch (err) { const msg = err instanceof Error && err.message ? err.message : "Impossible de fusionner tous les doublons" toast.error(msg) } finally { setMergingAll(false) } } return (
🧹

Des méthodes simples pour nettoyer vos contacts

Obtenez de l'aide pour fusionner les contacts en double, ajouter des informations utiles, et bien encore

{subView === "merge" && (

Fusionner les doublons ({mergeSuggestions.length})

{mergeSuggestions.length > 0 && ( )}
{mergeSuggestions.length === 0 && (

Aucun doublon détecté

)} {mergeSuggestions.map((suggestion) => { const pairKey = mergePairKey(suggestion.contactA.id, suggestion.contactB.id) const isMerging = mergingPairKey === pairKey return ( handleMerge(suggestion)} onIgnore={() => handleIgnore(suggestion)} /> ) })}
)} {subView === "coordinates" && }
) } function MergeSuggestionCard({ suggestion, merging, onMerge, onIgnore, }: { suggestion: MergeSuggestion merging: boolean onMerge: () => void onIgnore: () => void }) { const { contactA, contactB, reason } = suggestion return (

{REASON_LABELS[reason]}

) } function ContactMiniCard({ contact }: { contact: import("@/lib/contacts/types").FullContact }) { const displayName = fullContactDisplayName(contact) const name = displayName || contact.emails[0]?.value || "?" return (

{name}

{contact.emails[0] && (

{contact.emails[0].value}

)} {contact.phones[0] && (

{contact.phones[0].value} ({contact.phones[0].label})

)}
) }