checkout accessibility/category products

This commit is contained in:
haniyeroozmand
2026-04-02 14:18:37 +03:30
parent 65c28948a3
commit a2de32dfad
12 changed files with 270 additions and 301 deletions

View File

@@ -3,6 +3,8 @@
import { useCart } from "@/components/context/cartcontext"; import { useCart } from "@/components/context/cartcontext";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react"; // اضافه شدن useState
import { import {
Trash2, Trash2,
ShoppingBag, ShoppingBag,
@@ -11,14 +13,33 @@ import {
Minus, Minus,
ShieldCheck, ShieldCheck,
Truck, Truck,
CreditCard, ChevronRight, Check CreditCard
} from "lucide-react"; } from "lucide-react";
// مسیر ایمپورت کامپوننت Notlogin را بر اساس ساختار پوشه‌بندی خود تنظیم کنید
import NotLogin from "@/components/Notlogin";
export default function CartPage() { export default function CartPage() {
const { cart, clearCart, addToCart, decreaseQuantity } = useCart(); const { cart, clearCart, addToCart, decreaseQuantity } = useCart();
const router = useRouter();
// استیت برای مدیریت نمایش مودال/کامپوننت لاگین نشده‌ها
const [showNotLogin, setShowNotLogin] = useState(false);
// بررسی لاگین کاربر
const handleCheckoutNavigation = () => {
const token = localStorage.getItem('accessToken');
if (token) {
// اگر لاگین بود برود به چک اوت
router.push('/checkout');
} else {
// اگر لاگین نبود، کامپوننت Notlogin نمایش داده شود
setShowNotLogin(true);
}
};
// تبدیل رشته قیمت به عدد // تبدیل رشته قیمت به عدد
const parsePrice = (priceStr?: string | null) => { const parsePrice = (priceStr?: number | null) => {
if (!priceStr) return 0; if (!priceStr) return 0;
return Number(priceStr.toString().replace(/,/g, '')); return Number(priceStr.toString().replace(/,/g, ''));
}; };
@@ -51,9 +72,16 @@ export default function CartPage() {
return ( return (
<main className="bg-gray-50/30 min-h-screen pb-20"> <main className="bg-gray-50/30 min-h-screen pb-20">
{/* رندر کردن کامپوننت Notlogin به صورت شرطی */}
{/* اگر نیاز است که کاربر بتواند آن را ببندد، پراپ onClose را به آن پاس بدهید */}
{showNotLogin && (
<NotLogin
buttonText="بازگشت به سبد خرید"
onClose={() => setShowNotLogin(false)} />
)}
<div className="container mx-auto px-4 py-8 max-w-6xl"> <div className="container mx-auto px-4 py-8 max-w-6xl">
{/* بخش هدر */}
<div className="mb-10"> <div className="mb-10">
<div className="flex items-center justify-between mb-8"> <div className="flex items-center justify-between mb-8">
<h1 className="text-xl md:text-3xl font-black text-gray-800 flex items-center gap-3"> <h1 className="text-xl md:text-3xl font-black text-gray-800 flex items-center gap-3">
@@ -64,19 +92,13 @@ export default function CartPage() {
</h1> </h1>
</div> </div>
{/* بخش هدر و تایم‌لاین */}
<div className="mb-10"> <div className="mb-10">
{/* Breadcrumb (تایم‌لاین اکتیو شده برای مرحله ۲) */}
<div className="relative w-full max-w-2xl mx-auto px-2 sm:px-0 mb-12"> <div className="relative w-full max-w-2xl mx-auto px-2 sm:px-0 mb-12">
{/* خط سراسری بک‌گراند */}
<div className="absolute top-[20px] sm:top-[24px] left-[16.5%] right-[16.5%] h-[2px] bg-gray-200 z-0"> <div className="absolute top-[20px] sm:top-[24px] left-[16.5%] right-[16.5%] h-[2px] bg-gray-200 z-0">
{/* پر شدن خط مرحله دوم (۵۰ درصد پیشرفت) */}
<div className="h-full bg-[#ffb900] w-[50%] transition-all duration-500 ease-in-out"></div> <div className="h-full bg-[#ffb900] w-[50%] transition-all duration-500 ease-in-out"></div>
</div> </div>
<div className="flex justify-between relative z-10"> <div className="flex justify-between relative z-10">
{/* مرحله 1 (تکمیل شده) */}
<Link href="/cart" className="flex flex-col items-center w-1/3 group cursor-pointer"> <Link href="/cart" className="flex flex-col items-center w-1/3 group cursor-pointer">
<div className=" w-10 h-10 sm:w-12 sm:h-12 bg-[#ffb900] text-[#1A2332] rounded-full flex items-center justify-center shadow-[0_0_15px_rgba(255,185,0,0.4)] mb-2 sm:mb-3 ring-[6px] ring-[#ffb900]/20 transition-all"> <div className=" w-10 h-10 sm:w-12 sm:h-12 bg-[#ffb900] text-[#1A2332] rounded-full flex items-center justify-center shadow-[0_0_15px_rgba(255,185,0,0.4)] mb-2 sm:mb-3 ring-[6px] ring-[#ffb900]/20 transition-all">
<ShoppingBag className="w-5 h-5 sm:w-6 sm:h-6" strokeWidth={2} /> <ShoppingBag className="w-5 h-5 sm:w-6 sm:h-6" strokeWidth={2} />
@@ -84,16 +106,14 @@ export default function CartPage() {
<span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">سبد خرید</span> <span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">سبد خرید</span>
</Link> </Link>
{/* مرحله 2 (اکتیو و فعلی) */} {/* کلیک روی آیکون اطلاعات ارسال */}
<Link href={'/checkout'} className="flex flex-col items-center w-1/3"> <div onClick={handleCheckoutNavigation} className="flex flex-col items-center w-1/3 cursor-pointer group">
{/* استفاده از ring زرد با opacity برای نشان دادن حالت اکتیو */} <div className="w-10 h-10 sm:w-12 sm:h-12 bg-white border-2 border-gray-200 text-gray-400 rounded-full flex items-center justify-center mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc] group-hover:border-[#ffb900] transition-colors">
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-white border-2 border-gray-200 text-gray-400 rounded-full flex items-center justify-center mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc]">
<Truck className="w-4 h-4 sm:w-5 sm:h-5" strokeWidth={2} /> <Truck className="w-4 h-4 sm:w-5 sm:h-5" strokeWidth={2} />
</div> </div>
<span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">اطلاعات ارسال</span> <span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">اطلاعات ارسال</span>
</Link> </div>
{/* مرحله 3 (غیرفعال) */}
<div className="flex flex-col items-center w-1/3"> <div className="flex flex-col items-center w-1/3">
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-white border-2 border-gray-200 text-gray-400 rounded-full flex items-center justify-center mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc]"> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-white border-2 border-gray-200 text-gray-400 rounded-full flex items-center justify-center mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc]">
<CreditCard className="w-4 h-4 sm:w-5 sm:h-5" /> <CreditCard className="w-4 h-4 sm:w-5 sm:h-5" />
@@ -106,8 +126,6 @@ export default function CartPage() {
</div> </div>
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8"> <div className="flex flex-col lg:flex-row gap-6 lg:gap-8">
{/* بخش لیست محصولات */}
<div className="flex-1"> <div className="flex-1">
<div className="bg-white rounded-[1rem] p-4 md:p-8 shadow-sm "> <div className="bg-white rounded-[1rem] p-4 md:p-8 shadow-sm ">
<div className="flex justify-between items-center mb-6 pb-6 border-b border-gray-100"> <div className="flex justify-between items-center mb-6 pb-6 border-b border-gray-100">
@@ -121,50 +139,25 @@ export default function CartPage() {
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
{cart.map((item) => { {cart.map((item) => {
const itemTotal = parsePrice(item.price) * item.quantity; const itemTotal = parsePrice(item.price) * item.quantity;
return ( return (
<div key={item.id} className="group flex flex-col sm:flex-row gap-4 sm:gap-6 items-start sm:items-center border-b border-gray-50 pb-6 last:border-0 last:pb-0"> <div key={item.id} className="group flex flex-col sm:flex-row gap-4 sm:gap-6 items-start sm:items-center border-b border-gray-50 pb-6 last:border-0 last:pb-0">
{/* تصویر محصول */}
<div className="bg-gray-50/80 p-3 rounded-2xl border border-gray-100 shrink-0 relative group-hover:bg-white transition-colors duration-300 w-full sm:w-auto flex justify-center"> <div className="bg-gray-50/80 p-3 rounded-2xl border border-gray-100 shrink-0 relative group-hover:bg-white transition-colors duration-300 w-full sm:w-auto flex justify-center">
<Image src={item.image} alt={item.title} width={100} height={100} className="object-contain w-24 h-24 drop-shadow-sm" /> <Image src={item.image} alt={item.title} width={100} height={100} className="object-contain w-24 h-24 drop-shadow-sm" />
</div> </div>
{/* اطلاعات محصول */}
<div className="flex-1 w-full"> <div className="flex-1 w-full">
<p className="text-xs font-medium text-blue-500 mb-1.5">{item.brand}</p> <p className="text-xs font-medium text-blue-500 mb-1.5">{item.brand}</p>
<h3 className="text-sm md:text-base font-bold text-gray-800 line-clamp-2 leading-tight mb-3 group-hover:text-blue-600 transition-colors">{item.title}</h3> <h3 className="text-sm md:text-base font-bold text-gray-800 line-clamp-2 leading-tight mb-3 group-hover:text-blue-600 transition-colors">{item.title}</h3>
{/* ویژگی‌ها (Badges) */}
<div className="flex flex-wrap gap-2 text-[10px] md:text-xs font-medium text-gray-600">
<span className="bg-gray-100/80 px-2.5 py-1 rounded-md" dir="ltr">L : {item.l}</span>
<span className="bg-gray-100/80 px-2.5 py-1 rounded-md" dir="ltr">D : {item.d}</span>
</div> </div>
</div>
{/* قیمت و کنترلر */}
<div className="flex sm:flex-col justify-between sm:justify-end items-center sm:items-end w-full sm:w-auto gap-4 shrink-0 mt-2 sm:mt-0"> <div className="flex sm:flex-col justify-between sm:justify-end items-center sm:items-end w-full sm:w-auto gap-4 shrink-0 mt-2 sm:mt-0">
<span className="font-black text-base md:text-lg text-gray-900"> <span className="font-black text-base md:text-lg text-gray-900">
{item.price ? `${itemTotal.toLocaleString('fa-IR')} ت` : 'استعلام'} {item.price ? `${itemTotal.toLocaleString('fa-IR')} ت` : 'استعلام'}
</span> </span>
{/* کنترلر تعداد طرح کپسولی */}
<div className="flex items-center gap-1 bg-gray-50 border border-gray-200 rounded-full p-1 shadow-sm"> <div className="flex items-center gap-1 bg-gray-50 border border-gray-200 rounded-full p-1 shadow-sm">
<button <button onClick={() => addToCart(item)} className="w-7 h-7 md:w-8 md:h-8 flex items-center justify-center rounded-full bg-white text-gray-600 hover:text-green-600 hover:shadow-sm transition-all">
onClick={() => addToCart(item)}
className="w-7 h-7 md:w-8 md:h-8 flex items-center justify-center rounded-full bg-white text-gray-600 hover:text-green-600 hover:shadow-sm transition-all"
>
<Plus size={14} strokeWidth={2.5} /> <Plus size={14} strokeWidth={2.5} />
</button> </button>
<span className="text-xs md:text-sm font-bold text-gray-800 w-6 text-center">{item.quantity}</span>
<span className="text-xs md:text-sm font-bold text-gray-800 w-6 text-center"> <button onClick={() => decreaseQuantity(item.id)} className="w-7 h-7 md:w-8 md:h-8 flex items-center justify-center rounded-full bg-white text-gray-600 hover:text-red-500 hover:shadow-sm transition-all">
{item.quantity}
</span>
<button
onClick={() => decreaseQuantity(item.id)}
className="w-7 h-7 md:w-8 md:h-8 flex items-center justify-center rounded-full bg-white text-gray-600 hover:text-red-500 hover:shadow-sm transition-all"
>
{item.quantity === 1 ? <Trash2 size={14} /> : <Minus size={14} strokeWidth={2.5} />} {item.quantity === 1 ? <Trash2 size={14} /> : <Minus size={14} strokeWidth={2.5} />}
</button> </button>
</div> </div>
@@ -176,26 +169,20 @@ export default function CartPage() {
</div> </div>
</div> </div>
{/* بخش صورتحساب (Sidebar) */}
<div className="w-full lg:w-[340px] shrink-0"> <div className="w-full lg:w-[340px] shrink-0">
<div className="bg-white rounded-[1rem] p-4 md:p-8 shadow-sm sticky top-6"> <div className="bg-white rounded-[1rem] p-4 md:p-8 shadow-sm sticky top-6">
<h2 className="text-lg md:text-xl font-bold text-gray-800 mb-6">خلاصه صورتحساب</h2> <h2 className="text-lg md:text-xl font-bold text-gray-800 mb-6">خلاصه صورتحساب</h2>
<div className="space-y-4 mb-6"> <div className="space-y-4 mb-6">
<div className="flex justify-between items-center text-xs md:text-sm"> <div className="flex justify-between items-center text-xs md:text-sm">
<span className="text-gray-500">تعداد کالاها</span> <span className="text-gray-500">تعداد کالاها</span>
<span className="font-bold text-gray-800">{totalItems} عدد</span> <span className="font-bold text-gray-800">{totalItems} عدد</span>
</div> </div>
<div className="flex justify-between items-center text-xs md:text-sm"> <div className="flex justify-between items-center text-xs md:text-sm">
<span className="text-gray-500">هزینه ارسال</span> <span className="text-gray-500">هزینه ارسال</span>
<span className="text-blue-600 font-bold bg-blue-50 px-2 py-1 rounded-md text-[10px] md:text-xs">در مرحله بعد</span> <span className="text-blue-600 font-bold bg-blue-50 px-2 py-1 rounded-md text-[10px] md:text-xs">در مرحله بعد</span>
</div> </div>
</div> </div>
{/* خط‌چین جداکننده */}
<div className="w-full border-t-2 border-dashed border-gray-200 my-6"></div> <div className="w-full border-t-2 border-dashed border-gray-200 my-6"></div>
<div className="flex justify-between items-center mb-8"> <div className="flex justify-between items-center mb-8">
<span className="text-xs md:text-sm font-bold text-gray-600">مبلغ قابل پرداخت</span> <span className="text-xs md:text-sm font-bold text-gray-600">مبلغ قابل پرداخت</span>
<span className="font-black text-xl md:text-2xl text-[#ffb900] tracking-tight"> <span className="font-black text-xl md:text-2xl text-[#ffb900] tracking-tight">
@@ -204,22 +191,19 @@ export default function CartPage() {
</span> </span>
</div> </div>
<Link href={'/checkout'} className="w-full bg-[#ffb900] hover:bg-[#e5a600] text-black py-3 md:py-4 rounded-xl font-bold text-base md:text-lg transition-all shadow-[0_4px_15px_rgba(255,185,0,0.2)] hover:shadow-[0_6px_20px_rgba(255,185,0,0.3)] flex justify-center items-center gap-2 mb-6"> {/* دکمه تایید و ادامه */}
<button
onClick={handleCheckoutNavigation}
className="w-full bg-[#ffb900] hover:bg-[#e5a600] text-black py-3 md:py-4 rounded-xl font-bold text-base md:text-lg transition-all shadow-[0_4px_15px_rgba(255,185,0,0.2)] hover:shadow-[0_6px_20px_rgba(255,185,0,0.3)] flex justify-center items-center gap-2 mb-6"
>
تایید و ادامه تایید و ادامه
<ChevronLeft size={20} /> <ChevronLeft size={20} />
</Link> </button>
{/* بج‌های اعتماد (Trust Badges) */}
<div className="flex items-center justify-center gap-2 md:gap-4 text-[10px] md:text-xs font-medium text-gray-400 bg-gray-50 py-3 rounded-xl border border-gray-100"> <div className="flex items-center justify-center gap-2 md:gap-4 text-[10px] md:text-xs font-medium text-gray-400 bg-gray-50 py-3 rounded-xl border border-gray-100">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1"><ShieldCheck size={14} className="text-green-500" /> پرداخت امن</div>
<ShieldCheck size={14} className="text-green-500" />
پرداخت امن
</div>
<div className="w-1 h-1 bg-gray-300 rounded-full"></div> <div className="w-1 h-1 bg-gray-300 rounded-full"></div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1"><Truck size={14} className="text-blue-500" /> ارسال سریع</div>
<Truck size={14} className="text-blue-500" />
ارسال سریع
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,40 +1,61 @@
import ProductCard from "@/components/productcard"; // 1. ایمپورت کردن کامپوننت کارت محصول
import { getProductsByCategory } from "@/public/src/services/products/api"; import { getProductsByCategory } from "@/public/src/services/products/api";
import { Product } from "@/public/src/types/product"; // بهتر است تایپ محصول را هم برای خوانایی ایمپورت کنید
export default async function CategoryPage({ params }:any) { export default async function CategoryPage({ params }: any) {
const { categoryName } = await params; const { categoryName } = await params;
console.log("🔎 CategoryPage slug:", categoryName); // منطق دریافت دیتا از API بدون تغییر باقی می‌ماند
const categoryData = await getProductsByCategory(categoryName);
const products = await getProductsByCategory(categoryName);
// مدیریت حالتی که محصولی یافت نشود
if (!categoryData || !categoryData.items || categoryData.items.length === 0) {
return ( return (
<div className="p-8"> <div className="container mx-auto py-20 text-center">
<h1 className="text-2xl font-bold text-gray-800">
<h1 className="text-xl font-bold mb-6"> محصولی در دستهبندی «{decodeURIComponent(categoryName)}» یافت نشد.
دستهبندی: {categoryName}
</h1> </h1>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{products.items.map((product:any) => (
<div key={product.id} className="border p-4 rounded-xl">
<div className="h-32 bg-gray-100 rounded mb-3" />
<h3 className="text-sm font-medium">
{product.title}
</h3>
<p className="text-xs text-gray-500">
{product.brand}
</p>
</div>
))}
</div>
</div> </div>
); );
}
const pageTitle = categoryData.items[0]?.primaryCategory?.name || decodeURIComponent(categoryName);
return (
<main className="bg-gray-50 min-h-screen py-10">
<div className="max-w-7xl mx-auto px-4">
{/* عنوان و تعداد محصولات */}
<div className="mb-8">
<h1 className="text-2xl font-bold text-gray-800 flex items-center gap-2">
{pageTitle}
<span className="text-sm font-normal text-gray-500 bg-gray-200 px-3 py-1 rounded-full">
{categoryData.meta.total} کالا
</span>
</h1>
<div className="relative mt-3 h-[2px] bg-gray-200 w-full">
<div className="absolute right-0 top-0 h-[2px] w-24 bg-yellow-500"></div>
</div>
</div>
{/* 2. جایگزینی div ساده با کامپوننت ProductCard در حلقه */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
{categoryData.items.map((apiProduct: any) => {
// 3. تبدیل داده‌های API به فرمت مورد انتظار کامپوننت ProductCard
const productForCard: Product = {
id: apiProduct.id,
title: apiProduct.title,
brand: apiProduct.brand,
slug: apiProduct.slug,
price: apiProduct.calculated_price, // تبدیل نام فیلد
stock: apiProduct.stock,
image: apiProduct.mainImageUrl || "/placeholder.png", // تبدیل نام فیلد و افزودن جایگزین
attributes: apiProduct.attributes,
};
return <ProductCard key={productForCard.id} product={productForCard} />;
})}
</div>
</div>
</main>
);
} }

View File

@@ -2,44 +2,78 @@
import { useCart } from "@/components/context/cartcontext"; import { useCart } from "@/components/context/cartcontext";
import Link from "next/link"; import Link from "next/link";
import { useState } from "react"; import { useState, useEffect } from "react";
import { import {
ShoppingBag, ShoppingBag,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
Truck, Truck,
CreditCard, CreditCard,
Check,
MapPin, MapPin,
User, User,
Phone, Lock // آیکون قفل برای صفحه لاگین نشده
Mail
} from "lucide-react"; } from "lucide-react";
export default function CheckoutPage() { export default function CheckoutPage() {
const { cart } = useCart(); const { cart } = useCart();
// استیت برای روش ارسال (صرفا جهت نمایش UI)
const [shippingMethod, setShippingMethod] = useState('post'); const [shippingMethod, setShippingMethod] = useState('post');
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const token = localStorage.getItem('accessToken');
if (token) {
setIsAuthenticated(true);
}
setIsLoading(false); // پایان بررسی وضعیت
}, []);
// محاسبه قیمت کل و تعداد // محاسبه قیمت کل و تعداد
const parsePrice = (priceStr?: string | null) => { const parsePrice = (priceStr?: number | null) => {
if (!priceStr) return 0; if (!priceStr) return 0;
return Number(priceStr.toString().replace(/,/g, '')); return Number(priceStr.toString().replace(/,/g, ''));
}; };
const totalPrice = cart.reduce((total, item) => total + (parsePrice(item.price) * item.quantity), 0); const totalPrice = cart.reduce((total, item) => total + (parsePrice(item.price) * item.quantity), 0);
const totalItems = cart.reduce((total, item) => total + item.quantity, 0); const totalItems = cart.reduce((total, item) => total + item.quantity, 0);
// هزینه ارسال فرضی
const shippingCost = shippingMethod === 'post' ? 45000 : 75000; const shippingCost = shippingMethod === 'post' ? 45000 : 75000;
const finalPrice = totalPrice + shippingCost; const finalPrice = totalPrice + shippingCost;
// حالت در حال بررسی توکن
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50/30">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#ffb900]"></div>
</div>
);
}
// اگر کاربر لاگین نبود، این صفحه به جای محتوای چک‌اوت نمایش داده می‌شود
if (!isAuthenticated) {
return (
<div className="min-h-screen bg-gray-50/30 flex flex-col items-center justify-center p-6" dir="rtl">
<div className="bg-white p-12 rounded-[2rem] shadow-sm border border-gray-100 flex flex-col items-center max-w-md w-full text-center">
<div className="bg-gray-100 w-24 h-24 rounded-full flex items-center justify-center mb-6">
<Lock size={40} className="text-gray-400" />
</div>
<h2 className="text-xl font-bold text-gray-800 mb-3">دسترسی محدود</h2>
<p className="text-gray-500 mb-8 leading-relaxed text-sm">
برای ثبت نهایی سفارش و مشاهده این صفحه، باید ابتدا وارد حساب کاربری خود شوید یا ثبتنام کنید.
</p>
<Link href="/cart" className="w-full bg-[#ffb900] hover:bg-[#e5a600] text-[#1A2332] font-bold py-3 rounded-xl transition-all flex items-center justify-center gap-2">
<ChevronRight size={18} />
بازگشت به سبد خرید
</Link>
</div>
</div>
);
}
// اگر لاگین بود، محتوای اصلی رندر می‌شود (کد اصلی شما در اینجا قرار می‌گیرد)
return ( return (
<main className="bg-gray-50/30 min-h-screen pb-20" dir="rtl"> <main className="bg-gray-50/30 min-h-screen pb-20" dir="rtl">
<div className="container mx-auto px-4 py-8 max-w-6xl"> <div className="container mx-auto px-4 py-8 max-w-6xl">
{/* بخش هدر و تایم‌لاین */}
<div className="mb-10"> <div className="mb-10">
<div className="flex items-center justify-between mb-8"> <div className="flex items-center justify-between mb-8">
<h1 className="text-xl md:text-3xl font-black text-[#1A2332]"> <h1 className="text-xl md:text-3xl font-black text-[#1A2332]">
@@ -51,16 +85,12 @@ export default function CheckoutPage() {
</Link> </Link>
</div> </div>
{/* Breadcrumb (تایم‌لاین اکتیو شده برای مرحله ۲) */}
<div className="relative w-full max-w-2xl mx-auto px-2 sm:px-0 mb-12"> <div className="relative w-full max-w-2xl mx-auto px-2 sm:px-0 mb-12">
{/* خط سراسری بک‌گراند */}
<div className="absolute top-[20px] sm:top-[24px] left-[16.5%] right-[16.5%] h-[2px] bg-gray-200 z-0"> <div className="absolute top-[20px] sm:top-[24px] left-[16.5%] right-[16.5%] h-[2px] bg-gray-200 z-0">
{/* پر شدن خط مرحله دوم (۵۰ درصد پیشرفت) */}
<div className="h-full bg-[#ffb900] w-[50%] transition-all duration-500 ease-in-out"></div> <div className="h-full bg-[#ffb900] w-[50%] transition-all duration-500 ease-in-out"></div>
</div> </div>
<div className="flex justify-between relative z-10"> <div className="flex justify-between relative z-10">
{/* مرحله 1 (تکمیل شده) */}
<Link href="/cart" className="flex flex-col items-center w-1/3 group cursor-pointer"> <Link href="/cart" className="flex flex-col items-center w-1/3 group cursor-pointer">
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-[#ffb900] text-[#1A2332] rounded-full flex items-center justify-center shadow-md mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc] transition-transform group-hover:scale-110"> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-[#ffb900] text-[#1A2332] rounded-full flex items-center justify-center shadow-md mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc] transition-transform group-hover:scale-110">
<ShoppingBag className="w-5 h-5 sm:w-6 sm:h-6" strokeWidth={2} /> <ShoppingBag className="w-5 h-5 sm:w-6 sm:h-6" strokeWidth={2} />
@@ -68,16 +98,13 @@ export default function CheckoutPage() {
<span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">سبد خرید</span> <span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">سبد خرید</span>
</Link> </Link>
{/* مرحله 2 (اکتیو و فعلی) */}
<div className="flex flex-col items-center w-1/3"> <div className="flex flex-col items-center w-1/3">
{/* استفاده از ring زرد با opacity برای نشان دادن حالت اکتیو */}
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-[#ffb900] text-[#1A2332] rounded-full flex items-center justify-center shadow-[0_0_15px_rgba(255,185,0,0.4)] mb-2 sm:mb-3 ring-[6px] ring-[#ffb900]/20 transition-all"> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-[#ffb900] text-[#1A2332] rounded-full flex items-center justify-center shadow-[0_0_15px_rgba(255,185,0,0.4)] mb-2 sm:mb-3 ring-[6px] ring-[#ffb900]/20 transition-all">
<Truck className="w-4 h-4 sm:w-5 sm:h-5" strokeWidth={2} /> <Truck className="w-4 h-4 sm:w-5 sm:h-5" strokeWidth={2} />
</div> </div>
<span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">اطلاعات ارسال</span> <span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">اطلاعات ارسال</span>
</div> </div>
{/* مرحله 3 (غیرفعال) */}
<div className="flex flex-col items-center w-1/3"> <div className="flex flex-col items-center w-1/3">
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-white border-2 border-gray-200 text-gray-400 rounded-full flex items-center justify-center mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc]"> <div className="w-10 h-10 sm:w-12 sm:h-12 bg-white border-2 border-gray-200 text-gray-400 rounded-full flex items-center justify-center mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc]">
<CreditCard className="w-4 h-4 sm:w-5 sm:h-5" /> <CreditCard className="w-4 h-4 sm:w-5 sm:h-5" />
@@ -89,11 +116,7 @@ export default function CheckoutPage() {
</div> </div>
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8"> <div className="flex flex-col lg:flex-row gap-6 lg:gap-8">
{/* بخش فرم اطلاعات */}
<div className="flex-1 space-y-6"> <div className="flex-1 space-y-6">
{/* فرم مشخصات گیرنده */}
<div className="bg-white rounded-[1rem] p-6 md:p-8 shadow-sm "> <div className="bg-white rounded-[1rem] p-6 md:p-8 shadow-sm ">
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-100"> <div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-100">
<div className="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]"> <div className="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]">
@@ -101,30 +124,18 @@ export default function CheckoutPage() {
</div> </div>
<h2 className="text-base md:text-lg font-bold text-[#1A2332]">مشخصات گیرنده</h2> <h2 className="text-base md:text-lg font-bold text-[#1A2332]">مشخصات گیرنده</h2>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5"> <div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium text-gray-600 block">نام و نام خانوادگی</label> <label className="text-sm font-medium text-gray-600 block">نام و نام خانوادگی</label>
<input <input type="text" placeholder="مثال: علی رضایی" className="w-full bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 placeholder:text-[13px]" />
type="text"
placeholder="مثال: علی رضایی"
className="w-full bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 placeholder:text-[13px]"
/>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium text-gray-600 block">شماره موبایل</label> <label className="text-sm font-medium text-gray-600 block">شماره موبایل</label>
<div className="relative"> <input type="tel" placeholder="09123456789" className="w-full text-right bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl pl-4 pr-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 placeholder:text-[13px]" />
<input
type="tel"
placeholder="09123456789"
className="w-full text-right bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl pl-4 pr-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 placeholder:text-[13px]"
/>
</div>
</div> </div>
</div> </div>
</div> </div>
{/* فرم آدرس */}
<div className="bg-white rounded-[1rem] p-6 md:p-8 shadow-sm"> <div className="bg-white rounded-[1rem] p-6 md:p-8 shadow-sm">
<div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-100"> <div className="flex items-center gap-3 mb-6 pb-4 border-b border-gray-100">
<div className="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]"> <div className="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]">
@@ -132,7 +143,6 @@ export default function CheckoutPage() {
</div> </div>
<h2 className="text-base md:text-lg font-bold text-[#1A2332]">آدرس پستی</h2> <h2 className="text-base md:text-lg font-bold text-[#1A2332]">آدرس پستی</h2>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5"> <div className="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium text-gray-600 block">استان</label> <label className="text-sm font-medium text-gray-600 block">استان</label>
@@ -151,63 +161,39 @@ export default function CheckoutPage() {
</select> </select>
</div> </div>
</div> </div>
<div className="space-y-2 mb-5"> <div className="space-y-2 mb-5">
<label className="text-sm font-medium text-gray-600 block">آدرس دقیق پستی</label> <label className="text-sm font-medium text-gray-600 block">آدرس دقیق پستی</label>
<textarea <textarea rows={3} placeholder="خیابان، کوچه، پلاک، واحد..." className="w-full text-[13px] bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 resize-none" />
rows={3}
placeholder="خیابان، کوچه، پلاک، واحد..."
className="w-full text-[13px] bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 resize-none"
/>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5"> <div className="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5">
<div className=" space-y-2"> <div className=" space-y-2">
<label className="text-sm font-medium text-gray-600 block">کد پستی (۱۰ رقمی)</label> <label className="text-sm font-medium text-gray-600 block">کد پستی (۱۰ رقمی)</label>
<input <input type="text" placeholder="1234567890" className="w-full bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 placeholder:text-[13px]" />
type="text"
placeholder="1234567890"
className="w-full bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 placeholder:text-[13px]"
/>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium text-gray-600 block">ایمیل</label> <label className="text-sm font-medium text-gray-600 block">ایمیل</label>
<input <input type="email" placeholder="test@email.com" className="w-full bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 placeholder:text-[13px]" />
type="email" </div>
placeholder="test@email.com" </div>
className="w-full bg-gray-50 border border-gray-200 text-[#1A2332] rounded-xl px-4 py-3 outline-none focus:ring-2 focus:ring-[#ffb900]/40 focus:border-[#ffb900] transition-all placeholder:text-gray-400 placeholder:text-[13px]" </div>
/>
</div> </div>
</div>
</div>
</div>
{/* بخش صورتحساب (Sidebar) */}
<div className="w-full lg:w-[400px] shrink-0"> <div className="w-full lg:w-[400px] shrink-0">
<div className="bg-white rounded-[1rem] p-6 md:p-8 shadow-sm sticky top-6"> <div className="bg-white rounded-[1rem] p-6 md:p-8 shadow-sm sticky top-6">
<h2 className="text-lg md:text-xl font-bold text-[#1A2332] mb-6">خلاصه سفارش</h2> <h2 className="text-lg md:text-xl font-bold text-[#1A2332] mb-6">خلاصه سفارش</h2>
<div className="space-y-4 mb-6"> <div className="space-y-4 mb-6">
<div className="flex justify-between items-center text-xs md:text-sm"> <div className="flex justify-between items-center text-xs md:text-sm">
<span className="text-gray-500">مبلغ کالاها ({totalItems})</span> <span className="text-gray-500">مبلغ کالاها ({totalItems})</span>
<span className="font-bold text-[#1A2332]">{totalPrice > 0 ? totalPrice.toLocaleString('fa-IR') : '۰'} <span className="text-[10px] font-normal text-gray-500">تومان</span></span> <span className="font-bold text-[#1A2332]">{totalPrice > 0 ? totalPrice.toLocaleString('fa-IR') : '۰'} <span className="text-[10px] font-normal text-gray-500">تومان</span></span>
</div> </div>
<div className="flex justify-between items-center text-xs md:text-sm"> <div className="flex justify-between items-center text-xs md:text-sm">
<span className="text-gray-500">هزینه ارسال</span> <span className="text-gray-500">هزینه ارسال</span>
<span className="font-bold text-[#1A2332]">{shippingCost.toLocaleString('fa-IR')} <span className="text-[10px] font-normal text-gray-500">تومان</span></span> <span className="font-bold text-[#1A2332]">{shippingCost.toLocaleString('fa-IR')} <span className="text-[10px] font-normal text-gray-500">تومان</span></span>
</div> </div>
</div> </div>
{/* خط‌چین جداکننده */}
<div className="w-full border-t-2 border-dashed border-gray-100 my-6"></div> <div className="w-full border-t-2 border-dashed border-gray-100 my-6"></div>
{/* انتخاب روش ارسال */}
<div className="bg-white rounded-[1rem] pt-4 pb-4"> <div className="bg-white rounded-[1rem] pt-4 pb-4">
<div className="flex items-center gap-3 mb-6 pb-4 "> <div className="flex items-center gap-3 mb-6 pb-4 ">
<div className="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]"> <div className="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]">
@@ -215,9 +201,7 @@ export default function CheckoutPage() {
</div> </div>
<h2 className="text-base md:text-lg font-bold text-[#1A2332]">نحوه ارسال</h2> <h2 className="text-base md:text-lg font-bold text-[#1A2332]">نحوه ارسال</h2>
</div> </div>
<div className="space-y-3"> <div className="space-y-3">
{/* گزینه پست */}
<label className={`flex items-center justify-between p-4 rounded-xl border-1 cursor-pointer transition-all ${shippingMethod === 'post' ? 'border-[#ffb900] bg-[#ffb900]/3' : 'border-gray-100 hover:border-gray-200 bg-white'}`}> <label className={`flex items-center justify-between p-4 rounded-xl border-1 cursor-pointer transition-all ${shippingMethod === 'post' ? 'border-[#ffb900] bg-[#ffb900]/3' : 'border-gray-100 hover:border-gray-200 bg-white'}`}>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className={`w-5 h-5 rounded-full border-1 flex items-center justify-center ${shippingMethod === 'post' ? 'border-[#ffb900]' : 'border-gray-300'}`}> <div className={`w-5 h-5 rounded-full border-1 flex items-center justify-center ${shippingMethod === 'post' ? 'border-[#ffb900]' : 'border-gray-300'}`}>
@@ -229,17 +213,8 @@ export default function CheckoutPage() {
</div> </div>
</div> </div>
<div className="font-bold text-[#1A2332]">۴۵,۰۰۰ <span className="text-xs font-normal text-gray-500">تومان</span></div> <div className="font-bold text-[#1A2332]">۴۵,۰۰۰ <span className="text-xs font-normal text-gray-500">تومان</span></div>
<input <input type="radio" name="shipping" value="post" className="hidden" checked={shippingMethod === 'post'} onChange={() => setShippingMethod('post')} />
type="radio"
name="shipping"
value="post"
className="hidden"
checked={shippingMethod === 'post'}
onChange={() => setShippingMethod('post')}
/>
</label> </label>
{/* گزینه تیپاکس */}
<label className={`flex items-center justify-between p-4 rounded-xl border-1 cursor-pointer transition-all ${shippingMethod === 'tipax' ? 'border-[#ffb900] bg-[#ffb900]/3' : 'border-gray-100 hover:border-gray-200 bg-white'}`}> <label className={`flex items-center justify-between p-4 rounded-xl border-1 cursor-pointer transition-all ${shippingMethod === 'tipax' ? 'border-[#ffb900] bg-[#ffb900]/3' : 'border-gray-100 hover:border-gray-200 bg-white'}`}>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className={`w-5 h-5 rounded-full border-1 flex items-center justify-center ${shippingMethod === 'tipax' ? 'border-[#ffb900]' : 'border-gray-300'}`}> <div className={`w-5 h-5 rounded-full border-1 flex items-center justify-center ${shippingMethod === 'tipax' ? 'border-[#ffb900]' : 'border-gray-300'}`}>
@@ -251,18 +226,11 @@ export default function CheckoutPage() {
</div> </div>
</div> </div>
<div className="font-bold text-[#1A2332]">۷۵,۰۰۰ <span className="text-xs font-normal text-gray-500">تومان</span></div> <div className="font-bold text-[#1A2332]">۷۵,۰۰۰ <span className="text-xs font-normal text-gray-500">تومان</span></div>
<input <input type="radio" name="shipping" value="tipax" className="hidden" checked={shippingMethod === 'tipax'} onChange={() => setShippingMethod('tipax')} />
type="radio"
name="shipping"
value="tipax"
className="hidden"
checked={shippingMethod === 'tipax'}
onChange={() => setShippingMethod('tipax')}
/>
</label> </label>
</div> </div>
</div> </div>
{/* خط‌چین جداکننده */}
<div className="w-full border-t-2 border-dashed border-gray-100 my-6"></div> <div className="w-full border-t-2 border-dashed border-gray-100 my-6"></div>
<div className="flex justify-between items-center mb-8"> <div className="flex justify-between items-center mb-8">

View File

@@ -24,6 +24,7 @@ import {
TicketStatus, TicketStatus,
updateAdminTicket, updateAdminTicket,
} from "@/public/src/services/tickets/api"; } from "@/public/src/services/tickets/api";
import NotLogin from '@/components/Notlogin';
const sampleOrders = [ const sampleOrders = [
{ id: "PR-10452", status: "در حال پردازش", statusColor: "amber", total: "500,000", regDate: "15 دی 1404", deliveryDate: "20 دی 1404", delivered: false }, { id: "PR-10452", status: "در حال پردازش", statusColor: "amber", total: "500,000", regDate: "15 دی 1404", deliveryDate: "20 دی 1404", delivered: false },
@@ -414,19 +415,7 @@ export default function DashboardPage() {
if (!authorized) { if (!authorized) {
return ( return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 px-4" dir="rtl"> <div className="min-h-screen flex items-center justify-center bg-gray-50 px-4" dir="rtl">
<div className="bg-white shadow-xl rounded-2xl p-10 max-w-md w-full text-center border border-gray-100"> <NotLogin/>
<div className="flex justify-center mb-6">
<div className="bg-red-100 p-4 rounded-full">
<ShieldX className="w-10 h-10 text-red-600" />
</div>
</div>
<h1 className="text-2xl font-bold text-gray-800 mb-2">دسترسی غیرمجاز</h1>
<p className="text-gray-500 text-sm leading-relaxed mb-6">برای مشاهده داشبورد باید ابتدا وارد حساب کاربری خود شوید.</p>
<button onClick={() => router.push("/")} className="flex mx-auto cursor-pointer items-center gap-2 bg-gray-900 text-white px-4 py-2 rounded-lg text-sm hover:bg-black transition">
<Home className="w-4 h-4" />
بازگشت به صفحه اصلی
</button>
</div>
</div> </div>
); );
} }

View File

@@ -1,5 +1,5 @@
"use client"; "use client";
import { useState } from "react"; import { useState, useEffect } from "react";
import { import {
Headphones, FileText, Truck, ShieldCheck, CircleDashed, Headphones, FileText, Truck, ShieldCheck, CircleDashed,
Disc, Disc,
@@ -11,23 +11,28 @@ import {
import ProductCard from "@/components/productcard"; import ProductCard from "@/components/productcard";
import ArticleCard from "@/components/articlecard"; import ArticleCard from "@/components/articlecard";
import FAQItem from "@/components/faq"; import FAQItem from "@/components/faq";
import { products } from "@/lib/data";
import { articles } from "@/lib/data"; import { articles } from "@/lib/data";
import Link from "next/link"; import Link from "next/link";
import { useCategories } from "@/components/context/categoryprovider"; import { useCategories } from "@/components/context/categoryprovider";
import { getProducts } from "@/public/src/services/products/api"; import { getProducts } from "@/public/src/services/products/api";
export default function Home() {
export default async function Home() {
const [activeTab, setActiveTab] = useState(0); const [activeTab, setActiveTab] = useState(0);
const [products, setProducts] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const brands = ["NTN", "KOYO", "NACHI", "TIMKEN", "FAG", "SKF"]; const brands = ["NTN", "KOYO", "NACHI", "TIMKEN", "FAG", "SKF"];
const latestArticles = articles.slice(-4); const latestArticles = articles.slice(-4);
const { rootCategories } = useCategories(); const { rootCategories } = useCategories();
useEffect(() => {
const fetchInitialProducts = async () => {
try {
setLoading(true);
const data = await getProducts(1, 20); const data = await getProducts(1, 20);
const products = data.items.map((p: any) => ({ const formattedProducts = data.items.map((p: any) => ({
id: p.id, id: p.id,
title: p.title, title: p.title,
brand: p.brand, brand: p.brand,
@@ -38,6 +43,17 @@ export default async function Home() {
attributes: p.attributes attributes: p.attributes
})); }));
setProducts(formattedProducts);
} catch (error) {
console.error("Error fetching products:", error);
} finally {
setLoading(false);
}
};
fetchInitialProducts();
}, []);
const features = [ const features = [
{ {
icon: Headphones, icon: Headphones,
@@ -61,10 +77,8 @@ export default async function Home() {
}, },
]; ];
const tabs = ["پرفروش‌ترین‌ها", "تخفیف‌دار", "جدیدترین‌ها"]; const tabs = ["پرفروش‌ترین‌ها", "تخفیف‌دار", "جدیدترین‌ها"];
const faqs = [ const faqs = [
{ {
question: "آیا تمامی قطعات دارای ضمانت اصالت هستند؟", question: "آیا تمامی قطعات دارای ضمانت اصالت هستند؟",
@@ -83,7 +97,6 @@ export default async function Home() {
}, },
]; ];
const categoryIcons = [ const categoryIcons = [
CircleDashed, CircleDashed,
Disc, Disc,
@@ -98,35 +111,24 @@ export default async function Home() {
{/* hero section */} {/* hero section */}
<section className="bg-[#0b1d36] text-white py-16"> <section className="bg-[#0b1d36] text-white py-16">
<div className="container mx-auto text-center"> <div className="container mx-auto text-center">
{/* Badge */}
<div className="inline-block border mb-4 px-3 py-2 bg-[#443A27] text-[#ffb900] text-xs rounded-full"> <div className="inline-block border mb-4 px-3 py-2 bg-[#443A27] text-[#ffb900] text-xs rounded-full">
تأمینکننده برتر قطعات تأمینکننده برتر قطعات
</div> </div>
{/* Heading */}
<h1 className=" leading-snug md:text-5xl font-extrabold mb-4"> <h1 className=" leading-snug md:text-5xl font-extrabold mb-4">
تخصصیترین مرجع تخصصیترین مرجع
<br></br> <br></br>
بلبرینگ و قطعات صنعتی بلبرینگ و قطعات صنعتی
</h1> </h1>
{/* Subtitle */}
<p className=" text-[15px] text-[#9a9a9a] mb-10"> <p className=" text-[15px] text-[#9a9a9a] mb-10">
دسترسی به بیش از 10.000 قطعه با ضمانت اصالت کالا دسترسی به بیش از 10.000 قطعه با ضمانت اصالت کالا
</p> </p>
</div> </div>
</section> </section>
{/* hero search */} {/* hero search */}
<div className="w-full px-4"> <div className="w-full px-4">
<div className=" max-w-[70.5rem] mx-auto px-4 bg-white -mt-20 text-gray-800 rounded-2xl shadow-lg p-6 flex flex-col md:flex-row gap-4 md:gap-2 items-center justify-between"> <div className=" max-w-[70.5rem] mx-auto px-4 bg-white -mt-20 text-gray-800 rounded-2xl shadow-lg p-6 flex flex-col md:flex-row gap-4 md:gap-2 items-center justify-between">
{/* Filters */}
<div className="grid grid-cols-1 md:grid-cols-6 w-full gap-4 justify-between"> <div className="grid grid-cols-1 md:grid-cols-6 w-full gap-4 justify-between">
<div className="flex flex-col md:col-span-2"> <div className="flex flex-col md:col-span-2">
<label htmlFor="partNumber" className="text-xs mb-3 text-black font-bold"> <label htmlFor="partNumber" className="text-xs mb-3 text-black font-bold">
شماره فنی (Part Number) شماره فنی (Part Number)
@@ -138,7 +140,6 @@ export default async function Home() {
className="px-4 py-3 bg-[#f9f9f9] rounded-xl border border-gray-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]" className="px-4 py-3 bg-[#f9f9f9] rounded-xl border border-gray-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]"
/> />
</div> </div>
<div className="flex flex-col"> <div className="flex flex-col">
<label htmlFor="innerDiameter" className="text-xs mb-3 text-black font-bold"> <label htmlFor="innerDiameter" className="text-xs mb-3 text-black font-bold">
قطر داخل قطر داخل
@@ -150,7 +151,6 @@ export default async function Home() {
className="px-4 bg-[#f9f9f9] py-3 rounded-xl border border-gray-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]" className="px-4 bg-[#f9f9f9] py-3 rounded-xl border border-gray-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]"
/> />
</div> </div>
<div className="flex flex-col"> <div className="flex flex-col">
<label htmlFor="outerDiameter" className="text-xs mb-3 text-black font-bold"> <label htmlFor="outerDiameter" className="text-xs mb-3 text-black font-bold">
قطر خارج قطر خارج
@@ -162,7 +162,6 @@ export default async function Home() {
className="px-4 bg-[#f9f9f9] py-3 rounded-xl border border-gray-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]" className="px-4 bg-[#f9f9f9] py-3 rounded-xl border border-gray-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]"
/> />
</div> </div>
<div className="flex flex-col"> <div className="flex flex-col">
<label htmlFor="thickness" className="text-xs mb-3 text-black font-bold"> <label htmlFor="thickness" className="text-xs mb-3 text-black font-bold">
ضخامت ضخامت
@@ -174,17 +173,11 @@ export default async function Home() {
className="px-4 bg-[#f9f9f9] py-3 rounded-xl border border-gray-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]" className="px-4 bg-[#f9f9f9] py-3 rounded-xl border border-gray-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#e6d3a3]"
/> />
</div> </div>
{/* Button */}
<button <button
className="mt-6 rounded-xl border-2 md:w-auto md:h-auto w-[100px] h-[48px] border-[#e6d3a3] bg-[#ffb900] text-black font-semibold text-xs transition-all hover:bg-[#da9800]"> className="mt-6 rounded-xl border-2 md:w-auto md:h-auto w-[100px] h-[48px] border-[#e6d3a3] bg-[#ffb900] text-black font-semibold text-xs transition-all hover:bg-[#da9800]">
جستجوی قطعه جستجوی قطعه
</button> </button>
</div> </div>
</div> </div>
</div> </div>
@@ -201,7 +194,6 @@ export default async function Home() {
<div className="p-3 bg-gray-100 rounded-lg"> <div className="p-3 bg-gray-100 rounded-lg">
<Icon size={24} className="text-gray-700" /> <Icon size={24} className="text-gray-700" />
</div> </div>
<div> <div>
<h3 className="font-semibold text-gray-800 text-sm"> <h3 className="font-semibold text-gray-800 text-sm">
{item.title} {item.title}
@@ -217,30 +209,19 @@ export default async function Home() {
{/* categories card */} {/* categories card */}
<section className="w-full py-12 bg-gray-50"> <section className="w-full py-12 bg-gray-50">
<div className="max-w-6xl mx-auto px-4"> <div className="max-w-6xl mx-auto px-4">
{/* title */}
<div className="w-full mb-10"> <div className="w-full mb-10">
<div className="flex"> <div className="flex">
<h2 className="text-xl font-bold text-gray-800"> <h2 className="text-xl font-bold text-gray-800">
دسته بندی های بلبرینگ دسته بندی های بلبرینگ
</h2> </h2>
</div> </div>
{/* line */}
<div className="relative mt-3 h-[2px] bg-gray-200 w-full"> <div className="relative mt-3 h-[2px] bg-gray-200 w-full">
<div className="absolute right-0 top-0 h-[2px] w-24 bg-yellow-500"></div> <div className="absolute right-0 top-0 h-[2px] w-24 bg-yellow-500"></div>
</div> </div>
</div> </div>
{/* cards */}
<div className="grid justify-center grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-6"> <div className="grid justify-center grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-6">
{rootCategories.map((cat, index) => { {rootCategories.map((cat, index) => {
const Icon = categoryIcons[index % categoryIcons.length]; const Icon = categoryIcons[index % categoryIcons.length];
return ( return (
<Link <Link
key={cat.id} key={cat.id}
@@ -249,11 +230,9 @@ export default async function Home() {
> >
<Icon size={34} strokeWidth={1} className="text-gray-400 mb-3 group-hover:text-blue-500 transition-colors" /> <Icon size={34} strokeWidth={1} className="text-gray-400 mb-3 group-hover:text-blue-500 transition-colors" />
<span className="text-sm font-medium text-gray-700 group-hover:text-blue-600">{cat.name}</span> <span className="text-sm font-medium text-gray-700 group-hover:text-blue-600">{cat.name}</span>
</Link> </Link>
); );
})} })}
</div> </div>
</div> </div>
</section> </section>
@@ -262,15 +241,12 @@ export default async function Home() {
<section className="py-12"> <section className="py-12">
<div className="max-w-6xl mx-auto px-4"> <div className="max-w-6xl mx-auto px-4">
<div className="items-center justify-between mb-8"> <div className="items-center justify-between mb-8">
{/* title */}
<div className="flex flex-wrap md:flex-nowrap justify-between w-full mb-4"> <div className="flex flex-wrap md:flex-nowrap justify-between w-full mb-4">
<div className="flex"> <div className="flex">
<h3 className="text-xl font-bold text-gray-800"> <h3 className="text-xl font-bold text-gray-800">
پیشنهاد ویژه صنعتی پیشنهاد ویژه صنعتی
</h3> </h3>
</div> </div>
{/* tabs */}
<div className="flex mt-6 mb-4 md:mt-0 md:mb-0 gap-8 text-sm"> <div className="flex mt-6 mb-4 md:mt-0 md:mb-0 gap-8 text-sm">
{tabs.map((tab, index) => ( {tabs.map((tab, index) => (
<button <button
@@ -287,114 +263,93 @@ export default async function Home() {
))} ))}
</div> </div>
</div> </div>
{/* line */}
<div className="relative mt-3 h-[2px] bg-gray-200 w-full"> <div className="relative mt-3 h-[2px] bg-gray-200 w-full">
<div className="absolute right-0 top-0 h-[2px] w-24 bg-yellow-500"></div> <div className="absolute right-0 top-0 h-[2px] w-24 bg-yellow-500"></div>
</div> </div>
</div> </div>
{/* 4. نمایش وضعیت لودینگ یا لیست محصولات */}
{loading ? (
<div className="flex justify-center py-10">
<p>در حال بارگذاری محصولات...</p>
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
{products.slice(-4).map((product: any) => ( {products.slice(-4).map((product: any) => (
<ProductCard key={product.id} product={product} /> <ProductCard key={product.id} product={product} />
))} ))}
</div> </div>
)}
</div> </div>
</section> </section>
{/* blog */} {/* blog */}
<section className="py-16 bg-white"> <section className="py-16 bg-white">
<div className="max-w-6xl mx-auto px-4"> <div className="max-w-6xl mx-auto px-4">
{/* header */}
<div className="mb-8"> <div className="mb-8">
<div className="flex justify-between w-full mb-4"> <div className="flex justify-between w-full mb-4">
<h3 className="text-xl font-bold text-gray-800"> <h3 className="text-xl font-bold text-gray-800">
مجله فنی و مهندسی مجله فنی و مهندسی
</h3> </h3>
<a className="text-[#ffb900] text-sm cursor-pointer"> <a className="text-[#ffb900] text-sm cursor-pointer">
مشاهده آرشیو مشاهده آرشیو
</a> </a>
</div> </div>
<div className="relative h-[2px] bg-gray-200 w-full"> <div className="relative h-[2px] bg-gray-200 w-full">
<div className="absolute right-0 top-0 h-[2px] w-24 bg-yellow-500" /> <div className="absolute right-0 top-0 h-[2px] w-24 bg-yellow-500" />
</div> </div>
</div> </div>
{/* grid */}
<div className="grid md:grid-cols-3 lg:grid-cols-4 gap-6"> <div className="grid md:grid-cols-3 lg:grid-cols-4 gap-6">
{latestArticles.map((article, i) => ( {latestArticles.map((article, i) => (
<ArticleCard key={i} article={article} /> <ArticleCard key={i} article={article} />
))} ))}
</div> </div>
</div> </div>
</section> </section>
{/* search */} {/* search */}
<section className="py-16"> <section className="py-16">
<div className="max-w-6xl mx-auto px-4"> <div className="max-w-6xl mx-auto px-4">
<div className="bg-[#0b1e3b] rounded-2xl p-10 flex flex-col lg:flex-row items-center justify-between gap-8 relative overflow-hidden"> <div className="bg-[#0b1e3b] rounded-2xl p-10 flex flex-col lg:flex-row items-center justify-between gap-8 relative overflow-hidden">
{/* right content */}
<div className="text-right text-white max-w-md"> <div className="text-right text-white max-w-md">
<h2 className="text-2xl font-bold text-amber-400 mb-3"> <h2 className="text-2xl font-bold text-amber-400 mb-3">
قطعه خاصی مد نظرتان است؟ قطعه خاصی مد نظرتان است؟
</h2> </h2>
<p className="text-sm text-gray-300 leading-7"> <p className="text-sm text-gray-300 leading-7">
تیم فنی ما آماده است تا قطعات صنعتی مورد نیاز شما را در سریعترین زمان تیم فنی ما آماده است تا قطعات صنعتی مورد نیاز شما را در سریعترین زمان
ممکن تأمین کند. مشخصات قطعه را وارد کنید. ممکن تأمین کند. مشخصات قطعه را وارد کنید.
</p> </p>
<div className="flex items-center gap-2 mt-3 text-sm text-gray-300"> <div className="flex items-center gap-2 mt-3 text-sm text-gray-300">
<MessageCircleCheckIcon size={16} /> <MessageCircleCheckIcon size={16} />
پاسخگویی سریع در واتساپ پاسخگویی سریع در واتساپ
</div> </div>
</div> </div>
{/* search box */}
<div className="bg-[#2a3548] p-4 flex-wrap rounded-xl flex items-center gap-3 w-full max-w-xl"> <div className="bg-[#2a3548] p-4 flex-wrap rounded-xl flex items-center gap-3 w-full max-w-xl">
<input <input
type="text" type="text"
placeholder="شماره تماس" placeholder="شماره تماس"
className="flex-1 bg-gray-200 rounded-lg md:px-4 px-0 py-3 outline-none text-xs" className="flex-1 bg-gray-200 rounded-lg md:px-4 px-0 py-3 outline-none text-xs"
/> />
<input <input
type="text" type="text"
placeholder=" کد فنی قطعه" placeholder=" کد فنی قطعه"
className="flex-1 bg-gray-200 rounded-lg px-0 md:px-4 py-3 outline-none text-xs" className="flex-1 bg-gray-200 rounded-lg px-0 md:px-4 py-3 outline-none text-xs"
/> />
<button className="bg-amber-500 hover:bg-amber-600 text-black px-3 py-3 rounded-lg flex items-center gap-2 text-xs cursor-pointer"> <button className="bg-amber-500 hover:bg-amber-600 text-black px-3 py-3 rounded-lg flex items-center gap-2 text-xs cursor-pointer">
ثبت درخواست ثبت درخواست
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
{/* faq */} {/* faq */}
<section className="py-6"> <section className="py-6">
<div className="max-w-3xl mx-auto px-4"> <div className="max-w-3xl mx-auto px-4">
<h2 className="text-center text-xl font-bold mb-10"> <h2 className="text-center text-xl font-bold mb-10">
سوالات پرتکرار مشتریان سوالات پرتکرار مشتریان
</h2> </h2>
{/* faq list */}
<div className="space-y-4"> <div className="space-y-4">
{faqs.map((faq, index) => ( {faqs.map((faq, index) => (
<FAQItem <FAQItem
@@ -404,19 +359,15 @@ export default async function Home() {
/> />
))} ))}
</div> </div>
</div> </div>
</section> </section>
{/* brands */} {/* brands */}
<section className="w-full py-20 px-6"> <section className="w-full py-20 px-6">
<div className="max-w-6xl mx-auto text-center"> <div className="max-w-6xl mx-auto text-center">
<h2 className="text-gray-500 text-sm font-semibold mb-10"> <h2 className="text-gray-500 text-sm font-semibold mb-10">
تامین کننده برندهای معتبر جهان تامین کننده برندهای معتبر جهان
</h2> </h2>
<div className="flex flex-wrap justify-center items-center gap-8"> <div className="flex flex-wrap justify-center items-center gap-8">
{brands.map((brand) => ( {brands.map((brand) => (
<div <div
@@ -429,11 +380,9 @@ export default async function Home() {
</div> </div>
))} ))}
</div> </div>
</div> </div>
</section> </section>
</main> </main>
); );
} }

View File

@@ -17,7 +17,7 @@ export default async function ProductsPage() {
})); }));
return ( return (
<div className="container mx-auto p-4"> <div className="container mx-auto px-4 max-w-6xl">
<h1 className="text-2xl font-bold mb-6">همه محصولات</h1> <h1 className="text-2xl font-bold mb-6">همه محصولات</h1>

58
components/Notlogin.tsx Normal file
View File

@@ -0,0 +1,58 @@
'use client';
import { Home, ShieldX, ArrowRight } from 'lucide-react';
import { useRouter } from "next/navigation";
// تعریف تایپ برای پراپ‌ها (ورودی‌های کامپوننت)
interface NotLoginProps {
buttonText?: string;
returnPath?: string;
onClose?: () => void;
}
export default function NotLogin({
buttonText = "بازگشت به صفحه اصلی", // مقدار پیش‌فرض
returnPath = "/", // مقدار پیش‌فرض
onClose
}: NotLoginProps) {
const router = useRouter();
// مدیریت عملیات کلیک روی دکمه
const handleAction = () => {
if (onClose) {
// اگر در حالت مودال (مثلا سبد خرید) استفاده شده بود، فقط مودال بسته شود
onClose();
} else {
// اگر به عنوان صفحه جایگزین (مثلا چک‌اوت) استفاده شد، به مسیر پاس داده شده برود
router.push(returnPath);
}
};
// تغییر آیکون بر اساس مسیر هدایت (اختیاری برای زیبایی بیشتر)
const renderIcon = () => {
if (returnPath === "/") return <Home className="w-4 h-4" />;
return <ArrowRight className="w-4 h-4" />;
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 px-4" dir="rtl">
<div className="bg-white shadow-xl rounded-2xl p-10 max-w-md w-full text-center border border-gray-100">
<div className="flex justify-center mb-6">
<div className="bg-red-100 p-4 rounded-full">
<ShieldX className="w-10 h-10 text-red-600" />
</div>
</div>
<h1 className="text-2xl font-bold text-gray-800 mb-2">دسترسی غیرمجاز</h1>
<p className="text-gray-500 text-sm leading-relaxed mb-6">برای ادامه مراحل باید ابتدا وارد حساب کاربری خود شوید.</p>
<button
onClick={handleAction}
className="flex mx-auto cursor-pointer items-center gap-2 bg-gray-900 text-white px-5 py-2.5 rounded-lg text-sm hover:bg-black transition"
>
{renderIcon()}
{buttonText}
</button>
</div>
</div>
);
}

View File

@@ -331,7 +331,7 @@ export function Header() {
const parsePrice = (priceStr?: string | null) => { const parsePrice = (priceStr?: number | null) => {
if (!priceStr) return 0; if (!priceStr) return 0;
return Number(priceStr.toString().replace(/,/g, '')); return Number(priceStr.toString().replace(/,/g, ''));
}; };
@@ -659,7 +659,7 @@ export function Header() {
</button> </button>
{userMenuOpen && ( {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="absolute z-100 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"> <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="font-semibold text-[#1A2332]">{user.displayName}</p>
<p className="mt-1 text-[11px] text-gray-500">دسترسی سریع به بخشهای پیشخوان</p> <p className="mt-1 text-[11px] text-gray-500">دسترسی سریع به بخشهای پیشخوان</p>

View File

@@ -67,7 +67,7 @@ export default function ProductCard({ product }: ProductCardProps) {
{product.attributes?.[0] && ( {product.attributes?.[0] && (
<div className="flex border-b border-[#dfdfdf] mb-2 pb-2 justify-between"> <div className="flex border-b border-[#dfdfdf] mb-2 pb-2 justify-between">
<p>{product.attributes[0].name}:</p> {/* 👈 نام attribute اول */} <p>{product.attributes[0].name}:</p> {/* 👈 نام attribute اول */}
<p dir="ltr" className="font-semibold text-black">{product.attributes[0].valueText || "-"}</p> {/* 👈 مقدار attribute اول */} <p className="font-semibold text-black">{product.attributes[0].valueText || "-"}</p> {/* 👈 مقدار attribute اول */}
</div> </div>
)} )}
@@ -77,7 +77,7 @@ export default function ProductCard({ product }: ProductCardProps) {
{product.attributes?.[1] && ( {product.attributes?.[1] && (
<div className="flex justify-between"> <div className="flex justify-between">
<p>{product.attributes[1].name}:</p> {/* 👈 نام attribute دوم */} <p>{product.attributes[1].name}:</p> {/* 👈 نام attribute دوم */}
<p dir="ltr" className="font-semibold text-black">{product.attributes[1].valueText || "-"}</p> {/* 👈 مقدار attribute دوم */} <p className="font-semibold text-black">{product.attributes[1].valueText || "-"}</p> {/* 👈 مقدار attribute دوم */}
</div> </div>
)} )}
</div> </div>

View File

@@ -1,15 +1,6 @@
import { API_BASE_URL } from "../config"; import { API_BASE_URL } from "../config";
import { RegisterPayload } from "../../types/auth";
export interface RegisterPayload { import { LoginPayload } from "../../types/auth";
phone: string;
username: string;
password: string;
fullName: string;
}
export interface LoginPayload {
username: string;
password: string;
}
export async function registerUser(data: RegisterPayload) { export async function registerUser(data: RegisterPayload) {
const response = await fetch(`${API_BASE_URL}/auth/register/password`, { const response = await fetch(`${API_BASE_URL}/auth/register/password`, {

View File

@@ -9,7 +9,6 @@ export async function getProductsByCategory(
try { try {
const url = `${API_BASE_URL}/products/categories/${slug}?page=${page}&limit=${limit}`; const url = `${API_BASE_URL}/products/categories/${slug}?page=${page}&limit=${limit}`;
console.log("📡 Fetching:", url);
const res = await fetch(url, { const res = await fetch(url, {
method: "GET", method: "GET",
@@ -34,7 +33,6 @@ export async function getProductsByCategory(
} }
} }
export async function getProducts(page = 1, limit = 20) { export async function getProducts(page = 1, limit = 20) {
try { try {
const url = `${API_BASE_URL}/products?page=${page}&limit=${limit}`; const url = `${API_BASE_URL}/products?page=${page}&limit=${limit}`;

11
public/src/types/auth.tsx Normal file
View File

@@ -0,0 +1,11 @@
export interface RegisterPayload {
phone: string;
username: string;
password: string;
fullName: string;
}
export interface LoginPayload {
username: string;
password: string;
}