Files
sitemente/components/mission-control/HorusChat.tsx
T

151 lines
4.4 KiB
TypeScript

"use client";
import { useState, useEffect, useRef } from "react";
import { motion } from "framer-motion";
interface Message {
id: string;
role: "user" | "assistant";
content: string;
timestamp: number;
}
export default function VoiceChat() {
const [input, setInput] = useState("");
const [messages, setMessages] = useState<Message[]>([
{
id: "welcome",
role: "assistant",
content: "👁️ ¡Hola! Soy Horus. Chatea conmigo aquí o en Telegram. ¿En qué puedo ayudarte?",
timestamp: Date.now(),
}
]);
const [isProcessing, setIsProcessing] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
const handleSend = async () => {
if (!input.trim() || isProcessing) return;
const userMessage: Message = {
id: `user_${Date.now()}`,
role: "user",
content: input,
timestamp: Date.now(),
};
setMessages(prev => [...prev, userMessage]);
setInput("");
setIsProcessing(true);
try {
// Send to SiteMente AI agent
const res = await fetch("/api/chat/agent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: userMessage.content,
locale: "es"
}),
});
const data = await res.json();
const botMessage: Message = {
id: `bot_${Date.now()}`,
role: "assistant",
content: data.response || "Entendido.",
timestamp: Date.now(),
};
setMessages(prev => [...prev, botMessage]);
} catch (e) {
console.error("Chat error:", e);
setMessages(prev => [...prev, {
id: `error_${Date.now()}`,
role: "assistant",
content: "Error de conexión. Prueba en Telegram.",
timestamp: Date.now(),
}]);
} finally {
setIsProcessing(false);
}
};
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSend();
}
};
return (
<div className="rounded-xl border border-white/10 bg-white/5 overflow-hidden">
{/* Header */}
<div className="flex items-center justify-between px-4 py-3 border-b border-white/10">
<div className="flex items-center gap-2">
<span className="text-lg">💬</span>
<span className="font-semibold">Horus Chat</span>
</div>
<div className="flex items-center gap-2">
{isProcessing && (
<span className="text-xs text-brand-pink animate-pulse">Escribiendo...</span>
)}
</div>
</div>
{/* Messages */}
<div className="p-4 space-y-3 min-h-[280px] max-h-[400px] overflow-y-auto">
{messages.map((msg) => (
<motion.div
key={msg.id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className={`flex ${msg.role === "user" ? "justify-end" : "justify-start"}`}
>
<div
className={`max-w-[85%] px-4 py-2 rounded-2xl text-sm ${
msg.role === "user"
? "bg-brand-pink text-white rounded-br-md"
: "bg-white/10 text-white/90 rounded-bl-md"
}`}
>
{msg.content}
</div>
</motion.div>
))}
<div ref={messagesEndRef} />
</div>
{/* Input */}
<div className="p-3 border-t border-white/10">
<div className="flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Escribe un mensaje..."
className="flex-1 bg-white/5 border border-white/10 rounded-lg px-4 py-2 text-sm text-white placeholder:text-white/40 focus:outline-none focus:border-brand-pink"
disabled={isProcessing}
/>
<button
onClick={handleSend}
disabled={!input.trim() || isProcessing}
className="px-4 py-2 bg-brand-pink hover:bg-[#ff7bc0] disabled:opacity-50 disabled:cursor-not-allowed rounded-lg text-sm font-medium transition"
>
Enviar
</button>
</div>
<p className="text-xs text-white/40 mt-2 text-center">
O chatea directamente en Telegram
</p>
</div>
</div>
);
}