checkout accessibility/category products
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
<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 (
|
return (
|
||||||
<div className="p-8">
|
<main className="bg-gray-50 min-h-screen py-10">
|
||||||
|
<div className="max-w-7xl mx-auto px-4">
|
||||||
<h1 className="text-xl font-bold mb-6">
|
{/* عنوان و تعداد محصولات */}
|
||||||
دستهبندی: {categoryName}
|
<div className="mb-8">
|
||||||
</h1>
|
<h1 className="text-2xl font-bold text-gray-800 flex items-center gap-2">
|
||||||
|
{pageTitle}
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
<span className="text-sm font-normal text-gray-500 bg-gray-200 px-3 py-1 rounded-full">
|
||||||
|
{categoryData.meta.total} کالا
|
||||||
{products.items.map((product:any) => (
|
</span>
|
||||||
<div key={product.id} className="border p-4 rounded-xl">
|
</h1>
|
||||||
|
<div className="relative mt-3 h-[2px] bg-gray-200 w-full">
|
||||||
<div className="h-32 bg-gray-100 rounded mb-3" />
|
<div className="absolute right-0 top-0 h-[2px] w-24 bg-yellow-500"></div>
|
||||||
|
|
||||||
<h3 className="text-sm font-medium">
|
|
||||||
{product.title}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<p className="text-xs text-gray-500">
|
|
||||||
{product.brand}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</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>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
|
||||||
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>
|
||||||
|
|
||||||
</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">
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
141
app/page.tsx
141
app/page.tsx
@@ -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,32 +11,48 @@ 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();
|
||||||
const data = await getProducts(1, 20);
|
|
||||||
|
|
||||||
const products = data.items.map((p: any) => ({
|
useEffect(() => {
|
||||||
id: p.id,
|
const fetchInitialProducts = async () => {
|
||||||
title: p.title,
|
try {
|
||||||
brand: p.brand,
|
setLoading(true);
|
||||||
slug: p.slug,
|
const data = await getProducts(1, 20);
|
||||||
price: p.calculated_price,
|
|
||||||
stock: p.stock,
|
const formattedProducts = data.items.map((p: any) => ({
|
||||||
image: p.mainImageUrl || "/placeholder.png",
|
id: p.id,
|
||||||
attributes: p.attributes
|
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 = [
|
const features = [
|
||||||
{
|
{
|
||||||
@@ -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>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
|
{/* 4. نمایش وضعیت لودینگ یا لیست محصولات */}
|
||||||
{products.slice(-4).map((product: any) => (
|
{loading ? (
|
||||||
<ProductCard key={product.id} product={product} />
|
<div className="flex justify-center py-10">
|
||||||
))}
|
<p>در حال بارگذاری محصولات...</p>
|
||||||
</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>
|
||||||
|
)}
|
||||||
|
|
||||||
</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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
58
components/Notlogin.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import { API_BASE_URL } from "../config";
|
import { API_BASE_URL } from "../config";
|
||||||
|
import { RegisterPayload } from "../../types/auth";
|
||||||
|
import { LoginPayload } from "../../types/auth";
|
||||||
|
|
||||||
export interface RegisterPayload {
|
|
||||||
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`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -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
11
public/src/types/auth.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export interface RegisterPayload {
|
||||||
|
phone: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
fullName: string;
|
||||||
|
}
|
||||||
|
export interface LoginPayload {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user