Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Introduced new ContactAvatar and ContactAvatarPicker components for enhanced avatar management in contact views. - Updated ContactDetailView and ContactFormView to utilize the new avatar components, improving user experience when adding or editing contacts. - Enhanced ContactHoverCard and ContactRow components to display avatars, providing a more visually appealing interface. - Added loading and error states in ContactsListView for better user feedback during data fetching. - Implemented a new ContactsLoadState component to handle loading and error scenarios in the contacts list. - Updated package.json to include @formkit/auto-animate for improved UI animations.
140 lines
4.6 KiB
TypeScript
140 lines
4.6 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { Loader2, Sparkles } from "lucide-react"
|
|
import { toast } from "sonner"
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog"
|
|
import { Button } from "@/components/ui/button"
|
|
import { useImproveContact } from "@/lib/api/hooks/use-improve-contact"
|
|
import { useUpdateContact } from "@/lib/api/hooks/use-contact-mutations"
|
|
import { fullContactToApiContact } from "@/lib/api/adapters"
|
|
import type { ApiEnrichedContactData } from "@/lib/contacts/discovery-types"
|
|
import {
|
|
fullContactToImprovePayload,
|
|
improvedDataToDraftFields,
|
|
mergeImprovedContact,
|
|
} from "@/lib/contacts/improve-contact"
|
|
import { contactApiPath } from "@/lib/contacts/contact-api-path"
|
|
import type { FullContact } from "@/lib/contacts/types"
|
|
import { DiscoveryFieldChips } from "@/components/gmail/contacts-page/discovery-field-chips"
|
|
import {
|
|
CONTACTS_MUTED_TEXT,
|
|
CONTACTS_PRIMARY_BTN_CLASS,
|
|
} from "@/lib/contacts-chrome-classes"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
interface ContactImproveDialogProps {
|
|
contact: FullContact
|
|
open: boolean
|
|
onOpenChange: (open: boolean) => void
|
|
}
|
|
|
|
export function ContactImproveDialog({ contact, open, onOpenChange }: ContactImproveDialogProps) {
|
|
const improveMutation = useImproveContact()
|
|
const updateMutation = useUpdateContact()
|
|
const [improved, setImproved] = useState<ApiEnrichedContactData | null>(null)
|
|
|
|
useEffect(() => {
|
|
if (!open) {
|
|
setImproved(null)
|
|
improveMutation.reset()
|
|
return
|
|
}
|
|
const payload = fullContactToImprovePayload(contact)
|
|
improveMutation.mutate(payload, {
|
|
onSuccess: (data) => setImproved(data),
|
|
onError: (err) => {
|
|
const msg = err instanceof Error ? err.message : "Échec de l'amélioration IA"
|
|
toast.error(msg)
|
|
},
|
|
})
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- relancer à l'ouverture pour ce contact
|
|
}, [open, contact.id])
|
|
|
|
const loading = improveMutation.isPending
|
|
const chipItems = improved ? improvedDataToDraftFields(improved) : []
|
|
|
|
function handleApply() {
|
|
if (!improved) return
|
|
const merged = mergeImprovedContact(contact, improved)
|
|
const path = contactApiPath(contact)
|
|
if (!contact.etag) {
|
|
toast.error("Impossible d'enregistrer : version du contact inconnue. Rechargez la liste.")
|
|
return
|
|
}
|
|
updateMutation.mutate(
|
|
{ path, etag: contact.etag, contact: fullContactToApiContact(merged) },
|
|
{
|
|
onSuccess: () => {
|
|
toast.success("Contact mis à jour")
|
|
onOpenChange(false)
|
|
},
|
|
onError: (err) => {
|
|
const msg =
|
|
err instanceof Error && err.message ? err.message : "Impossible d'enregistrer les modifications"
|
|
toast.error(msg)
|
|
},
|
|
},
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="sm:max-w-lg">
|
|
<DialogHeader>
|
|
<DialogTitle className="flex items-center gap-2">
|
|
<Sparkles className="h-4 w-4 text-amber-500" />
|
|
Amélioration IA
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
{loading && (
|
|
<div className={cn("flex items-center justify-center gap-2 py-8 text-sm", CONTACTS_MUTED_TEXT)}>
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
Analyse et nettoyage en cours…
|
|
</div>
|
|
)}
|
|
|
|
{!loading && improveMutation.isError && (
|
|
<p className={cn("py-4 text-sm", CONTACTS_MUTED_TEXT)}>
|
|
L'amélioration a échoué. Vérifiez la configuration LLM dans les réglages.
|
|
</p>
|
|
)}
|
|
|
|
{!loading && improved && (
|
|
<div className="space-y-3">
|
|
<p className={cn("text-sm", CONTACTS_MUTED_TEXT)}>
|
|
Aperçu des informations nettoyées et réorganisées. Aucune donnée n'est
|
|
enregistrée tant que vous n'appliquez pas les changements.
|
|
</p>
|
|
<DiscoveryFieldChips items={chipItems} editable={false} />
|
|
</div>
|
|
)}
|
|
|
|
<DialogFooter className="gap-3 sm:gap-3">
|
|
<Button type="button" variant="link" onClick={() => onOpenChange(false)}>
|
|
Annuler
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
disabled={!improved || loading || updateMutation.isPending}
|
|
onClick={handleApply}
|
|
className={CONTACTS_PRIMARY_BTN_CLASS}
|
|
>
|
|
{updateMutation.isPending ? (
|
|
<Loader2 className="mr-1.5 h-4 w-4 animate-spin" />
|
|
) : null}
|
|
Appliquer
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|