Skip to content

Commit 42b87a2

Browse files
UFO navigation and DAO UI
Improve UI/UX with floating UFO-style navigation, enhance DAO dashboard visuals (faster skeletons and richer charts), replace chatbot with dynamic AI prototype, and overhaul contact/CoNetWorKing sections into space-terminal style. Also fix animated text type and integrate lazy-loaded AI iframe. X-Lovable-Edit-ID: edt-6e5a4763-2e0a-41a1-9836-69ce8bbe78ce
2 parents b5579eb + c5852c1 commit 42b87a2

5 files changed

Lines changed: 834 additions & 466 deletions

File tree

src/components/AIChatbot.tsx

Lines changed: 148 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,106 @@
1-
import React, { useEffect, useCallback, useState } from 'react';
1+
import React, { useEffect, useCallback, useState, useMemo } from 'react';
22
import { motion, AnimatePresence } from 'framer-motion';
3-
import { X, Loader2 } from 'lucide-react';
3+
import { X, Loader2, Shield, Zap, HeadphonesIcon, Bot } from 'lucide-react';
44
import { toast } from 'sonner';
55
import aiTorAvatar from '@/assets/ai-tor-avatar.jpg';
66

7-
const PROACTIVE_MESSAGES = [
8-
"¿Puedo ayudarte a encontrar algo?",
9-
"¿Tienes alguna pregunta sobre AlienFlowSpace?",
10-
"¿Necesitas ayuda con los NFTs o las DAOs?",
11-
"¿Te gustaría conocer más sobre nuestro ecosistema?",
12-
"¿En qué puedo asistirte hoy?"
7+
// AI Personalities with unique messages and slight visual variations
8+
const AI_PERSONALITIES = [
9+
{
10+
id: 'support',
11+
name: 'AI Tor',
12+
role: 'Soporte Técnico',
13+
icon: HeadphonesIcon,
14+
color: 'from-alien-green to-emerald-500',
15+
borderColor: 'border-alien-green/50',
16+
messages: [
17+
"¿Tienes algún problema técnico? Estoy aquí para ayudarte.",
18+
"¿Necesitas ayuda con tu wallet o transacciones?",
19+
"¿Problemas conectando? Déjame echarte una mano.",
20+
"¿Algo no funciona como esperabas? Cuéntame.",
21+
]
22+
},
23+
{
24+
id: 'dao',
25+
name: 'Voz de la DAO',
26+
role: 'Estrategia',
27+
icon: Zap,
28+
color: 'from-alien-gold to-yellow-500',
29+
borderColor: 'border-alien-gold/50',
30+
messages: [
31+
"¿Quieres saber cómo participar en las votaciones?",
32+
"Hay propuestas activas esperando tu voto...",
33+
"¿Conoces los beneficios de ser miembro de la DAO?",
34+
"La comunidad está decidiendo el futuro. ¿Te unes?",
35+
]
36+
},
37+
{
38+
id: 'sentinel',
39+
name: 'Alien Sentinel',
40+
role: 'Seguridad',
41+
icon: Shield,
42+
color: 'from-purple-500 to-violet-600',
43+
borderColor: 'border-purple-500/50',
44+
messages: [
45+
"¿Preocupado por la seguridad de tus activos?",
46+
"Recuerda: nunca compartas tus claves privadas.",
47+
"¿Quieres consejos para proteger tu wallet?",
48+
"La seguridad primero. ¿Alguna duda?",
49+
]
50+
},
51+
{
52+
id: 'guide',
53+
name: 'Cosmic Guide',
54+
role: 'Navegador',
55+
icon: Bot,
56+
color: 'from-cyan-500 to-blue-500',
57+
borderColor: 'border-cyan-500/50',
58+
messages: [
59+
"¿Primera vez en AlienFlowSpace? Te guío.",
60+
"Hay mucho por explorar. ¿Por dónde empezamos?",
61+
"¿Buscas algo específico en el ecosistema?",
62+
"Déjame mostrarte lo que puedes hacer aquí.",
63+
]
64+
}
1365
];
1466

1567
const AIChatbot = () => {
1668
const [isOpen, setIsOpen] = useState(false);
1769
const [isLoading, setIsLoading] = useState(true);
1870
const [showProactive, setShowProactive] = useState(false);
19-
const [proactiveMessage, setProactiveMessage] = useState('');
2071
const [hasInteracted, setHasInteracted] = useState(false);
21-
// Nuevo estado para cargar el iframe solo tras el primer render de la DApp
2272
const [shouldLoadIframe, setShouldLoadIframe] = useState(false);
73+
const [currentPersonality, setCurrentPersonality] = useState(0);
74+
const [proactiveMessage, setProactiveMessage] = useState('');
75+
76+
// Get current personality
77+
const personality = useMemo(() => AI_PERSONALITIES[currentPersonality], [currentPersonality]);
78+
const PersonalityIcon = personality.icon;
2379

24-
// EFICIENCIA: Cargamos el iframe 2 segundos después para no bloquear el hilo principal de la DApp
80+
// Lazy load iframe after initial render
2581
useEffect(() => {
2682
const timer = setTimeout(() => {
2783
setShouldLoadIframe(true);
2884
}, 2000);
2985
return () => clearTimeout(timer);
3086
}, []);
3187

32-
// Proactive engagement - show after 45 seconds of inactivity
88+
// Proactive engagement with rotating personalities
3389
useEffect(() => {
3490
if (hasInteracted || isOpen) return;
3591

3692
const timer = setTimeout(() => {
37-
const randomMessage = PROACTIVE_MESSAGES[Math.floor(Math.random() * PROACTIVE_MESSAGES.length)];
93+
// Pick random personality and message
94+
const randomPersonalityIndex = Math.floor(Math.random() * AI_PERSONALITIES.length);
95+
const randomPersonality = AI_PERSONALITIES[randomPersonalityIndex];
96+
const randomMessage = randomPersonality.messages[Math.floor(Math.random() * randomPersonality.messages.length)];
97+
98+
setCurrentPersonality(randomPersonalityIndex);
3899
setProactiveMessage(randomMessage);
39100
setShowProactive(true);
40101

41-
// Auto-hide after 8 seconds
42-
setTimeout(() => setShowProactive(false), 8000);
102+
// Auto-hide after 10 seconds
103+
setTimeout(() => setShowProactive(false), 10000);
43104
}, 45000);
44105

45106
return () => clearTimeout(timer);
@@ -61,10 +122,7 @@ const AIChatbot = () => {
61122
setIsLoading(true);
62123
setShowProactive(false);
63124
setHasInteracted(true);
64-
setShouldLoadIframe(true); // Aseguramos carga si el usuario hace clic rápido
65-
toast.info('AI Assistant Loading', {
66-
description: 'Opening AI Tor Assistant...'
67-
});
125+
setShouldLoadIframe(true);
68126
}, []);
69127

70128
const handleIframeLoad = useCallback(() => {
@@ -86,36 +144,62 @@ const AIChatbot = () => {
86144
)}
87145
</AnimatePresence>
88146

89-
{/* Proactive Engagement Bubble */}
147+
{/* Proactive Engagement Bubble with Personality */}
90148
<AnimatePresence>
91149
{showProactive && !isOpen && (
92150
<motion.div
93151
initial={{ opacity: 0, x: 20, scale: 0.8 }}
94152
animate={{ opacity: 1, x: 0, scale: 1 }}
95153
exit={{ opacity: 0, x: 20, scale: 0.8 }}
96154
className="fixed bottom-36 right-4 sm:bottom-40 sm:right-8 z-40
97-
max-w-[250px] p-3 rounded-xl rounded-br-sm
98-
bg-gradient-to-br from-alien-gold/90 to-alien-gold-dark/90
99-
backdrop-blur-md border border-alien-gold-light/50
100-
shadow-lg shadow-alien-gold/20 cursor-pointer"
155+
max-w-[280px] rounded-2xl rounded-br-sm overflow-hidden
156+
backdrop-blur-xl border shadow-2xl cursor-pointer"
157+
style={{
158+
background: 'linear-gradient(135deg, rgba(17,17,25,0.95) 0%, rgba(30,30,40,0.95) 100%)',
159+
borderColor: `rgba(${personality.id === 'support' ? '57,255,20' : personality.id === 'dao' ? '240,216,130' : personality.id === 'sentinel' ? '168,85,247' : '34,211,238'},0.4)`
160+
}}
101161
onClick={handleOpen}
102162
>
103-
<p className="text-alien-space-dark font-exo text-sm font-medium">
104-
{proactiveMessage}
105-
</p>
163+
{/* Personality Header */}
164+
<div className={`bg-gradient-to-r ${personality.color} p-2 px-3 flex items-center gap-2`}>
165+
<PersonalityIcon className="w-4 h-4 text-white" />
166+
<div className="flex-1">
167+
<p className="text-white text-xs font-nasalization font-bold">{personality.name}</p>
168+
<p className="text-white/80 text-[10px] font-exo">{personality.role}</p>
169+
</div>
170+
<div className="w-6 h-6 rounded-full overflow-hidden border border-white/30">
171+
<img
172+
src={aiTorAvatar}
173+
alt={personality.name}
174+
className="w-full h-full object-cover"
175+
style={{
176+
filter: personality.id === 'sentinel' ? 'hue-rotate(270deg)' :
177+
personality.id === 'guide' ? 'hue-rotate(180deg)' :
178+
personality.id === 'dao' ? 'sepia(30%)' : 'none'
179+
}}
180+
/>
181+
</div>
182+
</div>
183+
{/* Message */}
184+
<div className="p-3">
185+
<p className="text-gray-200 font-exo text-sm leading-relaxed">
186+
{proactiveMessage}
187+
</p>
188+
</div>
189+
{/* Close button */}
106190
<button
107191
onClick={(e) => { e.stopPropagation(); setShowProactive(false); }}
108-
className="absolute -top-2 -right-2 w-5 h-5 bg-alien-space-dark rounded-full
109-
flex items-center justify-center border border-alien-gold/50
110-
hover:bg-alien-gold/20 transition-colors"
192+
className="absolute -top-2 -right-2 w-6 h-6 bg-alien-space-dark rounded-full
193+
flex items-center justify-center border border-gray-600
194+
hover:bg-gray-700 transition-colors shadow-lg"
111195
>
112-
<X className="w-3 h-3 text-alien-gold" />
196+
<X className="w-3 h-3 text-gray-400" />
113197
</button>
114198
</motion.div>
115199
)}
116200
</AnimatePresence>
117201

118-
{/* Floating Button */}
202+
{/* Floating Button with pulsing ring */}
119203
<motion.button
120204
initial={{ opacity: 0, scale: 0.5 }}
121205
animate={{ opacity: 1, scale: 1 }}
@@ -125,18 +209,22 @@ const AIChatbot = () => {
125209
bg-gradient-to-br from-alien-gold to-alien-gold-dark backdrop-blur-md
126210
border-2 border-alien-gold-light rounded-full
127211
hover:scale-110 transition-all duration-300
128-
shadow-2xl hover:shadow-alien-gold/50 ai-button-pulse
129-
overflow-hidden w-14 h-14 sm:w-16 sm:h-16"
212+
shadow-2xl hover:shadow-alien-gold/50
213+
overflow-hidden w-14 h-14 sm:w-16 sm:h-16
214+
relative"
130215
aria-label="Open AI Assistant"
131216
>
217+
{/* Pulsing ring */}
218+
<span className="absolute inset-0 rounded-full animate-ping bg-alien-gold/30" style={{ animationDuration: '2s' }} />
219+
<span className="absolute inset-0 rounded-full animate-pulse bg-gradient-to-br from-alien-green/20 to-transparent" />
132220
<img
133221
src={aiTorAvatar}
134222
alt="AI Tor"
135-
className="w-full h-full object-cover"
223+
className="w-full h-full object-cover relative z-10"
136224
/>
137225
</motion.button>
138226

139-
{/* Chat Window - Fullscreen on mobile, optimized size on desktop */}
227+
{/* Chat Window */}
140228
<AnimatePresence>
141229
{isOpen && (
142230
<motion.div
@@ -152,24 +240,29 @@ const AIChatbot = () => {
152240
sm:max-h-[calc(100vh-10rem)]
153241
bg-alien-space-dark/98 backdrop-blur-xl
154242
border-2 border-alien-gold/40 rounded-xl
155-
shadow-2xl overflow-hidden chat-glow"
243+
shadow-2xl overflow-hidden"
244+
style={{
245+
boxShadow: '0 0 40px rgba(57,255,20,0.1), 0 0 80px rgba(240,216,130,0.05)'
246+
}}
156247
>
157248
{/* Header */}
158249
<div className="bg-gradient-to-r from-alien-space-dark via-alien-space/90 to-alien-space-dark
159250
border-b border-alien-gold/30 p-3 sm:p-4 flex items-center justify-between">
160251
<div className="flex items-center gap-3">
161-
<div className="w-10 h-10 sm:w-11 sm:h-11 rounded-full overflow-hidden border-2 border-alien-gold/50">
252+
<div className="w-10 h-10 sm:w-11 sm:h-11 rounded-full overflow-hidden border-2 border-alien-gold/50 relative">
162253
<img
163254
src={aiTorAvatar}
164255
alt="AI Tor"
165256
className="w-full h-full object-cover"
166257
/>
258+
{/* Online indicator */}
259+
<span className="absolute bottom-0 right-0 w-3 h-3 bg-alien-green rounded-full border-2 border-alien-space-dark" />
167260
</div>
168261
<div>
169262
<h3 className="text-alien-gold font-bold font-nasalization text-sm sm:text-base">
170-
AI Assistant
263+
AI Tor
171264
</h3>
172-
<p className="text-xs text-alien-green font-exo">AI Tor</p>
265+
<p className="text-xs text-alien-green font-exo">Asistente Inteligente</p>
173266
</div>
174267
<div className="flex items-center gap-1.5 ml-2">
175268
{isLoading ? (
@@ -179,10 +272,7 @@ const AIChatbot = () => {
179272
<span className="typing-dot" style={{ animationDelay: '0.4s' }} />
180273
</div>
181274
) : (
182-
<>
183-
<span className="w-2 h-2 bg-alien-green rounded-full animate-pulse" />
184-
<span className="text-xs text-muted-foreground hidden sm:inline">Online</span>
185-
</>
275+
<span className="text-xs text-alien-green/80 hidden sm:inline font-exo">Conectado</span>
186276
)}
187277
</div>
188278
</div>
@@ -203,24 +293,31 @@ const AIChatbot = () => {
203293
animate={{ opacity: 1 }}
204294
exit={{ opacity: 0 }}
205295
className="absolute inset-0 top-14 sm:top-16 flex flex-col items-center justify-center
206-
bg-alien-space-dark/95 z-10"
296+
bg-alien-space-dark/98 z-10"
207297
>
208298
<div className="relative">
209299
<div className="w-20 h-20 rounded-full overflow-hidden border-2 border-alien-gold/50 mb-4">
210300
<img
211301
src={aiTorAvatar}
212302
alt="AI Tor Loading"
213-
className="w-full h-full object-cover animate-pulse"
303+
className="w-full h-full object-cover"
214304
/>
215305
</div>
216306
<Loader2 className="absolute -bottom-1 -right-1 h-8 w-8 text-alien-gold animate-spin" />
217307
</div>
218-
<p className="text-alien-gold font-nasalization text-sm mt-4 uppercase tracking-tighter">Establishing Link...</p>
308+
<p className="text-alien-gold font-nasalization text-sm mt-4 uppercase tracking-wider">
309+
Sincronizando...
310+
</p>
311+
<div className="flex gap-1.5 mt-3">
312+
<span className="w-2 h-2 bg-alien-green rounded-full animate-bounce" style={{ animationDelay: '0s' }} />
313+
<span className="w-2 h-2 bg-alien-green rounded-full animate-bounce" style={{ animationDelay: '0.15s' }} />
314+
<span className="w-2 h-2 bg-alien-green rounded-full animate-bounce" style={{ animationDelay: '0.3s' }} />
315+
</div>
219316
</motion.div>
220317
)}
221318
</AnimatePresence>
222319

223-
{/* iframe Content con carga diferida */}
320+
{/* iframe Content with lazy loading */}
224321
{shouldLoadIframe && (
225322
<iframe
226323
src="https://aitor.lovable.app/"
@@ -230,7 +327,9 @@ const AIChatbot = () => {
230327
onLoad={handleIframeLoad}
231328
onError={() => {
232329
setIsLoading(false);
233-
toast.error('AI Assistant Error');
330+
toast.error('Error de conexión', {
331+
description: 'No se pudo conectar con AI Tor. Intenta de nuevo.'
332+
});
234333
}}
235334
/>
236335
)}

src/components/AnimatedText.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
2-
import { motion, TargetAndTransition, VariantLabels } from "framer-motion";
1+
import { motion, TargetAndTransition, VariantLabels, Transition } from "framer-motion";
32
import React from "react";
43

54
// Props for the AnimatedText component
65
interface AnimatedTextProps {
7-
children: React.ReactNode; // Content of the animated text
8-
className?: string; // Additional classes to customize styles
9-
initial?: boolean | TargetAndTransition | VariantLabels; // Initial animation configuration (optional)
10-
animate?: boolean | TargetAndTransition | VariantLabels; // Final animation configuration (optional)
11-
transition?: { duration?: number; ease?: string | number[]; delay?: number }; // Transition configuration (optional)
6+
children: React.ReactNode;
7+
className?: string;
8+
initial?: boolean | TargetAndTransition | VariantLabels;
9+
animate?: boolean | TargetAndTransition | VariantLabels;
10+
transition?: Transition;
1211
}
1312

1413
const AnimatedText: React.FC<AnimatedTextProps> = ({
1514
children,
1615
className = "",
17-
initial = { opacity: 0, y: 20 }, // Default values
16+
initial = { opacity: 0, y: 20 },
1817
animate = { opacity: 1, y: 0 },
1918
transition = { duration: 0.8 },
2019
}) => {

0 commit comments

Comments
 (0)