// components/AIDemoPanel.tsx import { useState, useEffect, useRef } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Button, Input } from 'ui' import { AiIconAnimation, cn } from 'ui' import errorGif from 'public/images/ai/error.gif' import Image from 'next/image' import styles from './assistant.module.css' interface DemoMessage { id: string role: 'user' | 'assistant' content: string | React.ReactNode render?: React.ReactNode createdAt: Date } export const AIDemoPanel = ({ incomingMessages = [] }: { incomingMessages?: DemoMessage[] }) => { const [messages, setMessages] = useState([]) const [isComplete, setIsComplete] = useState(false) const [pendingMessages, setPendingMessages] = useState([]) const [input, setInput] = useState('') const [showFade, setShowFade] = useState(false) const messagesEndRef = useRef(null) const scrollContainerRef = useRef(null) const scrollToBottom = () => { const container = messagesEndRef.current?.closest('.overflow-y-auto') if (container) { container.scrollTo({ top: container.scrollHeight, behavior: 'smooth', }) } } const handleScroll = () => { const container = scrollContainerRef.current if (container) { const scrollPercentage = (container.scrollTop / (container.scrollHeight - container.clientHeight)) * 100 const isScrollable = container.scrollHeight > container.clientHeight const isAtBottom = scrollPercentage >= 100 setShowFade(isScrollable && !isAtBottom) } } useEffect(() => { scrollToBottom() handleScroll() }, [messages]) useEffect(() => { const container = scrollContainerRef.current if (container) { container.addEventListener('scroll', handleScroll) handleScroll() } return () => { if (container) { container.removeEventListener('scroll', handleScroll) } } }, []) useEffect(() => { if (pendingMessages.length > 0) { const timer = setTimeout(() => { setMessages((prev) => [...prev, pendingMessages[0]]) setPendingMessages((prev) => prev.slice(1)) }, 3000) return () => clearTimeout(timer) } else if (!isComplete) { setIsComplete(true) } }, [isComplete, pendingMessages]) useEffect(() => { if (incomingMessages.length > 0) { setMessages((prev) => [...prev, ...pendingMessages, incomingMessages[0]]) setPendingMessages(incomingMessages.slice(1)) } }, [incomingMessages]) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (!input.trim()) return const userMessage: DemoMessage = { id: Date.now().toString(), role: 'user', content: input, createdAt: new Date(), } const assistantMessage: DemoMessage = { id: (Date.now() + 1).toString(), role: 'assistant', content: (
Try the assistant in your dashboard for real results
), createdAt: new Date(), } const followUp: DemoMessage = { id: (Date.now() + 1).toString(), role: 'assistant', content: "Sorry, custom queries aren't supported in this demo.", createdAt: new Date(), } setMessages((prev) => [...prev, userMessage]) setPendingMessages((prev) => [...prev, assistantMessage, followUp]) setInput('') } return (
{/* Header */}
Assistant
{/* Messages */}
{messages.map((message, index) => ( <> {message.content} {message.render &&
{message.render}
}
))} {messages.length > 0 && messages[messages.length - 1].role === 'user' && ( Thinking
. . .
)}
{/* Input */} {showFade && (
)}
setInput(e.target.value)} placeholder="Ask your data anything..." className={cn( 'w-full bg-background-muted rounded-md [&>textarea]:border-1 [&>textarea]:rounded-md [&>textarea]:!outline-none [&>textarea]:!ring-offset-0 [&>textarea]:!ring-0 focus:outline-none' )} />
) }