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 Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react"; // اضافه شدن useState
import {
Trash2,
ShoppingBag,
@@ -11,14 +13,33 @@ import {
Minus,
ShieldCheck,
Truck,
CreditCard, ChevronRight, Check
CreditCard
} from "lucide-react";
// مسیر ایمپورت کامپوننت Notlogin را بر اساس ساختار پوشه‌بندی خود تنظیم کنید
import NotLogin from "@/components/Notlogin";
export default function CartPage() {
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;
return Number(priceStr.toString().replace(/,/g, ''));
};
@@ -51,9 +72,16 @@ export default function CartPage() {
return (
<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="mb-10">
<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">
@@ -64,19 +92,13 @@ export default function CartPage() {
</h1>
</div>
{/* بخش هدر و تایم‌لاین */}
<div className="mb-10">
{/* Breadcrumb (تایم‌لاین اکتیو شده برای مرحله ۲) */}
<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="h-full bg-[#ffb900] w-[50%] transition-all duration-500 ease-in-out"></div>
</div>
<div className="flex justify-between relative z-10">
{/* مرحله 1 (تکمیل شده) */}
<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">
<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>
</Link>
{/* مرحله 2 (اکتیو و فعلی) */}
<Link href={'/checkout'} className="flex flex-col items-center w-1/3">
{/* استفاده از 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]">
{/* کلیک روی آیکون اطلاعات ارسال */}
<div onClick={handleCheckoutNavigation} className="flex flex-col items-center w-1/3 cursor-pointer group">
<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">
<Truck className="w-4 h-4 sm:w-5 sm:h-5" strokeWidth={2} />
</div>
<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="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" />
@@ -106,8 +126,6 @@ export default function CartPage() {
</div>
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8">
{/* بخش لیست محصولات */}
<div className="flex-1">
<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">
@@ -121,50 +139,25 @@ export default function CartPage() {
<div className="flex flex-col gap-6">
{cart.map((item) => {
const itemTotal = parsePrice(item.price) * item.quantity;
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 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" />
</div>
{/* اطلاعات محصول */}
<div className="flex-1 w-full">
<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>
{/* ویژگی‌ها (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 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">
{item.price ? `${itemTotal.toLocaleString('fa-IR')} ت` : 'استعلام'}
</span>
{/* کنترلر تعداد طرح کپسولی */}
<div className="flex items-center gap-1 bg-gray-50 border border-gray-200 rounded-full p-1 shadow-sm">
<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"
>
<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">
<Plus size={14} strokeWidth={2.5} />
</button>
<span className="text-xs md:text-sm font-bold text-gray-800 w-6 text-center">
{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"
>
<span className="text-xs md:text-sm font-bold text-gray-800 w-6 text-center">{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} />}
</button>
</div>
@@ -176,26 +169,20 @@ export default function CartPage() {
</div>
</div>
{/* بخش صورتحساب (Sidebar) */}
<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">
<h2 className="text-lg md:text-xl font-bold text-gray-800 mb-6">خلاصه صورتحساب</h2>
<div className="space-y-4 mb-6">
<div className="flex justify-between items-center text-xs md:text-sm">
<span className="text-gray-500">تعداد کالاها</span>
<span className="font-bold text-gray-800">{totalItems} عدد</span>
</div>
<div className="flex justify-between items-center text-xs md:text-sm">
<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>
</div>
</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">
<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">
@@ -204,22 +191,19 @@ export default function CartPage() {
</span>
</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} />
</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 gap-1">
<ShieldCheck size={14} className="text-green-500" />
پرداخت امن
</div>
<div className="flex items-center gap-1"><ShieldCheck size={14} className="text-green-500" /> پرداخت امن</div>
<div className="w-1 h-1 bg-gray-300 rounded-full"></div>
<div className="flex items-center gap-1">
<Truck size={14} className="text-blue-500" />
ارسال سریع
</div>
<div className="flex items-center gap-1"><Truck size={14} className="text-blue-500" /> ارسال سریع</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 { Product } from "@/public/src/types/product"; // بهتر است تایپ محصول را هم برای خوانایی ایمپورت کنید
export default async function CategoryPage({ params }:any) {
export default async function CategoryPage({ params }: any) {
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 (
<div className="container mx-auto py-20 text-center">
<h1 className="text-2xl font-bold text-gray-800">
محصولی در دستهبندی «{decodeURIComponent(categoryName)}» یافت نشد.
</h1>
</div>
);
}
const pageTitle = categoryData.items[0]?.primaryCategory?.name || decodeURIComponent(categoryName);
return (
<div className="p-8">
<h1 className="text-xl font-bold mb-6">
دستهبندی: {categoryName}
</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>
<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>
</div>
</main>
);
}

View File

@@ -2,44 +2,78 @@
import { useCart } from "@/components/context/cartcontext";
import Link from "next/link";
import { useState } from "react";
import { useState, useEffect } from "react";
import {
ShoppingBag,
ChevronLeft,
ChevronRight,
Truck,
CreditCard,
Check,
MapPin,
User,
Phone,
Mail
Lock // آیکون قفل برای صفحه لاگین نشده
} from "lucide-react";
export default function CheckoutPage() {
const { cart } = useCart();
// استیت برای روش ارسال (صرفا جهت نمایش UI)
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;
return Number(priceStr.toString().replace(/,/g, ''));
};
const totalPrice = cart.reduce((total, item) => total + (parsePrice(item.price) * item.quantity), 0);
const totalItems = cart.reduce((total, item) => total + item.quantity, 0);
// هزینه ارسال فرضی
const shippingCost = shippingMethod === 'post' ? 45000 : 75000;
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 (
<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="mb-10">
<div className="flex items-center justify-between mb-8">
<h1 className="text-xl md:text-3xl font-black text-[#1A2332]">
@@ -51,16 +85,12 @@ export default function CheckoutPage() {
</Link>
</div>
{/* Breadcrumb (تایم‌لاین اکتیو شده برای مرحله ۲) */}
<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="h-full bg-[#ffb900] w-[50%] transition-all duration-500 ease-in-out"></div>
</div>
<div className="flex justify-between relative z-10">
{/* مرحله 1 (تکمیل شده) */}
<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">
<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>
</Link>
{/* مرحله 2 (اکتیو و فعلی) */}
<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">
<Truck className="w-4 h-4 sm:w-5 sm:h-5" strokeWidth={2} />
</div>
<span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">اطلاعات ارسال</span>
</div>
{/* مرحله 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]">
<CreditCard className="w-4 h-4 sm:w-5 sm:h-5" />
@@ -89,11 +116,7 @@ export default function CheckoutPage() {
</div>
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8">
{/* بخش فرم اطلاعات */}
<div className="flex-1 space-y-6">
{/* فرم مشخصات گیرنده */}
<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="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]">
@@ -101,30 +124,18 @@ export default function CheckoutPage() {
</div>
<h2 className="text-base md:text-lg font-bold text-[#1A2332]">مشخصات گیرنده</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600 block">نام و نام خانوادگی</label>
<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]"
/>
<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]" />
</div>
<div className="space-y-2">
<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]"
/>
</div>
<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 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="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]">
@@ -132,7 +143,6 @@ export default function CheckoutPage() {
</div>
<h2 className="text-base md:text-lg font-bold text-[#1A2332]">آدرس پستی</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600 block">استان</label>
@@ -151,63 +161,39 @@ export default function CheckoutPage() {
</select>
</div>
</div>
<div className="space-y-2 mb-5">
<label className="text-sm font-medium text-gray-600 block">آدرس دقیق پستی</label>
<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"
/>
<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" />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5">
<div className=" space-y-2">
<label className="text-sm font-medium text-gray-600 block">کد پستی (۱۰ رقمی)</label>
<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]"
/>
<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]" />
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600 block">ایمیل</label>
<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]"
/>
<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]" />
</div>
</div>
</div>
</div>
{/* بخش صورتحساب (Sidebar) */}
<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">
<h2 className="text-lg md:text-xl font-bold text-[#1A2332] mb-6">خلاصه سفارش</h2>
<div className="space-y-4 mb-6">
<div className="flex justify-between items-center text-xs md:text-sm">
<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>
</div>
<div className="flex justify-between items-center text-xs md:text-sm">
<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>
</div>
</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="flex items-center gap-3 mb-6 pb-4 ">
<div className="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]">
@@ -215,9 +201,7 @@ export default function CheckoutPage() {
</div>
<h2 className="text-base md:text-lg font-bold text-[#1A2332]">نحوه ارسال</h2>
</div>
<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'}`}>
<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'}`}>
@@ -229,17 +213,8 @@ export default function CheckoutPage() {
</div>
</div>
<div className="font-bold text-[#1A2332]">۴۵,۰۰۰ <span className="text-xs font-normal text-gray-500">تومان</span></div>
<input
type="radio"
name="shipping"
value="post"
className="hidden"
checked={shippingMethod === 'post'}
onChange={() => setShippingMethod('post')}
/>
<input type="radio" name="shipping" value="post" className="hidden" checked={shippingMethod === 'post'} onChange={() => setShippingMethod('post')} />
</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'}`}>
<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'}`}>
@@ -251,18 +226,11 @@ export default function CheckoutPage() {
</div>
</div>
<div className="font-bold text-[#1A2332]">۷۵,۰۰۰ <span className="text-xs font-normal text-gray-500">تومان</span></div>
<input
type="radio"
name="shipping"
value="tipax"
className="hidden"
checked={shippingMethod === 'tipax'}
onChange={() => setShippingMethod('tipax')}
/>
<input type="radio" name="shipping" value="tipax" className="hidden" checked={shippingMethod === 'tipax'} onChange={() => setShippingMethod('tipax')} />
</label>
</div>
</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">

View File

@@ -24,6 +24,7 @@ import {
TicketStatus,
updateAdminTicket,
} from "@/public/src/services/tickets/api";
import NotLogin from '@/components/Notlogin';
const sampleOrders = [
{ 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) {
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={() => 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>
<NotLogin/>
</div>
);
}

View File

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

View File

@@ -17,7 +17,7 @@ export default async function ProductsPage() {
}));
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>