'use client'; import { useState } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Archive, Mail, MessageCircle, Phone, RotateCcw } from 'lucide-react'; import { format } from 'date-fns'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { TagBadge } from '@/components/shared/tag-badge'; import { ArchiveConfirmDialog } from '@/components/shared/archive-confirm-dialog'; import { DetailHeaderStrip } from '@/components/shared/detail-header-strip'; import { PortalInviteButton } from '@/components/clients/portal-invite-button'; import { GdprExportButton } from '@/components/clients/gdpr-export-button'; import { apiFetch } from '@/lib/api/client'; import { cn } from '@/lib/utils'; import { getCountryName } from '@/lib/i18n/countries'; interface ClientDetailHeaderProps { client: { id: string; fullName: string; nationalityIso?: string | null; archivedAt?: string | null; createdAt?: string; contacts?: Array<{ channel: string; value: string; valueE164?: string | null; isPrimary: boolean; label?: string | null; }>; tags?: Array<{ id: string; name: string; color: string }>; clientPortalEnabled?: boolean; }; } export function ClientDetailHeader({ client }: ClientDetailHeaderProps) { const queryClient = useQueryClient(); const [archiveOpen, setArchiveOpen] = useState(false); const isArchived = !!client.archivedAt; const archiveMutation = useMutation({ mutationFn: () => apiFetch(`/api/v1/clients/${client.id}`, { method: 'DELETE' }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['clients', client.id] }); queryClient.invalidateQueries({ queryKey: ['clients'] }); setArchiveOpen(false); }, }); const restoreMutation = useMutation({ mutationFn: () => apiFetch(`/api/v1/clients/${client.id}/restore`, { method: 'POST' }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['clients', client.id] }); queryClient.invalidateQueries({ queryKey: ['clients'] }); setArchiveOpen(false); }, }); const primaryEmail = client.contacts?.find((c) => c.channel === 'email' && c.isPrimary)?.value ?? client.contacts?.find((c) => c.channel === 'email')?.value; const primaryPhoneContact = client.contacts?.find((c) => c.channel === 'phone' && c.isPrimary) ?? client.contacts?.find((c) => c.channel === 'phone'); const primaryPhone = primaryPhoneContact?.value; // wa.me requires the E.164 number without the leading "+". Strip from the // canonical E.164 form when available; otherwise strip non-digits from the // display value as a best-effort fallback. const whatsappNumber = primaryPhoneContact?.valueE164 ? primaryPhoneContact.valueE164.replace(/^\+/, '') : primaryPhoneContact?.value ? primaryPhoneContact.value.replace(/[^\d]/g, '') : null; const country = client.nationalityIso ? getCountryName(client.nationalityIso, 'en') : null; const addedLabel = client.createdAt ? `Added ${format(new Date(client.createdAt), 'MMM d, yyyy')}` : null; const meta = [country, addedLabel].filter(Boolean) as string[]; return ( <> {client.fullName} {isArchived && ( Archived )} {meta.length > 0 ? ( {meta.join(' ยท ')} ) : null} {primaryEmail ? ( Email ) : null} {primaryPhone ? ( Call ) : null} {whatsappNumber ? ( WhatsApp ) : null} {!isArchived && client.clientPortalEnabled !== false ? ( ) : null} {client.tags && client.tags.length > 0 && ( {client.tags.map((tag) => ( ))} )} {/* Top-right: archive/restore as a small icon button - destructive action sits out of the primary action flow. */} setArchiveOpen(true)} aria-label={isArchived ? 'Restore client' : 'Archive client'} title={isArchived ? 'Restore client' : 'Archive client'} className={cn( 'shrink-0 rounded-md p-1.5 text-muted-foreground/70 transition-colors', 'hover:bg-foreground/5 hover:text-foreground', isArchived ? 'hover:text-foreground' : 'hover:text-destructive', )} > {isArchived ? : } { if (isArchived) { restoreMutation.mutate(); } else { archiveMutation.mutate(); } }} isLoading={archiveMutation.isPending || restoreMutation.isPending} /> > ); }
{meta.join(' ยท ')}