This commit is contained in:
Your Name committed 2026-06-27 23:59:16 -04:00
1 parent 1c0313495f
commit ac387185e0
15 files changed
+1855 -12

No files matched your search

+36 -1
View File
@@ -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">