"use client"
import * as React from "react"
import { AnimatePresence, motion } from "framer-motion"
import { ArrowUpCircle, ChevronDown, Clock, File, ImageIcon, Paperclip, Send, X } from "lucide-react"
import { format } from "date-fns"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/card"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Textarea } from "@/components/ui/textarea"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
import { cn } from "@/lib/utils"
// Types for the chat component
type MessageType = "user" | "agent" | "system"
interface Message {
id: string
content: string
timestamp: Date
type: MessageType
attachments?: Attachment[]
status?: "sent" | "delivered" | "read"
isTyping?: boolean
}
interface Attachment {
id: string
name: string
type: "image" | "file"
url: string
size?: string
}
interface CustomerSupportChatProps {
agentName?: string
agentAvatar?: string
companyName?: string
companyLogo?: string
primaryColor?: string
initialMessage?: string
position?: "bottom-right" | "bottom-left"
className?: string
defaultOpen?: boolean
}
export function CustomerSupportChat({
agentName = "Support Agent",
agentAvatar = "/diverse-avatars.png",
companyName = "Customer Support",
companyLogo = "/abstract-logo.png",
initialMessage = "Hello! How can I help you today?",
position = "bottom-right",
className,
defaultOpen = true,
}: CustomerSupportChatProps) {
const [isOpen, setIsOpen] = React.useState(defaultOpen)
const [isAgentTyping, setIsAgentTyping] = React.useState(false)
const [isAgentOnline, setIsAgentOnline] = React.useState(true)
const [newMessage, setNewMessage] = React.useState("")
const [messages, setMessages] = React.useState<Message[]>([
{
id: "1",
content: initialMessage,
timestamp: new Date(),
type: "agent",
status: "read",
},
])
const messagesEndRef = React.useRef<HTMLDivElement>(null)
// Simulate agent coming online/offline
React.useEffect(() => {
const interval = setInterval(() => {
setIsAgentOnline((prev) => !prev)
}, 60000) // Toggle every minute for demo purposes
return () => clearInterval(interval)
}, [])
// Auto-scroll to bottom when messages change
React.useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
}, [messages, isAgentTyping])
// Simulate agent typing and response when user sends a message
const simulateAgentResponse = (userMessage: string) => {
// Start typing indicator
setIsAgentTyping(true)
// Simulate typing delay (1-3 seconds)
const typingDelay = Math.floor(Math.random() * 2000) + 1000
setTimeout(() => {
setIsAgentTyping(false)
// Generate a response based on user message
let response = "Thank you for your message. Our team will get back to you shortly."
if (userMessage.toLowerCase().includes("hello") || userMessage.toLowerCase().includes("hi")) {
response = "Hello there! How can I assist you today?"
} else if (userMessage.toLowerCase().includes("help")) {
response = "I'd be happy to help. Could you please provide more details about your issue?"
} else if (userMessage.toLowerCase().includes("price") || userMessage.toLowerCase().includes("cost")) {
response = "Our pricing information can be found on our pricing page. Would you like me to send you the link?"
} else if (userMessage.toLowerCase().includes("thank")) {
response = "You're welcome! Is there anything else I can help you with?"
}
// Add agent response
setMessages((prev) => [
...prev,
{
id: Date.now().toString(),
content: response,
timestamp: new Date(),
type: "agent",
status: "sent",
},
])
}, typingDelay)
}
const handleSendMessage = () => {
if (newMessage.trim() === "") return
// Add user message
const userMessage = {
id: Date.now().toString(),
content: newMessage,
timestamp: new Date(),
type: "user" as MessageType,
status: "sent",
}
setMessages((prev) => [...prev, userMessage])
setNewMessage("")
// Simulate agent response
simulateAgentResponse(newMessage)
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
handleSendMessage()
}
}
return (
<div className={cn("z-50", position === "bottom-right" ? "bottom-4 right-4" : "bottom-4 left-4", className)}>
<AnimatePresence>
{isOpen ? (
<motion.div
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 20, scale: 0.95 }}
transition={{ duration: 0.2 }}
className="flex flex-col w-80 sm:w-96 h-[500px] rounded-lg shadow-lg bg-background border"
>
<Card className="flex flex-col h-full">
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-4">
<div className="flex items-center gap-2">
<Avatar className="h-8 w-8">
<AvatarImage src={companyLogo || "/placeholder.svg"} alt={companyName} />
<AvatarFallback>{companyName.substring(0, 2)}</AvatarFallback>
</Avatar>
<div>
<h3 className="font-semibold text-sm">{companyName}</h3>
<div className="flex items-center gap-1.5">
<div
className={cn("h-1.5 w-1.5 rounded-full", isAgentOnline ? "bg-green-500" : "bg-amber-500")}
/>
<p className="text-xs text-muted-foreground">{isAgentOnline ? "Online" : "Away"}</p>
</div>
</div>
</div>
<Button variant="ghost" size="icon" onClick={() => setIsOpen(false)}>
<X className="h-4 w-4" />
</Button>
</CardHeader>
<CardContent className="flex-1 p-0 overflow-hidden">
<ScrollArea className="h-full p-4">
<div className="flex flex-col gap-3">
{messages.map((message) => (
<div
key={message.id}
className={cn(
"flex flex-col max-w-[80%] rounded-lg p-3",
message.type === "user"
? "ml-auto bg-primary text-primary-foreground"
: message.type === "system"
? "mx-auto bg-muted text-muted-foreground text-xs"
: "mr-auto bg-muted",
)}
>
{message.type === "agent" && (
<div className="flex items-center gap-2 mb-1">
<Avatar className="h-5 w-5">
<AvatarImage src={agentAvatar || "/placeholder.svg"} alt={agentName} />
<AvatarFallback>{agentName.substring(0, 2)}</AvatarFallback>
</Avatar>
<span className="text-xs font-medium">{agentName}</span>
</div>
)}
<div className="text-sm">{message.content}</div>
{message.attachments && message.attachments.length > 0 && (
<div className="flex flex-wrap gap-2 mt-2">
{message.attachments.map((attachment) => (
<div
key={attachment.id}
className={cn(
"flex items-center gap-1 text-xs p-1.5 rounded",
message.type === "user" ? "bg-primary/20" : "bg-background",
)}
>
{attachment.type === "image" ? (
<ImageIcon className="h-3.5 w-3.5" />
) : (
<File className="h-3.5 w-3.5" />
)}
<span className="max-w-[100px] truncate">{attachment.name}</span>
</div>
))}
</div>
)}
<div
className={cn(
"flex items-center gap-1 text-xs mt-1",
message.type === "user" ? "ml-auto" : "mr-auto",
)}
>
<span className="opacity-70">{format(message.timestamp, "h:mm a")}</span>
{message.type === "user" && message.status && (
<span>
{message.status === "sent" && "✓"}
{message.status === "delivered" && "✓✓"}
{message.status === "read" && "✓✓"}
</span>
)}
</div>
</div>
))}
{isAgentTyping && (
<div className="flex items-center gap-2 max-w-[80%] mr-auto">
<Avatar className="h-5 w-5">
<AvatarImage src={agentAvatar || "/placeholder.svg"} alt={agentName} />
<AvatarFallback>{agentName.substring(0, 2)}</AvatarFallback>
</Avatar>
<div className="bg-muted p-3 rounded-lg">
<div className="flex gap-1 items-center">
<div className="h-1.5 w-1.5 bg-foreground/70 rounded-full animate-bounce [animation-delay:-0.3s]"></div>
<div className="h-1.5 w-1.5 bg-foreground/70 rounded-full animate-bounce [animation-delay:-0.15s]"></div>
<div className="h-1.5 w-1.5 bg-foreground/70 rounded-full animate-bounce"></div>
</div>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
</ScrollArea>
</CardContent>
<CardFooter className="p-3 pt-0">
<div className="flex flex-col w-full gap-3">
<div className="flex items-center gap-1 text-xs text-muted-foreground">
<Clock className="h-3 w-3" />
<span>Typical reply time: ~5 minutes</span>
</div>
<div className="relative">
<Textarea
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Type your message..."
className="min-h-10 resize-none pr-12"
/>
<div className="absolute right-2 bottom-2 flex items-center gap-1">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="h-7 w-7">
<Paperclip className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Attach file</TooltipContent>
</Tooltip>
</TooltipProvider>
<Button
size="icon"
className="h-7 w-7"
onClick={handleSendMessage}
disabled={newMessage.trim() === ""}
>
<Send className="h-4 w-4" />
</Button>
</div>
</div>
</div>
</CardFooter>
</Card>
</motion.div>
) : (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.2 }}
className="flex flex-col items-end gap-2"
>
<AnimatePresence>
{!isOpen && messages.length > 1 && (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.9 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.9 }}
className="max-w-xs bg-background rounded-lg p-3 shadow-lg border mb-2"
>
<div className="flex items-center gap-2 mb-1">
<Avatar className="h-6 w-6">
<AvatarImage src={agentAvatar || "/placeholder.svg"} alt={agentName} />
<AvatarFallback>{agentName.substring(0, 2)}</AvatarFallback>
</Avatar>
<span className="text-xs font-medium">{agentName}</span>
<Button
variant="ghost"
size="icon"
className="h-5 w-5 ml-auto"
onClick={() => setMessages([messages[0]])}
>
<X className="h-3 w-3" />
</Button>
</div>
<p className="text-sm">{messages[messages.length - 1].content}</p>
</motion.div>
)}
</AnimatePresence>
<Button onClick={() => setIsOpen(true)} size="icon" className="h-12 w-12 rounded-full shadow-lg">
{isOpen ? <ChevronDown className="h-6 w-6" /> : <ArrowUpCircle className="h-6 w-6" />}
</Button>
</motion.div>
)}
</AnimatePresence>
</div>
)
}
export default CustomerSupportChat