Spaces:
Running
Running
| import React, { useState, useRef, useEffect } from 'react' | |
| const SUGGESTIONS = [ | |
| "What are the top fintech unicorns?", | |
| "Tell me about SaaS startups in Bangalore", | |
| "What is DPIIT recognition?", | |
| "Which sectors are growing fastest?", | |
| "Compare edtech vs healthtech", | |
| ] | |
| export default function ChatWidget({ onClose }) { | |
| const [messages, setMessages] = useState([ | |
| { role: 'assistant', content: '👋 Hi! I\'m Bharat Tech Atlas AI. Ask me about Indian startups, sectors, funding trends, or any company!' } | |
| ]) | |
| const [input, setInput] = useState('') | |
| const [loading, setLoading] = useState(false) | |
| const scrollRef = useRef(null) | |
| useEffect(() => { | |
| scrollRef.current?.scrollIntoView({ behavior: 'smooth' }) | |
| }, [messages]) | |
| const sendMessage = async (text) => { | |
| if (!text.trim() || loading) return | |
| const userMsg = { role: 'user', content: text } | |
| setMessages(prev => [...prev, userMsg]) | |
| setInput('') | |
| setLoading(true) | |
| try { | |
| const resp = await fetch('/api/chat/completions', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ messages: [...messages, userMsg].map(m => ({ role: m.role, content: m.content })) }), | |
| }) | |
| const data = await resp.json() | |
| setMessages(prev => [...prev, { role: 'assistant', content: data.content || 'Sorry, I had trouble responding.' }]) | |
| } catch (err) { | |
| setMessages(prev => [...prev, { role: 'assistant', content: '⚠️ Network error. Please try again.' }]) | |
| } finally { | |
| setLoading(false) | |
| } | |
| } | |
| return ( | |
| <div className="fixed bottom-4 right-4 z-50 w-[380px] max-w-[calc(100vw-2rem)] h-[520px] max-h-[calc(100vh-2rem)] bg-atlas-bg border border-atlas-border rounded-2xl shadow-2xl flex flex-col overflow-hidden"> | |
| {/* Header */} | |
| <div className="flex items-center justify-between px-4 py-3 border-b border-atlas-border bg-atlas-surface"> | |
| <div className="flex items-center gap-2"> | |
| <span className="text-lg">🤖</span> | |
| <div> | |
| <h3 className="text-sm font-semibold text-atlas-text">Bharat Tech Atlas AI</h3> | |
| <p className="text-[10px] text-atlas-muted">Powered by Qwen2.5-0.5B</p> | |
| </div> | |
| </div> | |
| <button onClick={onClose} className="text-atlas-muted hover:text-atlas-text text-lg">✕</button> | |
| </div> | |
| {/* Messages */} | |
| <div className="flex-1 overflow-y-auto p-3 space-y-3"> | |
| {messages.map((m, i) => ( | |
| <div key={i} className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}> | |
| <div className={`max-w-[85%] rounded-xl px-3 py-2 text-xs leading-relaxed ${ | |
| m.role === 'user' | |
| ? 'bg-brand-500/20 text-brand-300 rounded-br-none' | |
| : 'bg-atlas-surface text-atlas-muted rounded-bl-none' | |
| }`}> | |
| {m.content} | |
| </div> | |
| </div> | |
| ))} | |
| {loading && ( | |
| <div className="flex justify-start"> | |
| <div className="bg-atlas-surface rounded-xl rounded-bl-none px-3 py-2"> | |
| <div className="flex gap-1"> | |
| <span className="w-1.5 h-1.5 bg-atlas-muted rounded-full animate-bounce" style={{ animationDelay: '0ms' }} /> | |
| <span className="w-1.5 h-1.5 bg-atlas-muted rounded-full animate-bounce" style={{ animationDelay: '150ms' }} /> | |
| <span className="w-1.5 h-1.5 bg-atlas-muted rounded-full animate-bounce" style={{ animationDelay: '300ms' }} /> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| <div ref={scrollRef} /> | |
| </div> | |
| {/* Suggestions (only show when few messages) */} | |
| {messages.length < 3 && ( | |
| <div className="px-3 pb-2 flex flex-wrap gap-1.5"> | |
| {SUGGESTIONS.map(s => ( | |
| <button key={s} onClick={() => sendMessage(s)} | |
| className="text-[10px] px-2 py-1 rounded-full bg-atlas-surface border border-atlas-border text-atlas-muted hover:text-atlas-text hover:border-brand-500/30 transition-colors"> | |
| {s} | |
| </button> | |
| ))} | |
| </div> | |
| )} | |
| {/* Input */} | |
| <div className="px-3 py-2 border-t border-atlas-border flex gap-2"> | |
| <input | |
| value={input} | |
| onChange={e => setInput(e.target.value)} | |
| onKeyDown={e => e.key === 'Enter' && sendMessage(input)} | |
| placeholder="Ask about startups, sectors, funding..." | |
| className="flex-1 bg-atlas-surface border border-atlas-border rounded-lg px-3 py-2 text-xs text-atlas-text placeholder:text-atlas-muted/50 focus:outline-none focus:border-brand-500/50" | |
| /> | |
| <button onClick={() => sendMessage(input)} disabled={loading || !input.trim()} | |
| className="px-3 py-2 bg-brand-500/20 text-brand-400 rounded-lg text-xs font-medium hover:bg-brand-500/30 disabled:opacity-30 transition-colors"> | |
| Send | |
| </button> | |
| </div> | |
| </div> | |
| ) | |
| } | |