-
{product.categories.map((cat) => (
- {cat.name} ))}
{t('by')}{' '} {(product as { brands: Array<{ id: string; name: string }> }).brands .map((b) => b.name) .join(', ')}
)} {/* Title */}{product.name}
{/* Tags */} {(product as unknown as { tags?: Array<{ id: string; name: string }> }).tags && (product as unknown as { tags: Array<{ id: string; name: string }> }).tags.length > 0 && (-
{(product as unknown as { tags: Array<{ id: string; name: string }> }).tags.map(
(tag) => (
- #{tag.name} ) )}
{t('instantDownload')}
) : ({t('filesIncluded')} ({product.downloads.length})
-
{product.downloads.map((file: DownloadFile) => (
- {file.name} {file.size && ( {' '} ( {file.size < 1024 * 1024 ? `${(file.size / 1024).toFixed(0)} KB` : `${(file.size / (1024 * 1024)).toFixed(1)} MB`} ) )} ))}
{t('whatsInTheBox')}
-
{product.kitComponents.map((c) => (
-
{c.image ? (
) : ( )} {c.name} x{c.quantity}
))}
{modifierError}
}{addToCartError === 'CART_FULL' ? t('cartFull') : t('addToCartFailed')}
)} {/* Sold out is not the end of the page. `canOfferStockAlert` is the whole gate — it also covers the merchant's switch and backorderable items, where an alert would tell someone to come back and do what they can already do. Pass the selected variant: without it a shopper who wanted the medium is mailed when the small returns. */} {canOfferStockAlert(inventory, stockAlertsEnabled) && ({t('downloadAfterPurchase')}
} {/* Description */} {description && ({t('description')}
{'html' in description ? ( // Security: merchant-authored product description HTML, // sanitized via `sanitizeProductHtml` (isomorphic-dompurify, // allowlisted tags) before injecting. ) : ({description.text}
)}{t('specifications')}
| {field.definitionName} |
|
|---|