ultisuite-client/components/gmail/contacts-page/contact-improve-dialog.tsx
R3D347HR4Y 07d57f13a8
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
Add Contact Avatar Features and Improve UI Components
- 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.
2026-06-06 20:26:51 +02:00

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&apos;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&apos;est
enregistrée tant que vous n&apos;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>
)
}