Skip to content

Commit ec52160

Browse files
Changes
1 parent ddeca66 commit ec52160

7 files changed

Lines changed: 903 additions & 390 deletions

File tree

src/components/AIChatbot.tsx

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

77
// AI Personalities with unique messages and slight visual variations
8+
// Now includes promotional messages integrated into personalities
89
const AI_PERSONALITIES = [
910
{
1011
id: 'support',
@@ -61,6 +62,34 @@ const AI_PERSONALITIES = [
6162
"¿Buscas algo específico en el ecosistema?",
6263
"Déjame mostrarte lo que puedes hacer aquí.",
6364
]
65+
},
66+
{
67+
id: 'promo',
68+
name: 'AI Tor',
69+
role: 'Promociones',
70+
icon: Sparkles,
71+
color: 'from-pink-500 to-rose-500',
72+
borderColor: 'border-pink-500/50',
73+
messages: [
74+
"🚀 ¡Únete a AlienFlowSpace DAO y obtén NFTs exclusivos!",
75+
"🎁 Descubre los beneficios de ser miembro de la DAO",
76+
"💎 Colecciona NFTs únicos en nuestro ecosistema",
77+
"🌟 ¡Nuevas propuestas en la DAO! Tu voto cuenta",
78+
"🔥 Explora Academy, Clubs y CoNetWorKing - ¡Todo te espera!",
79+
]
80+
},
81+
{
82+
id: 'welcome',
83+
name: 'AI Tor',
84+
role: 'Bienvenida',
85+
icon: Rocket,
86+
color: 'from-orange-500 to-amber-500',
87+
borderColor: 'border-orange-500/50',
88+
messages: [
89+
"👽 ¡Bienvenido viajero! ¿Primera vez aquí? Te ayudo a explorar",
90+
"🌌 El universo AlienFlowSpace te espera. ¿Qué quieres descubrir?",
91+
"✨ ¿Listo para tu viaje cósmico? Pregúntame lo que quieras",
92+
]
6493
}
6594
];
6695

@@ -72,6 +101,7 @@ const AIChatbot = () => {
72101
const [shouldLoadIframe, setShouldLoadIframe] = useState(false);
73102
const [currentPersonality, setCurrentPersonality] = useState(0);
74103
const [proactiveMessage, setProactiveMessage] = useState('');
104+
const [messageCount, setMessageCount] = useState(0);
75105

76106
// Get current personality
77107
const personality = useMemo(() => AI_PERSONALITIES[currentPersonality], [currentPersonality]);
@@ -85,10 +115,13 @@ const AIChatbot = () => {
85115
return () => clearTimeout(timer);
86116
}, []);
87117

88-
// Proactive engagement with rotating personalities
118+
// Proactive engagement with rotating personalities - shorter initial delay
89119
useEffect(() => {
90120
if (hasInteracted || isOpen) return;
91121

122+
// First message after 30 seconds
123+
const initialDelay = messageCount === 0 ? 30000 : 120000; // 30s first, then 2min
124+
92125
const timer = setTimeout(() => {
93126
// Pick random personality and message
94127
const randomPersonalityIndex = Math.floor(Math.random() * AI_PERSONALITIES.length);
@@ -98,21 +131,31 @@ const AIChatbot = () => {
98131
setCurrentPersonality(randomPersonalityIndex);
99132
setProactiveMessage(randomMessage);
100133
setShowProactive(true);
134+
setMessageCount(prev => prev + 1);
101135

102-
// Auto-hide after 10 seconds
103-
setTimeout(() => setShowProactive(false), 10000);
104-
}, 45000);
136+
// Auto-hide after 12 seconds
137+
setTimeout(() => setShowProactive(false), 12000);
138+
}, initialDelay);
105139

106140
return () => clearTimeout(timer);
107-
}, [hasInteracted, isOpen]);
141+
}, [hasInteracted, isOpen, messageCount]);
108142

109-
// Track user interaction
143+
// Track user interaction - only on meaningful interactions
110144
useEffect(() => {
111-
const handleInteraction = () => setHasInteracted(true);
112-
window.addEventListener('click', handleInteraction, { once: true });
113-
window.addEventListener('scroll', handleInteraction, { once: true });
145+
const handleInteraction = () => {
146+
// Don't mark as interacted if just scrolling slightly
147+
if (window.scrollY > 200) {
148+
setHasInteracted(true);
149+
}
150+
};
151+
152+
const handleClick = () => setHasInteracted(true);
153+
154+
window.addEventListener('click', handleClick, { once: true });
155+
window.addEventListener('scroll', handleInteraction);
156+
114157
return () => {
115-
window.removeEventListener('click', handleInteraction);
158+
window.removeEventListener('click', handleClick);
116159
window.removeEventListener('scroll', handleInteraction);
117160
};
118161
}, []);
@@ -144,19 +187,19 @@ const AIChatbot = () => {
144187
)}
145188
</AnimatePresence>
146189

147-
{/* Proactive Engagement Bubble with Personality */}
190+
{/* Proactive Engagement Bubble with Personality - FIXED: Right side */}
148191
<AnimatePresence>
149192
{showProactive && !isOpen && (
150193
<motion.div
151194
initial={{ opacity: 0, x: 20, scale: 0.8 }}
152195
animate={{ opacity: 1, x: 0, scale: 1 }}
153196
exit={{ opacity: 0, x: 20, scale: 0.8 }}
154197
className="fixed bottom-36 right-4 sm:bottom-40 sm:right-8 z-40
155-
max-w-[280px] rounded-2xl rounded-br-sm overflow-hidden
198+
max-w-[300px] rounded-2xl rounded-br-sm overflow-hidden
156199
backdrop-blur-xl border shadow-2xl cursor-pointer"
157200
style={{
158201
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)`
202+
borderColor: `rgba(${personality.id === 'support' ? '57,255,20' : personality.id === 'dao' ? '240,216,130' : personality.id === 'sentinel' ? '168,85,247' : personality.id === 'promo' ? '236,72,153' : personality.id === 'welcome' ? '249,115,22' : '34,211,238'},0.4)`
160203
}}
161204
onClick={handleOpen}
162205
>
@@ -175,7 +218,9 @@ const AIChatbot = () => {
175218
style={{
176219
filter: personality.id === 'sentinel' ? 'hue-rotate(270deg)' :
177220
personality.id === 'guide' ? 'hue-rotate(180deg)' :
178-
personality.id === 'dao' ? 'sepia(30%)' : 'none'
221+
personality.id === 'dao' ? 'sepia(30%)' :
222+
personality.id === 'promo' ? 'hue-rotate(320deg)' :
223+
personality.id === 'welcome' ? 'hue-rotate(30deg)' : 'none'
179224
}}
180225
/>
181226
</div>
@@ -185,6 +230,12 @@ const AIChatbot = () => {
185230
<p className="text-gray-200 font-exo text-sm leading-relaxed">
186231
{proactiveMessage}
187232
</p>
233+
{/* CTA for promo messages */}
234+
{personality.id === 'promo' && (
235+
<span className="inline-block mt-2 text-xs text-alien-gold font-nasalization hover:text-alien-green transition-colors">
236+
Descubre más →
237+
</span>
238+
)}
188239
</div>
189240
{/* Close button */}
190241
<button
@@ -199,7 +250,7 @@ const AIChatbot = () => {
199250
)}
200251
</AnimatePresence>
201252

202-
{/* Floating Button with pulsing ring */}
253+
{/* Floating Button - FIXED: Positioned at bottom RIGHT */}
203254
<motion.button
204255
initial={{ opacity: 0, scale: 0.5 }}
205256
animate={{ opacity: 1, scale: 1 }}
@@ -340,4 +391,4 @@ const AIChatbot = () => {
340391
);
341392
};
342393

343-
export default AIChatbot;
394+
export default AIChatbot;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { TrendingUp, TrendingDown } from 'lucide-react';
3+
4+
interface CryptoPrice {
5+
id: string;
6+
symbol: string;
7+
price: number;
8+
change24h: number;
9+
}
10+
11+
const CRYPTO_IDS = ['bitcoin', 'ethereum', 'solana', 'polygon-ecosystem-token'];
12+
13+
const CompactPriceTicker: React.FC = () => {
14+
const [prices, setPrices] = useState<CryptoPrice[]>([]);
15+
const [currentIndex, setCurrentIndex] = useState(0);
16+
17+
useEffect(() => {
18+
const fetchPrices = async () => {
19+
try {
20+
const response = await fetch(
21+
`https://api.coingecko.com/api/v3/simple/price?ids=${CRYPTO_IDS.join(',')}&vs_currencies=usd&include_24hr_change=true`
22+
);
23+
const data = await response.json();
24+
25+
const symbolMap: Record<string, string> = {
26+
'bitcoin': 'BTC',
27+
'ethereum': 'ETH',
28+
'solana': 'SOL',
29+
'polygon-ecosystem-token': 'POL'
30+
};
31+
32+
const formattedPrices: CryptoPrice[] = CRYPTO_IDS.map(id => ({
33+
id,
34+
symbol: symbolMap[id] || id.toUpperCase(),
35+
price: data[id]?.usd || 0,
36+
change24h: data[id]?.usd_24h_change || 0
37+
}));
38+
39+
setPrices(formattedPrices);
40+
} catch (error) {
41+
console.error('Error fetching crypto prices:', error);
42+
}
43+
};
44+
45+
fetchPrices();
46+
const interval = setInterval(fetchPrices, 60000); // Update every minute
47+
return () => clearInterval(interval);
48+
}, []);
49+
50+
// Rotate through cryptos
51+
useEffect(() => {
52+
const rotateInterval = setInterval(() => {
53+
setCurrentIndex(prev => (prev + 1) % prices.length);
54+
}, 4000);
55+
return () => clearInterval(rotateInterval);
56+
}, [prices.length]);
57+
58+
if (prices.length === 0) {
59+
return null;
60+
}
61+
62+
const currentCrypto = prices[currentIndex];
63+
const isPositive = currentCrypto?.change24h >= 0;
64+
65+
return (
66+
<div className="flex items-center gap-2 px-3 py-1.5 bg-alien-space-dark/60 rounded-full border border-alien-gold/20 text-xs font-mono">
67+
<span className="text-alien-gold font-semibold">{currentCrypto?.symbol}</span>
68+
<span className="text-gray-300">
69+
${currentCrypto?.price >= 1000
70+
? currentCrypto?.price.toLocaleString(undefined, { maximumFractionDigits: 0 })
71+
: currentCrypto?.price.toFixed(2)
72+
}
73+
</span>
74+
<span className={`flex items-center gap-0.5 ${isPositive ? 'text-alien-green' : 'text-red-400'}`}>
75+
{isPositive ? <TrendingUp className="w-3 h-3" /> : <TrendingDown className="w-3 h-3" />}
76+
{Math.abs(currentCrypto?.change24h || 0).toFixed(1)}%
77+
</span>
78+
</div>
79+
);
80+
};
81+
82+
export default CompactPriceTicker;

src/components/Header/DesktopNav.tsx

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
21
import React from 'react';
32
import { Link } from 'react-router-dom';
4-
import { Globe, ChevronDown } from 'lucide-react';
3+
import { Globe, ChevronDown, ArrowLeftRight } from 'lucide-react';
54
import {
65
DropdownMenu,
76
DropdownMenuContent,
@@ -11,7 +10,11 @@ import {
1110
import { Button } from "@/components/ui/button";
1211
import { translateTo } from '@/lib/translator';
1312

14-
const DesktopNav = () => {
13+
interface DesktopNavProps {
14+
isCompact?: boolean;
15+
}
16+
17+
const DesktopNav: React.FC<DesktopNavProps> = ({ isCompact = false }) => {
1518
const navLinks = [
1619
{ to: "/", label: "Home" },
1720
{ to: "/about", label: "About" },
@@ -40,14 +43,16 @@ const DesktopNav = () => {
4043
};
4144

4245
return (
43-
<nav className="hidden lg:flex items-center gap-8">
44-
<div className="flex items-center space-x-6">
46+
<nav className="hidden lg:flex items-center gap-6">
47+
<div className={`flex items-center ${isCompact ? 'space-x-4' : 'space-x-6'}`}>
4548
{/* Main Navigation */}
4649
{navLinks.map((link) => (
4750
<Link
4851
key={link.to}
4952
to={link.to}
50-
className="text-alien-gold hover:text-alien-green px-3 py-2 font-nasalization transition-all duration-300 hover:scale-105 relative group"
53+
className={`text-alien-gold hover:text-alien-green font-nasalization transition-all duration-300 hover:scale-105 relative group ${
54+
isCompact ? 'text-sm px-2 py-1' : 'px-3 py-2'
55+
}`}
5156
>
5257
{link.label}
5358
<span className="absolute inset-x-0 -bottom-1 h-0.5 bg-alien-green scale-x-0 group-hover:scale-x-100 transition-transform duration-300 origin-left"></span>
@@ -59,10 +64,12 @@ const DesktopNav = () => {
5964
<DropdownMenuTrigger asChild>
6065
<Button
6166
variant="ghost"
62-
className="text-alien-gold hover:text-alien-green bg-transparent hover:bg-alien-space-light/20 px-3 py-2 rounded-lg flex items-center group font-nasalization transition-all duration-300 hover:scale-105"
67+
className={`text-alien-gold hover:text-alien-green bg-transparent hover:bg-alien-space-light/20 rounded-lg flex items-center group font-nasalization transition-all duration-300 hover:scale-105 ${
68+
isCompact ? 'text-sm px-2 py-1' : 'px-3 py-2'
69+
}`}
6370
>
64-
<span>Explore Spaces</span>
65-
<ChevronDown className="ml-2 h-4 w-4 transform transition-transform duration-300 group-data-[state=open]:rotate-180" />
71+
<span>{isCompact ? 'Spaces' : 'Explore Spaces'}</span>
72+
<ChevronDown className={`ml-1 transform transition-transform duration-300 group-data-[state=open]:rotate-180 ${isCompact ? 'h-3 w-3' : 'h-4 w-4'}`} />
6673
</Button>
6774
</DropdownMenuTrigger>
6875
<DropdownMenuContent
@@ -88,16 +95,31 @@ const DesktopNav = () => {
8895
</div>
8996
</DropdownMenuContent>
9097
</DropdownMenu>
98+
99+
{/* DEX Button */}
100+
<a
101+
href="https://app.uniswap.org/swap"
102+
target="_blank"
103+
rel="noopener noreferrer"
104+
className={`flex items-center gap-1.5 bg-gradient-to-r from-alien-green/20 to-alien-gold/20 hover:from-alien-green/30 hover:to-alien-gold/30 border border-alien-green/40 hover:border-alien-green/60 rounded-full font-nasalization text-alien-green hover:text-alien-gold transition-all duration-300 hover:scale-105 ${
105+
isCompact ? 'text-xs px-2.5 py-1' : 'text-sm px-3 py-1.5'
106+
}`}
107+
>
108+
<ArrowLeftRight className={isCompact ? 'w-3 h-3' : 'w-4 h-4'} />
109+
<span>DEX</span>
110+
</a>
91111

92112
{/* Language Selector Dropdown */}
93113
<DropdownMenu>
94114
<DropdownMenuTrigger asChild>
95115
<Button
96116
variant="ghost"
97-
className="flex items-center text-alien-gold hover:text-alien-green focus:outline-none group p-2 hover:bg-alien-space-light/20 rounded-lg transition-all duration-300 hover:scale-105"
117+
className={`flex items-center text-alien-gold hover:text-alien-green focus:outline-none group hover:bg-alien-space-light/20 rounded-lg transition-all duration-300 hover:scale-105 ${
118+
isCompact ? 'p-1.5' : 'p-2'
119+
}`}
98120
>
99-
<Globe className="h-5 w-5" />
100-
<ChevronDown className="ml-1 h-4 w-4 transform transition-transform duration-300 group-data-[state=open]:rotate-180" />
121+
<Globe className={isCompact ? 'h-4 w-4' : 'h-5 w-5'} />
122+
<ChevronDown className={`ml-1 transform transition-transform duration-300 group-data-[state=open]:rotate-180 ${isCompact ? 'h-3 w-3' : 'h-4 w-4'}`} />
101123
</Button>
102124
</DropdownMenuTrigger>
103125
<DropdownMenuContent
@@ -128,4 +150,4 @@ const DesktopNav = () => {
128150
);
129151
};
130152

131-
export default DesktopNav;
153+
export default DesktopNav;

0 commit comments

Comments
 (0)