'use client'; import { useState, useEffect, useCallback } from 'react'; const ROLES = ['Economic Buyer', 'Champion', 'Technical Evaluator', 'End User', 'Procurement', 'Executive Sponsor'] as const; const SENTIMENTS = ['Champion', 'Supportive', 'Neutral', 'Skeptical', 'Blocker'] as const; type Role = (typeof ROLES)[number]; type Sentiment = (typeof SENTIMENTS)[number]; interface Contact { id: number; Account_Name: string; Contact_Name: string; Role: Role; Sentiment: Sentiment; Email?: string; LinkedIn_URL?: string; } interface ContactMapProps { accountName: string; } const SENTIMENT_COLORS: Record = { Champion: 'bg-emerald-500', Supportive: 'bg-emerald-400', Neutral: 'bg-yellow-400', Skeptical: 'bg-red-400', Blocker: 'bg-red-600', }; const ROLE_BADGE_CLASSES = 'inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#1B1D36]/10 text-[#1B1D36] dark:bg-[#0098C7]/15 dark:text-[#0098C7]'; const emptyForm = { Contact_Name: '', Role: 'Technical Evaluator' as Role, Sentiment: 'Neutral' as Sentiment, Email: '', LinkedIn_URL: '' }; export function ContactMap({ accountName }: ContactMapProps) { const [contacts, setContacts] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState(emptyForm); const [submitting, setSubmitting] = useState(false); const fetchContacts = useCallback(async () => { try { const res = await fetch('/api/phase2', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'contacts.list', Account_Name: accountName }), }); const json = await res.json(); setContacts(json.data ?? []); } catch { console.error('Failed to fetch contacts'); } finally { setLoading(false); } }, [accountName]); useEffect(() => { fetchContacts(); }, [fetchContacts]); const resetForm = () => { setForm(emptyForm); setShowForm(false); setEditingId(null); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!form.Contact_Name.trim()) return; setSubmitting(true); try { await fetch('/api/phase2', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'contacts.upsert', Account_Name: accountName, Contact_Name: form.Contact_Name.trim(), Role: form.Role, Sentiment: form.Sentiment, Email: form.Email.trim() || undefined, LinkedIn_URL: form.LinkedIn_URL.trim() || undefined, ...(editingId != null ? { id: editingId } : {}), }), }); resetForm(); await fetchContacts(); } catch { console.error('Failed to save contact'); } finally { setSubmitting(false); } }; const handleEdit = (contact: Contact) => { setEditingId(contact.id); setForm({ Contact_Name: contact.Contact_Name, Role: contact.Role, Sentiment: contact.Sentiment, Email: contact.Email ?? '', LinkedIn_URL: contact.LinkedIn_URL ?? '', }); setShowForm(true); }; const handleDelete = async (id: number) => { try { await fetch('/api/phase2', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'contacts.delete', id }), }); await fetchContacts(); } catch { console.error('Failed to delete contact'); } }; const selectClasses = 'w-full rounded-lg border border-card-border bg-background px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-[#0098C7]'; const inputClasses = selectClasses; return (

Stakeholder Map

{!showForm && ( )}
{/* Inline Form */} {showForm && (
setForm({ ...form, Contact_Name: e.target.value })} placeholder="Contact name" className={inputClasses} />
setForm({ ...form, Email: e.target.value })} placeholder="Email (optional)" className={inputClasses} /> setForm({ ...form, LinkedIn_URL: e.target.value })} placeholder="LinkedIn URL (optional)" className={inputClasses} />
)} {/* Contacts List */} {loading ? (

Loading contacts...

) : contacts.length === 0 ? (

No stakeholders added yet.

) : (
{contacts.map((contact) => (
{contact.Contact_Name} {contact.Role}
{contact.Email && ( {contact.Email} )} {contact.LinkedIn_URL && ( )}
))}
)}
); }