ifixkart-storefront/components/store/MegaMenu.tsx

283 lines
14 KiB
TypeScript

/**
* @component MegaMenu
* @purpose Navigation bar matching TechShop reference UI with sticky header scroll effect, toggleable floating category dropdown menu when scrolled down (Image 2), and interactive mega flyouts.
* @dependencies catalogService, lucide-react
*/
'use client';
import { useState, useEffect, useRef } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { Menu, ChevronDown, ChevronRight, Percent, Flame, Sparkles, Smartphone, Laptop, Tv, Watch, Camera, Zap } from 'lucide-react';
import { catalogService, CategoryResponse, ProductResponse } from '@/services/api/catalogService';
interface SubCategoryGroup {
name: string;
items: string[];
}
const MEGA_DROPDOWNS: Record<string, SubCategoryGroup[]> = {
shop: [
{ name: 'Shop Layouts', items: ['Catalog Grid 4-Cols', 'Catalog List View', 'Category Sidebar Filters', 'Deal of the Day'] },
{ name: 'Product Types', items: ['Smartphones', 'Laptops & MacBooks', 'Smartwatches & Fitness', 'Wireless Audio'] },
],
categories: [
{ name: 'Consumer Electronics', items: ['Smartphones & Accessories', 'Tablets & E-Readers', 'Cameras & Photography', 'Audio & Headphones'] },
{ name: 'Computers & Office', items: ['Laptops & Ultraportables', 'Desktop PCs & Gaming', 'Monitors & Displays', 'Chargers & Power Adapters'] },
],
products: [
{ name: 'Top Trending', items: ['Apple iPhone 15 Pro Max', 'Sony WH-1000XM5 Headphones', 'Google Pixel 8 Pro', 'Apple Watch Ultra 2'] },
{ name: 'Certified Repairs', items: ['OLED Screen Replacement', 'Battery Health Calibration', 'Camera Module Fix', 'Water Damage Restoration'] },
],
};
export function MegaMenu() {
const [isSticky, setIsSticky] = useState(false);
const [categories, setCategories] = useState<CategoryResponse[]>([]);
const [featuredProducts, setFeaturedProducts] = useState<ProductResponse[]>([]);
const [categoriesOpen, setCategoriesOpen] = useState(false);
const [activeCategory, setActiveCategory] = useState<string | null>(null);
const [activeNavDropdown, setActiveNavDropdown] = useState<string | null>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
catalogService.getCategories().then((res) => {
if (Array.isArray(res)) setCategories(res);
});
catalogService.getProducts().then((res) => setFeaturedProducts(res.slice(0, 3)));
const handleScroll = () => {
setIsSticky(window.scrollY > 120);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
setCategoriesOpen(false);
setActiveCategory(null);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const categoryList = categories;
const getCategoryIcon = (slug: string) => {
switch (slug) {
case 'cameras': return <Camera className="w-4 h-4 text-[#1877f2]" />;
case 'phones': return <Smartphone className="w-4 h-4 text-[#1877f2]" />;
case 'tv-speaker': return <Tv className="w-4 h-4 text-[#1877f2]" />;
case 'smart-devices': return <Watch className="w-4 h-4 text-[#1877f2]" />;
case 'laptop': return <Laptop className="w-4 h-4 text-[#1877f2]" />;
case 'chargers-cables': return <Zap className="w-4 h-4 text-[#1877f2]" />;
default: return <Sparkles className="w-4 h-4 text-[#1877f2]" />;
}
};
return (
<nav className={`w-full bg-white border-b border-gray-200 transition-all duration-200 z-50 ${
isSticky ? 'fixed top-0 left-0 right-0 shadow-md backdrop-blur-md bg-white/95 animate-in slide-in-from-top-2' : 'relative'
}`}>
<div className="max-w-7xl mx-auto px-4 flex items-center justify-between">
{/* Left: "All Categories" Toggle Button & Floating Dropdown Menu (Image 2) */}
<div ref={dropdownRef} className="relative w-64 shrink-0" onMouseLeave={() => { setCategoriesOpen(false); setActiveCategory(null); }}>
<button
onClick={() => setCategoriesOpen(!categoriesOpen)}
onMouseEnter={() => setCategoriesOpen(true)}
className="w-full bg-[#1877f2] hover:bg-[#1565c0] text-white font-bold text-xs uppercase tracking-wide py-3.5 px-4 rounded-t-lg flex items-center justify-between transition-colors cursor-pointer"
>
<div className="flex items-center gap-2">
<Menu className="w-4 h-4" />
<span>All Categories</span>
</div>
<ChevronDown className={`w-4 h-4 transition-transform duration-200 ${categoriesOpen ? 'rotate-180' : ''}`} />
</button>
{/* Floating Dropdown Category Menu (Triggered on Click or Hover, Matching Image 2) */}
{categoriesOpen && (
<div className="absolute top-full left-0 flex z-50 animate-in fade-in slide-in-from-top-1 duration-150">
{/* Main Categories List */}
<div className="w-64 bg-white border-x border-b border-gray-200 rounded-b-xl shadow-2xl py-2 divide-y divide-gray-100 text-xs">
{categoryList.map((cat) => (
<div
key={cat.category_id}
onMouseEnter={() => setActiveCategory(cat.slug)}
className="relative"
>
<Link
href={`/products?category=${cat.slug}`}
onClick={() => setCategoriesOpen(false)}
className={`flex items-center justify-between px-4 py-2.5 font-medium transition-colors ${
activeCategory === cat.slug ? 'bg-blue-50 text-[#1877f2]' : 'hover:bg-gray-50 text-gray-700 hover:text-[#1877f2]'
}`}
>
<div className="flex items-center gap-3">
{getCategoryIcon(cat.slug)}
<span>{cat.name}</span>
</div>
<ChevronRight className="w-3.5 h-3.5 text-gray-400" />
</Link>
</div>
))}
</div>
{/* Subcategory Flyout Panel */}
{activeCategory && (
<div className="w-64 bg-white border border-gray-200 shadow-2xl rounded-r-xl p-4 text-xs z-50 ml-0.5 border-l-2 border-l-[#1877f2] animate-in fade-in slide-in-from-left-2 duration-150">
<h4 className="font-extrabold text-[#1877f2] uppercase tracking-wider mb-3 border-b border-gray-100 pb-2 flex items-center gap-1.5">
<Sparkles className="w-3.5 h-3.5 text-[#1877f2]" /> Subcategories & Items
</h4>
<ul className="space-y-2 font-medium text-gray-700">
<li>
<Link href={`/products?category=${activeCategory}`} onClick={() => setCategoriesOpen(false)} className="hover:text-[#1877f2] block transition-colors">
Top Flagship Models
</Link>
</li>
<li>
<Link href={`/products?category=${activeCategory}`} onClick={() => setCategoriesOpen(false)} className="hover:text-[#1877f2] block transition-colors">
Certified OEM Accessories
</Link>
</li>
<li>
<Link href={`/products?category=${activeCategory}`} onClick={() => setCategoriesOpen(false)} className="hover:text-[#1877f2] block transition-colors">
Discounted Refurbished Stock
</Link>
</li>
<li>
<Link href="/services" onClick={() => setCategoriesOpen(false)} className="text-[#e4382f] font-bold block hover:underline pt-1">
Repair & Replacement Service
</Link>
</li>
</ul>
</div>
)}
</div>
)}
</div>
{/* Center: Horizontal Navigation Links */}
<div className="hidden lg:flex items-center gap-7 text-xs font-semibold text-gray-800 py-3.5 pl-6 flex-1 relative">
<Link href="/" className="text-[#1877f2] hover:text-[#1877f2] transition-colors">
Home
</Link>
{/* Shop Nav Item */}
<div
className="relative py-1 cursor-pointer"
onMouseEnter={() => setActiveNavDropdown('shop')}
onMouseLeave={() => setActiveNavDropdown(null)}
>
<Link href="/products" className="hover:text-[#1877f2] transition-colors flex items-center gap-1">
Shop <ChevronDown className="w-3 h-3 text-gray-400" />
</Link>
{activeNavDropdown === 'shop' && MEGA_DROPDOWNS.shop && (
<div className="absolute top-full left-0 w-96 bg-white border border-gray-200 shadow-2xl rounded-xl p-5 z-50 grid grid-cols-2 gap-4 animate-in fade-in slide-in-from-top-1 duration-150">
{MEGA_DROPDOWNS.shop.map((grp, idx) => (
<div key={idx}>
<h5 className="font-extrabold text-[#1877f2] uppercase text-[11px] mb-2">{grp.name}</h5>
<ul className="space-y-1.5 text-xs text-gray-600 font-medium">
{grp.items.map((item, i) => (
<li key={i}>
<Link href="/products" className="hover:text-[#1877f2] transition-colors block">
{item}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
)}
</div>
{/* Categories Nav Item with SALE badge */}
<div
className="relative py-1 cursor-pointer"
onMouseEnter={() => setActiveNavDropdown('categories')}
onMouseLeave={() => setActiveNavDropdown(null)}
>
<Link href="/products" className="hover:text-[#1877f2] transition-colors flex items-center gap-1">
Categories
<span className="bg-[#e4ecf7] text-[#1877f2] text-[9px] font-bold px-1.5 py-0.5 rounded uppercase">
SALE
</span>
<ChevronDown className="w-3 h-3 text-gray-400" />
</Link>
{activeNavDropdown === 'categories' && MEGA_DROPDOWNS.categories && (
<div className="absolute top-full left-0 w-96 bg-white border border-gray-200 shadow-2xl rounded-xl p-5 z-50 grid grid-cols-2 gap-4 animate-in fade-in slide-in-from-top-1 duration-150">
{MEGA_DROPDOWNS.categories.map((grp, idx) => (
<div key={idx}>
<h5 className="font-extrabold text-[#1877f2] uppercase text-[11px] mb-2">{grp.name}</h5>
<ul className="space-y-1.5 text-xs text-gray-600 font-medium">
{grp.items.map((item, i) => (
<li key={i}>
<Link href="/products" className="hover:text-[#1877f2] transition-colors block">
{item}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
)}
</div>
{/* Products Nav Item with HOT badge */}
<div
className="relative py-1 cursor-pointer"
onMouseEnter={() => setActiveNavDropdown('products')}
onMouseLeave={() => setActiveNavDropdown(null)}
>
<Link href="/products" className="hover:text-[#1877f2] transition-colors flex items-center gap-1">
Products
<span className="bg-red-50 text-[#e4382f] text-[9px] font-bold px-1.5 py-0.5 rounded uppercase">
HOT
</span>
<ChevronDown className="w-3 h-3 text-gray-400" />
</Link>
{activeNavDropdown === 'products' && (
<div className="absolute top-full left-0 w-[450px] bg-white border border-gray-200 shadow-2xl rounded-xl p-5 z-50 animate-in fade-in slide-in-from-top-1 duration-150">
<h5 className="font-extrabold text-[#1877f2] uppercase text-[11px] mb-3">Featured Tech Products</h5>
<div className="grid grid-cols-3 gap-3">
{featuredProducts.map((p) => (
<Link key={p.product_id} href={`/products/${p.slug}`} className="group text-center">
<div className="relative w-full h-20 bg-gray-50 rounded-lg border border-gray-100 overflow-hidden mb-1">
<Image src={p.images[0]?.image_url || ''} alt={p.name} fill className="object-contain p-1 group-hover:scale-105 transition-transform" />
</div>
<span className="text-[10px] font-bold text-gray-900 group-hover:text-[#1877f2] line-clamp-1 block">{p.name}</span>
<span className="text-[10px] font-extrabold text-[#e4382f]">${p.variants[0]?.price}</span>
</Link>
))}
</div>
</div>
)}
</div>
<Link href="/products?on_sale=true" className="hover:text-[#1877f2] transition-colors flex items-center gap-1">
Top deals <ChevronDown className="w-3 h-3 text-gray-400" />
</Link>
<Link href="/services" className="hover:text-[#1877f2] transition-colors flex items-center gap-1 text-[#e4382f] font-bold">
Device Repairs <ChevronDown className="w-3 h-3 text-gray-400" />
</Link>
</div>
{/* Right Super Discount Tag */}
<div className="hidden md:flex items-center gap-1.5 text-xs font-bold text-gray-900 shrink-0">
<span className="w-5 h-5 rounded-full bg-[#e4382f] text-white flex items-center justify-center">
<Percent className="w-3 h-3" />
</span>
<span>Super Discount</span>
</div>
</div>
</nav>
);
}