Spaces:
Running
Running
| import React, { useState, useRef, useEffect } from 'react'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Input } from '@/components/ui/input'; | |
| import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; | |
| import { Send, Bot, User, Loader2 } from 'lucide-react'; | |
| interface Message { | |
| id: string; | |
| content: string; | |
| role: 'user' | 'assistant'; | |
| timestamp: Date; | |
| } | |
| const ChatBot = () => { | |
| const [messages, setMessages] = useState<Message[]>([ | |
| { | |
| id: '1', | |
| content: 'Hello! I\'m your AI assistant powered by V1Q. How can I help you today?', | |
| role: 'assistant', | |
| timestamp: new Date() | |
| } | |
| ]); | |
| const [input, setInput] = useState(''); | |
| const [isLoading, setIsLoading] = useState(false); | |
| const messagesEndRef = useRef<HTMLDivElement>(null); | |
| const scrollToBottom = () => { | |
| messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); | |
| }; | |
| useEffect(() => { | |
| scrollToBottom(); | |
| }, [messages]); | |
| const sendMessage = async () => { | |
| if (!input.trim() || isLoading) return; | |
| const userMessage: Message = { | |
| id: Date.now().toString(), | |
| content: input, | |
| role: 'user', | |
| timestamp: new Date() | |
| }; | |
| setMessages(prev => [...prev, userMessage]); | |
| setInput(''); | |
| setIsLoading(true); | |
| try { | |
| // Simulate API call to V1Q model | |
| await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 2000)); | |
| const assistantMessage: Message = { | |
| id: (Date.now() + 1).toString(), | |
| content: generateResponse(input), | |
| role: 'assistant', | |
| timestamp: new Date() | |
| }; | |
| setMessages(prev => [...prev, assistantMessage]); | |
| } catch (error) { | |
| console.error('Error sending message:', error); | |
| const errorMessage: Message = { | |
| id: (Date.now() + 1).toString(), | |
| content: 'Sorry, I encountered an error. Please try again.', | |
| role: 'assistant', | |
| timestamp: new Date() | |
| }; | |
| setMessages(prev => [...prev, errorMessage]); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }; | |
| const generateResponse = (userInput: string): string => { | |
| // Simple response generation for demo purposes | |
| const responses = [ | |
| "That's an interesting question! Let me help you with that.", | |
| "I understand what you're asking. Here's what I think...", | |
| "Great question! Based on my knowledge, I can tell you that...", | |
| "I'd be happy to help you with that. Here's my response...", | |
| "That's a thoughtful inquiry. Let me provide you with some insights..." | |
| ]; | |
| return responses[Math.floor(Math.random() * responses.length)] + | |
| ` Regarding "${userInput}", I'm processing this with the V1Q model to provide you with the most accurate response possible.`; | |
| }; | |
| const handleKeyPress = (e: React.KeyboardEvent) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| sendMessage(); | |
| } | |
| }; | |
| return ( | |
| <div className="max-w-4xl mx-auto h-[600px] flex flex-col"> | |
| <Card className="flex-1 flex flex-col"> | |
| <CardHeader className="border-b"> | |
| <CardTitle className="flex items-center gap-2"> | |
| <Bot className="h-6 w-6 text-primary" /> | |
| AI Assistant (V1Q) | |
| </CardTitle> | |
| </CardHeader> | |
| <CardContent className="flex-1 flex flex-col p-0"> | |
| <div className="flex-1 overflow-y-auto p-4 space-y-4"> | |
| {messages.map((message) => ( | |
| <div | |
| key={message.id} | |
| className={`flex gap-3 ${ | |
| message.role === 'user' ? 'justify-end' : 'justify-start' | |
| }`} | |
| > | |
| <div | |
| className={`flex gap-3 max-w-[80%] ${ | |
| message.role === 'user' ? 'flex-row-reverse' : 'flex-row' | |
| }`} | |
| > | |
| <div className="flex-shrink-0"> | |
| {message.role === 'user' ? ( | |
| <div className="w-8 h-8 bg-primary rounded-full flex items-center justify-center"> | |
| <User className="h-4 w-4 text-primary-foreground" /> | |
| </div> | |
| ) : ( | |
| <div className="w-8 h-8 bg-secondary rounded-full flex items-center justify-center"> | |
| <Bot className="h-4 w-4 text-secondary-foreground" /> | |
| </div> | |
| )} | |
| </div> | |
| <div | |
| className={`rounded-lg p-3 ${ | |
| message.role === 'user' | |
| ? 'bg-primary text-primary-foreground' | |
| : 'bg-muted text-muted-foreground' | |
| }`} | |
| > | |
| <p className="text-sm">{message.content}</p> | |
| <p className="text-xs opacity-70 mt-1"> | |
| {message.timestamp.toLocaleTimeString([], { | |
| hour: '2-digit', | |
| minute: '2-digit' | |
| })} | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| {isLoading && ( | |
| <div className="flex gap-3 justify-start"> | |
| <div className="flex gap-3 max-w-[80%]"> | |
| <div className="w-8 h-8 bg-secondary rounded-full flex items-center justify-center"> | |
| <Bot className="h-4 w-4 text-secondary-foreground" /> | |
| </div> | |
| <div className="rounded-lg p-3 bg-muted"> | |
| <div className="flex items-center gap-2"> | |
| <Loader2 className="h-4 w-4 animate-spin" /> | |
| <span className="text-sm text-muted-foreground">Thinking...</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| <div ref={messagesEndRef} /> | |
| </div> | |
| <div className="p-4 border-t"> | |
| <div className="flex gap-2"> | |
| <Input | |
| value={input} | |
| onChange={(e) => setInput(e.target.value)} | |
| onKeyPress={handleKeyPress} | |
| placeholder="Type your message here..." | |
| disabled={isLoading} | |
| className="flex-1" | |
| /> | |
| <Button | |
| onClick={sendMessage} | |
| disabled={!input.trim() || isLoading} | |
| size="icon" | |
| > | |
| <Send className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| </div> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| ); | |
| }; | |
| export default ChatBot |