import React from "react"; import { ArrowLeft, Briefcase, Calendar, CheckCircle2, ChevronRight, Circle, HelpCircle, Link2, Mail, MapPin, Navigation, Phone, PhoneCall, Plus, Sparkles, UserPlus, Video, } from "lucide-react"; import { cn } from "@/lib/utils"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import type { AiConvAppointmentData, AiConvContact, AiConvDataField, AiConvMeetingType, } from "./types"; import { ContactAvatar, displayContactName, PANEL_HEADER_HEIGHT, } from "./helpers"; // --------------------------------------------------------------------------- // AICollectedDataSection // --------------------------------------------------------------------------- export interface AICollectedDataSectionProps { fields: AiConvDataField[]; className?: string; } export function AICollectedDataSection({ fields, className, }: AICollectedDataSectionProps) { return (
{fields.map((field, i) => (
{field.label}
{field.value} {field.confidence === "confirmed" ? ( ) : ( )}
))}
); } // --------------------------------------------------------------------------- // LeadInfoPanel — internal helpers // --------------------------------------------------------------------------- const APPOINTMENT_STATUS_LABEL: Record< AiConvAppointmentData["status"], string > = { requested: "Lead requested", confirmed: "Confirmed", pending: "Pending confirmation", cancelled: "Cancelled", }; const MEETING_ICON: Record = { video: Video, phone: Phone, "in-person": MapPin, }; const MEETING_LABEL: Record = { video: "Video Call", phone: "Phone Call", "in-person": "In Person", }; const MEETING_DETAIL_ICON: Record = { video: Link2, phone: PhoneCall, "in-person": Navigation, }; function MeetingDetailRow({ meetingType, detail, }: { meetingType: AiConvMeetingType; detail: string; }) { const DetailIcon = MEETING_DETAIL_ICON[meetingType]; const isLink = detail.startsWith("http"); return (
{isLink ? ( {detail} ) : ( {detail} )}
); } interface AppointmentSectionProps { appointment: AiConvAppointmentData; contactId: string; isAnonymous: boolean; onApproveAppointment?: () => void; onDeclineAppointment?: () => void; onRescheduleAppointment?: (contactId: string) => void; } function AppointmentSection({ appointment, contactId, isAnonymous, onApproveAppointment, onDeclineAppointment, onRescheduleAppointment, }: AppointmentSectionProps) { const AppointmentIcon = MEETING_ICON[appointment.meetingType]; const canReschedule = !isAnonymous && !!onRescheduleAppointment; return (
{appointment.datetime}
{MEETING_LABEL[appointment.meetingType]}
{appointment.meetingDetail && ( )} {APPOINTMENT_STATUS_LABEL[appointment.status]} {appointment.status === "requested" && (
{canReschedule && ( )}
)} {(appointment.status === "confirmed" || appointment.status === "cancelled") && canReschedule && ( )} {(appointment.summary || (appointment.actionItems?.length ?? 0) > 0) && (
Meeting Summary
{appointment.summary && (

{appointment.summary}

)} {(appointment.actionItems?.length ?? 0) > 0 && (
{appointment.actionItems?.map((item, i) => (
{item.done ? ( ) : ( )} {item.label}
))}
)}
)}
); } function PanelSectionHeader({ children }: { children: React.ReactNode }) { return (

{children}

); } function PanelSection({ children, last = false, }: { children: React.ReactNode; last?: boolean; }) { return (
{children}
); } // --------------------------------------------------------------------------- // LeadInfoPanel // --------------------------------------------------------------------------- export interface LeadInfoPanelProps { contact: AiConvContact; firstSeen?: string; source?: string; /** Conversation topic tag — e.g. "Buy a Home", "Refinance", "Investment". */ topic?: string; aiFields?: AiConvDataField[]; appointment?: AiConvAppointmentData; internalNotes?: string; notesSaveStatus?: "idle" | "saving" | "saved"; isCollapsed?: boolean; /** True when this lead's contact info already exists in the system. Disables Add to Clients. */ isKnownContact?: boolean; onAddToContacts?: () => void; onCreateOpportunity?: () => void; onBookAppointment?: () => void; onApproveAppointment?: () => void; onDeclineAppointment?: () => void; onRescheduleAppointment?: (contactId: string) => void; onNotesChange?: (v: string) => void; onToggleCollapse?: () => void; /** Mobile only — back to chat thread. */ onBack?: () => void; className?: string; } export function LeadInfoPanel({ contact, firstSeen, source = "Website Chatbot", topic, aiFields = [], appointment, internalNotes = "", notesSaveStatus = "idle", isCollapsed = false, isKnownContact = false, onAddToContacts, onCreateOpportunity, onBookAppointment, onApproveAppointment, onDeclineAppointment, onRescheduleAppointment, onNotesChange, onToggleCollapse, onBack, className, }: LeadInfoPanelProps) { const isAnonymous = !contact.name.trim(); const addToContactsDisabled = isAnonymous || isKnownContact; return (
{onBack && ( )} Lead Info {onToggleCollapse && ( } /> {isCollapsed ? "Expand panel" : "Collapse panel"} )}
{!isCollapsed && (
Client

{displayContactName(contact.name)}

{source}

{topic && ( {topic} )}
{(contact.email || contact.phone || firstSeen) && (
{contact.email && ( )} {contact.phone && ( )} {firstSeen && (

First seen: {firstSeen}

)}
)}
AI-Collected Data {aiFields.length > 0 ? ( <>
confirmed estimated
) : (

AI is still gathering information...

)}
Appointment {appointment ? ( ) : ( )} CRM Actions
Add to Clients } /> {isKnownContact && ( Already a client )}

Internal Notes

{notesSaveStatus === "saving" && ( Saving... )} {notesSaveStatus === "saved" && ( Saved )}