166 lines
4.9 KiB
TypeScript
166 lines
4.9 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 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>
|
||
);
|
||
}
|