287 lines
12 KiB
TypeScript
287 lines
12 KiB
TypeScript
import Link from "next/link";
|
||
import { Search, Heart, User, ShoppingCart, ChevronLeft, LogOut, Menu, X } from "lucide-react";
|
||
import { useState, useEffect } from "react";
|
||
import { motion, AnimatePresence } from "framer-motion";
|
||
import Image from "next/image";
|
||
import { useRouter } from "next/router";
|
||
import authService from "../services/auth";
|
||
import cartService from "../services/cart";
|
||
|
||
export default function Header() {
|
||
const router = useRouter();
|
||
// Состояние для отслеживания прокрутки страницы
|
||
const [scrolled, setScrolled] = useState(false);
|
||
// Состояние для отслеживания аутентификации пользователя
|
||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||
// Состояние для отображения выпадающего меню пользователя
|
||
const [showUserMenu, setShowUserMenu] = useState(false);
|
||
// Состояние для отображения мобильного меню
|
||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||
// Состояние для хранения количества товаров в корзине
|
||
const [cartItemsCount, setCartItemsCount] = useState(0);
|
||
|
||
// Эффект для проверки аутентификации при загрузке компонента
|
||
useEffect(() => {
|
||
setIsAuthenticated(authService.isAuthenticated());
|
||
}, []);
|
||
|
||
// Эффект для загрузки количества товаров в корзине
|
||
useEffect(() => {
|
||
const fetchCartItemsCount = async () => {
|
||
if (authService.isAuthenticated()) {
|
||
try {
|
||
const cart = await cartService.getCart();
|
||
setCartItemsCount(cart.items_count);
|
||
} catch (error) {
|
||
console.error('Ошибка при загрузке корзины:', error);
|
||
setCartItemsCount(0);
|
||
}
|
||
} else {
|
||
setCartItemsCount(0);
|
||
}
|
||
};
|
||
|
||
fetchCartItemsCount();
|
||
|
||
// Обновляем количество товаров в корзине при изменении маршрута
|
||
const handleRouteChange = () => {
|
||
fetchCartItemsCount();
|
||
};
|
||
|
||
router.events.on('routeChangeComplete', handleRouteChange);
|
||
return () => {
|
||
router.events.off('routeChangeComplete', handleRouteChange);
|
||
};
|
||
}, [isAuthenticated, router.events]);
|
||
|
||
// Эффект для отслеживания прокрутки
|
||
useEffect(() => {
|
||
const handleScroll = () => {
|
||
const isScrolled = window.scrollY > 50;
|
||
if (isScrolled !== scrolled) {
|
||
setScrolled(isScrolled);
|
||
}
|
||
};
|
||
|
||
window.addEventListener('scroll', handleScroll);
|
||
return () => {
|
||
window.removeEventListener('scroll', handleScroll);
|
||
};
|
||
}, [scrolled]);
|
||
|
||
// Функция для выхода из системы
|
||
const handleLogout = () => {
|
||
authService.logout();
|
||
setIsAuthenticated(false);
|
||
setShowUserMenu(false);
|
||
setCartItemsCount(0);
|
||
router.push('/');
|
||
};
|
||
|
||
// Функция для возврата на предыдущую страницу
|
||
const goBack = () => {
|
||
router.back();
|
||
};
|
||
|
||
// Проверяем, находимся ли мы на главной странице
|
||
const isHomePage = router.pathname === "/";
|
||
// Проверяем, находимся ли мы на странице категорий или коллекций
|
||
const isDetailPage = router.pathname.includes("[slug]");
|
||
|
||
// Функция для переключения отображения меню пользователя
|
||
const toggleUserMenu = () => {
|
||
setShowUserMenu(!showUserMenu);
|
||
};
|
||
|
||
// Функция для переключения мобильного меню
|
||
const toggleMobileMenu = () => {
|
||
setMobileMenuOpen(!mobileMenuOpen);
|
||
};
|
||
|
||
// Закрыть мобильное меню при переходе на другую страницу
|
||
useEffect(() => {
|
||
setMobileMenuOpen(false);
|
||
}, [router.pathname]);
|
||
|
||
// Закрыть меню пользователя при клике вне его
|
||
useEffect(() => {
|
||
const handleClickOutside = (event: MouseEvent) => {
|
||
const target = event.target as HTMLElement;
|
||
if (showUserMenu && !target.closest('.user-menu-container')) {
|
||
setShowUserMenu(false);
|
||
}
|
||
};
|
||
|
||
document.addEventListener('mousedown', handleClickOutside);
|
||
return () => {
|
||
document.removeEventListener('mousedown', handleClickOutside);
|
||
};
|
||
}, [showUserMenu]);
|
||
|
||
return (
|
||
<header className={`fixed w-full z-50 transition-all duration-300 bg-white ${scrolled ? 'shadow-md' : 'shadow-sm'}`}>
|
||
<nav className="py-4 transition-all duration-300 text-black">
|
||
<div className="container mx-auto px-4 flex items-center justify-between">
|
||
{/* Мобильная кнопка меню */}
|
||
<button
|
||
className="lg:hidden flex items-center justify-center"
|
||
onClick={toggleMobileMenu}
|
||
aria-label="Меню"
|
||
>
|
||
{mobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
||
</button>
|
||
|
||
{/* Десктопное меню */}
|
||
<div className="hidden lg:flex items-center space-x-6">
|
||
<Link href="/category" className="text-sm font-medium hover:opacity-70 transition-opacity">
|
||
Каталог
|
||
</Link>
|
||
<Link href="/all-products" className="text-sm font-medium hover:opacity-70 transition-opacity">
|
||
Все товары
|
||
</Link>
|
||
<Link href="/collections" className="text-sm font-medium hover:opacity-70 transition-opacity">
|
||
Коллекции
|
||
</Link>
|
||
<Link href="/new-arrivals" className="text-sm font-medium hover:opacity-70 transition-opacity">
|
||
Новинки
|
||
</Link>
|
||
<Link href="/order-tracking" className="text-sm font-medium hover:opacity-70 transition-opacity">
|
||
Отследить заказ
|
||
</Link>
|
||
</div>
|
||
|
||
{/* Логотип - центрирован на десктопе, слева на мобильных */}
|
||
<Link href="/" className="lg:absolute lg:left-1/2 lg:transform lg:-translate-x-1/2">
|
||
<div className="relative h-8 w-24 md:h-10 md:w-32">
|
||
<Image
|
||
src="/logo.png"
|
||
alt="Brand Logo"
|
||
fill
|
||
className="object-contain"
|
||
priority
|
||
/>
|
||
</div>
|
||
</Link>
|
||
|
||
{/* Иконки справа */}
|
||
<div className="flex items-center space-x-3 md:space-x-5">
|
||
<Link href="/favorites" className="relative hover:opacity-70 transition-opacity">
|
||
<Heart className="w-5 h-5" />
|
||
<span className="absolute -top-2 -right-2 bg-black text-white text-xs rounded-full w-4 h-4 flex items-center justify-center">
|
||
0
|
||
</span>
|
||
</Link>
|
||
|
||
<div className="relative user-menu-container">
|
||
<button
|
||
onClick={toggleUserMenu}
|
||
className="hover:opacity-70 transition-opacity focus:outline-none"
|
||
>
|
||
<User className="w-5 h-5" />
|
||
</button>
|
||
|
||
{showUserMenu && (
|
||
<div className="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50">
|
||
{isAuthenticated ? (
|
||
<>
|
||
<Link href="/account" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||
Мой профиль
|
||
</Link>
|
||
<Link href="/account/orders" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||
Мои заказы
|
||
</Link>
|
||
{/* Ссылка на админ-панель, если пользователь админ */}
|
||
<Link href="/admin" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||
Админ-панель
|
||
</Link>
|
||
<button
|
||
onClick={handleLogout}
|
||
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
||
>
|
||
<div className="flex items-center">
|
||
<LogOut className="w-4 h-4 mr-2" />
|
||
Выйти
|
||
</div>
|
||
</button>
|
||
</>
|
||
) : (
|
||
<>
|
||
<Link href="/login" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||
Войти
|
||
</Link>
|
||
<Link href="/register" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||
Регистрация
|
||
</Link>
|
||
</>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<Link href="/cart" className="relative hover:opacity-70 transition-opacity">
|
||
<ShoppingCart className="w-5 h-5" />
|
||
<span className="absolute -top-2 -right-2 bg-black text-white text-xs rounded-full w-4 h-4 flex items-center justify-center">
|
||
{cartItemsCount}
|
||
</span>
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
{/* Мобильное меню */}
|
||
<AnimatePresence>
|
||
{mobileMenuOpen && (
|
||
<motion.div
|
||
initial={{ opacity: 0, height: 0 }}
|
||
animate={{ opacity: 1, height: 'auto' }}
|
||
exit={{ opacity: 0, height: 0 }}
|
||
transition={{ duration: 0.3 }}
|
||
className="lg:hidden bg-white border-t border-gray-100 shadow-md"
|
||
>
|
||
<div className="container mx-auto px-4 py-4">
|
||
<div className="flex flex-col space-y-4">
|
||
<Link
|
||
href="/category"
|
||
className="text-sm font-medium py-2 hover:opacity-70 transition-opacity border-b border-gray-100"
|
||
>
|
||
Каталог
|
||
</Link>
|
||
<Link
|
||
href="/all-products"
|
||
className="text-sm font-medium py-2 hover:opacity-70 transition-opacity border-b border-gray-100"
|
||
>
|
||
Все товары
|
||
</Link>
|
||
<Link
|
||
href="/collections"
|
||
className="text-sm font-medium py-2 hover:opacity-70 transition-opacity border-b border-gray-100"
|
||
>
|
||
Коллекции
|
||
</Link>
|
||
<Link
|
||
href="/new-arrivals"
|
||
className="text-sm font-medium py-2 hover:opacity-70 transition-opacity border-b border-gray-100"
|
||
>
|
||
Новинки
|
||
</Link>
|
||
<Link
|
||
href="/order-tracking"
|
||
className="text-sm font-medium py-2 hover:opacity-70 transition-opacity border-b border-gray-100"
|
||
>
|
||
Отследить заказ
|
||
</Link>
|
||
<Link
|
||
href="/search"
|
||
className="text-sm font-medium py-2 hover:opacity-70 transition-opacity border-b border-gray-100 flex items-center"
|
||
>
|
||
<Search className="w-4 h-4 mr-2" />
|
||
Поиск
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
</AnimatePresence>
|
||
</header>
|
||
);
|
||
}
|