Files
parsshop/components/header.tsx
2026-03-27 22:48:14 +03:30

409 lines
27 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import React, { useState, useEffect, useRef } from 'react';
import { usePathname, useRouter } from 'next/navigation';
import Link from 'next/link';
import Image from 'next/image';
import { Category } from '@/public/src/types/categories';
import { ShoppingCart, Trash2, Search, X, ChevronDown } from "lucide-react";
import { useCart } from './context/cartcontext';
import { products } from '@/lib/data';
import '@/public/src/css/header.css';
const topBarLinks = [
{ label: "بخش صنعتی", href: "/" },
{ label: "بخش خودرویی", href: "/automotive" },
];
const mainNavLinks = [
{ label: "صفحه اصلی", href: "/" },
{ label: "محصولات", href: "/products" },
{ label: "مقالات", href: "/blog" },
{ label: "درباره ما", href: "/about" },
{ label: "تماس با ما", href: "/contact" },
];
// 1. استخراج دسته‌بندی‌های منحصر به فرد از محصولات
const uniqueCategories = Array.from(new Set(products.map(p => p.category)));
export function Header({ categories }: { categories: Category[] }) {
const [menuOpen, setMenuOpen] = useState(false);
const pathname = usePathname();
const { cart, removeFromCart } = useCart();
const [searchTerm, setSearchTerm] = useState('');
const [showResults, setShowResults] = useState(false);
const [filteredProducts, setFilteredProducts] = useState<any[]>([]);
const searchRef = useRef<HTMLDivElement>(null);
const router = useRouter();
const safeCategories = categories || [];
const rootCategories = safeCategories.filter(cat => cat.parent === null);
const parsePrice = (priceStr?: string | null) => {
if (!priceStr) return 0;
return Number(priceStr.toString().replace(/,/g, ''));
};
const totalPrice = cart.reduce((total, item) => total + (parsePrice(item.price) * item.quantity), 0);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (searchRef.current && !searchRef.current.contains(event.target as Node)) {
setShowResults(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
useEffect(() => {
if (searchTerm.trim().length > 1) {
const results = products.filter(p =>
p.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
p.brand?.toLowerCase().includes(searchTerm.toLowerCase()) ||
p.d?.toLowerCase().includes(searchTerm.toLowerCase()) ||
p.l?.toLowerCase().includes(searchTerm.toLowerCase())
).slice(0, 6);
setFilteredProducts(results);
setShowResults(true);
} else {
setFilteredProducts([]);
setShowResults(false);
}
}, [searchTerm]);
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
if (searchTerm.trim()) {
setShowResults(false);
router.push(`/search?q=${encodeURIComponent(searchTerm)}`);
}
};
// ... (بقیه کدهای شما از اینجا تا بخش <nav> دست نخورده باقی می‌ماند)
// ... (بخش‌های top bar, main bar, search, cart, etc. بدون تغییر هستند)
return (
<header>
<div>
{/* ... کدهای مربوط به mobile slider, top bar, main bar ... */}
{/* overlay */}
{menuOpen && (
<div
className="fixed inset-0 bg-black/40 z-40 md:hidden"
onClick={() => setMenuOpen(false)}
/>
)}
{/* mobile slider */}
<div
className={`fixed top-0 right-0 h-full w-[240px] max-w-[90vw] bg-white z-50 transform transition-transform duration-300 md:hidden
${menuOpen ? "translate-x-0" : "translate-x-full"}`}
>
<div className="p-6 flex flex-col gap-6">
{mainNavLinks.map((link) => (
<a
key={link.label}
href={link.href}
className="text-sm text-gray-700"
onClick={() => setMenuOpen(false)}
>
{link.label}
</a>
))}
<button className="inline-flex items-center justify-center px-6 py-2 rounded-xl border-1 border-[#ffb900] text-[#ffb900] text-xs tracking-wide focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]/50">
استعلام قیمت
</button>
{/* mobile contact */}
<div className="mt-6 border-t pt-4 flex flex-col gap-4 md:hidden">
<div className="flex items-center gap-2 text-gray-700 text-xs">
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 11.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 11.5c0 6-7.5 10-7.5 10s-7.5-4-7.5-10a7.5 7.5 0 1115 0z" />
</svg>
<span className="whitespace-nowrap">شیراز، معالیآباد</span>
</div>
<a href="tel:07112345678" className="flex items-center gap-2 text-gray-700 hover:text-amber-500 transition text-xs">
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 6.75c0 7.87 6.38 14.25 14.25 14.25.62 0 1.12-.5 1.12-1.12v-2.26c0-.53-.37-.99-.88-1.1l-2.77-.6a1.12 1.12 0 00-1.17.54l-.66 1.16a12.04 12.04 0 01-5.54-5.54l1.16-.66c.4-.23.6-.7.54-1.17l-.6-2.77a1.12 1.12 0 00-1.1-.88H3.37c-.62 0-1.12.5-1.12 1.12z" />
</svg>
<span dir="ltr">07112345678</span>
</a>
</div>
</div>
</div>
{/* top bar */}
<div className="bg-[#1a2332] text-white text-sm ">
<div className="container max-w-6xl mx-auto px-4 flex flex-wrap md:flex-nowrap justify-between items-center gap-2 md:gap-0">
{/* links */}
<div className="flex flex-wrap items-center gap-1 md:gap-4 w-full md:w-auto md:justify-start">
{topBarLinks.map((link) => {
// --- منطق جدید برای فعال بودن ---
let isActive = false;
if (link.href === "/automotive") {
// بخش خودرویی فقط زمانی فعال است که آدرس با /automotive شروع شود
isActive = pathname?.startsWith("/automotive");
} else if (link.href === "/") {
// بخش صنعتی در تمام صفحات (جز خودرویی) همیشه فعال می‌ماند
isActive = !pathname?.startsWith("/automotive");
}
// ---------------------------------
const baseClasses = "hover:text-amber-300 transition duration-200 px-3 md:px-6 py-2 text-xs";
const activeClasses = isActive ? "text-amber-400 border-t-2 border-amber-400 ac" : "text-white";
return (
<a
key={link.href}
href={link.href}
className={`${baseClasses} ${activeClasses}`}
>
{link.label}
</a>
);
})}
</div>
{/* contact */}
<div className="flex flex-wrap hidden md:flex md:flex-nowrap justify-center md:justify-end items-center gap-3 md:gap-6 text-sm text-white w-full md:w-auto">
<div className="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 11.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 11.5c0 6-7.5 10-7.5 10s-7.5-4-7.5-10a7.5 7.5 0 1115 0z" />
</svg>
<span className="whitespace-nowrap text-xs">شیراز، معالیآباد</span>
</div>
<div>
<a href="tel:07112345678" className="flex items-center gap-2 hover:text-amber-300 transition">
<svg xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 6.75c0 7.87 6.38 14.25 14.25 14.25.62 0 1.12-.5 1.12-1.12v-2.26c0-.53-.37-.99-.88-1.1l-2.77-.6a1.12 1.12 0 00-1.17.54l-.66 1.16a12.04 12.04 0 01-5.54-5.54l1.16-.66c.4-.23.6-.7.54-1.17l-.6-2.77a1.12 1.12 0 00-1.1-.88H3.37c-.62 0-1.12.5-1.12 1.12z" />
</svg>
<span className="whitespace-nowrap font-medium text-xs" dir="ltr">07112345678</span>
</a>
</div>
</div>
</div>
</div>
{/* main bar */}
<div className="bg-white border-b-2 border-b-[#f4f4f4] py-4">
<div className="container max-w-6xl mx-auto px-3 flex justify-between items-center flex-wrap gap-2 min-w-0">
{/* logo */}
<div className="items-center hidden md:flex md:order-1 order-2 space-x-2 rtl:space-x-reverse min-w-[100px] md:min-w-[150px]">
<div className="text-3xl font-bold text-green-500">B</div>
<span className="text-xl font-semibold text-[#1a2332]">Bearing Site</span>
</div>
{/* hamburger */}
<button className="md:hidden order-1" onClick={() => setMenuOpen(true)}>
<svg xmlns="http://www.w3.org/2000/svg" className="w-6 h-6 text-gray-800" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
{/* search */}
<div ref={searchRef} className="relative flex order-4 md:order-2 w-full md:w-[35%] min-w-0">
<form onSubmit={handleSearch} className="w-full">
<div className="flex flex-row-reverse w-full border border-gray-300 rounded-3xl overflow-hidden focus-within:ring-2 focus-within:ring-blue-500 bg-white shadow-sm transition-all">
<button type="submit" className="bg-gray-800 p-2.5 text-white hover:bg-gray-700 transition">
<Search size={20} />
</button>
<input
type="text"
placeholder="جستجوی قطعه، کد، یا دسته‌بندی..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onFocus={() => searchTerm.length > 1 && setShowResults(true)}
className="w-full p-3 text-gray-800 placeholder-gray-400 focus:outline-none text-xs font-medium"
/>
{searchTerm && (
<button
onClick={() => setSearchTerm('')}
className=" text-gray-400 mr-4 hover:text-gray-600"
>
<X size={14} />
</button>
)}
</div>
</form>
{/* دراپ‌داون نتایج لحظه‌ای */}
{showResults && filteredProducts.length > 0 && (
<div className="absolute top-full left-0 right-0 mt-2 bg-white border border-gray-200 rounded-2xl shadow-xl z-[100] overflow-hidden animate-in fade-in slide-in-from-top-2 duration-200">
<div className="p-2 flex flex-col">
{filteredProducts.map((product) => {
const productSlug = product.title.replace(/\s+/g, '-');
return (
<Link
key={product.id}
href={`/products/${product.id}/${encodeURIComponent(productSlug)}`}
onClick={() => setShowResults(false)}
className="flex items-center gap-3 p-2 hover:bg-gray-50 rounded-xl transition group"
>
<div className="bg-gray-100 p-1 rounded-lg shrink-0 w-10 h-10 flex items-center justify-center">
<Image src={product.image} alt={product.title} width={32} height={32} className="object-contain" />
</div>
<div className="flex-1 min-w-0 text-right">
<p className="text-[11px] font-bold text-gray-800 truncate group-hover:text-blue-600 transition-colors">{product.title}</p>
<p className="text-[9px] text-gray-500">{product.brand}</p>
</div>
<div className="text-[10px] font-bold text-gray-700 bg-gray-50 px-2 py-1 rounded-md shrink-0">
{product.price ? `${Number(product.price.toString().replace(/,/g, '')).toLocaleString('fa-IR')} ت` : 'استعلام'}
</div>
</Link>
);
})}
</div>
<button onClick={handleSearch} className="w-full bg-gray-50 py-2.5 text-[11px] text-blue-600 font-bold border-t border-gray-100 hover:bg-blue-50 transition-colors">
مشاهده تمام نتایج برای "{searchTerm}"
</button>
</div>
)}
</div>
{/* User/Cart Actions */}
<div className='flex items-center order-3 gap-2'>
{/* دراپ‌داون سبد خرید */}
<div className="relative group before:absolute before:-inset-x-5 before:-inset-y-3 before:content-['']">
<Link href="/cart" className="flex items-center justify-center p-2.5 bg-white border border-gray-300/60 rounded-xl text-gray-700 hover:bg-gray-50 transition relative z-10">
<ShoppingCart className="w-4 h-4 text-gray-500" strokeWidth={1.8} />
{cart.length > 0 && (
<span className="absolute -top-1.5 -right-1.5 bg-[#f92a35] text-white text-[10px] font-bold w-4 h-4 flex items-center justify-center rounded-full shadow-sm">
{cart.reduce((total, item) => total + item.quantity, 0)}
</span>
)}
</Link>
<div className="absolute top-full left-0 w-full h-3 bg-transparent hidden group-hover:block"></div>
<div className="absolute top-[calc(100%+12px)] left-0 w-80 bg-white border border-gray-200 rounded-2xl shadow-xl hidden group-hover:flex flex-col overflow-hidden z-50">
{cart.length === 0 ? (
<div className="p-6 text-center text-sm text-gray-500 flex flex-col items-center gap-2">
<ShoppingCart className="w-8 h-8 text-gray-200" />
<span>سبد خرید شما خالی است</span>
</div>
) : (
<>
<div className="flex justify-between items-center p-4 border-b border-gray-100 bg-gray-50/50">
<span className="text-xs font-semibold text-gray-500">{cart.length} کالا</span>
<Link href="/cart" className="text-xs text-blue-500 hover:text-blue-700 font-medium transition">مشاهده سبد خرید</Link>
</div>
<div className="max-h-64 overflow-y-auto p-2 flex flex-col gap-1 custom-scrollbar">
{cart.slice(0, 3).map((item) => {
const itemTotal = item.price ? (Number(item.price.toString().replace(/,/g, '')) * item.quantity).toLocaleString('fa-IR') : null;
return (
<div key={item.id} className="flex items-center gap-3 p-2 hover:bg-gray-50 rounded-xl transition group/item relative">
<div className="bg-white border border-gray-100 p-1 rounded-lg shrink-0">
<Image src={item.image} alt={item.title} width={40} height={40} className="object-contain w-10 h-10" />
</div>
<div className="flex-1 min-w-0">
<h4 className="text-xs font-semibold text-gray-800 truncate">{item.title}</h4>
<div className="flex items-center gap-2 mt-1">
<span className="text-[10px] text-gray-500 font-medium">{itemTotal ? `${itemTotal} تومان` : 'استعلام'}</span>
<span className="text-[9px] font-bold text-blue-600 bg-blue-50 px-1.5 py-0.5 rounded-md border border-blue-100">{item.quantity} عدد</span>
</div>
</div>
<button
onClick={(e) => { e.preventDefault(); e.stopPropagation(); removeFromCart(item.id); }}
className="text-gray-300 hover:text-red-500 transition p-1.5 opacity-0 group-hover/item:opacity-100"
title="حذف"
>
<Trash2 size={14} />
</button>
</div>
)
})}
{cart.length > 3 && (<p className="text-center text-[10px] text-gray-400 py-2 border-t border-gray-50 mt-1"> و {cart.length - 3} کالای دیگر...</p>)}
</div>
<div className="p-4 bg-white border-t border-gray-100">
<div className="flex justify-between items-center mb-3">
<span className="text-xs text-gray-500">مبلغ قابل پرداخت:</span>
<span className="text-sm font-bold text-gray-800">{totalPrice > 0 ? `${totalPrice.toLocaleString('fa-IR')} تومان` : 'استعلام'}</span>
</div>
<Link href="/cart" className="flex items-center justify-center w-full py-2 bg-[#ffb900] hover:bg-[#e6a600] text-black text-xs font-semibold rounded-xl transition-colors">ثبت سفارش</Link>
</div>
</>
)}
</div>
</div>
<button className="flex items-center gap-2 px-3 py-2.5 bg-white border border-gray-300/60 rounded-xl text-xs text-gray-700 hover:bg-gray-50 transition">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-4 h-4 text-gray-500"><path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.5 20.25a7.5 7.5 0 0115 0" /></svg>
<span>ورود / عضویت</span>
</button>
</div>
</div>
</div>
{/* nav -- این بخش تغییر کرده است */}
<div className='justify-center flex'>
<div className='flex max-w-6xl container mx-auto px-4 justify-between py-2 items-center'>
<nav className="hidden md:flex items-center space-x-6 rtl:space-x-4 text-gray-700 text-xs w-full lg:w-auto lg:order-none">
{/* لینک صفحه اصلی */}
<Link href="/" className="hover:text-blue-600 transition duration-200 font-medium">صفحه اصلی</Link>
<div className="relative group">
<div className="flex items-center gap-1 cursor-pointer hover:text-blue-600 transition duration-200 font-medium">
<span>دسته بندی محصولات</span>
<ChevronDown size={14} className="transition-transform duration-200 group-hover:rotate-180" />
</div>
<div className="absolute top-full right-0 w-full h-4 bg-transparent hidden group-hover:block"></div>
<div className="absolute top-[calc(100%+16px)] right-0 w-56 bg-white border border-gray-200 rounded-xl shadow-lg p-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 flex flex-col gap-1 z-50">
{rootCategories.length > 0 ? (
rootCategories.map((category) => (
<Link
key={category.id}
href={`/category/${category.slug}`}
className="block w-full text-right px-3 py-2 text-xs font-medium text-gray-600 hover:bg-gray-100 hover:text-blue-600 rounded-md transition-colors"
>
{category.name}
</Link>
))
) : (
<div className="text-center text-xs text-gray-500 py-2">
در حال بارگذاری...
</div>
)}
</div>
</div>
{/* نمایش بقیه لینک‌ها */}
{mainNavLinks.filter(link => link.href !== "/").map((link) => (
<Link key={link.label} href={link.href} className="hover:text-blue-600 transition duration-200 font-medium">
{link.label}
</Link>
))}
</nav>
<div className="text-xl text-gray-700 cursor-pointer hidden md:block">
<button className="inline-flex items-center justify-center px-6 py-2 rounded-xl border-1 border-[#ffb900] text-[#ffb900] text-xs tracking-wide focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]/50">
استعلام قیمت
</button>
</div>
</div>
</div>
</div>
</header>
);
}