import { useState, useCallback, useMemo } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSparkles, faPhone, faChevronDown, faChevronUp, faCalendarCheck, faClockRotateLeft, faPhoneMissed, faPhoneArrowDown, faPhoneArrowUp, faListCheck, } from '@fortawesome/pro-duotone-svg-icons'; import { AiChatPanel } from './ai-chat-panel'; import { Badge } from '@/components/base/badges/badges'; import { formatPhone, formatShortDate } from '@/lib/format'; import { cx } from '@/utils/cx'; import type { Lead, LeadActivity, Call, FollowUp, Patient, Appointment } from '@/types/entities'; import { AppointmentForm } from './appointment-form'; interface ContextPanelProps { selectedLead: Lead | null; activities: LeadActivity[]; calls: Call[]; followUps: FollowUp[]; appointments: Appointment[]; patients: Patient[]; callerPhone?: string; isInCall?: boolean; callUcid?: string | null; } const formatTimeAgo = (dateStr: string): string => { const minutes = Math.round((Date.now() - new Date(dateStr).getTime()) / 60000); if (minutes < 1) return 'Just now'; if (minutes < 60) return `${minutes}m ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}h ago`; return `${Math.floor(hours / 24)}d ago`; }; const formatDuration = (sec: number): string => { if (sec < 60) return `${sec}s`; return `${Math.floor(sec / 60)}m ${sec % 60}s`; }; const SectionHeader = ({ icon, label, count, expanded, onToggle }: { icon: any; label: string; count?: number; expanded: boolean; onToggle: () => void; }) => ( ); export const ContextPanel = ({ selectedLead, activities, calls, followUps, appointments, patients, callerPhone, isInCall }: ContextPanelProps) => { const [contextExpanded, setContextExpanded] = useState(true); const [insightExpanded, setInsightExpanded] = useState(true); const [actionsExpanded, setActionsExpanded] = useState(true); const [recentExpanded, setRecentExpanded] = useState(true); const [editingAppointment, setEditingAppointment] = useState(null); const lead = selectedLead; const firstName = lead?.contactName?.firstName ?? ''; const lastName = lead?.contactName?.lastName ?? ''; const fullName = `${firstName} ${lastName}`.trim(); const phone = lead?.contactPhone?.[0]; const callerContext = lead ? { callerPhone: phone?.number ?? callerPhone, leadId: lead.id, leadName: fullName, } : callerPhone ? { callerPhone } : undefined; // Filter data for this lead const leadCalls = useMemo(() => calls.filter(c => c.leadId === lead?.id || (callerPhone && c.callerNumber?.[0]?.number?.endsWith(callerPhone))) .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) .slice(0, 5), [calls, lead, callerPhone], ); const leadFollowUps = useMemo(() => followUps.filter(f => f.patientId === (lead as any)?.patientId && f.followUpStatus !== 'COMPLETED' && f.followUpStatus !== 'CANCELLED') .sort((a, b) => new Date(a.scheduledAt ?? '').getTime() - new Date(b.scheduledAt ?? '').getTime()) .slice(0, 3), [followUps, lead], ); const leadAppointments = useMemo(() => { const patientId = (lead as any)?.patientId; if (!patientId) return []; return appointments .filter(a => a.patientId === patientId && a.appointmentStatus !== 'CANCELLED' && a.appointmentStatus !== 'NO_SHOW') .sort((a, b) => new Date(a.scheduledAt ?? '').getTime() - new Date(b.scheduledAt ?? '').getTime()) .slice(0, 3); }, [appointments, lead]); const leadActivities = useMemo(() => activities.filter(a => a.leadId === lead?.id) .sort((a, b) => new Date(b.occurredAt ?? '').getTime() - new Date(a.occurredAt ?? '').getTime()) .slice(0, 5), [activities, lead], ); // Linked patient const linkedPatient = useMemo(() => patients.find(p => p.id === (lead as any)?.patientId), [patients, lead], ); // Auto-collapse context sections when chat starts const handleChatStart = useCallback(() => { setContextExpanded(false); }, []); const hasContext = !!(lead?.aiSummary || leadCalls.length || leadFollowUps.length || leadAppointments.length || leadActivities.length); return (
{/* Lead header — always visible */} {lead && (
{/* Expanded context sections */} {contextExpanded && (
{/* AI Insight */} {lead.aiSummary && (
setInsightExpanded(!insightExpanded)} /> {insightExpanded && (

{lead.aiSummary}

{lead.aiSuggestedAction && (

{lead.aiSuggestedAction}

)}
)}
)} {/* Quick Actions — upcoming appointments + follow-ups + linked patient */} {(leadAppointments.length > 0 || leadFollowUps.length > 0 || linkedPatient) && (
setActionsExpanded(!actionsExpanded)} /> {actionsExpanded && (
{leadAppointments.map(appt => (
{appt.doctorName ?? 'Appointment'} {appt.department} {appt.scheduledAt && ( — {formatShortDate(appt.scheduledAt)} )}
{appt.appointmentStatus?.replace(/_/g, ' ') ?? 'Scheduled'}
))} {leadFollowUps.map(fu => (
{fu.followUpType?.replace(/_/g, ' ') ?? 'Follow-up'} {fu.scheduledAt && ( {formatShortDate(fu.scheduledAt)} )}
{fu.followUpStatus?.replace(/_/g, ' ') ?? 'Pending'}
))} {linkedPatient && (
Patient: {linkedPatient.fullName?.firstName} {linkedPatient.fullName?.lastName} {linkedPatient.patientType && ( {linkedPatient.patientType} )}
)}
)}
)} {/* Recent calls + activities */} {(leadCalls.length > 0 || leadActivities.length > 0) && (
setRecentExpanded(!recentExpanded)} /> {recentExpanded && (
{leadCalls.map(call => (
{call.callStatus === 'MISSED' ? 'Missed' : call.callDirection === 'INBOUND' ? 'Inbound' : 'Outbound'} call {call.durationSeconds != null && call.durationSeconds > 0 && ( — {formatDuration(call.durationSeconds)} )} {call.disposition && ( , {call.disposition.replace(/_/g, ' ')} )}
{formatTimeAgo(call.startedAt ?? call.createdAt)}
))} {leadActivities .filter(a => !leadCalls.some(c => a.summary?.includes(c.callerNumber?.[0]?.number ?? '---'))) .slice(0, 3) .map(a => (
{a.summary} {a.occurredAt && ( {formatTimeAgo(a.occurredAt)} )}
)) }
)}
)} {/* No context available */} {!hasContext && (

No history for this lead yet.

)}
)}
)} {/* AI Chat — fills remaining space */}
{/* Appointment edit form */} {editingAppointment && ( { if (!open) setEditingAppointment(null); }} callerNumber={callerPhone} leadName={fullName} leadId={lead?.id} patientId={editingAppointment.patientId} existingAppointment={{ id: editingAppointment.id, scheduledAt: editingAppointment.scheduledAt ?? '', doctorName: editingAppointment.doctorName ?? '', doctorId: editingAppointment.doctorId ?? undefined, department: editingAppointment.department ?? '', reasonForVisit: editingAppointment.reasonForVisit ?? undefined, status: editingAppointment.appointmentStatus ?? 'SCHEDULED', }} onSaved={() => setEditingAppointment(null)} /> )}
); };