x
This commit is contained in:
1 parent
1c0313495f
commit
ac387185e0
15 files changed
+1855
-12
No files matched your search
@@ -7,7 +7,7 @@ import ProductCard from '../components/ProductCard'
|
||||
import ProductReviews from '../components/ProductReviews'
|
||||
import ProductImageGallery from '../components/ProductImageGallery'
|
||||
import DynamicPageRenderer from '../components/DynamicPageRenderer'
|
||||
import { API_BASE, getCategoryImage, formatPrice, type Category, type Product, type PageSection, type TranslatableString } from '../types'
|
||||
import { API_BASE, getCategoryImage, formatPrice, type Category, type Product, type PageSection, type ProductAttribute, type TranslatableString } from '../types'
|
||||
import { useCartStore } from '../store/cartStore'
|
||||
import { marked } from 'marked'
|
||||
import DOMPurify from 'dompurify'
|
||||
@@ -38,6 +38,18 @@ function resolveOptionValue(val: string, options: any[] | null | undefined, lang
|
||||
return val;
|
||||
}
|
||||
|
||||
function formatAttributeValue(attribute: ProductAttribute): string {
|
||||
const value = attribute.value
|
||||
if (value === null || value === undefined || value === '') return ''
|
||||
if (typeof value === 'boolean') return value ? 'Yes' : 'No'
|
||||
if (typeof value === 'number') {
|
||||
const unit = attribute.attributeDefinition.unit ? ` ${attribute.attributeDefinition.unit}` : ''
|
||||
return `${value}${unit}`
|
||||
}
|
||||
if (typeof value === 'string') return value
|
||||
return JSON.stringify(value)
|
||||
}
|
||||
|
||||
export default function ProductDetailPage() {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
|
||||
@@ -143,6 +155,7 @@ export default function ProductDetailPage() {
|
||||
}
|
||||
|
||||
const currentVariant = product.variants[selectedVariant]
|
||||
const visibleAttributes = (product.attributes || []).filter((attribute) => formatAttributeValue(attribute))
|
||||
|
||||
const handleAddToCart = () => {
|
||||
if (!product || !currentVariant) return
|
||||
@@ -226,6 +239,28 @@ export default function ProductDetailPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{visibleAttributes.length > 0 && (
|
||||
<section className="space-y-4 border-t border-spa-stone/10 pt-6">
|
||||
<div>
|
||||
<p className="text-spa-forest uppercase tracking-[0.3em] text-xs font-bold opacity-80">Product Specs</p>
|
||||
<h2 className="text-xl font-bold text-pop-black">Specifications</h2>
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
{visibleAttributes.map((attribute) => {
|
||||
const label = typeof attribute.attributeDefinition.label === 'string'
|
||||
? attribute.attributeDefinition.label
|
||||
: resolveTranslatable(attribute.attributeDefinition.label, i18n.language)
|
||||
return (
|
||||
<div key={attribute.id} className="flex items-start justify-between gap-6 rounded-2xl border border-spa-stone/10 bg-white/70 px-4 py-3">
|
||||
<span className="text-sm font-semibold text-pop-black">{label}</span>
|
||||
<span className="text-sm text-spa-stone text-right">{formatAttributeValue(attribute)}</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Variant Selector */}
|
||||
<div className="space-y-3">
|
||||
<p className="text-xs uppercase tracking-widest text-spa-stone/80 font-bold">
|
||||
|
||||
Reference in new issue
Block a user