Files
parsshop/components/header.tsx
haniyeroozmand 8b733c817c first commit
2026-03-21 18:58:07 +03:30

434 lines
28 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 { 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">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}
// اصلاح 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>
);
}