| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- import * as React from 'react';
- import { ChatWidgetSettings, ChatMessage } from '../types';
- import { sendChatMessage } from '../services/geminiService';
- import { Icon } from './ui/Icon';
- interface ChatWidgetProps {
- settings: ChatWidgetSettings;
- }
- const ChatWidget: React.FC<ChatWidgetProps> = ({ settings }) => {
- const [isOpen, setIsOpen] = React.useState(false);
- const [messages, setMessages] = React.useState<ChatMessage[]>([]);
- const [userInput, setUserInput] = React.useState('');
- const [isLoading, setIsLoading] = React.useState(false);
- const messagesEndRef = React.useRef<HTMLDivElement>(null);
- React.useEffect(() => {
- if (isOpen && messages.length === 0) {
- setMessages([{ id: `msg-ai-${Date.now()}`, sender: 'ai', text: 'Hello! How can I help you today?' }]);
- }
- }, [isOpen, messages.length]);
- React.useEffect(() => {
- if (messagesEndRef.current) {
- messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
- }
- }, [messages, isLoading]);
- const handleSendMessage = React.useCallback(async () => {
- if (!userInput.trim()) return;
- const userMessage: ChatMessage = { id: `msg-user-${Date.now()}`, sender: 'user', text: userInput };
- setMessages(prev => [...prev, userMessage]);
- const messageToSend = userInput;
- setUserInput('');
- setIsLoading(true);
- const aiResponse = await sendChatMessage(messageToSend);
- setMessages(prev => [...prev, aiResponse]);
- setIsLoading(false);
- }, [userInput]);
- return (
- <>
- <div
- className={`absolute bottom-24 right-5 w-full max-w-sm h-[70vh] max-h-[600px] bg-white dark:bg-gray-800 rounded-2xl shadow-2xl flex flex-col transition-all duration-300 ease-in-out z-40 ${isOpen ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-10 pointer-events-none'}`}
- style={{ backgroundColor: settings.panelBackgroundColor }}
- >
- <div className="flex-shrink-0 p-4 flex justify-between items-center rounded-t-2xl" style={{ backgroundColor: settings.headerBackgroundColor, color: settings.headerTextColor }}>
- <h3 className="font-bold text-lg">AI Assistant</h3>
- <button onClick={() => setIsOpen(false)} style={{ color: settings.headerTextColor }}>
- <Icon className="h-6 w-6"><path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" /></Icon>
- </button>
- </div>
- <div className="flex-1 p-4 overflow-y-auto space-y-4">
- {messages.map(msg => (
- <div key={msg.id} className={`flex items-start gap-3 ${msg.sender === 'user' ? 'justify-end' : ''}`}>
- {msg.sender === 'ai' && (
- <div className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" style={{ backgroundColor: settings.aiMessageBackgroundColor, color: settings.aiMessageTextColor }}>
- <Icon className="h-5 w-5"><path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /></Icon>
- </div>
- )}
- <div className="max-w-xs p-3 rounded-lg text-sm"
- style={{
- backgroundColor: msg.sender === 'user' ? settings.userMessageBackgroundColor : settings.aiMessageBackgroundColor,
- color: msg.sender === 'user' ? settings.userMessageTextColor : settings.aiMessageTextColor,
- borderRadius: msg.sender === 'user' ? '1rem 1rem 0.25rem 1rem' : '1rem 1rem 1rem 0.25rem'
- }}
- >
- <p>{msg.text}</p>
- </div>
- </div>
- ))}
- {isLoading && (
- <div className="flex items-start gap-3">
- <div className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" style={{ backgroundColor: settings.aiMessageBackgroundColor }}>
- <Icon className="h-5 w-5" style={{ color: settings.aiMessageTextColor }}><path d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /></Icon>
- </div>
- <div className="max-w-xs p-3 rounded-lg" style={{ backgroundColor: settings.aiMessageBackgroundColor }}>
- <div className="flex items-center space-x-1">
- <span className="w-2 h-2 rounded-full animate-pulse delay-0" style={{ backgroundColor: settings.aiMessageTextColor }}></span>
- <span className="w-2 h-2 rounded-full animate-pulse delay-150" style={{ backgroundColor: settings.aiMessageTextColor }}></span>
- <span className="w-2 h-2 rounded-full animate-pulse delay-300" style={{ backgroundColor: settings.aiMessageTextColor }}></span>
- </div>
- </div>
- </div>
- )}
- <div ref={messagesEndRef} />
- </div>
- <div className="flex-shrink-0 p-4 border-t border-gray-200 dark:border-gray-700 flex items-center gap-3">
- <input
- type="text"
- value={userInput}
- onChange={e => setUserInput(e.target.value)}
- onKeyPress={e => e.key === 'Enter' && !isLoading && handleSendMessage()}
- placeholder="Ask me anything..."
- className="flex-1 bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white p-2 rounded-md border border-gray-300 dark:border-gray-600 focus:ring-2 focus:ring-brand-primary focus:outline-none"
- disabled={isLoading}
- />
- <button onClick={handleSendMessage} disabled={isLoading} className="p-2 rounded-full text-white disabled:opacity-50" style={{ backgroundColor: settings.headerBackgroundColor }}>
- <Icon className="h-6 w-6"><path strokeLinecap="round" strokeLinejoin="round" d="M5 10l7-7m0 0l7 7m-7-7v18" /></Icon>
- </button>
- </div>
- </div>
- <button
- onClick={() => setIsOpen(!isOpen)}
- className={`absolute bottom-5 right-5 h-16 w-16 rounded-full shadow-lg flex items-center justify-center text-white transition-transform transform hover:scale-110 z-40 ${isOpen ? 'animate-none' : 'animate-breathing'}`}
- style={{ backgroundColor: settings.iconColor }}
- aria-label="Open AI Assistant"
- >
- <Icon className="h-8 w-8 transition-transform duration-300" style={{ transform: isOpen ? 'rotate(180deg) scale(0.75)' : 'rotate(0) scale(1)'}}>
- {isOpen
- ? <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
- : <path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
- }
- </Icon>
- </button>
- </>
- );
- };
- export default ChatWidget;
|