'use client'; import { useState, useRef, useEffect } from 'react'; import { Send, Brain, Trash2, Radio } from 'lucide-react'; import { Badge } from './ui/badge'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import './Roger.css'; interface Message { id: string; role: 'user' | 'assistant'; content: string; sources?: Array<{ domain: string; platform: string; similarity: number; }>; timestamp: Date; } const FloatingChatBox = () => { const [isOpen, setIsOpen] = useState(false); const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [isLoading, setIsLoading] = useState(false); const [domainFilter, setDomainFilter] = useState(null); const scrollContainerRef = useRef(null); const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; // Auto-scroll to bottom useEffect(() => { if (scrollContainerRef.current) { scrollContainerRef.current.scrollTop = scrollContainerRef.current.scrollHeight; } }, [messages, isLoading]); // Handle body scroll when chat is open (mobile) useEffect(() => { if (isOpen) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = 'unset'; } return () => { document.body.style.overflow = 'unset'; }; }, [isOpen]); const sendMessage = async () => { if (!input.trim() || isLoading) return; const userMessage: Message = { id: Date.now().toString(), role: 'user', content: input, timestamp: new Date() }; setMessages(prev => [...prev, userMessage]); const currentInput = input; setInput(''); setIsLoading(true); try { const response = await fetch(`${API_BASE}/api/rag/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: currentInput, domain_filter: domainFilter, use_history: true }) }); const data = await response.json(); const assistantMessage: Message = { id: (Date.now() + 1).toString(), role: 'assistant', content: data.answer || 'No response received.', sources: data.sources, timestamp: new Date() }; setMessages(prev => [...prev, assistantMessage]); } catch (error) { const errorMessage: Message = { id: (Date.now() + 1).toString(), role: 'assistant', content: 'Failed to connect to Roger Intelligence. Please ensure the backend is running.', timestamp: new Date() }; setMessages(prev => [...prev, errorMessage]); } finally { setIsLoading(false); } }; const clearHistory = async () => { try { await fetch(`${API_BASE}/api/rag/clear`, { method: 'POST' }); setMessages([]); } catch (error) { console.error('Failed to clear history:', error); } }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }; const toggleChat = () => { setIsOpen(!isOpen); }; const domains = ['political', 'economic', 'weather', 'social', 'intelligence']; return (
{/* Backdrop */}
setIsOpen(false)} className={`absolute top-0 left-0 w-screen h-screen bg-black transition-opacity duration-500 ${isOpen ? 'opacity-40 flex' : 'opacity-0 hidden'}`} /> {/* Roger Button */}

Roger

{/* Chat Container */}
{/* Header - with safe area for iPhone notch */}

Roger

Intelligence Assistant

Close

{/* Domain Filter - scrollable on mobile */}
setDomainFilter(null)} > All {domains.map(domain => ( setDomainFilter(domain)} > {domain} ))}
{/* Messages Container */} {messages.length > 0 ? (
{/* Today Badge */}

Today

{messages.map((msg) => (
{msg.role === 'assistant' ? (
{msg.content}
) : (

{msg.content}

)} {/* Sources */} {msg.sources && msg.sources.length > 0 && (

Sources:

{msg.sources.slice(0, 3).map((src, i) => ( {src.domain} ({Math.round(src.similarity * 100)}%) ))}
)}
))} {/* Typing Indicator */} {isLoading && (
)}
) : (

Hello! I'm Roger, your intelligence assistant.

Ask me anything about Sri Lanka's political, economic, weather, or social intelligence data.

Try asking:

"What are the latest political events?"

"Any weather warnings today?"

)} {/* Input Area - with safe area for bottom */}