const { useState, useEffect } = React; function ContactDetail({ contactId }) { const [contact, setContact] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showDraftModal, setShowDraftModal] = useState(false); const [draftSubject, setDraftSubject] = useState(''); const [draftBody, setDraftBody] = useState(''); const [fromIdentity, setFromIdentity] = useState('iain@gmail'); const [interactionSummary, setInteractionSummary] = useState(''); const [talkinPoints, setTalkingPoints] = useState([]); const [generatingPoints, setGeneratingPoints] = useState(false); useEffect(() => { if (!contactId) return; loadContact(); }, [contactId]); const loadContact = async () => { try { const res = await fetch(`/api/contacts/${contactId}`); if (!res.ok) throw new Error('Contact not found'); const data = await res.json(); setContact(data); setLoading(false); } catch (err) { setError(err.message); setLoading(false); } }; const generateTalkingPoints = async () => { if (!contactId) return; setGeneratingPoints(true); try { const res = await fetch(`/api/contacts/${contactId}/talking-points`, { method: 'POST', }); const data = await res.json(); setTalkingPoints(data.talking_points || []); } catch (err) { console.error('Error generating talking points:', err); } finally { setGeneratingPoints(false); } }; const submitDraft = async () => { if (!draftSubject || !draftBody) { alert('Subject and body are required'); return; } try { const res = await fetch('/api/email-drafts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contact_id: contactId, subject: draftSubject, body: draftBody, from_identity: fromIdentity, }), }); const data = await res.json(); alert('Draft created: ' + data.status); setShowDraftModal(false); setDraftSubject(''); setDraftBody(''); } catch (err) { alert('Error creating draft: ' + err.message); } }; const logInteraction = async () => { if (!interactionSummary) { alert('Please enter an interaction summary'); return; } try { const res = await fetch(`/api/contacts/${contactId}/interactions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ interaction_type: 'email', direction: 'outbound', channel: 'email', summary: interactionSummary, }), }); const data = await res.json(); alert('Interaction logged'); setInteractionSummary(''); loadContact(); } catch (err) { alert('Error logging interaction: ' + err.message); } }; if (loading) return
Loading...
; if (error) return
Error: {error}
; if (!contact) return null; const c = contact.contact; const channels = contact.channels || []; const interactions = contact.interactions || []; const enrichments = contact.enrichments || []; const emails = channels.filter(ch => ch.channel_type === 'email').map(ch => ch.value); return (
{/* Header */}

{c.full_name}

{c.current_title} at {c.organisation} • {c.location}
{c.tier}
{/* Contact Info Card */}
Contact Information
EMAIL
{emails.length > 0 ? ( emails.map(email => (
{email}
)) ) : (
No email
)}
STATUS
{c.warmth_status}
Last contacted: {c.last_contacted ? new Date(c.last_contacted).toLocaleDateString() : 'Never'}
Next due: {c.next_due_date ? new Date(c.next_due_date).toLocaleDateString() : 'None'}
{/* Talking Points (from web/news) */}
Talking Points
{talkinPoints.length > 0 ? ( talkinPoints.map((point, idx) => (
{point.headline}
{point.summary}
View source →
)) ) : (

No talking points yet. Click "Generate New" to fetch recent news and updates.

)}
{/* Email Drafting */}
Draft Email
{/* Interactions / History */}
Interaction History
{interactions.length === 0 ? (

No interactions logged yet

) : ( interactions.map(interaction => (
{interaction.summary}
{interaction.interaction_type} • {new Date(interaction.date).toLocaleDateString()}
)) )}
{/* Log Interaction */}
Log New Interaction