UEA-PRODEM
This commit is contained in:
+146
-115
@@ -1,176 +1,207 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { Menu, X, Phone, ChevronDown } from 'lucide-react';
|
||||
import { Menu, X, Phone, ChevronDown, ArrowRight } from 'lucide-react';
|
||||
import { LOGO, NAV_LINKS, CONTACT } from '../config/site';
|
||||
|
||||
type NavItem = {
|
||||
name: string;
|
||||
href: string;
|
||||
submenu?: { name: string; href: string }[];
|
||||
};
|
||||
|
||||
const Navigation = () => {
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const [isServicesOpen, setIsServicesOpen] = useState(false);
|
||||
const [openSubmenu, setOpenSubmenu] = useState<string | null>(null);
|
||||
const [mobileOpenSubmenu, setMobileOpenSubmenu] = useState<string | null>(null);
|
||||
const location = useLocation();
|
||||
const submenuTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 50);
|
||||
};
|
||||
|
||||
const handleScroll = () => setIsScrolled(window.scrollY > 50);
|
||||
window.addEventListener('scroll', handleScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// Fechar menu mobile ao mudar de rota
|
||||
useEffect(() => {
|
||||
setIsMobileMenuOpen(false);
|
||||
setIsServicesOpen(false);
|
||||
setOpenSubmenu(null);
|
||||
setMobileOpenSubmenu(null);
|
||||
}, [location]);
|
||||
|
||||
const isActive = (path: string) => {
|
||||
if (path === '/') {
|
||||
return location.pathname === '/';
|
||||
}
|
||||
if (path === '/') return location.pathname === '/';
|
||||
return location.pathname.startsWith(path);
|
||||
};
|
||||
|
||||
// Renderiza o logo (SVG inline ou imagem)
|
||||
const handleSubmenuEnter = (name: string) => {
|
||||
if (submenuTimeout.current) clearTimeout(submenuTimeout.current);
|
||||
setOpenSubmenu(name);
|
||||
};
|
||||
|
||||
const handleSubmenuLeave = () => {
|
||||
submenuTimeout.current = setTimeout(() => setOpenSubmenu(null), 200);
|
||||
};
|
||||
|
||||
const renderLogo = () => {
|
||||
if (LOGO.imageUrl) {
|
||||
return <img src={LOGO.imageUrl} alt={LOGO.text} className="h-10 w-auto" />;
|
||||
}
|
||||
return (
|
||||
<div dangerouslySetInnerHTML={{ __html: LOGO.svgInline }} className="flex-shrink-0" />
|
||||
);
|
||||
return <div dangerouslySetInnerHTML={{ __html: LOGO.svgInline }} className="flex-shrink-0" />;
|
||||
};
|
||||
|
||||
return (
|
||||
<nav className={`nav-sticky ${isScrolled ? 'scrolled' : ''}`}>
|
||||
<div className="container-custom">
|
||||
<div className="flex items-center justify-between h-20">
|
||||
{/* Logo - importado de config/site.ts */}
|
||||
<Link to={LOGO.href} className="flex items-center" aria-label="Avanzato Tecnologia - Pagina inicial">
|
||||
<nav
|
||||
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
|
||||
isScrolled
|
||||
? 'bg-[#0e0e0e]/95 backdrop-blur-md shadow-lg shadow-black/20 border-b border-[#2a2a2a]/50'
|
||||
: 'bg-transparent'
|
||||
}`}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto px-4 lg:px-6">
|
||||
<div className="flex items-center justify-between h-[72px]">
|
||||
{/* Logo */}
|
||||
<Link to={LOGO.href} className="flex items-center shrink-0" aria-label="Avanzato Tecnologia">
|
||||
{renderLogo()}
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation - importado de config/site.ts */}
|
||||
<div className="hidden lg:flex items-center gap-8">
|
||||
{NAV_LINKS.map((link) => (
|
||||
<div key={link.name} className="relative">
|
||||
{link.submenu ? (
|
||||
<div
|
||||
className="relative"
|
||||
onMouseEnter={() => setIsServicesOpen(true)}
|
||||
onMouseLeave={() => setIsServicesOpen(false)}
|
||||
>
|
||||
{/* Desktop Menu */}
|
||||
<div className="hidden lg:flex items-center gap-1">
|
||||
{NAV_LINKS.map((item: NavItem) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="relative"
|
||||
onMouseEnter={() => item.submenu && handleSubmenuEnter(item.name)}
|
||||
onMouseLeave={handleSubmenuLeave}
|
||||
>
|
||||
{item.submenu ? (
|
||||
<>
|
||||
<button
|
||||
className={`flex items-center gap-1 nav-link text-sm font-medium transition-colors ${
|
||||
link.submenu.some(s => location.pathname.startsWith(s.href))
|
||||
? 'text-[#cbf400]'
|
||||
: 'text-gray-300 hover:text-white'
|
||||
className={`flex items-center gap-1 px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${
|
||||
isActive(item.href) || item.submenu.some(s => isActive(s.href))
|
||||
? 'text-[#cbf400] bg-[#cbf400]/10'
|
||||
: 'text-gray-300 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
{link.name}
|
||||
<ChevronDown className={`w-4 h-4 transition-transform ${isServicesOpen ? 'rotate-180' : ''}`} />
|
||||
{item.name}
|
||||
<ChevronDown className={`w-3.5 h-3.5 transition-transform duration-200 ${openSubmenu === item.name ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
{isServicesOpen && (
|
||||
<div className="absolute top-full left-0 mt-2 w-56 bg-[#1a1a1a] rounded-xl border border-white/10 shadow-xl py-2 z-50">
|
||||
{link.submenu.map((sub) => (
|
||||
|
||||
{/* Submenu Dropdown */}
|
||||
{openSubmenu === item.name && (
|
||||
<div className="absolute top-full left-0 mt-1 w-64 bg-[#1a1a1a] rounded-xl border border-[#2a2a2a] shadow-2xl shadow-black/40 overflow-hidden animate-in fade-in slide-in-from-top-2 duration-200">
|
||||
<div className="p-2">
|
||||
{item.submenu.map((sub) => (
|
||||
<Link
|
||||
key={sub.href}
|
||||
to={sub.href}
|
||||
className={`flex items-center justify-between px-3 py-2.5 rounded-lg text-sm transition-all duration-150 ${
|
||||
isActive(sub.href)
|
||||
? 'text-[#cbf400] bg-[#cbf400]/10'
|
||||
: 'text-gray-300 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
{sub.name}
|
||||
<ArrowRight className="w-3.5 h-3.5 opacity-0 -translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all" />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Link
|
||||
to={item.href}
|
||||
className={`px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${
|
||||
isActive(item.href)
|
||||
? 'text-[#cbf400] bg-[#cbf400]/10'
|
||||
: 'text-gray-300 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* CTA Button */}
|
||||
<Link
|
||||
to="/contato"
|
||||
className="ml-3 bg-[#cbf400] text-[#0e0e0e] px-5 py-2.5 rounded-xl text-sm font-bold hover:bg-[#b8e000] transition-all duration-200 hover:shadow-lg hover:shadow-[#cbf400]/20 flex items-center gap-2"
|
||||
>
|
||||
<Phone className="w-4 h-4" />
|
||||
{CONTACT.phone}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Mobile Toggle */}
|
||||
<button
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
className="lg:hidden p-2 rounded-lg text-gray-300 hover:text-white hover:bg-white/5 transition-colors"
|
||||
aria-label="Menu"
|
||||
>
|
||||
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{isMobileMenuOpen && (
|
||||
<div className="lg:hidden bg-[#0e0e0e]/98 backdrop-blur-lg border-t border-[#2a2a2a]">
|
||||
<div className="max-w-7xl mx-auto px-4 py-4 space-y-1">
|
||||
{NAV_LINKS.map((item: NavItem) => (
|
||||
<div key={item.name}>
|
||||
{item.submenu ? (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setMobileOpenSubmenu(mobileOpenSubmenu === item.name ? null : item.name)}
|
||||
className="w-full flex items-center justify-between px-4 py-3 rounded-xl text-gray-300 hover:text-white hover:bg-white/5 transition-colors"
|
||||
>
|
||||
<span className="font-medium">{item.name}</span>
|
||||
<ChevronDown className={`w-4 h-4 transition-transform ${mobileOpenSubmenu === item.name ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
{mobileOpenSubmenu === item.name && (
|
||||
<div className="pl-4 space-y-1 pb-2">
|
||||
{item.submenu.map((sub) => (
|
||||
<Link
|
||||
key={sub.name}
|
||||
key={sub.href}
|
||||
to={sub.href}
|
||||
className="block px-4 py-2.5 text-sm text-gray-300 hover:text-[#cbf400] hover:bg-white/5 transition-colors"
|
||||
className={`block px-4 py-2.5 rounded-xl text-sm transition-colors ${
|
||||
isActive(sub.href) ? 'text-[#cbf400] bg-[#cbf400]/10' : 'text-gray-400 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
{sub.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Link
|
||||
to={link.href}
|
||||
className={`nav-link text-sm font-medium transition-colors ${
|
||||
isActive(link.href) ? 'text-[#cbf400]' : 'text-gray-300 hover:text-white'
|
||||
to={item.href}
|
||||
className={`block px-4 py-3 rounded-xl font-medium transition-colors ${
|
||||
isActive(item.href) ? 'text-[#cbf400] bg-[#cbf400]/10' : 'text-gray-300 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
{link.name}
|
||||
{item.name}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Desktop CTA - importado de config/site.ts */}
|
||||
<div className="hidden lg:flex items-center gap-4">
|
||||
{/* Mobile CTA */}
|
||||
<a
|
||||
href={`tel:${CONTACT.phoneRaw}`}
|
||||
className="flex items-center gap-2 text-sm text-gray-400 hover:text-[#cbf400] transition-colors"
|
||||
href={`https://wa.me/${CONTACT.phoneRaw}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 bg-[#cbf400] text-[#0e0e0e] px-5 py-3.5 rounded-xl font-bold mt-3"
|
||||
>
|
||||
<Phone className="w-4 h-4" />
|
||||
{CONTACT.phone}
|
||||
WhatsApp {CONTACT.phone}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
className="lg:hidden p-2 text-gray-300 hover:text-white"
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu - importado de config/site.ts */}
|
||||
{isMobileMenuOpen && (
|
||||
<div className="lg:hidden py-4 border-t border-white/10">
|
||||
<div className="flex flex-col gap-4">
|
||||
{NAV_LINKS.map((link) => (
|
||||
<div key={link.name}>
|
||||
{link.submenu ? (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setIsServicesOpen(!isServicesOpen)}
|
||||
className="flex items-center justify-between w-full text-gray-300 hover:text-[#cbf400] transition-colors py-2"
|
||||
>
|
||||
<span className="font-medium">{link.name}</span>
|
||||
<ChevronDown className={`w-4 h-4 transition-transform ${isServicesOpen ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
{isServicesOpen && (
|
||||
<div className="pl-4 mt-2 space-y-2">
|
||||
{link.submenu.map((sub) => (
|
||||
<Link
|
||||
key={sub.name}
|
||||
to={sub.href}
|
||||
className="block text-sm text-gray-400 hover:text-[#cbf400] py-1"
|
||||
>
|
||||
{sub.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Link
|
||||
to={link.href}
|
||||
className={`block py-2 font-medium transition-colors ${
|
||||
isActive(link.href) ? 'text-[#cbf400]' : 'text-gray-300 hover:text-[#cbf400]'
|
||||
}`}
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<a
|
||||
href={`tel:${CONTACT.phoneRaw}`}
|
||||
className="flex items-center gap-2 text-sm text-gray-400 hover:text-[#cbf400] pt-4 border-t border-white/10"
|
||||
>
|
||||
<Phone className="w-4 h-4" />
|
||||
{CONTACT.phone}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user