|
1 | | -import React, { useEffect, useCallback, useState, useMemo } from 'react'; |
| 1 | +import React, { useEffect, useState, useMemo, useCallback } from 'react'; |
2 | 2 | import { motion, AnimatePresence } from 'framer-motion'; |
3 | | -import { X, Loader2, Zap, Sparkles, Rocket, HeadphonesIcon, Shield } from 'lucide-react'; |
4 | | -import { toast } from 'sonner'; |
| 3 | +import { X, Loader2, Sparkles, Rocket, Zap } from 'lucide-react'; |
5 | 4 | import aiTorAvatar from '@/assets/ai-tor-avatar.jpg'; |
6 | 5 |
|
7 | | -// Configuración de Personalidades/Roles basada en tus versiones previas |
8 | 6 | const AI_DAPP_ROLES = [ |
9 | | - { |
10 | | - id: 'welcome', |
11 | | - name: 'AI Tor', |
12 | | - role: 'Sistemas Tor', |
13 | | - icon: Rocket, |
14 | | - color: 'from-alien-green to-emerald-500', |
15 | | - borderColor: 'rgba(57,255,20,0.5)', |
16 | | - messages: [ |
17 | | - "👽 ¡Conexión establecida! Soy AI Tor. ¿Exploramos el ecosistema juntas?", |
18 | | - "✨ ¿Buscas algo específico en la DAO? Estoy sincronizado y listo.", |
19 | | - "🌌 Tu viaje cósmico empieza aquí. Pregúntame lo que necesites." |
20 | | - ] |
21 | | - }, |
22 | | - { |
23 | | - id: 'promo', |
24 | | - name: 'AI Tor', |
25 | | - role: 'Oportunidad', |
26 | | - icon: Sparkles, |
27 | | - color: 'from-pink-500 to-rose-500', |
28 | | - borderColor: 'rgba(236,72,153,0.6)', |
29 | | - messages: [ |
30 | | - "🚀 ¡Misión Especial! Únete a la DAO y desbloquea NFTs exclusivos.", |
31 | | - "🔥 Academy, Clubs y CoNetWorKing... todo a tu alcance.", |
32 | | - "💎 Coleccionables únicos detectados. ¿Quieres verlas?" |
33 | | - ] |
34 | | - }, |
35 | | - { |
36 | | - id: 'dao', |
37 | | - name: 'Voz de la DAO', |
38 | | - role: 'Estrategia', |
39 | | - icon: Zap, |
40 | | - color: 'from-alien-gold to-yellow-500', |
41 | | - borderColor: 'rgba(240,216,130,0.6)', |
42 | | - messages: [ |
43 | | - "⚖️ Alerta de Gobernanza: ¡Hay nuevas propuestas! Tu voto es tu poder.", |
44 | | - "🏛️ ¿Listo para influir en el presente? Mira cómo participar en la DAO.", |
45 | | - "🌟 Formar parte de la DAO es decidir el presente." |
46 | | - ] |
47 | | - } |
| 7 | + { id: 'welcome', role: 'Sistemas Tor', icon: Rocket, color: '#39FF14', messages: ["👽 ¡Conexión establecida! Soy AI Tor. ¿Exploramos el ecosistema?"] }, |
| 8 | + { id: 'promo', role: 'Oportunidad', icon: Sparkles, color: '#EC4899', messages: ["🚀 ¡Misión Especial! Únete a la DAO y desbloquea NFTs."] }, |
| 9 | + { id: 'dao', role: 'Voz de la DAO', icon: Zap, color: '#F0D882', messages: ["⚖️ ¡Nuevas propuestas de gobernanza! Tu voto es tu poder."] } |
48 | 10 | ]; |
49 | 11 |
|
50 | 12 | const AIChatbot = () => { |
51 | 13 | const [isOpen, setIsOpen] = useState(false); |
52 | 14 | const [isLoading, setIsLoading] = useState(true); |
53 | 15 | const [showProactive, setShowProactive] = useState(false); |
54 | | - const [proactiveMessage, setProactiveMessage] = useState(''); |
55 | 16 | const [hasInteracted, setHasInteracted] = useState(false); |
56 | 17 | const [currentRoleIndex, setCurrentRoleIndex] = useState(0); |
57 | | - const [messageCount, setMessageCount] = useState(0); |
58 | 18 |
|
59 | 19 | const currentRole = useMemo(() => AI_DAPP_ROLES[currentRoleIndex], [currentRoleIndex]); |
60 | 20 |
|
61 | | - // 1. Gestión Proactiva con tus tiempos: 18s y 33s |
| 21 | + // Gestión Proactiva (Tiempos 18s / 33s) |
62 | 22 | useEffect(() => { |
63 | 23 | if (hasInteracted || isOpen) return; |
64 | | - |
65 | | - const delay = messageCount === 0 ? 18000 : 33000; |
66 | | - |
67 | 24 | const timer = setTimeout(() => { |
68 | | - let nextIndex = messageCount === 0 ? 0 : Math.floor(Math.random() * AI_DAPP_ROLES.length); |
69 | | - |
70 | | - const role = AI_DAPP_ROLES[nextIndex]; |
71 | | - const randomMsg = role.messages[Math.floor(Math.random() * role.messages.length)]; |
72 | | - |
73 | | - setCurrentRoleIndex(nextIndex); |
74 | | - setProactiveMessage(randomMsg); |
| 25 | + setCurrentRoleIndex(Math.floor(Math.random() * AI_DAPP_ROLES.length)); |
75 | 26 | setShowProactive(true); |
76 | | - setMessageCount(prev => prev + 1); |
77 | | - |
78 | | - // Auto-ocultar después de 10 segundos |
79 | 27 | setTimeout(() => setShowProactive(false), 10000); |
80 | | - }, delay); |
81 | | - |
| 28 | + }, 18000); |
82 | 29 | return () => clearTimeout(timer); |
83 | | - }, [hasInteracted, isOpen, messageCount]); |
| 30 | + }, [hasInteracted, isOpen]); |
84 | 31 |
|
85 | | - // 2. Tracking de interacción robusto (Cualquier interacción humana real) |
| 32 | + // Tracking de interacción (Escucha si el usuario mueve el mouse o tecla) |
86 | 33 | useEffect(() => { |
87 | 34 | const handleInteraction = () => setHasInteracted(true); |
88 | | - const events = ['mousedown', 'keydown', 'touchstart', 'wheel']; |
89 | | - |
90 | | - events.forEach(event => window.addEventListener(event, handleInteraction, { once: true })); |
91 | | - return () => events.forEach(event => window.removeEventListener(event, handleInteraction)); |
92 | | - }, []); |
93 | | - |
94 | | - // 3. Handlers con Feedback (Toast) |
95 | | - const handleOpen = useCallback(() => { |
96 | | - setIsOpen(true); |
97 | | - setIsLoading(true); |
98 | | - setShowProactive(false); |
99 | | - setHasInteracted(true); |
100 | | - toast.info('AI Assistant', { |
101 | | - description: 'Estableciendo enlace con AI Tor...', |
102 | | - duration: 3000, |
103 | | - }); |
104 | | - }, []); |
105 | | - |
106 | | - const handleIframeLoad = useCallback(() => { |
107 | | - setIsLoading(false); |
108 | | - }, []); |
109 | | - |
110 | | - const handleIframeError = useCallback(() => { |
111 | | - setIsLoading(false); |
112 | | - toast.error('Error de Enlace', { |
113 | | - description: 'No se pudo conectar con AI Tor. Reintenta más tarde.' |
114 | | - }); |
| 35 | + const events = ['mousedown', 'keydown', 'touchstart']; |
| 36 | + events.forEach(e => window.addEventListener(e, handleInteraction, { once: true })); |
| 37 | + return () => events.forEach(e => window.removeEventListener(e, handleInteraction)); |
115 | 38 | }, []); |
116 | 39 |
|
117 | 40 | return ( |
118 | 41 | <> |
119 | | - {/* Overlay con Blur dinámico */} |
120 | | - <AnimatePresence> |
121 | | - {isOpen && ( |
122 | | - <motion.div |
123 | | - initial={{ opacity: 0 }} |
124 | | - animate={{ opacity: 1 }} |
125 | | - exit={{ opacity: 0 }} |
126 | | - onClick={() => setIsOpen(false)} |
127 | | - className="fixed inset-0 z-40 bg-black/80 backdrop-blur-sm" |
128 | | - /> |
129 | | - )} |
130 | | - </AnimatePresence> |
| 42 | + {/* Botón Avatar con entrada desde el Hiperespacio */} |
| 43 | + <motion.button |
| 44 | + initial={{ scale: 0, rotate: -180, filter: "blur(20px)" }} |
| 45 | + animate={{ scale: 1, rotate: 0, filter: "blur(0px)" }} |
| 46 | + transition={{ type: "spring", stiffness: 200, damping: 20, delay: 1.5 }} |
| 47 | + whileHover={{ scale: 1.1, boxShadow: `0 0 25px ${currentRole.color}44` }} |
| 48 | + onClick={() => setIsOpen(!isOpen)} |
| 49 | + className="fixed bottom-6 right-6 z-[120] w-14 h-14 sm:w-16 sm:h-16 rounded-full border-2 border-alien-gold/40 overflow-hidden bg-black shadow-2xl" |
| 50 | + > |
| 51 | + <img src={aiTorAvatar} alt="AI Tor" className="w-full h-full object-cover" /> |
| 52 | + </motion.button> |
131 | 53 |
|
132 | | - {/* Burbuja Proactiva / Promocional */} |
| 54 | + {/* Burbuja Proactiva */} |
133 | 55 | <AnimatePresence> |
134 | 56 | {showProactive && !isOpen && ( |
135 | 57 | <motion.div |
136 | | - initial={{ opacity: 0, x: 20, scale: 0.8, y: 0 }} |
137 | | - animate={{ opacity: 1, x: 0, scale: 1, y: 0 }} |
138 | | - exit={{ opacity: 0, x: 20, scale: 0.8 }} |
139 | | - className="fixed bottom-36 right-4 sm:bottom-40 sm:right-8 z-40 |
140 | | - max-w-[300px] p-4 rounded-2xl rounded-br-sm border-2 |
141 | | - shadow-[0_10px_40px_rgba(0,0,0,0.5)] cursor-pointer" |
142 | | - style={{ |
143 | | - background: 'linear-gradient(135deg, rgba(15,15,25,0.98) 0%, rgba(30,30,45,0.98) 100%)', |
144 | | - borderColor: currentRole.borderColor |
145 | | - }} |
146 | | - onClick={handleOpen} |
| 58 | + initial={{ opacity: 0, x: 50, scale: 0.8 }} |
| 59 | + animate={{ opacity: 1, x: 0, scale: 1 }} |
| 60 | + exit={{ opacity: 0, x: 50, scale: 0.8 }} |
| 61 | + onClick={() => setIsOpen(true)} |
| 62 | + className="fixed bottom-24 right-6 z-[121] max-w-[260px] p-4 rounded-2xl bg-black/90 backdrop-blur-xl border border-white/10 shadow-2xl cursor-pointer" |
| 63 | + style={{ borderLeft: `4px solid ${currentRole.color}` }} |
147 | 64 | > |
148 | | - <div className="flex gap-3"> |
149 | | - <div className="shrink-0 relative"> |
150 | | - <div className="w-12 h-12 rounded-full overflow-hidden border border-alien-gold/30"> |
151 | | - <img src={aiTorAvatar} alt="AI Tor" className="w-full h-full object-cover" /> |
152 | | - </div> |
153 | | - <div className="absolute -bottom-1 -right-1 bg-alien-space-dark rounded-full p-1 border border-alien-gold/40"> |
154 | | - <currentRole.icon className="w-3 h-3 text-alien-gold" /> |
155 | | - </div> |
156 | | - </div> |
157 | | - <div className="flex-1"> |
158 | | - <div className="flex items-center justify-between mb-1"> |
159 | | - <span className="text-[10px] font-nasalization text-alien-gold tracking-widest uppercase italic"> |
160 | | - {currentRole.role} |
161 | | - </span> |
162 | | - </div> |
163 | | - <p className="text-gray-200 font-exo text-[13px] leading-snug"> |
164 | | - {proactiveMessage} |
165 | | - </p> |
166 | | - {currentRole.id === 'promo' && ( |
167 | | - <span className="text-[10px] text-alien-gold mt-2 block font-bold animate-pulse"> |
168 | | - VER MÁS AHORA → |
169 | | - </span> |
170 | | - )} |
171 | | - </div> |
| 65 | + <div className="flex items-center gap-2 mb-1"> |
| 66 | + <currentRole.icon size={12} style={{ color: currentRole.color }} /> |
| 67 | + <span className="text-[10px] font-nasalization text-alien-gold tracking-widest uppercase">{currentRole.role}</span> |
172 | 68 | </div> |
173 | | - <button |
174 | | - onClick={(e) => { e.stopPropagation(); setShowProactive(false); }} |
175 | | - className="absolute -top-2 -right-2 w-6 h-6 bg-alien-space-dark rounded-full |
176 | | - flex items-center justify-center border border-white/10 hover:bg-red-900/40 transition-colors" |
177 | | - > |
178 | | - <X className="w-3 h-3 text-gray-400" /> |
179 | | - </button> |
| 69 | + <p className="text-white text-xs font-exo leading-tight">{currentRole.messages[0]}</p> |
180 | 70 | </motion.div> |
181 | 71 | )} |
182 | 72 | </AnimatePresence> |
183 | 73 |
|
184 | | - {/* Botón Flotante Principal (Avatar) */} |
185 | | - <motion.button |
186 | | - initial={{ opacity: 0, scale: 0.5 }} |
187 | | - animate={{ opacity: 1, scale: 1 }} |
188 | | - whileHover={{ scale: 1.1 }} |
189 | | - whileTap={{ scale: 0.9 }} |
190 | | - onClick={handleOpen} |
191 | | - className="fixed bottom-20 right-4 sm:bottom-24 sm:right-8 z-40 |
192 | | - w-14 h-14 sm:w-16 sm:h-16 rounded-full border-2 border-alien-gold-light/50 |
193 | | - shadow-2xl overflow-hidden ai-button-pulse" |
194 | | - > |
195 | | - <img src={aiTorAvatar} alt="AI Tor" className="w-full h-full object-cover" /> |
196 | | - </motion.button> |
197 | | - |
198 | | - {/* Ventana de Chat (Iframe de Lovable) */} |
| 74 | + {/* Interfaz de Chat Adaptable (Móvil/Tablet/PC) */} |
199 | 75 | <AnimatePresence> |
200 | 76 | {isOpen && ( |
201 | 77 | <motion.div |
202 | | - initial={{ opacity: 0, y: 100, scale: 0.8 }} |
203 | | - animate={{ opacity: 1, y: 0, scale: 1 }} |
204 | | - exit={{ opacity: 0, y: 100, scale: 0.8 }} |
205 | | - className="fixed z-50 inset-2 sm:inset-auto sm:bottom-24 sm:right-8 |
206 | | - sm:w-[360px] md:w-[400px] sm:h-[550px] md:h-[600px] |
207 | | - bg-alien-space-dark/98 backdrop-blur-xl border-2 border-alien-gold/40 |
208 | | - rounded-2xl shadow-2xl overflow-hidden flex flex-col chat-glow" |
| 78 | + initial={{ opacity: 0, scale: 0, x: 150, y: 150, filter: "blur(20px)" }} |
| 79 | + animate={{ opacity: 1, scale: 1, x: 0, y: 0, filter: "blur(0px)" }} |
| 80 | + exit={{ opacity: 0, scale: 0, x: 150, y: 150, filter: "blur(20px)" }} |
| 81 | + transition={{ duration: 0.5, ease: "circOut" }} |
| 82 | + className="fixed z-[9999] bottom-6 right-6 left-6 sm:left-auto sm:w-[380px] md:w-[420px] h-[75vh] max-h-[600px] bg-alien-space-dark rounded-[2.5rem] border-2 border-alien-gold/30 shadow-[0_0_80px_rgba(0,0,0,0.9)] overflow-hidden flex flex-col" |
209 | 83 | > |
210 | | - {/* Header de la Interfaz */} |
211 | | - <div className="bg-gradient-to-r from-alien-space-dark via-alien-space to-alien-space-dark |
212 | | - border-b border-alien-gold/20 p-4 flex items-center justify-between"> |
| 84 | + {/* Header Interface */} |
| 85 | + <div className="p-5 bg-black/60 border-b border-white/10 flex justify-between items-center"> |
213 | 86 | <div className="flex items-center gap-3"> |
214 | | - <div className="w-10 h-10 rounded-full border-2 border-alien-gold/40 overflow-hidden"> |
215 | | - <img src={aiTorAvatar} alt="AI Tor" className="w-full h-full object-cover" /> |
| 87 | + <div className="w-9 h-9 rounded-full border border-alien-green/50 p-0.5"> |
| 88 | + <img src={aiTorAvatar} alt="AI" className="w-full h-full rounded-full object-cover" /> |
216 | 89 | </div> |
217 | 90 | <div> |
218 | | - <h3 className="text-alien-gold font-bold font-nasalization text-[12px] tracking-wider">AI TOR INTERFACE</h3> |
219 | | - <div className="flex items-center gap-1.5"> |
220 | | - <span className="w-2 h-2 bg-alien-green rounded-full animate-pulse" /> |
221 | | - <span className="text-[10px] text-alien-green font-exo uppercase tracking-tighter">Sincronización Cuántica</span> |
222 | | - </div> |
| 91 | + <h3 className="text-alien-gold font-nasalization text-[10px] tracking-widest leading-none">AI TOR INTERFACE</h3> |
| 92 | + <span className="text-alien-green text-[8px] font-exo uppercase animate-pulse">Online • Quantum Link</span> |
223 | 93 | </div> |
224 | 94 | </div> |
225 | | - <button onClick={() => setIsOpen(false)} className="p-2 hover:bg-white/10 rounded-lg text-alien-gold"> |
226 | | - <X size={20} /> |
| 95 | + <button onClick={() => setIsOpen(false)} className="p-2 hover:bg-white/10 rounded-full text-white/50 hover:text-alien-green transition-all"> |
| 96 | + <X size={22} /> |
227 | 97 | </button> |
228 | 98 | </div> |
229 | 99 |
|
230 | | - {/* Contenedor del Iframe con Loading State */} |
231 | | - <div className="flex-1 relative bg-black"> |
| 100 | + {/* Iframe & Radar de Carga */} |
| 101 | + <div className="flex-1 bg-black relative"> |
232 | 102 | {isLoading && ( |
233 | | - <div className="absolute inset-0 flex flex-col items-center justify-center bg-alien-space-dark/95 z-10"> |
234 | | - <div className="relative mb-4"> |
235 | | - <div className="w-20 h-20 rounded-full overflow-hidden border-2 border-alien-gold/30"> |
236 | | - <img src={aiTorAvatar} alt="Loading" className="w-full h-full object-cover animate-pulse opacity-50" /> |
237 | | - </div> |
238 | | - <Loader2 className="absolute -bottom-2 -right-2 h-8 w-8 text-alien-gold animate-spin" /> |
239 | | - </div> |
240 | | - <p className="text-alien-gold font-nasalization text-[10px] tracking-widest uppercase">Enlazando Red Neural...</p> |
241 | | - {/* Animación de puntos que pedías */} |
242 | | - <div className="flex gap-1.5 mt-4"> |
243 | | - <div className="w-1.5 h-1.5 bg-alien-gold rounded-full animate-bounce" /> |
244 | | - <div className="w-1.5 h-1.5 bg-alien-gold rounded-full animate-bounce [animation-delay:-.3s]" /> |
245 | | - <div className="w-1.5 h-1.5 bg-alien-gold rounded-full animate-bounce [animation-delay:-.5s]" /> |
246 | | - </div> |
| 103 | + <div className="absolute inset-0 flex flex-col items-center justify-center bg-alien-space-dark z-10"> |
| 104 | + <motion.div |
| 105 | + animate={{ scale: [1, 1.3, 1], opacity: [0.2, 0.5, 0.2] }} |
| 106 | + transition={{ repeat: Infinity, duration: 1.5 }} |
| 107 | + className="w-24 h-24 rounded-full border border-alien-green absolute" |
| 108 | + /> |
| 109 | + <Loader2 className="w-10 h-10 text-alien-green animate-spin mb-4" /> |
| 110 | + <p className="text-alien-green font-nasalization text-[8px] tracking-[0.4em]">SYNCHRONIZING...</p> |
247 | 111 | </div> |
248 | 112 | )} |
249 | | - <iframe |
250 | | - src="https://aitor.lovable.app/" |
251 | | - className="w-full h-full border-none" |
252 | | - title="AI Tor Assistant" |
253 | | - allow="microphone; camera; clipboard-write; geolocation" |
254 | | - onLoad={handleIframeLoad} |
255 | | - onError={handleIframeError} |
256 | | - /> |
| 113 | + <iframe src="https://aitor.lovable.app/" className="w-full h-full border-none" onLoad={() => setIsLoading(false)} title="AI Tor" /> |
257 | 114 | </div> |
258 | 115 | </motion.div> |
259 | 116 | )} |
|
0 commit comments