import { useState, useRef, useEffect, type ReactNode } from 'react'; import { Send, MessageCircle, Settings, Search, BarChart3, User, Bot, Sparkles, HelpCircle, Moon, Sun, Globe, Database, BookOpen, Crown, Zap, Menu, ChevronLeft, ChevronRight, ThumbsUp, ThumbsDown, MessageSquare } from 'lucide-react'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Message } from './Message'; import { TypingIndicator } from './TypingIndicator'; import { Sheet, SheetContent, SheetTrigger, SheetTitle, SheetDescription } from './ui/sheet'; import { Textarea } from './ui/textarea'; import { motion, AnimatePresence } from 'motion/react'; interface ChatMessage { id: string; content: string; isUser: boolean; timestamp: Date; isPlusResponse?: boolean; sources?: Array<{ name: string; icon: ReactNode; url?: string }>; } export function ChatInterface() { const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(''); const [isTyping, setIsTyping] = useState(false); const [showComingSoon, setShowComingSoon] = useState(false); const [isDarkMode, setIsDarkMode] = useState(false); const [isUnivAiPlusMode, setIsUnivAiPlusMode] = useState(false); const [hasUsedPlusResponse, setHasUsedPlusResponse] = useState(false); const [currentSources, setCurrentSources] = useState>([]); const [showFeedback, setShowFeedback] = useState(false); const [feedbackType, setFeedbackType] = useState<'positive' | 'negative' | null>(null); const [feedbackComment, setFeedbackComment] = useState(''); const [lastBotMessageId, setLastBotMessageId] = useState(null); const [leftSheetOpen, setLeftSheetOpen] = useState(false); const [rightSheetOpen, setRightSheetOpen] = useState(false); const messagesEndRef = useRef(null); // Initialize messages based on current mode useEffect(() => { const initialMessage: ChatMessage = { id: '1', content: isUnivAiPlusMode ? "Feel free to explore UnivAi+++ for a richer, more responsive conversation experience. Please note: due to limited computational resources, the AI can provide only one response per session. Make it count!" : "Hello! I'm your AI assistant. How can I help you today? Feel free to ask me anything!", isUser: false, timestamp: new Date(), isPlusResponse: isUnivAiPlusMode }; setMessages([initialMessage]); setHasUsedPlusResponse(false); setCurrentSources([]); // Reset sources when switching modes setShowFeedback(false); setFeedbackType(null); setFeedbackComment(''); }, [isUnivAiPlusMode]); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; useEffect(() => { scrollToBottom(); }, [messages, isTyping]); useEffect(() => { // Apply dark mode class to document if (isDarkMode) { document.documentElement.classList.add('dark'); } else { document.documentElement.classList.remove('dark'); } }, [isDarkMode]); const handleSendMessage = async (e: React.FormEvent) => { e.preventDefault(); if (!inputValue.trim()) return; if (isUnivAiPlusMode && hasUsedPlusResponse) return; // Prevent sending if already used Plus response const userMessage: ChatMessage = { id: Date.now().toString(), content: inputValue, isUser: true, timestamp: new Date(), }; setMessages(prev => [...prev, userMessage]); setInputValue(''); setIsTyping(true); try { const res = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: inputValue, dev_mode: isUnivAiPlusMode, }), }); const data = await res.json(); // Optionally, you can extract sources from data if provided const botResponse: ChatMessage = { id: (Date.now() + 1).toString(), content: data.response, isUser: false, timestamp: new Date(), isPlusResponse: isUnivAiPlusMode, sources: [], // Optionally fill from data.source }; setMessages(prev => [...prev, botResponse]); setCurrentSources([]); // Optionally update if sources are available setIsTyping(false); setLastBotMessageId(botResponse.id); // Show feedback form after bot response setShowFeedback(true); setFeedbackType(null); setFeedbackComment(''); // Mark Plus response as used if (isUnivAiPlusMode) { setHasUsedPlusResponse(true); } } catch (err) { setIsTyping(false); } }; const handleFeedbackSubmit = async () => { console.log('Feedback submitted:', { messageId: lastBotMessageId, feedbackType, feedbackComment }); if (!lastBotMessageId) return; const lastBotMsg = messages.find(m => m.id === lastBotMessageId); if (!lastBotMsg) return; try { await fetch('/api/feedback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: lastBotMsg.content, response: lastBotMsg.content, feedback: feedbackType, }), }); setFeedbackType(null); setFeedbackComment(''); } catch (err) { // Optionally handle error } }; const handleComingSoonClick = () => { setShowComingSoon(true); setTimeout(() => { setShowComingSoon(false); }, 2000); }; const toggleDarkMode = () => { setIsDarkMode(!isDarkMode); }; const toggleUnivAiMode = () => { setIsUnivAiPlusMode(!isUnivAiPlusMode); }; const sidebarItems = [ { icon: , active: true }, { icon: , active: false }, { icon: , active: false }, { icon: , active: false }, { icon: , active: false }, ]; const isInputDisabled = isTyping || (isUnivAiPlusMode && hasUsedPlusResponse); // Sidebar content components const LeftSidebarContent = () => ( <> {/* Logo */}
{isUnivAiPlusMode ? : }
{/* Navigation */}
{sidebarItems.map((item, index) => ( {item.active ? item.icon : } ))}
); const RightSidebarContent = () => ( <> {/* AI Status Card - More compact */}
{isUnivAiPlusMode ? : }
{/* Fixed height container for title */}
{isUnivAiPlusMode ? 'UnivAi+++' : 'UnivAi'} {isUnivAiPlusMode && }
{/* Fixed height container for description */}
{isUnivAiPlusMode ? 'Smarter with human-like responses' : 'Any PUP-Related Queries?' }
{/* Fixed button */}
{/* Chat Stats - Equalized for both modes */}

Chat Statistics

Response Time {isUnivAiPlusMode ? '2.5s' : '1.2s'}
Status {isUnivAiPlusMode ? 'Premium' : 'Online'}
{isUnivAiPlusMode ? 'Responses Left' : 'Queries Processed'} {isUnivAiPlusMode ? (hasUsedPlusResponse ? '0' : '1') : '∞'}
{/* Sources - More compact */}

Sources

{currentSources.length === 0 ? (

Sources will appear here after AI responses

) : ( currentSources.map((source, index) => (
{source.icon}
{source.name}
{source.url && ( )}
)) )}
); return (
{/* Coming Soon Toast */} {showComingSoon && (

Coming soon!

)}
{/* Usage Limit Warning */} {isUnivAiPlusMode && hasUsedPlusResponse && (

UnivAi+++ limit reached. Switch to UnivAi to continue.

)}
{/* Dark Mode Toggle - Top Right */} {isDarkMode ? : } {/* Mobile Left Sheet Trigger */} Navigation Menu Access chat navigation, statistics, search, profile, and settings
{/* Sidebar - Desktop only */} {/* Main Container */}
{/* Header */}
{isUnivAiPlusMode ? : }
{isUnivAiPlusMode ? 'UnivAi+++' : 'UnivAi'} {isUnivAiPlusMode && }

{isUnivAiPlusMode ? 'Premium AI Experience' : 'Always ready to help'}

{/* Chat Container */}
{/* Messages Area */}
{/* Messages */}
{messages.map((message) => ( ))} {isTyping && }
{/* Feedback Section - Appears after AI responses */} {showFeedback && messages.length > 1 && (

How was this response?