434 lines
28 KiB
TypeScript
434 lines
28 KiB
TypeScript
"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 { ShoppingCart, Trash2 } from "lucide-react";
|
||
import { useCart } from './context/cartcontext';
|
||
import { Search, X } from 'lucide-react';
|
||
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" },
|
||
];
|
||
|
||
export function Header() {
|
||
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 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);
|
||
|
||
const isLinkActive = (href: string): boolean => {
|
||
if (href === "/") {
|
||
return pathname === "/";
|
||
}
|
||
return pathname === href;
|
||
};
|
||
|
||
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)}`);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<header>
|
||
<div>
|
||
|
||
{/* 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">071‑1234‑5678</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">071‑1234‑5678</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}
|
||
// اصلاح URL برای تطابق با /products/[id]/[slug]
|
||
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 z-50 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} />
|
||
|
||
{/* بج (Badge) تعداد محصولات */}
|
||
{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="flex space-x-6 rtl:space-x-4 text-gray-700 text-xs w-full lg:w-auto lg:order-none">
|
||
{mainNavLinks.map((link) => (
|
||
<a key={link.label} href={link.href} className="hover:text-blue-600 transition duration-200 font-medium hidden md:block">
|
||
{link.label}
|
||
</a>
|
||
))}
|
||
</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>
|
||
);
|
||
}
|