Add product/single product api

This commit is contained in:
haniyeroozmand
2026-03-30 22:35:43 +03:30
parent c24d0ef511
commit 65c28948a3
10 changed files with 338 additions and 285 deletions

View File

@@ -1,18 +1,19 @@
'use client';
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import { Product } from '@/public/src/types/product';
export interface Product {
id: string;
title: string;
image: string;
l: string;
d: string;
brand: string;
price?: string | null;
badge?: string;
stock: boolean;
}
// export interface Product {
// id: string;
// title: string;
// image: string;
// l: string;
// d: string;
// brand: string;
// price?: string | null;
// badge?: string;
// stock: boolean;
// }
export interface CartItem extends Product {
quantity: number;

View File

@@ -1,31 +1,24 @@
'use client';
'use client';
import { Phone, Plus, ShoppingCart, Minus, Trash2 } from "lucide-react";
import Image from "next/image";
import Link from 'next/link';
import { useCart, Product } from "./context/cartcontext";
import { useCart } from "./context/cartcontext";
import { Product } from "@/public/src/types/product";
interface ProductCardProps {
product: Product;
}
export default function ProductCard({ product }: ProductCardProps) {
const { addToCart, decreaseQuantity, cart } = useCart();
// پیدا کردن محصول در سبد خرید
const cartItem = cart.find(item => item.id === product.id);
const quantity = cartItem ? cartItem.quantity : 0;
const slug = product.slug;
const generateSlug = (text: string) => {
if (!text) return "";
return text
.trim()
.replace(/[\s\u200c]+/g, '-')
.replace(/[^\w\u0600-\u06FF0-9\-]/g, '')
.replace(/\-\-+/g, '-');
};
const slug = generateSlug(product.title);
const handleIncrease = (e: React.MouseEvent) => {
e.preventDefault();
@@ -39,13 +32,14 @@ export default function ProductCard({ product }: ProductCardProps) {
decreaseQuantity(product.id);
};
// پاکسازی قیمت از ویرگول و تبدیل به عدد برای نمایش صحیح
const formattedPrice = product.price
? Number(product.price.toString().replace(/,/g, '')).toLocaleString('fa-IR')
const formattedPrice = product.price
? Number(product.price.toString().replace(/,/g, '')).toLocaleString('fa-IR')
: null;
return (
<Link href={`/products/${product.id}/${slug}`} scroll={true} className="bg-white gap-1 flex flex-col justify-between border border-gray-200 rounded-xl shadow-sm p-4 transition-all hover:shadow-md">
<Link href={`/product/${slug}`} scroll={true} className="bg-white gap-1 flex flex-col justify-between border border-gray-200 rounded-xl shadow-sm p-4 transition-all hover:shadow-md">
<div className="relative flex justify-center mb-4">
<span
@@ -55,7 +49,7 @@ export default function ProductCard({ product }: ProductCardProps) {
</span>
<Image
src={product.image}
src={product.image || "/placeholder.png"}
width={150}
height={150}
alt={product.title}
@@ -64,48 +58,58 @@ export default function ProductCard({ product }: ProductCardProps) {
<p className="text-[#808080] text-sm">{product.brand}</p>
<h3 className="text-sm font-semibold">{product.title}</h3>
<div className="text-xs text-gray-500 mt-2">
<div className="flex border-b border-[#dfdfdf] mb-2 pb-2 justify-between">
<p>قطر داخلی (L):</p>
<p dir="ltr" className="font-semibold text-black">{product.l}</p>
</div>
{/*
✅ این بخش را تغییر می‌دهیم تا نام attribute ها را بگیریم
اگر attribute اول وجود داشت، نامش را بگیر و نمایش بده
اگر attribute اول نبود، نمایش نده
*/}
{product.attributes?.[0] && (
<div className="flex border-b border-[#dfdfdf] mb-2 pb-2 justify-between">
<p>{product.attributes[0].name}:</p> {/* 👈 نام attribute اول */}
<p dir="ltr" className="font-semibold text-black">{product.attributes[0].valueText || "-"}</p> {/* 👈 مقدار attribute اول */}
</div>
)}
<div className="flex justify-between">
<p>قطر خارجی (D):</p>
<p dir="ltr" className="font-semibold text-black">{product.d}</p>
</div>
{/*
✅ مشابه بالا برای attribute دوم
*/}
{product.attributes?.[1] && (
<div className="flex justify-between">
<p>{product.attributes[1].name}:</p> {/* 👈 نام attribute دوم */}
<p dir="ltr" className="font-semibold text-black">{product.attributes[1].valueText || "-"}</p> {/* 👈 مقدار attribute دوم */}
</div>
)}
</div>
{/* بخش قیمت و دکمه خرید */}
{product.price ? (
<div className="flex justify-between mt-3 items-center min-h-[32px]">
<span className="text-[0.85em] font-bold text-gray-800">
{formattedPrice} تومان
</span>
{/* بررسی موجودی برای نمایش دکمه سبد خرید */}
{product.stock ? (
quantity > 0 ? (
<div
<div
className="flex items-center gap-3 bg-white border border-gray-200 rounded-lg py-1 px-2 shadow-sm"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<button
<button
onClick={handleIncrease}
className="text-gray-500 hover:text-green-600 transition-colors"
>
<Plus size={16} />
</button>
<span className="text-xs font-bold text-gray-800 w-3 text-center">
{quantity}
</span>
<button
<button
onClick={handleDecrease}
className="text-gray-500 hover:text-red-500 transition-colors"
>
@@ -113,7 +117,7 @@ export default function ProductCard({ product }: ProductCardProps) {
</button>
</div>
) : (
<button
<button
onClick={handleIncrease}
className="rounded-lg border border-[#e6e6e6] bg-gray-50 flex items-center p-2 text-gray-600 hover:bg-gray-200 transition-colors"
title="افزودن به سبد خرید"
@@ -122,7 +126,6 @@ export default function ProductCard({ product }: ProductCardProps) {
</button>
)
) : (
// جایگزین دکمه خرید وقتی محصول ناموجود است
<span className="text-[10px] text-red-500 font-medium bg-red-50 px-2 py-1 rounded-md">
عدم موجودی
</span>