refactor: reorganizar modulos dentro de app
Coloca las secciones, datos, componentes, servicios y tipos dentro de sus dominios de app y normaliza los imports con el alias @. Co-authored-by: Copilot <[email protected]>
This commit is contained in:
1 parent
efba699905
commit
794a659369
73 files changed
+172
-172
No files matched your search
@@ -0,0 +1,145 @@
|
||||
'use client';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Menu, X } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Button } from './Button';
|
||||
import { pageLinks } from '@/app/home/data/navigationData';
|
||||
|
||||
export const Navbar = () => {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const rawPathname = usePathname();
|
||||
const pathname = rawPathname?.replace(/\/$/, '') || '/';
|
||||
|
||||
const scrollToContact = () => {
|
||||
setMenuOpen(false);
|
||||
const el = document.getElementById('contacto');
|
||||
if (el) el.scrollIntoView({ behavior: 'smooth' });
|
||||
else window.location.href = '/#contacto';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 80);
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setMenuOpen(false);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.header
|
||||
className="fixed top-0 left-0 right-0 z-50"
|
||||
initial={{ y: -72, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ duration: 0.65, ease: [0.22, 1, 0.36, 1] }}
|
||||
role="banner"
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background: scrolled ? 'rgba(255,255,255,0.97)' : 'transparent',
|
||||
borderBottom: scrolled ? '1px solid rgba(0,0,0,0.08)' : '1px solid transparent',
|
||||
backdropFilter: scrolled ? 'blur(16px)' : 'none',
|
||||
WebkitBackdropFilter: scrolled ? 'blur(16px)' : 'none',
|
||||
transition: 'background 200ms, border-color 200ms',
|
||||
}}
|
||||
>
|
||||
<div className="mx-auto flex max-w-7xl items-center justify-between px-8 py-4 md:px-14">
|
||||
<Link href="/" aria-label="Rynext Technologies — inicio" className="flex items-center gap-2.5">
|
||||
<img
|
||||
src="/icons/Logo_negro.svg"
|
||||
alt="Logo"
|
||||
className="h-11 w-11 shrink-0 object-contain"
|
||||
/>
|
||||
<div className="flex flex-col leading-none">
|
||||
<span style={{ fontFamily: "'Quicksand', sans-serif", fontWeight: 700, fontSize: 19, letterSpacing: '-0.01em', color: '#111827' }}>
|
||||
Rynext
|
||||
</span>
|
||||
<span style={{ fontFamily: "'Quicksand', sans-serif", fontWeight: 500, fontSize: 12, letterSpacing: '0.04em', color: '#6b7280' }}>
|
||||
Technologies
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Desktop nav */}
|
||||
<nav className="hidden items-center gap-8 md:flex" aria-label="Navegación principal">
|
||||
{pageLinks.filter(l => l.href !== '/privacidad').map((link) => {
|
||||
const active = pathname === link.href;
|
||||
return (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="text-sm font-semibold transition-colors"
|
||||
style={{ color: active ? '#0a0a0a' : '#6a6866', textDecoration: 'none' }}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.color = '#0a0a0a')}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.color = active ? '#0a0a0a' : '#6a6866')}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
|
||||
<button
|
||||
onClick={scrollToContact}
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 text-sm font-bold text-white transition hover:scale-[1.02] hover:opacity-90"
|
||||
style={{ background: '#3b82f6', border: 'none', cursor: 'pointer', borderRadius: 3 }}
|
||||
>
|
||||
Hablemos
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
{/* Mobile menu button */}
|
||||
<button
|
||||
onClick={() => setMenuOpen((v) => !v)}
|
||||
className="flex items-center justify-center rounded p-2 md:hidden"
|
||||
style={{ color: '#0a0a0a', background: 'none', border: 'none', cursor: 'pointer' }}
|
||||
aria-label={menuOpen ? 'Cerrar menú' : 'Abrir menú'}
|
||||
aria-expanded={menuOpen}
|
||||
>
|
||||
{menuOpen ? <X size={22} /> : <Menu size={22} />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.header>
|
||||
|
||||
{/* Mobile menu */}
|
||||
<AnimatePresence>
|
||||
{menuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -8 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="fixed inset-x-0 top-[73px] z-40 px-8 py-6 md:hidden"
|
||||
style={{ background: '#ffffff', borderBottom: '1px solid #dddbd5' }}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Menú de navegación"
|
||||
>
|
||||
<nav className="flex flex-col gap-5">
|
||||
{pageLinks.map((link) => (
|
||||
<Link
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="text-left text-base font-semibold"
|
||||
style={{ color: pathname === link.href ? '#0a0a0a' : '#6a6866', textDecoration: 'none' }}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
<div className="pt-2">
|
||||
<Button onClick={scrollToContact} className="w-full py-3 text-center text-sm font-bold text-white">
|
||||
Hablemos
|
||||
</Button>
|
||||
</div>
|
||||
</nav>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user