first commit
This commit is contained in:
100
html/components/ChatBot.tsx
Normal file
100
html/components/ChatBot.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { chatWithExpert } from '../services/geminiService';
|
||||
import { Message } from '../types';
|
||||
|
||||
const ChatBot: React.FC = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [input, setInput] = useState('');
|
||||
const [messages, setMessages] = useState<Message[]>([
|
||||
{ role: 'model', text: "Welcome to Hanmo! I'm your Technical Assistant. How can I help you with our DCS, SCADA, or field instrument solutions today?" }
|
||||
]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollRef.current) {
|
||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||
}
|
||||
}, [messages, isOpen]);
|
||||
|
||||
const handleSend = async () => {
|
||||
if (!input.trim() || isLoading) return;
|
||||
|
||||
const userMsg = input.trim();
|
||||
setInput('');
|
||||
setMessages(prev => [...prev, { role: 'user', text: userMsg }]);
|
||||
setIsLoading(true);
|
||||
|
||||
const history = messages.map(m => ({ role: m.role, text: m.text }));
|
||||
const responseText = await chatWithExpert(userMsg, history);
|
||||
|
||||
setMessages(prev => [...prev, { role: 'model', text: responseText }]);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-6 right-6 z-[60]">
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="w-14 h-14 md:w-16 md:h-16 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-2xl flex items-center justify-center transition-transform hover:scale-110 active:scale-95"
|
||||
>
|
||||
{isOpen ? <i className="fas fa-times text-2xl"></i> : <i className="fas fa-robot text-2xl"></i>}
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute bottom-20 right-0 w-[calc(100vw-3rem)] md:w-96 h-[500px] max-h-[70vh] bg-white rounded-3xl shadow-2xl flex flex-col overflow-hidden border border-slate-200 animate-in fade-in slide-in-from-bottom-5">
|
||||
<div className="p-5 bg-blue-600 text-white font-bold flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<i className="fas fa-microchip"></i>
|
||||
<span>Tech Advisor</span>
|
||||
</div>
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
||||
</div>
|
||||
|
||||
<div ref={scrollRef} className="flex-1 overflow-y-auto p-5 space-y-4 bg-slate-50">
|
||||
{messages.map((m, i) => (
|
||||
<div key={i} className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
|
||||
<div className={`max-w-[85%] p-4 rounded-2xl text-sm leading-relaxed ${
|
||||
m.role === 'user'
|
||||
? 'bg-blue-600 text-white rounded-tr-none shadow-md'
|
||||
: 'bg-white text-slate-700 shadow-sm border border-slate-100 rounded-tl-none'
|
||||
}`}>
|
||||
{m.text}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{isLoading && (
|
||||
<div className="flex justify-start">
|
||||
<div className="bg-white p-4 rounded-2xl border border-slate-100 rounded-tl-none flex gap-1.5 shadow-sm">
|
||||
<div className="w-1.5 h-1.5 bg-blue-400 rounded-full animate-bounce"></div>
|
||||
<div className="w-1.5 h-1.5 bg-blue-400 rounded-full animate-bounce [animation-delay:0.2s]"></div>
|
||||
<div className="w-1.5 h-1.5 bg-blue-400 rounded-full animate-bounce [animation-delay:0.4s]"></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-4 bg-white border-t border-slate-100 flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSend()}
|
||||
placeholder="Ask a technical question..."
|
||||
className="flex-1 bg-slate-100 text-sm p-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-600/20"
|
||||
/>
|
||||
<button
|
||||
onClick={handleSend}
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white w-12 h-12 rounded-xl flex items-center justify-center transition-colors"
|
||||
>
|
||||
<i className="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatBot;
|
||||
Reference in New Issue
Block a user