Files
sitemente/components/mission-control/HorusChat.tsx
T
2026-02-22 16:40:52 +00:00

166 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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 HorusChat() {
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]);
// Poll for responses from Horus (Telegram)
useEffect(() => {
let pollInterval: NodeJS.Timeout;
if (isProcessing) {
pollInterval = setInterval(async () => {
try {
const res = await fetch("/api/horus-mc-response");
const data = await res.json();
if (data.response) {
setMessages(prev => [...prev, {
id: `horus_${Date.now()}`,
role: "assistant",
content: data.response,
timestamp: Date.now(),
}]);
setIsProcessing(false);
}
} catch (e) {
console.error("Poll error:", e);
}
}, 3000);
}
return () => clearInterval(pollInterval);
}, [isProcessing]);
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 Horus via Telegram
await fetch("/api/horus-mc-send", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: userMessage.content }),
});
// Will receive response via polling
} catch (e) {
console.error("Send error:", e);
setMessages(prev => [...prev, {
id: `error_${Date.now()}`,
role: "assistant",
content: "Error de conexión. Prueba en Telegram.",
timestamp: Date.now(),
}]);
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</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 a Horus..."
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">
Respondo en Telegram - ¡Escríbeme ahí también!
</p>
</div>
</div>
);
}