1280 lines
83 KiB
TypeScript
1280 lines
83 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, Search, X, ChevronDown, Home, ShoppingBag, Wallet, MapPin, Headphones, IdCard, LogOut } from "lucide-react";
|
||
import { useCart } from './context/cartcontext';
|
||
import { products } from '@/lib/data';
|
||
import '@/public/src/css/header.css';
|
||
import { registerUser } from '@/public/src/services/auth/api';
|
||
import { loginUser } from '@/public/src/services/auth/api';
|
||
import { logoutUser } from '@/public/src/services/auth/api';
|
||
import { useCategories } from './context/categoryprovider';
|
||
|
||
const topBarLinks = [
|
||
{ label: "بخش صنعتی", href: "/" },
|
||
{ label: "بخش خودرویی", href: "/automotive" },
|
||
];
|
||
|
||
const mainNavLinks = [
|
||
{ label: "صفحه اصلی", href: "/" },
|
||
{ label: "محصولات", href: "/products" },
|
||
{ label: "مقالات", href: "/blog" },
|
||
{ label: "درباره ما", href: "/about" },
|
||
{ label: "تماس با ما", href: "/contact" },
|
||
];
|
||
|
||
|
||
type FloatingInputProps = {
|
||
name?: string;
|
||
value: string;
|
||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||
label: string;
|
||
type?: string;
|
||
dir?: "rtl" | "ltr";
|
||
inputMode?: React.HTMLAttributes<HTMLInputElement>["inputMode"];
|
||
autoComplete?: string;
|
||
className?: string;
|
||
leftSlot?: React.ReactNode;
|
||
rightSlot?: React.ReactNode;
|
||
};
|
||
|
||
function FloatingInput({
|
||
name,
|
||
value,
|
||
onChange,
|
||
label,
|
||
type = "text",
|
||
dir = "rtl",
|
||
inputMode,
|
||
autoComplete,
|
||
className = "",
|
||
leftSlot,
|
||
rightSlot,
|
||
}: FloatingInputProps) {
|
||
const hasLeftSlot = Boolean(leftSlot);
|
||
const hasRightSlot = Boolean(rightSlot);
|
||
|
||
return (
|
||
<label className="relative block w-full">
|
||
{leftSlot ? (
|
||
<div className="pointer-events-none absolute left-4 top-1/2 z-10 -translate-y-1/2">
|
||
{leftSlot}
|
||
</div>
|
||
) : null}
|
||
{rightSlot ? (
|
||
<div className="absolute right-4 top-1/2 z-10 -translate-y-1/2">
|
||
{rightSlot}
|
||
</div>
|
||
) : null}
|
||
<input
|
||
name={name}
|
||
value={value}
|
||
onChange={onChange}
|
||
type={type}
|
||
dir={dir}
|
||
inputMode={inputMode}
|
||
autoComplete={autoComplete}
|
||
placeholder=" "
|
||
className={`peer w-full rounded-2xl border border-gray-200 bg-gray-50 py-4 text-sm text-[#1A2332] outline-none transition placeholder:text-gray-400 focus:border-[#ffb900] focus:bg-white ${hasLeftSlot ? "pl-24" : "pl-4"} ${hasRightSlot ? "pr-12" : "pr-4"} ${className}`}
|
||
/>
|
||
<span
|
||
className={`pointer-events-none absolute top-4 z-10 origin-right rounded-full bg-white px-2 text-sm text-gray-400 transition-all duration-200 peer-focus:top-0 peer-focus:-translate-y-1/2 peer-focus:scale-90 peer-focus:text-[#1A2332] peer-[:not(:placeholder-shown)]:top-0 peer-[:not(:placeholder-shown)]:-translate-y-1/2 peer-[:not(:placeholder-shown)]:scale-90 ${hasRightSlot ? "right-12" : "right-3"}`}
|
||
>
|
||
{label}
|
||
</span>
|
||
</label>
|
||
);
|
||
}
|
||
|
||
function IranPhoneAdornment() {
|
||
return (
|
||
<div dir="ltr" className="flex items-center gap-2 text-sm text-gray-500">
|
||
<span className="text-base leading-none">🇮🇷</span>
|
||
<span className="font-medium">+98</span>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
const dashboardMenuItems = [
|
||
{ id: "dash", label: "پیشخوان", icon: Home, href: "/dashboard?tab=dash" },
|
||
{ id: "orders", label: "سفارشهای من", icon: ShoppingBag, href: "/dashboard?tab=orders" },
|
||
{ id: "wallet", label: "کیف پول", icon: Wallet, href: "/dashboard?tab=wallet" },
|
||
{ id: "address", label: "آدرسها", icon: MapPin, href: "/dashboard?tab=address" },
|
||
{ id: "tickets", label: "تیکت پشتیبانی", icon: Headphones, href: "/dashboard?tab=tickets" },
|
||
{ id: "profile", label: "مشخصات حساب", icon: IdCard, href: "/dashboard?tab=profile" },
|
||
];
|
||
|
||
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 [isOpen, setIsOpen] = useState(false);
|
||
const [formType, setFormType] = useState("mobile");
|
||
const [activeTab, setActiveTab] = useState("login");
|
||
const [registerError, setRegisterError] = useState("");
|
||
const [loginError, setLoginError] = useState("");
|
||
const [registerUsernameError, setRegisterUsernameError] = useState("");
|
||
const [loginUsernameError, setLoginUsernameError] = useState("");
|
||
const { rootCategories } = useCategories();
|
||
const [user, setUser] = useState<{ username: string; displayName: string } | null>(null);
|
||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||
const [showPassword, setShowPassword] = useState(false);
|
||
const [showLoginPassword, setShowLoginPassword] = useState(false);
|
||
const [loginMobile, setLoginMobile] = useState("");
|
||
const [showRegisterSuccessDialog, setShowRegisterSuccessDialog] = useState(false);
|
||
|
||
|
||
|
||
const [loginForm, setLoginForm] = useState({
|
||
username: "",
|
||
password: "",
|
||
});
|
||
|
||
const [registerForm, setRegisterForm] = useState({
|
||
phone: "",
|
||
fullName: "",
|
||
username: "",
|
||
password: "",
|
||
});
|
||
|
||
const usernamePattern = /^[A-Za-z0-9]*$/;
|
||
const usernameErrorMessage = "نام کاربری فقط باید با حروف انگلیسی و عدد وارد شود";
|
||
|
||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||
if (e.target.name === "username") {
|
||
if (!usernamePattern.test(e.target.value)) {
|
||
setRegisterUsernameError(usernameErrorMessage);
|
||
return;
|
||
}
|
||
setRegisterUsernameError("");
|
||
}
|
||
|
||
setRegisterForm({
|
||
...registerForm,
|
||
[e.target.name]: e.target.value,
|
||
});
|
||
};
|
||
|
||
|
||
useEffect(() => {
|
||
const token = localStorage.getItem("accessToken");
|
||
const username = localStorage.getItem("username");
|
||
const fullName = localStorage.getItem("fullName");
|
||
|
||
if (token && username) {
|
||
setUser({ username, displayName: fullName || username });
|
||
}
|
||
}, []);
|
||
|
||
|
||
const handleRegister = async () => {
|
||
|
||
setRegisterError("");
|
||
|
||
const phone = registerForm.phone.trim();
|
||
const username = registerForm.username.trim();
|
||
const password = registerForm.password.trim();
|
||
const fullName = registerForm.fullName.trim();
|
||
|
||
if (!usernamePattern.test(username)) {
|
||
setRegisterUsernameError(usernameErrorMessage);
|
||
return;
|
||
}
|
||
|
||
// ✅ بررسی خالی بودن فیلدها
|
||
if (!phone || !username || !password || !fullName) {
|
||
setRegisterError("لطفاً فیلدها را پر کنید");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
|
||
const payload = {
|
||
phone,
|
||
username,
|
||
password,
|
||
fullName,
|
||
};
|
||
|
||
const res = await registerUser(payload);
|
||
const role = res.data?.user?.role;
|
||
const displayName = res.data?.user?.fullName || fullName || username;
|
||
|
||
localStorage.setItem("accessToken", res.data.accessToken);
|
||
localStorage.setItem("refreshToken", res.data.refreshToken);
|
||
localStorage.setItem("username", username);
|
||
localStorage.setItem("fullName", displayName);
|
||
localStorage.setItem("role", role.toLowerCase());
|
||
console.log(res);
|
||
|
||
|
||
setUser({ username, displayName });
|
||
setIsOpen(false);
|
||
setShowRegisterSuccessDialog(true);
|
||
|
||
|
||
} catch (error: any) {
|
||
|
||
const message = error?.message?.toLowerCase() || "";
|
||
|
||
const usernameDuplicate = message.includes("username");
|
||
const phoneDuplicate = message.includes("phone");
|
||
|
||
if (usernameDuplicate && phoneDuplicate) {
|
||
setRegisterError("نام کاربری و شماره موبایل قبلاً ثبت شدهاند");
|
||
}
|
||
else if (usernameDuplicate) {
|
||
setRegisterError("این نام کاربری قبلاً ثبت شده است");
|
||
}
|
||
else if (phoneDuplicate) {
|
||
setRegisterError("این شماره موبایل قبلاً ثبت شده است");
|
||
}
|
||
else {
|
||
setRegisterError("خطا در ثبت نام");
|
||
}
|
||
}
|
||
};
|
||
|
||
const handleLogin = async () => {
|
||
setLoginError("");
|
||
|
||
const username = loginForm.username.trim();
|
||
const password = loginForm.password.trim();
|
||
|
||
if (!usernamePattern.test(username)) {
|
||
setLoginUsernameError(usernameErrorMessage);
|
||
return;
|
||
}
|
||
|
||
if (!username || !password) {
|
||
setLoginError("لطفاً نام کاربری و رمز عبور را وارد کنید");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const res = await loginUser({ username, password });
|
||
const role = res.data?.user?.role;
|
||
const displayName = res.data?.user?.fullName || username;
|
||
localStorage.setItem("role", role.toLowerCase());
|
||
localStorage.setItem("accessToken", res.data.accessToken);
|
||
localStorage.setItem("refreshToken", res.data.refreshToken);
|
||
localStorage.setItem("username", username);
|
||
localStorage.setItem("fullName", displayName);
|
||
console.log(res);
|
||
setUser({ username, displayName });
|
||
setIsOpen(false);
|
||
router.push("/dashboard?success=login");
|
||
|
||
|
||
} catch (error: any) {
|
||
const msg = error?.message?.toLowerCase() || "";
|
||
|
||
console.log("LOGIN ERROR RAW:", msg); // این خیلی مهمه
|
||
|
||
// 1) اول بررسی کنیم که نام کاربری وجود ندارد
|
||
if (
|
||
msg.includes("not found") ||
|
||
msg.includes("user") && msg.includes("not") ||
|
||
msg.includes("username") && msg.includes("not")
|
||
) {
|
||
setLoginError("این نام کاربری وجود ندارد");
|
||
return;
|
||
}
|
||
|
||
// 2) سپس رمز عبور اشتباه
|
||
if (
|
||
msg.includes("password") ||
|
||
msg.includes("invalid") ||
|
||
msg.includes("incorrect")
|
||
) {
|
||
setLoginError("رمز عبور اشتباه است");
|
||
return;
|
||
}
|
||
|
||
// 3) خطای عمومی
|
||
setLoginError("خطا در ورود. لطفاً دوباره تلاش کنید.");
|
||
}
|
||
};
|
||
|
||
const clearAuthState = () => {
|
||
localStorage.removeItem("accessToken");
|
||
localStorage.removeItem("refreshToken");
|
||
localStorage.removeItem("username");
|
||
localStorage.removeItem("fullName");
|
||
localStorage.removeItem("role");
|
||
setUser(null);
|
||
};
|
||
|
||
const handleLogout = async () => {
|
||
const token = localStorage.getItem("accessToken");
|
||
|
||
try {
|
||
if (token) {
|
||
await logoutUser(token);
|
||
}
|
||
} catch (error) {
|
||
console.error("Logout failed:", error);
|
||
} finally {
|
||
clearAuthState();
|
||
setUserMenuOpen(false);
|
||
router.push("/");
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
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)}`);
|
||
}
|
||
};
|
||
|
||
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">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}
|
||
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>
|
||
|
||
{!user ? (
|
||
<button
|
||
onClick={() => setIsOpen(true)}
|
||
className="flex cursor-pointer 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 className="relative">
|
||
<button
|
||
onClick={() => setUserMenuOpen((prev) => !prev)}
|
||
className="flex cursor-pointer 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" className="w-4 h-4 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8} d="M5.121 17.804A9 9 0 1118.879 17.804M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
||
</svg>
|
||
|
||
<span>{user.displayName}</span>
|
||
<ChevronDown className={`h-3.5 w-3.5 text-gray-400 transition-transform ${userMenuOpen ? "rotate-180" : ""}`} />
|
||
</button>
|
||
|
||
{userMenuOpen && (
|
||
<div className="absolute left-0 mt-2 w-56 overflow-hidden rounded-2xl border border-gray-200 bg-white p-2 text-xs shadow-lg">
|
||
<div className="mb-2 border-b border-gray-100 px-3 pb-2 text-right">
|
||
<p className="font-semibold text-[#1A2332]">{user.displayName}</p>
|
||
<p className="mt-1 text-[11px] text-gray-500">دسترسی سریع به بخشهای پیشخوان</p>
|
||
</div>
|
||
<div className="flex flex-col gap-1">
|
||
{dashboardMenuItems.map((item) => (
|
||
<button
|
||
key={item.id}
|
||
onClick={() => {
|
||
setUserMenuOpen(false);
|
||
router.push(item.href);
|
||
}}
|
||
className="flex w-full cursor-pointer items-center gap-3 rounded-xl px-3 py-2.5 text-right text-gray-700 hover:bg-gray-50 hover:text-[#1A2332]"
|
||
>
|
||
<item.icon className="h-4 w-4 text-gray-400" />
|
||
<span>{item.label}</span>
|
||
</button>
|
||
))}
|
||
</div>
|
||
<div className="mt-2 border-t border-gray-100 pt-2">
|
||
<button
|
||
onClick={() => {
|
||
setUserMenuOpen(false);
|
||
handleLogout();
|
||
}}
|
||
className="flex w-full cursor-pointer items-center gap-3 rounded-xl px-3 py-2.5 text-right text-red-500 hover:bg-red-50"
|
||
>
|
||
<LogOut className="h-4 w-4" />
|
||
<span>خروج</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
|
||
{isOpen && (
|
||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-[#1A2332]/40 backdrop-blur-sm transition-opacity" dir="rtl">
|
||
|
||
<div
|
||
className="absolute inset-0"
|
||
onClick={() => setIsOpen(false)}
|
||
></div>
|
||
|
||
<div className="relative w-full max-w-sm bg-white rounded-3xl shadow-2xl p-8">
|
||
|
||
{/* دکمه بستن */}
|
||
<button
|
||
onClick={() => setIsOpen(false)}
|
||
className="absolute cursor-pointer top-4 left-5 text-gray-400 hover:text-gray-700 transition"
|
||
>
|
||
✕
|
||
</button>
|
||
|
||
<div className="flex flex-col items-center mt-2 w-full">
|
||
|
||
{/* Tabs */}
|
||
<div className="flex w-full bg-gray-100 rounded-xl p-1 mb-6">
|
||
<button
|
||
onClick={() => setActiveTab("login")}
|
||
className={`flex-1 cursor-pointer py-2 text-sm rounded-lg transition ${activeTab === "login"
|
||
? "bg-white shadow text-[#1A2332] font-semibold"
|
||
: "text-gray-500"
|
||
}`}
|
||
>
|
||
ورود
|
||
</button>
|
||
|
||
<button
|
||
onClick={() => setActiveTab("register")}
|
||
className={`flex-1 cursor-pointer py-2 text-sm rounded-lg transition ${activeTab === "register"
|
||
? "bg-white shadow text-[#1A2332] font-semibold"
|
||
: "text-gray-500"
|
||
}`}
|
||
>
|
||
ثبت نام
|
||
</button>
|
||
|
||
</div>
|
||
|
||
{/* ---------------- LOGIN ---------------- */}
|
||
|
||
{activeTab === "login" && (
|
||
<div className="w-full space-y-5">
|
||
<div className="rounded-2xl border border-[#ffde7a] bg-[linear-gradient(135deg,#fff8dd_0%,#fffdf6_100%)] px-4 py-4 text-right">
|
||
<p className="text-base font-semibold text-[#1A2332]">ورود سریع به حساب کاربری</p>
|
||
<p className="mt-1 text-xs leading-6 text-gray-600">
|
||
سفارشها، وضعیت خرید و اطلاعات حسابتان را یکجا و بدون دردسر مدیریت کنید.
|
||
</p>
|
||
</div>
|
||
|
||
{formType === "mobile" && (
|
||
<div className="w-full space-y-4">
|
||
<FloatingInput
|
||
value={loginMobile}
|
||
onChange={(e) => setLoginMobile(e.target.value)}
|
||
type="tel"
|
||
dir="ltr"
|
||
inputMode="tel"
|
||
autoComplete="tel"
|
||
label="شماره موبایل"
|
||
className="text-left tracking-[0.18em]"
|
||
leftSlot={<IranPhoneAdornment />}
|
||
/>
|
||
|
||
<button className="w-full cursor-pointer rounded-2xl bg-[#ffb900] py-3.5 text-sm font-semibold text-[#1A2332] hover:bg-[#e5a600]">
|
||
ورود
|
||
</button>
|
||
|
||
<button
|
||
onClick={() => setFormType("password")}
|
||
className="w-full cursor-pointer text-xs text-gray-500 hover:text-[#1A2332]"
|
||
>
|
||
ورود با نام کاربری و رمز عبور
|
||
</button>
|
||
</div>
|
||
)}
|
||
|
||
{formType === "password" && (
|
||
<div className="w-full space-y-4">
|
||
<p className="text-xs leading-6 text-gray-500">
|
||
خوش برگشتی. برای ادامه، نام کاربری و رمز عبورت را وارد کن.
|
||
</p>
|
||
|
||
<FloatingInput
|
||
value={loginForm.username}
|
||
onChange={(e) => {
|
||
const username = e.target.value;
|
||
if (!usernamePattern.test(username)) {
|
||
setLoginUsernameError(usernameErrorMessage);
|
||
return;
|
||
}
|
||
setLoginUsernameError("");
|
||
setLoginForm((prev) => ({
|
||
...prev,
|
||
username,
|
||
}));
|
||
}}
|
||
type="text"
|
||
dir="ltr"
|
||
autoComplete="username"
|
||
label="نام کاربری"
|
||
className="text-left"
|
||
/>
|
||
{loginUsernameError && (
|
||
<div className="mt-1 text-right text-xs text-red-500">
|
||
{loginUsernameError}
|
||
</div>
|
||
)}
|
||
|
||
<FloatingInput
|
||
value={loginForm.password}
|
||
onChange={(e) => {
|
||
const password = e.target.value;
|
||
setLoginForm((prev) => ({
|
||
...prev,
|
||
password,
|
||
}));
|
||
if (!password) {
|
||
setShowLoginPassword(false);
|
||
}
|
||
}}
|
||
type={showLoginPassword ? "text" : "password"}
|
||
dir="ltr"
|
||
autoComplete="current-password"
|
||
label="رمز عبور"
|
||
className="text-left"
|
||
rightSlot={loginForm.password ? (
|
||
<button
|
||
type="button"
|
||
onClick={() => setShowLoginPassword(!showLoginPassword)}
|
||
className="cursor-pointer p-1 text-gray-500 hover:text-gray-700"
|
||
>
|
||
{showLoginPassword ? (
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M3 3l18 18M10.477 10.477A3 3 0 0113.5 13.5m-7.09-2.664A9.956 9.956 0 003 12s2.91-6 9-6a9.953 9.953 0 016.328 2.318M15.54 15.54A9.953 9.953 0 0112 18c-6.09 0-9-6-9-6a9.956 9.956 0 012.41-3.868" />
|
||
</svg>
|
||
) : (
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||
</svg>
|
||
)}
|
||
</button>
|
||
) : undefined}
|
||
/>
|
||
|
||
<button onClick={handleLogin} className="w-full cursor-pointer rounded-2xl bg-[#ffb900] py-3.5 text-sm font-semibold text-[#1A2332] hover:bg-[#e5a600]">
|
||
ورود
|
||
</button>
|
||
{loginError && (
|
||
<div className="mt-2 text-center text-sm text-red-500">
|
||
{loginError}
|
||
</div>
|
||
)}
|
||
|
||
<button
|
||
onClick={() => setFormType("mobile")}
|
||
className="w-full cursor-pointer text-xs text-gray-500 hover:text-[#1A2332]"
|
||
>
|
||
ورود با شماره موبایل
|
||
</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{false && activeTab === "login" && (
|
||
<div className="w-full">
|
||
|
||
{/* فرم موبایل */}
|
||
{formType === "mobile" && (
|
||
<div className="w-full space-y-4">
|
||
|
||
<input
|
||
type="tel"
|
||
placeholder="شماره موبایل"
|
||
className="w-full px-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm text-right focus:outline-none focus:border-[#ffb900]"
|
||
/>
|
||
|
||
<button className="w-full cursor-pointer py-3.5 bg-[#ffb900] hover:bg-[#e5a600] text-[#1A2332] font-semibold rounded-2xl text-sm">
|
||
ورود
|
||
</button>
|
||
|
||
<button
|
||
onClick={() => setFormType("password")}
|
||
className="w-full cursor-pointer text-xs text-gray-500 hover:text-[#1A2332]"
|
||
>
|
||
ورود با نام کاربری و رمز عبور
|
||
</button>
|
||
|
||
</div>
|
||
)}
|
||
|
||
{/* فرم یوزر پس */}
|
||
{formType === "password" && (
|
||
<div className="w-full space-y-4">
|
||
|
||
<input
|
||
type="text"
|
||
value={loginForm.username}
|
||
onChange={(e) =>
|
||
setLoginForm((prev) => ({
|
||
...prev,
|
||
username: e.target.value,
|
||
}))
|
||
}
|
||
placeholder="نام کاربری"
|
||
className="w-full px-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm text-right focus:outline-none focus:border-[#ffb900]"
|
||
/>
|
||
|
||
|
||
<div className="relative w-full">
|
||
<input
|
||
type={showLoginPassword ? "text" : "password"} // تغییر نوع بر اساس state
|
||
value={loginForm.password}
|
||
onChange={(e) =>
|
||
setLoginForm((prev) => ({
|
||
...prev,
|
||
password: e.target.value,
|
||
}))
|
||
}
|
||
placeholder="رمز عبور"
|
||
className="w-full px-4 pl-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm text-right focus:outline-none focus:border-[#ffb900]"
|
||
/>
|
||
|
||
<button
|
||
type="button"
|
||
onClick={() => setShowLoginPassword(!showLoginPassword)}
|
||
className="absolute cursor-pointer left-4 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700 p-1" // پدینگ اضافه شد تا کلیک راحتتر باشد
|
||
>
|
||
{showLoginPassword ? (
|
||
// آیکون چشم خط خورده (eye-off)
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M3 3l18 18M10.477 10.477A3 3 0 0113.5 13.5m-7.09-2.664A9.956 9.956 0 003 12s2.91-6 9-6a9.953 9.953 0 016.328 2.318M15.54 15.54A9.953 9.953 0 0112 18c-6.09 0-9-6-9-6a9.956 9.956 0 012.41-3.868" />
|
||
</svg>
|
||
) : (
|
||
// آیکون چشم باز (eye)
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||
</svg>
|
||
)}
|
||
</button>
|
||
</div>
|
||
|
||
<button onClick={handleLogin} className="w-full cursor-pointer py-3.5 bg-[#ffb900] hover:bg-[#e5a600] text-[#1A2332] font-semibold rounded-2xl text-sm">
|
||
ورود
|
||
</button>
|
||
{loginError && (
|
||
<div className="text-red-500 text-sm text-center mt-2">
|
||
{loginError}
|
||
</div>
|
||
)}
|
||
|
||
<button
|
||
onClick={() => setFormType("mobile")}
|
||
className="w-full cursor-pointer text-xs text-gray-500 hover:text-[#1A2332]"
|
||
>
|
||
ورود با شماره موبایل
|
||
</button>
|
||
|
||
</div>
|
||
)}
|
||
|
||
</div>
|
||
)}
|
||
|
||
{/* ---------------- REGISTER ---------------- */}
|
||
{activeTab === "register" && (
|
||
<div className="w-full space-y-4">
|
||
<div className="rounded-2xl border border-gray-200 bg-gray-50 px-4 py-4 text-right">
|
||
<p className="text-base font-semibold text-[#1A2332]">چند قدم تا شروع خرید هوشمند</p>
|
||
<p className="mt-1 text-xs leading-6 text-gray-600">
|
||
با ساخت حساب، پیگیری سفارشها و دسترسی سریع به سبد خرید همیشه همراهت میماند.
|
||
</p>
|
||
</div>
|
||
|
||
<FloatingInput
|
||
name="phone"
|
||
value={registerForm.phone}
|
||
onChange={handleChange}
|
||
type="tel"
|
||
dir="ltr"
|
||
inputMode="tel"
|
||
autoComplete="tel"
|
||
label="شماره موبایل"
|
||
className="text-left tracking-[0.18em]"
|
||
leftSlot={<IranPhoneAdornment />}
|
||
/>
|
||
|
||
<FloatingInput
|
||
name="fullName"
|
||
value={registerForm.fullName}
|
||
onChange={handleChange}
|
||
type="text"
|
||
autoComplete="name"
|
||
label="نام و نام خانوادگی"
|
||
/>
|
||
|
||
<FloatingInput
|
||
name="username"
|
||
value={registerForm.username}
|
||
onChange={handleChange}
|
||
type="text"
|
||
dir="ltr"
|
||
autoComplete="username"
|
||
label="نام کاربری"
|
||
className="text-left"
|
||
/>
|
||
{registerUsernameError && (
|
||
<div className="mt-1 text-right text-xs text-red-500">
|
||
{registerUsernameError}
|
||
</div>
|
||
)}
|
||
|
||
<FloatingInput
|
||
name="password"
|
||
value={registerForm.password}
|
||
onChange={(e) => {
|
||
handleChange(e);
|
||
if (!e.target.value) {
|
||
setShowPassword(false);
|
||
}
|
||
}}
|
||
type={showPassword ? "text" : "password"}
|
||
dir="ltr"
|
||
autoComplete="new-password"
|
||
label="رمز عبور"
|
||
className="text-left"
|
||
rightSlot={registerForm.password ? (
|
||
<button
|
||
type="button"
|
||
onClick={() => setShowPassword(!showPassword)}
|
||
className="cursor-pointer text-gray-500 hover:text-gray-700"
|
||
>
|
||
{showPassword ? (
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M3 3l18 18M10.477 10.477A3 3 0 0113.5 13.5m-7.09-2.664A9.956 9.956 0 003 12s2.91-6 9-6a9.953 9.953 0 016.328 2.318M15.54 15.54A9.953 9.953 0 0112 18c-6.09 0-9-6-9-6a9.956 9.956 0 012.41-3.868" />
|
||
</svg>
|
||
) : (
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||
</svg>
|
||
)}
|
||
</button>
|
||
) : undefined}
|
||
/>
|
||
|
||
<p className="text-xs leading-6 text-gray-500">
|
||
با ثبتنام، تجربه خرید سریعتر و دسترسی راحتتر به حساب کاربری برایت فعال میشود.
|
||
</p>
|
||
|
||
<button
|
||
onClick={handleRegister}
|
||
className="w-full cursor-pointer rounded-2xl bg-[#ffb900] py-3.5 text-sm font-semibold text-[#1A2332] hover:bg-[#e5a600]"
|
||
>
|
||
ثبت نام
|
||
</button>
|
||
|
||
{registerError && (
|
||
<div className="mt-2 text-center text-sm text-red-500">
|
||
{registerError}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
{false && activeTab === "register" && (
|
||
<div className="w-full space-y-4">
|
||
<input
|
||
name="phone"
|
||
value={registerForm.phone}
|
||
onChange={handleChange}
|
||
type="tel"
|
||
placeholder="شماره موبایل"
|
||
className="w-full px-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm text-right focus:outline-none focus:border-[#ffb900]"
|
||
/>
|
||
|
||
<input
|
||
name="fullName"
|
||
value={registerForm.fullName}
|
||
onChange={handleChange}
|
||
type="text"
|
||
placeholder="نام"
|
||
className="w-full px-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm text-right focus:outline-none focus:border-[#ffb900]"
|
||
/>
|
||
|
||
<input
|
||
name="username"
|
||
value={registerForm.username}
|
||
onChange={handleChange}
|
||
type="text"
|
||
placeholder="نام کاربری"
|
||
className='w-full px-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm text-right focus:outline-none focus:border-[#ffb900]'
|
||
/>
|
||
<div className="relative w-full">
|
||
<input
|
||
name="password"
|
||
value={registerForm.password}
|
||
onChange={handleChange}
|
||
type={showPassword ? "text" : "password"}
|
||
placeholder="رمز عبور"
|
||
className="w-full px-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm text-right focus:outline-none focus:border-[#ffb900]"
|
||
/>
|
||
|
||
<button
|
||
type="button"
|
||
onClick={() => setShowPassword(!showPassword)}
|
||
className="absolute cursor-pointer left-4 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||
>
|
||
{showPassword ? (
|
||
// eye-off icon
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M3 3l18 18M10.477 10.477A3 3 0 0113.5 13.5m-7.09-2.664A9.956 9.956 0 003 12s2.91-6 9-6a9.953 9.953 0 016.328 2.318M15.54 15.54A9.953 9.953 0 0112 18c-6.09 0-9-6-9-6a9.956 9.956 0 012.41-3.868" />
|
||
</svg>
|
||
) : (
|
||
// eye icon
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.477 0 8.268 2.943 9.542 7-1.274 4.057-5.065 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||
<path strokeLinecap="round" strokeLinejoin="round"
|
||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||
</svg>
|
||
)}
|
||
</button>
|
||
</div>
|
||
|
||
|
||
{/* <input
|
||
name="confirmPassword"
|
||
value={registerForm.confirmPassword}
|
||
onChange={handleChange}
|
||
type="password"
|
||
placeholder="تکرار رمز عبور"
|
||
className="w-full px-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm text-right focus:outline-none focus:border-[#ffb900]"
|
||
/> */}
|
||
|
||
<button
|
||
onClick={handleRegister}
|
||
className="w-full cursor-pointer py-3.5 bg-[#ffb900] hover:bg-[#e5a600] text-[#1A2332] font-semibold rounded-2xl text-sm"
|
||
>
|
||
ثبت نام
|
||
</button>
|
||
|
||
{registerError && (
|
||
<div className="text-red-500 text-sm text-center mt-2">
|
||
{registerError}
|
||
</div>
|
||
)}
|
||
|
||
</div>
|
||
)}
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
)}
|
||
|
||
{showRegisterSuccessDialog && (
|
||
<div className="fixed inset-0 z-[60] flex items-center justify-center bg-[#1A2332]/45 p-4 backdrop-blur-sm" dir="rtl">
|
||
<div
|
||
className="absolute inset-0"
|
||
onClick={() => setShowRegisterSuccessDialog(false)}
|
||
></div>
|
||
<div className="relative w-full max-w-sm rounded-3xl bg-white p-8 shadow-2xl">
|
||
<button
|
||
onClick={() => setShowRegisterSuccessDialog(false)}
|
||
className="absolute left-5 top-4 cursor-pointer text-gray-400 transition hover:text-gray-700"
|
||
>
|
||
<X className="h-5 w-5" />
|
||
</button>
|
||
|
||
<div className="text-right">
|
||
<div className="mb-5 flex h-14 w-14 items-center justify-center rounded-2xl bg-[#fff4cc] text-[#c78a00]">
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.8">
|
||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||
</svg>
|
||
</div>
|
||
<h3 className="text-xl font-bold text-[#1A2332]">تشکر از ثبت نام</h3>
|
||
<p className="mt-2 text-sm leading-7 text-gray-600">
|
||
حساب کاربری شما با موفقیت ساخته شد. حالا میتوانید وارد پیشخوان کاربری شوید و اطلاعات حساب و سفارشها را مدیریت کنید.
|
||
</p>
|
||
<div className="mt-6 flex flex-col gap-3">
|
||
<Link
|
||
href="/dashboard"
|
||
onClick={() => setShowRegisterSuccessDialog(false)}
|
||
className="flex items-center justify-center rounded-2xl bg-[#ffb900] px-4 py-3 text-sm font-semibold text-[#1A2332] hover:bg-[#e5a600]"
|
||
>
|
||
ورود به پیشخوان کاربری
|
||
</Link>
|
||
<button
|
||
onClick={() => setShowRegisterSuccessDialog(false)}
|
||
className="rounded-2xl border border-gray-200 px-4 py-3 text-sm font-medium text-gray-600 hover:bg-gray-50"
|
||
>
|
||
بستن
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
</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.map(cat => (
|
||
<Link className='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'
|
||
href={`/category/${cat.slug}`} key={cat.id}>
|
||
{cat.name}
|
||
</Link>
|
||
))}
|
||
|
||
</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>
|
||
);
|
||
}
|