389 lines
15 KiB
TypeScript
389 lines
15 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { Link } from '@/i18n/navigation';
|
|
import Image from 'next/image';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { ChevronDown, Info, BookOpen, LayoutDashboard, Code2, Menu, X } from 'lucide-react';
|
|
import { useTranslations } from 'next-intl';
|
|
import { useAuth } from '@/lib/context/auth-context';
|
|
import { usePathname } from '@/i18n/navigation';
|
|
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
|
|
|
|
interface LandingHeaderProps {
|
|
transparentOnTop?: boolean;
|
|
activePage?: 'about' | 'contact' | 'careers' | 'blog' | 'press' | 'docs';
|
|
}
|
|
|
|
export function LandingHeader({ transparentOnTop = false, activePage }: LandingHeaderProps) {
|
|
const t = useTranslations();
|
|
const [isScrolled, setIsScrolled] = useState(false);
|
|
const [isCompanyMenuOpen, setIsCompanyMenuOpen] = useState(false);
|
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
const { user, isAuthenticated, loading } = useAuth();
|
|
const pathname = usePathname();
|
|
|
|
useEffect(() => {
|
|
setIsMobileMenuOpen(false);
|
|
}, [pathname]);
|
|
|
|
const companyMenuItems = [
|
|
{
|
|
href: '/about',
|
|
label: t('navigation.about'),
|
|
icon: Info,
|
|
description: t('header.company.aboutDescription'),
|
|
},
|
|
{
|
|
href: '/blog',
|
|
label: t('navigation.blog'),
|
|
icon: BookOpen,
|
|
description: t('header.company.blogDescription'),
|
|
},
|
|
];
|
|
|
|
const isCompanyMenuActive = activePage !== undefined && ['about', 'blog'].includes(activePage);
|
|
|
|
const getUserInitials = () => {
|
|
if (!user) return '';
|
|
const firstInitial = user.firstName?.charAt(0)?.toUpperCase() || '';
|
|
const lastInitial = user.lastName?.charAt(0)?.toUpperCase() || '';
|
|
return firstInitial + lastInitial || user.email?.charAt(0)?.toUpperCase() || '?';
|
|
};
|
|
|
|
const getFullName = () => {
|
|
if (!user) return '';
|
|
if (user.firstName && user.lastName) {
|
|
return `${user.firstName} ${user.lastName}`;
|
|
}
|
|
return user.email || '';
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (!transparentOnTop) {
|
|
setIsScrolled(true);
|
|
return;
|
|
}
|
|
|
|
const handleScroll = () => {
|
|
setIsScrolled(window.scrollY > 50);
|
|
};
|
|
window.addEventListener('scroll', handleScroll);
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, [transparentOnTop]);
|
|
|
|
const isActiveLink = (href: string) => {
|
|
if (!activePage) return false;
|
|
return href === `/${activePage}`;
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<motion.nav
|
|
initial={{ y: -100 }}
|
|
animate={{ y: 0 }}
|
|
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
|
|
isScrolled ? 'bg-brand-navy/95 backdrop-blur-md shadow-lg' : 'bg-transparent'
|
|
}`}
|
|
>
|
|
<div className="max-w-7xl mx-auto px-4 lg:px-8">
|
|
<div className="flex items-center justify-between h-16 md:h-20">
|
|
<Link href="/" className="flex items-center space-x-2">
|
|
<Image
|
|
src="/assets/logos/logo-white.png"
|
|
alt="Xpeditis"
|
|
width={60}
|
|
height={70}
|
|
priority
|
|
className="h-auto"
|
|
/>
|
|
</Link>
|
|
|
|
{/* Mobile hamburger button */}
|
|
<button
|
|
className="md:hidden p-2 text-white hover:text-brand-turquoise transition-colors"
|
|
onClick={() => setIsMobileMenuOpen(true)}
|
|
aria-label={t('header.mobileMenu.open')}
|
|
>
|
|
<Menu className="w-6 h-6" />
|
|
</button>
|
|
|
|
<div className="hidden md:flex items-center space-x-8">
|
|
<Link
|
|
href="/#features"
|
|
className="text-white hover:text-brand-turquoise transition-colors font-medium"
|
|
>
|
|
{t('navigation.features')}
|
|
</Link>
|
|
<Link
|
|
href="/#pricing"
|
|
className="text-white hover:text-brand-turquoise transition-colors font-medium"
|
|
>
|
|
{t('navigation.pricing')}
|
|
</Link>
|
|
|
|
{/* Menu Entreprise */}
|
|
<div
|
|
className="relative"
|
|
onMouseEnter={() => setIsCompanyMenuOpen(true)}
|
|
onMouseLeave={() => setIsCompanyMenuOpen(false)}
|
|
>
|
|
<button
|
|
className={`flex items-center space-x-1 transition-colors font-medium ${
|
|
isCompanyMenuActive
|
|
? 'text-brand-turquoise'
|
|
: 'text-white hover:text-brand-turquoise'
|
|
}`}
|
|
>
|
|
<span>{t('navigation.company')}</span>
|
|
<ChevronDown
|
|
className={`w-4 h-4 transition-transform ${isCompanyMenuOpen ? 'rotate-180' : ''}`}
|
|
/>
|
|
</button>
|
|
|
|
<AnimatePresence>
|
|
{isCompanyMenuOpen && (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: 10 }}
|
|
transition={{ duration: 0.2 }}
|
|
className="absolute top-full left-1/2 -translate-x-1/2 pt-4 z-50"
|
|
>
|
|
<div className="bg-white rounded-2xl shadow-2xl border border-border overflow-hidden min-w-[280px]">
|
|
<div className="p-2">
|
|
{companyMenuItems.map(item => {
|
|
const IconComponent = item.icon;
|
|
const isActive = isActiveLink(item.href);
|
|
return (
|
|
<Link
|
|
key={item.href}
|
|
href={item.href}
|
|
className={`flex items-center space-x-3 px-4 py-3 rounded-xl transition-colors group ${
|
|
isActive ? 'bg-brand-turquoise/10' : 'hover:bg-neutral-50'
|
|
}`}
|
|
>
|
|
<div
|
|
className={`w-10 h-10 rounded-lg flex items-center justify-center transition-colors ${
|
|
isActive
|
|
? 'bg-brand-turquoise/20'
|
|
: 'bg-brand-turquoise/10 group-hover:bg-brand-turquoise/20'
|
|
}`}
|
|
>
|
|
<IconComponent className="w-5 h-5 text-brand-turquoise" />
|
|
</div>
|
|
<div>
|
|
<div
|
|
className={`font-medium transition-colors ${
|
|
isActive
|
|
? 'text-brand-turquoise'
|
|
: 'text-brand-navy group-hover:text-brand-turquoise'
|
|
}`}
|
|
>
|
|
{item.label}
|
|
</div>
|
|
<div className="text-sm text-neutral-500">{item.description}</div>
|
|
</div>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</div>
|
|
|
|
<Link
|
|
href="/contact"
|
|
className={`transition-colors font-medium ${
|
|
activePage === 'contact'
|
|
? 'text-brand-turquoise'
|
|
: 'text-white hover:text-brand-turquoise'
|
|
}`}
|
|
>
|
|
{t('navigation.contact')}
|
|
</Link>
|
|
|
|
<Link
|
|
href="/docs/api"
|
|
className={`flex items-center gap-1.5 transition-colors font-medium ${
|
|
activePage === 'docs'
|
|
? 'text-brand-turquoise'
|
|
: 'text-white hover:text-brand-turquoise'
|
|
}`}
|
|
>
|
|
<Code2 className="w-4 h-4" />
|
|
{t('navigation.docsApi')}
|
|
</Link>
|
|
|
|
{/* Language switcher */}
|
|
<LanguageSwitcher variant="dark" />
|
|
|
|
{/* Affichage conditionnel: connecté vs non connecté */}
|
|
{loading ? (
|
|
<div className="w-8 h-8 rounded-full bg-white/20 animate-pulse" />
|
|
) : isAuthenticated && user ? (
|
|
<Link href="/dashboard" className="flex items-center space-x-3 group">
|
|
<div className="w-10 h-10 rounded-full bg-brand-turquoise flex items-center justify-center text-white font-semibold text-sm border-2 border-white/30 group-hover:border-white transition-colors">
|
|
{getUserInitials()}
|
|
</div>
|
|
<span className="text-white font-medium group-hover:text-brand-turquoise transition-colors max-w-[150px] truncate">
|
|
{getFullName()}
|
|
</span>
|
|
<LayoutDashboard className="w-5 h-5 text-white/70 group-hover:text-brand-turquoise transition-colors" />
|
|
</Link>
|
|
) : (
|
|
<>
|
|
<Link
|
|
href="/login"
|
|
className="text-white hover:text-brand-turquoise transition-colors font-medium"
|
|
>
|
|
{t('header.cta.signIn')}
|
|
</Link>
|
|
<Link
|
|
href="/register"
|
|
className="px-6 py-2.5 bg-brand-turquoise text-white rounded-lg hover:bg-brand-turquoise/90 transition-all hover:shadow-lg font-medium"
|
|
>
|
|
{t('header.cta.startFree')}
|
|
</Link>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.nav>
|
|
|
|
{/* Mobile menu drawer */}
|
|
<AnimatePresence>
|
|
{isMobileMenuOpen && (
|
|
<>
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
className="fixed inset-0 z-50 bg-black/60 md:hidden"
|
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
/>
|
|
<motion.div
|
|
initial={{ x: '100%' }}
|
|
animate={{ x: 0 }}
|
|
exit={{ x: '100%' }}
|
|
transition={{ type: 'tween', duration: 0.25 }}
|
|
className="fixed top-0 right-0 bottom-0 z-50 w-[280px] bg-brand-navy flex flex-col md:hidden shadow-2xl"
|
|
>
|
|
<div className="flex items-center justify-between px-5 h-16 border-b border-white/10">
|
|
<Image
|
|
src="/assets/logos/logo-white.png"
|
|
alt="Xpeditis"
|
|
width={50}
|
|
height={60}
|
|
className="h-auto"
|
|
/>
|
|
<button
|
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
className="p-2 text-white/70 hover:text-white transition-colors"
|
|
aria-label={t('header.mobileMenu.close')}
|
|
>
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<nav className="flex-1 px-4 py-6 space-y-1 overflow-y-auto">
|
|
<Link
|
|
href="/#features"
|
|
className="flex items-center px-4 py-3 text-white/80 hover:text-white hover:bg-white/10 rounded-xl transition-colors font-medium"
|
|
>
|
|
{t('navigation.features')}
|
|
</Link>
|
|
<Link
|
|
href="/#pricing"
|
|
className="flex items-center px-4 py-3 text-white/80 hover:text-white hover:bg-white/10 rounded-xl transition-colors font-medium"
|
|
>
|
|
{t('navigation.pricing')}
|
|
</Link>
|
|
|
|
<div className="pt-2 pb-1 px-4 text-xs text-white/40 uppercase tracking-widest font-medium">
|
|
{t('navigation.company')}
|
|
</div>
|
|
{companyMenuItems.map(item => {
|
|
const IconComponent = item.icon;
|
|
return (
|
|
<Link
|
|
key={item.href}
|
|
href={item.href}
|
|
className="flex items-center space-x-3 px-4 py-3 text-white/80 hover:text-white hover:bg-white/10 rounded-xl transition-colors"
|
|
>
|
|
<IconComponent className="w-4 h-4 text-brand-turquoise" />
|
|
<span className="font-medium">{item.label}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
|
|
<Link
|
|
href="/contact"
|
|
className={`flex items-center px-4 py-3 rounded-xl transition-colors font-medium ${
|
|
activePage === 'contact'
|
|
? 'text-brand-turquoise bg-white/10'
|
|
: 'text-white/80 hover:text-white hover:bg-white/10'
|
|
}`}
|
|
>
|
|
{t('navigation.contact')}
|
|
</Link>
|
|
|
|
<Link
|
|
href="/docs/api"
|
|
className={`flex items-center space-x-2 px-4 py-3 rounded-xl transition-colors font-medium ${
|
|
activePage === 'docs'
|
|
? 'text-brand-turquoise bg-white/10'
|
|
: 'text-white/80 hover:text-white hover:bg-white/10'
|
|
}`}
|
|
>
|
|
<Code2 className="w-4 h-4" />
|
|
<span>{t('navigation.docsApi')}</span>
|
|
</Link>
|
|
|
|
<div className="pt-4 px-4">
|
|
<LanguageSwitcher variant="dark" />
|
|
</div>
|
|
</nav>
|
|
|
|
<div className="border-t border-white/10 p-4 space-y-3">
|
|
{loading ? (
|
|
<div className="h-10 rounded-xl bg-white/10 animate-pulse" />
|
|
) : isAuthenticated && user ? (
|
|
<Link
|
|
href="/dashboard"
|
|
className="flex items-center space-x-3 px-4 py-3 bg-brand-turquoise rounded-xl text-white font-medium"
|
|
>
|
|
<div className="w-8 h-8 rounded-full bg-white/20 flex items-center justify-center text-sm font-semibold flex-shrink-0">
|
|
{getUserInitials()}
|
|
</div>
|
|
<span className="flex-1 truncate">{getFullName()}</span>
|
|
<LayoutDashboard className="w-4 h-4 flex-shrink-0" />
|
|
</Link>
|
|
) : (
|
|
<>
|
|
<Link
|
|
href="/login"
|
|
className="flex items-center justify-center w-full px-4 py-3 border border-white/30 text-white rounded-xl hover:bg-white/10 transition-colors font-medium"
|
|
>
|
|
{t('header.cta.signIn')}
|
|
</Link>
|
|
<Link
|
|
href="/register"
|
|
className="flex items-center justify-center w-full px-4 py-3 bg-brand-turquoise text-white rounded-xl hover:bg-brand-turquoise/90 transition-colors font-medium"
|
|
>
|
|
{t('header.cta.startFree')}
|
|
</Link>
|
|
</>
|
|
)}
|
|
</div>
|
|
</motion.div>
|
|
</>
|
|
)}
|
|
</AnimatePresence>
|
|
</>
|
|
);
|
|
}
|