variant
This commit is contained in:
@@ -75,77 +75,193 @@ const BaseInformation: FC<BaseInformationProps> = ({ product, stats }) => {
|
||||
</div>
|
||||
|
||||
{/* نمایش variant ها */}
|
||||
<div className='mt-4 sm:mt-6'>
|
||||
{/* متراژ */}
|
||||
<div className='flex flex-wrap items-center gap-1.5 mb-4'>
|
||||
{product.variants.map((variant) => {
|
||||
if (variant.meterage) {
|
||||
const isSelected = variantId === variant._id
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={`text-[13px] px-4 py-2 border border-border rounded-lg whitespace-nowrap transition-colors ${isSelected
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
{variant.meterage.value}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</div>
|
||||
{product.variants.length > 0 && (
|
||||
<div className='mt-4 sm:mt-6'>
|
||||
{/* بررسی وجود فیلدهای خاص در variant ها */}
|
||||
{(() => {
|
||||
const hasMeterage = product.variants.some(v => v.meterage)
|
||||
const hasColor = product.variants.some(v => v.color)
|
||||
const hasSize = product.variants.some(v => v.size)
|
||||
const hasThemeValue = product.variants.some(v => v.themeValue)
|
||||
|
||||
{/* رنگ ها */}
|
||||
<div className='flex flex-wrap items-center gap-1.5 mb-4'>
|
||||
{product.variants.map((variant) => {
|
||||
if (variant.color) {
|
||||
const isSelected = variantId === variant._id
|
||||
// اگر هیچ فیلد خاصی وجود نداشت، نمایش عمومی
|
||||
if (!hasMeterage && !hasColor && !hasSize && !hasThemeValue) {
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={clx(
|
||||
'size-10 rounded-full flex justify-center items-center',
|
||||
variant.color.hexColor === '#FFFFFF' ? 'border border-border' : ''
|
||||
)}
|
||||
style={{ backgroundColor: variant.color.hexColor }}
|
||||
>
|
||||
{isSelected && <Image src='/images/tick.svg' alt='check' width={20} height={20} className={clx(
|
||||
'size-5',
|
||||
variant.color.hexColor === '#FFFFFF' ? 'filterBlack' : ''
|
||||
)} />}
|
||||
</button>
|
||||
<div className='flex flex-wrap items-center gap-1.5'>
|
||||
{product.variants.map((variant, index) => {
|
||||
const isSelected = variantId === variant._id
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={`text-[13px] px-4 py-2 border border-border rounded-lg whitespace-nowrap transition-colors ${isSelected
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
گزینه {index + 1}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* سایزها */}
|
||||
<div className='flex flex-wrap items-center gap-1.5'>
|
||||
{product.variants.map((variant) => {
|
||||
if (variant.size) {
|
||||
const isSelected = variantId === variant._id
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={`text-[13px] px-4 py-2 border border-border rounded-lg whitespace-nowrap transition-colors ${isSelected
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
{variant.size.value}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
// نمایش فیلدهای خاص
|
||||
return (
|
||||
<>
|
||||
{/* متراژ */}
|
||||
{hasMeterage && (
|
||||
<div className='flex flex-wrap items-center gap-1.5 mb-4'>
|
||||
{product.variants.map((variant) => {
|
||||
if (variant.meterage) {
|
||||
const isSelected = variantId === variant._id
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={`text-[13px] px-4 py-2 border border-border rounded-lg whitespace-nowrap transition-colors ${isSelected
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
{variant.meterage.value}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* رنگ ها */}
|
||||
{hasColor && (
|
||||
<div className='flex flex-wrap items-center gap-1.5 mb-4'>
|
||||
{product.variants.map((variant) => {
|
||||
if (variant.color) {
|
||||
const isSelected = variantId === variant._id
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={clx(
|
||||
'size-10 rounded-full flex justify-center items-center',
|
||||
variant.color.hexColor === '#FFFFFF' ? 'border border-border' : ''
|
||||
)}
|
||||
style={{ backgroundColor: variant.color.hexColor }}
|
||||
>
|
||||
{isSelected && <Image src='/images/tick.svg' alt='check' width={20} height={20} className={clx(
|
||||
'size-5',
|
||||
variant.color.hexColor === '#FFFFFF' ? 'filterBlack' : ''
|
||||
)} />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* سایزها */}
|
||||
{hasSize && (
|
||||
<div className='flex flex-wrap items-center gap-1.5 mb-4'>
|
||||
{product.variants.map((variant) => {
|
||||
if (variant.size) {
|
||||
const isSelected = variantId === variant._id
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={`text-[13px] px-4 py-2 border border-border rounded-lg whitespace-nowrap transition-colors ${isSelected
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
{variant.size.value}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* themeValue ها - نمایش داینامیک */}
|
||||
{hasThemeValue && (() => {
|
||||
// گروهبندی variantها بر اساس theme
|
||||
const themeGroups = new Map<string, typeof product.variants>()
|
||||
|
||||
product.variants.forEach(variant => {
|
||||
if (variant.themeValue) {
|
||||
const theme = variant.themeValue.theme
|
||||
if (!themeGroups.has(theme)) {
|
||||
themeGroups.set(theme, [])
|
||||
}
|
||||
themeGroups.get(theme)!.push(variant)
|
||||
}
|
||||
})
|
||||
|
||||
// تابع برای بررسی hex بودن value
|
||||
const isHexColor = (value: string | number): boolean => {
|
||||
if (typeof value !== 'string') return false
|
||||
const hexPattern = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/
|
||||
return hexPattern.test(value)
|
||||
}
|
||||
|
||||
return Array.from(themeGroups.entries()).map(([theme, variants]) => (
|
||||
<div key={theme} className='flex flex-wrap items-center gap-1.5 mb-4'>
|
||||
{variants.map((variant) => {
|
||||
if (variant.themeValue) {
|
||||
const isSelected = variantId === variant._id
|
||||
const value = typeof variant.themeValue.value === 'string'
|
||||
? variant.themeValue.value
|
||||
: String(variant.themeValue.value)
|
||||
const isHex = isHexColor(value)
|
||||
|
||||
// اگر hex بود، دایره رنگی نمایش بده
|
||||
if (isHex) {
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={clx(
|
||||
'size-10 rounded-full flex justify-center items-center',
|
||||
value === '#FFFFFF' ? 'border border-border' : ''
|
||||
)}
|
||||
style={{ backgroundColor: value }}
|
||||
>
|
||||
{isSelected && <Image src='/images/tick.svg' alt='check' width={20} height={20} className={clx(
|
||||
'size-5',
|
||||
value === '#FFFFFF' ? 'filterBlack' : ''
|
||||
)} />}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
// اگر hex نبود، name را نمایش بده
|
||||
return (
|
||||
<button
|
||||
key={variant._id}
|
||||
onClick={() => handleVariantSelect(variant._id)}
|
||||
className={`text-[13px] px-4 py-2 border border-border rounded-lg whitespace-nowrap transition-colors ${isSelected
|
||||
? 'bg-primary text-white border-primary'
|
||||
: 'hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
{variant.themeValue.name}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</div>
|
||||
))
|
||||
})()}
|
||||
</>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{
|
||||
product.specifications.length > 0 ?
|
||||
|
||||
@@ -67,7 +67,7 @@ const CategoryModal: FC<Props> = ({ isOpen, onClose }) => {
|
||||
onClick={() => handleMainCategoryClick(category._id)}
|
||||
>
|
||||
<div className="w-12 h-12 bg-white rounded-full flex items-center justify-center mb-1 shadow-sm">
|
||||
{category.icon ? (
|
||||
{category.icon && category.icon.trim() !== '' && (category.icon.startsWith('http://') || category.icon.startsWith('https://') || category.icon.startsWith('/')) ? (
|
||||
<Image
|
||||
src={category.icon}
|
||||
alt={category.title_fa}
|
||||
@@ -147,7 +147,7 @@ const CategoryModal: FC<Props> = ({ isOpen, onClose }) => {
|
||||
onClick={() => handleCategoryNavigate(item)}
|
||||
>
|
||||
<div className="w-14 h-14 bg-white rounded-full flex items-center justify-center mb-3 border border-gray-200">
|
||||
{item.icon ? (
|
||||
{item.icon && item.icon.trim() !== '' && (item.icon.startsWith('http://') || item.icon.startsWith('https://') || item.icon.startsWith('/')) ? (
|
||||
<Image
|
||||
src={item.icon}
|
||||
alt={item.title_fa}
|
||||
|
||||
@@ -62,7 +62,7 @@ const Menu: FC = () => {
|
||||
onClick={() => handleCategoryClick(category)}
|
||||
>
|
||||
<div className='flex items-center gap-3'>
|
||||
{category.icon && (
|
||||
{category.icon && category.icon.trim() !== '' && (category.icon.startsWith('http://') || category.icon.startsWith('https://') || category.icon.startsWith('/')) && (
|
||||
<Image
|
||||
src={category.icon}
|
||||
alt={category.title_fa}
|
||||
|
||||
@@ -38,10 +38,10 @@ export interface ProductShop {
|
||||
shopCode: number;
|
||||
owner: string;
|
||||
shopDescription: string;
|
||||
telephoneNumber: string;
|
||||
telephoneNumber: string | null;
|
||||
isChatActive: boolean;
|
||||
shopPostalCode: string;
|
||||
logo: string;
|
||||
shopPostalCode: string | null;
|
||||
logo: string | null;
|
||||
}
|
||||
|
||||
export interface ShipmentMethod {
|
||||
@@ -52,6 +52,17 @@ export interface ShipmentMethod {
|
||||
deliveryType: string;
|
||||
}
|
||||
|
||||
export interface SaleFormat {
|
||||
wholeSale: unknown[];
|
||||
}
|
||||
|
||||
export interface ThemeValue {
|
||||
_id: string;
|
||||
theme: string;
|
||||
name: string;
|
||||
value: number | string;
|
||||
}
|
||||
|
||||
export interface ProductVariant {
|
||||
_id: string;
|
||||
market_status: string;
|
||||
@@ -61,8 +72,10 @@ export interface ProductVariant {
|
||||
isFreeShip: boolean;
|
||||
isWholeSale: boolean;
|
||||
shop: ProductShop;
|
||||
saleFormat: SaleFormat;
|
||||
shipmentMethod: ShipmentMethod[];
|
||||
warranty: ProductWarranty;
|
||||
themeValue: ThemeValue;
|
||||
size?: ProductSize;
|
||||
color?: ProductColor;
|
||||
meterage?: ProductMeterage;
|
||||
@@ -86,7 +99,7 @@ export interface ProductCategory {
|
||||
theme: string;
|
||||
description: string;
|
||||
leaf: boolean;
|
||||
parent: string;
|
||||
parent: string | null;
|
||||
}
|
||||
|
||||
export interface ProductSpecification {
|
||||
@@ -146,7 +159,7 @@ export interface Product {
|
||||
url: string;
|
||||
title_fa: string;
|
||||
title_en: string;
|
||||
seoTitle: string | null;
|
||||
seoTitle: string;
|
||||
seoDescription: string | null;
|
||||
source: string;
|
||||
description: string;
|
||||
@@ -173,6 +186,7 @@ export interface ProductDetailResponse {
|
||||
reviews?: Review[];
|
||||
questions?: Question[];
|
||||
stats: {
|
||||
product: number;
|
||||
commentsCount: number;
|
||||
averageRate: number;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user