2025-04-02 20:42:47 -03:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
import { usePathname, useRouter } from 'next/navigation';
|
2025-04-27 09:25:48 -03:00
|
|
|
import Image from 'next/image';
|
2025-06-21 19:06:13 -03:00
|
|
|
import { useRef, useEffect, useState } from 'react';
|
|
|
|
import { useTheme } from './theme-provider';
|
2025-06-29 21:33:12 -03:00
|
|
|
import { ChevronLeft, ChevronRight, HardDrive, Terminal } from 'lucide-react';
|
2025-06-28 22:36:36 -03:00
|
|
|
import './client-nav.css'; // Ensure you have the CSS file for styles
|
2025-04-02 20:42:47 -03:00
|
|
|
|
|
|
|
const examples = [
|
2025-06-21 19:06:13 -03:00
|
|
|
{ name: "Chat", href: "/chat", color: "#25D366" }, // WhatsApp green
|
2025-06-28 19:30:35 -03:00
|
|
|
{ name: "Paper", href: "/paper", color: "#6366F1" }, // Indigo
|
2025-06-21 19:06:13 -03:00
|
|
|
{ name: "Mail", href: "/mail", color: "#FFD700" }, // Outlook yellow
|
2025-06-28 09:58:11 -03:00
|
|
|
{ name: "Calendar", href: "/calendar", color: "#1DB954" }, // Spotify green
|
|
|
|
{ name: "Meet", href: "/meet", color: "#059669" }, // Google Meet green
|
2025-06-21 21:40:06 -03:00
|
|
|
{ name: "Drive", href: "/drive", color: "#10B981" }, // Emerald green
|
2025-06-21 19:06:13 -03:00
|
|
|
{ name: "Editor", href: "/editor", color: "#2563EB" }, // Word blue
|
2025-06-28 19:30:35 -03:00
|
|
|
{ name: "Player", href: "/player", color: "Yellow" }, // YouTube red
|
2025-06-22 23:01:03 -03:00
|
|
|
{ name: "Tables", href: "/tables", color: "#8B5CF6" }, // Purple
|
2025-06-28 19:30:35 -03:00
|
|
|
{ name: "Dashboard", href: "/dashboard", color: "#6366F1" }, // Indigo
|
|
|
|
{ name: "Sources", href: "/sources", color: "#F59E0B" }, // Amber
|
2025-06-21 19:06:13 -03:00
|
|
|
{ name: "Settings", href: "/settings", color: "#6B7280" }, // Gray
|
2025-04-02 20:42:47 -03:00
|
|
|
];
|
|
|
|
|
2025-06-29 21:33:12 -03:00
|
|
|
export const Nav = ()=> {
|
2025-04-02 20:42:47 -03:00
|
|
|
const pathname = usePathname();
|
|
|
|
const router = useRouter();
|
2025-06-21 14:30:11 -03:00
|
|
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
|
|
const navItemsRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
2025-06-21 19:06:13 -03:00
|
|
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
|
|
|
const [showLoginMenu, setShowLoginMenu] = useState(false);
|
|
|
|
const [showScrollButtons, setShowScrollButtons] = useState(false);
|
|
|
|
const loginMenuRef = useRef<HTMLDivElement>(null);
|
|
|
|
const [showThemeMenu, setShowThemeMenu] = useState(false);
|
|
|
|
const themeMenuRef = useRef<HTMLDivElement>(null);
|
2025-06-29 21:33:12 -03:00
|
|
|
const [currentTime, setCurrentTime] = useState(new Date());
|
2025-06-21 19:06:13 -03:00
|
|
|
const { theme, setTheme, themes } = useTheme();
|
2025-06-21 14:30:11 -03:00
|
|
|
|
2025-06-29 21:33:12 -03:00
|
|
|
// Update time every second
|
|
|
|
useEffect(() => {
|
|
|
|
const timer = setInterval(() => {
|
|
|
|
setCurrentTime(new Date());
|
|
|
|
}, 1000);
|
|
|
|
|
|
|
|
return () => clearInterval(timer);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const formatTime = (date) => {
|
|
|
|
return date.toLocaleTimeString('en-US', {
|
|
|
|
hour12: false,
|
|
|
|
hour: '2-digit',
|
|
|
|
minute: '2-digit',
|
|
|
|
second: '2-digit'
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const formatDate = (date) => {
|
|
|
|
return date.toLocaleDateString('en-US', {
|
|
|
|
year: 'numeric',
|
|
|
|
month: '2-digit',
|
|
|
|
day: '2-digit'
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2025-06-21 14:30:11 -03:00
|
|
|
const isActive = (href: string) => {
|
|
|
|
if (href === '/') return pathname === href;
|
|
|
|
return pathname.startsWith(href);
|
|
|
|
};
|
|
|
|
|
2025-06-21 19:06:13 -03:00
|
|
|
const handleLogin = () => {
|
|
|
|
setIsLoggedIn(true);
|
|
|
|
setShowLoginMenu(false);
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleLogout = () => {
|
|
|
|
setIsLoggedIn(false);
|
|
|
|
setShowLoginMenu(false);
|
|
|
|
};
|
|
|
|
|
|
|
|
const checkScrollNeeded = () => {
|
|
|
|
if (scrollContainerRef.current) {
|
|
|
|
const container = scrollContainerRef.current;
|
|
|
|
const isScrollable = container.scrollWidth > container.clientWidth;
|
|
|
|
setShowScrollButtons(isScrollable);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const handleClickOutside = (event: MouseEvent) => {
|
|
|
|
if (loginMenuRef.current && !loginMenuRef.current.contains(event.target as Node)) {
|
|
|
|
setShowLoginMenu(false);
|
|
|
|
}
|
|
|
|
if (themeMenuRef.current && !themeMenuRef.current.contains(event.target as Node)) {
|
|
|
|
setShowThemeMenu(false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
document.addEventListener('mousedown', handleClickOutside);
|
|
|
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
checkScrollNeeded();
|
|
|
|
window.addEventListener('resize', checkScrollNeeded);
|
|
|
|
return () => window.removeEventListener('resize', checkScrollNeeded);
|
|
|
|
}, []);
|
|
|
|
|
2025-06-21 14:30:11 -03:00
|
|
|
useEffect(() => {
|
|
|
|
const script = document.createElement('script');
|
|
|
|
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js';
|
|
|
|
script.onload = () => {
|
2025-06-28 22:36:36 -03:00
|
|
|
const gsap = (window as any).gsap;
|
2025-06-21 14:30:11 -03:00
|
|
|
|
|
|
|
gsap.fromTo('.nav-item',
|
2025-06-21 19:06:13 -03:00
|
|
|
{ opacity: 0, y: -10, scale: 0.9 },
|
|
|
|
{ opacity: 1, y: 0, scale: 1, duration: 0.6, stagger: 0.1, ease: "back.out(1.7)" }
|
2025-06-21 14:30:11 -03:00
|
|
|
);
|
|
|
|
|
2025-06-21 19:06:13 -03:00
|
|
|
navItemsRefs.current.forEach((item) => {
|
2025-06-21 14:30:11 -03:00
|
|
|
if (item) {
|
|
|
|
item.addEventListener('mouseenter', () => {
|
2025-06-21 19:06:13 -03:00
|
|
|
gsap.to(item, { scale: 1.05, duration: 0.3, ease: "power2.out" });
|
2025-06-21 14:30:11 -03:00
|
|
|
});
|
|
|
|
item.addEventListener('mouseleave', () => {
|
2025-06-21 19:06:13 -03:00
|
|
|
gsap.to(item, { scale: 1, duration: 0.3, ease: "power2.out" });
|
2025-06-21 14:30:11 -03:00
|
|
|
});
|
|
|
|
item.addEventListener('click', () => {
|
2025-06-21 19:06:13 -03:00
|
|
|
gsap.to(item, { scale: 0.95, duration: 0.1, yoyo: true, repeat: 1, ease: "power2.inOut" });
|
2025-06-21 14:30:11 -03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-06-21 19:06:13 -03:00
|
|
|
gsap.fromTo('.theme-toggle',
|
|
|
|
{ opacity: 0, x: 20, scale: 0.8 },
|
|
|
|
{ opacity: 1, x: 0, scale: 1, duration: 0.8, delay: 0.3, ease: "elastic.out(1, 0.5)" }
|
|
|
|
);
|
|
|
|
|
|
|
|
gsap.fromTo('.login-button',
|
|
|
|
{ opacity: 0, x: 20, scale: 0.8 },
|
|
|
|
{ opacity: 1, x: 0, scale: 1, duration: 0.8, delay: 0.4, ease: "elastic.out(1, 0.5)" }
|
2025-06-21 14:30:11 -03:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
document.head.appendChild(script);
|
|
|
|
return () => {
|
|
|
|
if (document.head.contains(script)) {
|
|
|
|
document.head.removeChild(script);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const scrollLeft = () => {
|
2025-06-21 19:06:13 -03:00
|
|
|
scrollContainerRef.current?.scrollBy({ left: -150, behavior: 'smooth' });
|
2025-06-21 14:30:11 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
const scrollRight = () => {
|
2025-06-21 19:06:13 -03:00
|
|
|
scrollContainerRef.current?.scrollBy({ left: 150, behavior: 'smooth' });
|
|
|
|
};
|
|
|
|
|
|
|
|
const getThemeIcon = () => {
|
|
|
|
switch(theme.name) {
|
|
|
|
case 'retrowave': return '🌌';
|
|
|
|
case 'vapordream': return '🌀';
|
|
|
|
case 'y2kglow': return '💿';
|
|
|
|
case 'mellowgold': return '☮️';
|
|
|
|
case 'arcadeflash': return '🕹️';
|
|
|
|
case 'polaroidmemories': return '📸';
|
|
|
|
case 'midcenturymod': return '🪑';
|
|
|
|
case 'grungeera': return '🎸';
|
|
|
|
case 'discofever': return '🪩';
|
|
|
|
case 'saturdaycartoons': return '📺';
|
|
|
|
case 'oldhollywood': return '🎬';
|
|
|
|
case 'cyberpunk': return '🤖';
|
|
|
|
case 'seasidepostcard': return '🏖️';
|
|
|
|
case 'typewriter': return '⌨️';
|
|
|
|
case 'jazzage': return '🎷';
|
2025-06-28 19:30:35 -03:00
|
|
|
case 'xtreegold': return 'X';
|
2025-06-21 19:06:13 -03:00
|
|
|
default: return '🎨';
|
2025-06-21 14:30:11 -03:00
|
|
|
}
|
|
|
|
};
|
2025-04-02 20:42:47 -03:00
|
|
|
|
|
|
|
return (
|
2025-06-21 14:30:11 -03:00
|
|
|
<>
|
2025-06-29 21:33:12 -03:00
|
|
|
{/* Retro System Status Bar */}
|
|
|
|
<div className="fixed top-0 left-0 right-0 z-50 bg-gray-800 text-green-400 font-mono border-b border-green-600 text-xs">
|
|
|
|
<div className="flex items-center justify-between px-4 py-1">
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
<HardDrive className="w-3 h-3 text-green-400" />
|
|
|
|
<span className="text-green-300">RETRO NAVIGATOR v4.0</span>
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
|
|
|
|
<span className="text-green-400">READY</span>
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
<span className="text-green-300">THEME:</span>
|
|
|
|
<span className="text-yellow-400">{theme.label}</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
<span className="text-green-300">{formatDate(currentTime)}</span>
|
|
|
|
<span className="text-green-300">{formatTime(currentTime)}</span>
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
<Terminal className="w-3 h-3 text-green-400" />
|
|
|
|
<span className="text-green-400">SYS</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Main Navigation */}
|
|
|
|
<div className="nav-container" style={{ top: '24px' }}>
|
2025-06-21 14:30:11 -03:00
|
|
|
<div className="nav-inner">
|
|
|
|
<div className="nav-content">
|
|
|
|
<div className="logo-container">
|
|
|
|
<Image
|
|
|
|
src="/images/generalbots-logo.svg"
|
|
|
|
alt="Logo"
|
|
|
|
width={64}
|
|
|
|
height={24}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
2025-06-21 19:06:13 -03:00
|
|
|
{showScrollButtons && (
|
2025-06-29 21:33:12 -03:00
|
|
|
<button
|
|
|
|
className="w-8 h-8 bg-gray-800 border border-green-600 text-green-400 rounded hover:bg-green-900/30 hover:border-green-500 hover:text-green-300 transition-all flex items-center justify-center flex-shrink-0 mx-1"
|
|
|
|
onClick={scrollLeft}
|
|
|
|
aria-label="Scroll left"
|
|
|
|
>
|
|
|
|
<ChevronLeft className="w-4 h-4" />
|
2025-06-21 19:06:13 -03:00
|
|
|
</button>
|
|
|
|
)}
|
2025-06-21 14:30:11 -03:00
|
|
|
|
2025-06-21 19:06:13 -03:00
|
|
|
<div ref={scrollContainerRef} className="nav-scroll">
|
2025-06-21 14:30:11 -03:00
|
|
|
<div className="nav-items">
|
|
|
|
{examples.map((example, index) => {
|
|
|
|
const active = isActive(example.href);
|
|
|
|
return (
|
|
|
|
<button
|
|
|
|
key={example.href}
|
2025-06-28 22:36:36 -03:00
|
|
|
ref={el => { navItemsRefs.current[index] = el; }}
|
2025-06-21 14:30:11 -03:00
|
|
|
onClick={() => router.push(example.href)}
|
|
|
|
className={`nav-item ${active ? 'active' : ''}`}
|
2025-06-21 19:06:13 -03:00
|
|
|
style={{'--neon-color': example.color} as React.CSSProperties}
|
2025-06-29 21:33:12 -03:00
|
|
|
onMouseEnter={(e) => {
|
|
|
|
e.target.style.boxShadow = `0 0 15px ${example.color}60`;
|
|
|
|
e.target.style.borderColor = example.color;
|
|
|
|
e.target.style.color = example.color;
|
|
|
|
e.target.style.textShadow = `0 0 8px ${example.color}80`;
|
|
|
|
}}
|
|
|
|
onMouseLeave={(e) => {
|
|
|
|
if (!active) {
|
|
|
|
e.target.style.boxShadow = 'none';
|
|
|
|
e.target.style.borderColor = '';
|
|
|
|
e.target.style.color = '';
|
|
|
|
e.target.style.textShadow = 'none';
|
|
|
|
}
|
|
|
|
}}
|
2025-06-21 14:30:11 -03:00
|
|
|
>
|
|
|
|
{example.name}
|
|
|
|
<div className="neon-glow"></div>
|
|
|
|
</button>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2025-06-21 19:06:13 -03:00
|
|
|
{showScrollButtons && (
|
2025-06-29 21:33:12 -03:00
|
|
|
<button
|
|
|
|
className="w-8 h-8 bg-gray-800 border border-green-600 text-green-400 rounded hover:bg-green-900/30 hover:border-green-500 hover:text-green-300 transition-all flex items-center justify-center flex-shrink-0 mx-1"
|
|
|
|
onClick={scrollRight}
|
|
|
|
aria-label="Scroll right"
|
|
|
|
>
|
|
|
|
<ChevronRight className="w-4 h-4" />
|
2025-06-21 19:06:13 -03:00
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
|
|
|
|
<div className="auth-controls">
|
|
|
|
<div className="login-container" ref={loginMenuRef}>
|
|
|
|
<button
|
|
|
|
onClick={() => setShowLoginMenu(!showLoginMenu)}
|
|
|
|
className="login-button"
|
|
|
|
aria-label={isLoggedIn ? "User menu" : "Login"}
|
|
|
|
>
|
|
|
|
{isLoggedIn ? '👤' : '🔐'}
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{showLoginMenu && (
|
|
|
|
<div className="login-menu">
|
|
|
|
{!isLoggedIn ? (
|
|
|
|
<button onClick={handleLogin} className="menu-item">
|
|
|
|
Login
|
|
|
|
</button>
|
|
|
|
) : (
|
|
|
|
<button onClick={handleLogout} className="menu-item">
|
|
|
|
Logout
|
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="theme-container" ref={themeMenuRef}>
|
|
|
|
<button
|
|
|
|
onClick={() => setShowThemeMenu(!showThemeMenu)}
|
|
|
|
className="theme-toggle"
|
|
|
|
aria-label="Change theme"
|
|
|
|
>
|
|
|
|
{getThemeIcon()}
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{showThemeMenu && (
|
|
|
|
<div className="theme-menu">
|
|
|
|
{themes.map((t) => (
|
|
|
|
<button
|
|
|
|
key={t.name}
|
|
|
|
onClick={() => {
|
|
|
|
setTheme(t.name);
|
|
|
|
setShowThemeMenu(false);
|
|
|
|
}}
|
|
|
|
className={`theme-menu-item ${
|
|
|
|
theme.name === t.name ? 'active-theme' : ''
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
{t.label}
|
|
|
|
</button>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
2025-06-21 14:30:11 -03:00
|
|
|
</div>
|
2025-04-27 09:25:48 -03:00
|
|
|
</div>
|
2025-04-02 20:42:47 -03:00
|
|
|
</div>
|
2025-06-21 14:30:11 -03:00
|
|
|
|
2025-06-29 21:33:12 -03:00
|
|
|
<div className="nav-spacer" style={{ height: '88px' }}></div>
|
2025-06-21 14:30:11 -03:00
|
|
|
|
|
|
|
</>
|
2025-04-02 20:42:47 -03:00
|
|
|
);
|
2025-06-29 21:33:12 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default Nav;
|