import { useEffect, useState } from 'react'; import { useParams, useNavigate, Link } from 'react-router-dom'; import { useClientsStore } from '@/stores/clients'; import { api } from '@/lib/api'; import type { Event, Email, ActivityItem, ClientCreate } from '@/types'; import { ArrowLeft, Edit3, Trash2, Phone, Mail, MapPin, Building2, Briefcase, Gift, Heart, Star, Users, Calendar, Send, CheckCircle2, Sparkles, Clock, Activity, FileText, UserPlus, RefreshCw, Paperclip, Target, } from 'lucide-react'; import { usePinnedClients } from '@/hooks/usePinnedClients'; import { cn, formatDate, getRelativeTime, getInitials } from '@/lib/utils'; import Badge, { EventTypeBadge, EmailStatusBadge, StageBadge } from '@/components/Badge'; import { PageLoader } from '@/components/LoadingSpinner'; import Modal from '@/components/Modal'; import ClientForm from '@/components/ClientForm'; import EmailComposeModal from '@/components/EmailComposeModal'; import ClientNotes from '@/components/ClientNotes'; import LogInteractionModal from '@/components/LogInteractionModal'; import MeetingPrepModal from '@/components/MeetingPrepModal'; import EngagementBadge from '@/components/EngagementBadge'; import DuplicatesModal from '@/components/DuplicatesModal'; import ClientDocuments from '@/components/ClientDocuments'; import ClientGoals from '@/components/ClientGoals'; import ClientReferrals from '@/components/ClientReferrals'; import type { Interaction } from '@/types'; export default function ClientDetailPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { selectedClient, isLoading, fetchClient, updateClient, deleteClient, markContacted } = useClientsStore(); const [events, setEvents] = useState([]); const [emails, setEmails] = useState([]); const [activities, setActivities] = useState([]); const [activeTab, setActiveTab] = useState<'info' | 'notes' | 'activity' | 'events' | 'emails' | 'documents' | 'goals' | 'referrals'>('info'); const [, setInteractions] = useState([]); const [showEdit, setShowEdit] = useState(false); const [showCompose, setShowCompose] = useState(false); const [showLogInteraction, setShowLogInteraction] = useState(false); const [showMeetingPrep, setShowMeetingPrep] = useState(false); const [showDuplicates, setShowDuplicates] = useState(false); const [deleting, setDeleting] = useState(false); const { togglePin, isPinned } = usePinnedClients(); useEffect(() => { if (id) { fetchClient(id); api.getEvents({ clientId: id }).then(setEvents).catch(() => {}); api.getEmails({ clientId: id }).then(setEmails).catch(() => {}); api.getClientActivity(id).then(setActivities).catch(() => {}); api.getClientInteractions(id).then(setInteractions).catch(() => {}); } }, [id, fetchClient]); if (isLoading || !selectedClient) return ; const client = selectedClient; const handleDelete = async () => { if (!confirm('Delete this client? This cannot be undone.')) return; setDeleting(true); try { await deleteClient(client.id); navigate('/clients'); } catch { setDeleting(false); } }; const handleMarkContacted = async () => { await markContacted(client.id); }; const handleUpdate = async (data: ClientCreate) => { await updateClient(client.id, data); setShowEdit(false); }; const tabs: { key: typeof activeTab; label: string; count?: number; icon: typeof Users }[] = [ { key: 'info', label: 'Info', icon: Users }, { key: 'notes', label: 'Notes', icon: FileText }, { key: 'documents', label: 'Documents', icon: Paperclip }, { key: 'goals', label: 'Goals', icon: Target }, { key: 'referrals', label: 'Referrals', icon: UserPlus }, { key: 'activity', label: 'Timeline', count: activities.length, icon: Activity }, { key: 'events', label: 'Events', count: events.length, icon: Calendar }, { key: 'emails', label: 'Emails', count: emails.length, icon: Mail }, ]; return (
{/* Header */}
{getInitials(client.firstName, client.lastName)}

{client.firstName} {client.lastName}

{client.company && (

{client.role ? `${client.role} at ` : ''}{client.company}

)}
{ const stages = ['lead', 'prospect', 'onboarding', 'active', 'inactive']; const currentIdx = stages.indexOf(client.stage || 'lead'); const nextStage = stages[(currentIdx + 1) % stages.length]; await updateClient(client.id, { stage: nextStage as ClientCreate['stage'] }); }} /> {client.tags && client.tags.length > 0 && ( client.tags.map((tag) => {tag}) )}
{/* Tabs */}
{tabs.map(({ key, label, count, icon: Icon }) => ( ))}
{/* Tab Content */} {activeTab === 'info' && (
{/* Contact Info */}

Contact Information

{client.email && ( )} {client.phone && (
{client.phone}
)} {(client.street || client.city) && (
{[client.street, client.city, client.state, client.zip].filter(Boolean).join(', ')}
)} {client.company && (
{client.company}
)} {client.industry && (
{client.industry}
)}
Last contacted: {getRelativeTime(client.lastContacted)}
{/* Personal */}

Personal Details

{client.birthday && (
Birthday: {formatDate(client.birthday)}
)} {client.anniversary && (
Anniversary: {formatDate(client.anniversary)}
)} {client.interests && client.interests.length > 0 && (
Interests
{client.interests.map((i) => {i})}
)} {client.family?.spouse && (
Spouse: {client.family.spouse}
)} {client.family?.children && client.family.children.length > 0 && (
Children: {client.family.children.join(', ')}
)}
{/* Notes */} {client.notes && (

Notes

{client.notes}

)}
)} {activeTab === 'notes' && ( )} {activeTab === 'documents' && ( )} {activeTab === 'goals' && ( )} {activeTab === 'referrals' && ( )} {activeTab === 'activity' && (
{activities.length === 0 ? (

No activity recorded yet

) : (
{activities.map((item, index) => { const iconMap: Record = { email_sent: { icon: Send, color: 'text-emerald-600', bg: 'bg-emerald-100 dark:bg-emerald-900/30' }, email_drafted: { icon: FileText, color: 'text-amber-600', bg: 'bg-amber-100 dark:bg-amber-900/30' }, event_created: { icon: Calendar, color: 'text-blue-600', bg: 'bg-blue-100 dark:bg-blue-900/30' }, client_contacted: { icon: CheckCircle2, color: 'text-emerald-600', bg: 'bg-emerald-100 dark:bg-emerald-900/30' }, client_created: { icon: UserPlus, color: 'text-purple-600', bg: 'bg-purple-100 dark:bg-purple-900/30' }, client_updated: { icon: RefreshCw, color: 'text-slate-600', bg: 'bg-slate-100 dark:bg-slate-700' }, interaction: { icon: Phone, color: 'text-indigo-600 dark:text-indigo-400', bg: 'bg-indigo-100 dark:bg-indigo-900/30' }, }; const { icon: Icon, color, bg } = iconMap[item.type] || iconMap.client_updated; return (
{/* Timeline line */} {index < activities.length - 1 && (
)} {/* Icon */}
{/* Content */}

{item.title}

{item.description && (

{item.description}

)}

{formatDate(item.date)}

); })}
)}
)} {activeTab === 'events' && (
{events.length === 0 ? (

No events for this client

) : ( events.map((event) => (

{event.title}

{formatDate(event.date)} {event.recurring && 'ยท Recurring'}

)) )}
)} {activeTab === 'emails' && (
{emails.length === 0 ? (

No emails for this client

) : ( emails.map((email) => (

{email.subject}

{formatDate(email.createdAt)}

)) )}
)} {/* Edit Modal */} setShowEdit(false)} title="Edit Client" size="lg"> {/* Email Compose Modal */} setShowCompose(false)} clientId={client.id} clientName={`${client.firstName} ${client.lastName}`} onGenerated={(email) => setEmails((prev) => [email, ...prev])} /> {/* Meeting Prep Modal */} setShowMeetingPrep(false)} clientId={client.id} clientName={`${client.firstName} ${client.lastName}`} /> {/* Log Interaction Modal */} setShowLogInteraction(false)} clientId={client.id} clientName={`${client.firstName} ${client.lastName}`} onCreated={() => { // Refresh interactions and activity if (id) { api.getClientInteractions(id).then(setInteractions).catch(() => {}); api.getClientActivity(id).then(setActivities).catch(() => {}); fetchClient(id); // refresh lastContactedAt } }} /> {/* Duplicates Modal */} setShowDuplicates(false)} clientId={client.id} clientName={`${client.firstName} ${client.lastName}`} onMerged={() => { if (id) fetchClient(id); }} />
); }