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 (
);
}
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
)}
)}
);
}