feat: add JSON-LD structured data for product and site pages

JSON-LD was absent entirely (sitemap generation is separate backend
work, out of scope here). Added Product schema (name/description/
image/offers with price+availability) on item pages via setItemMeta(),
and a site-wide Organization schema via resetToDefaults(), both
injected as a single #seo-json-ld <script type=application/ld+json>
tag that gets replaced on navigation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-13 09:16:37 +04:00
parent ec8ed8f6a8
commit 3feb806caa

View File

@@ -86,6 +86,22 @@ export class SeoService {
// Standard meta // Standard meta
{ name: 'description', content: description }, { name: 'description', content: description },
]); ]);
this.setJsonLd({
'@context': 'https://schema.org',
'@type': 'Product',
name: item.name,
description,
image: imageUrl,
url: itemUrl,
offers: {
'@type': 'Offer',
price: price.toFixed(2),
priceCurrency: item.currency || 'RUB',
availability: (item.quantity ?? 0) > 0 ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
url: itemUrl,
},
});
} }
/** /**
@@ -139,6 +155,30 @@ export class SeoService {
// Remove product-specific tags // Remove product-specific tags
this.meta.removeTag("property='product:price:amount'"); this.meta.removeTag("property='product:price:amount'");
this.meta.removeTag("property='product:price:currency'"); this.meta.removeTag("property='product:price:currency'");
this.setJsonLd({
'@context': 'https://schema.org',
'@type': 'Organization',
name: this.siteName,
url: this.siteUrl,
...(defaultImage ? { logo: defaultImage } : {}),
});
}
/** Replace (or remove, when data is null) the page's JSON-LD structured-data script tag. */
private setJsonLd(data: Record<string, unknown> | null): void {
const existing = this.doc.getElementById('seo-json-ld');
existing?.remove();
if (!data) {
return;
}
const script = this.doc.createElement('script');
script.id = 'seo-json-ld';
script.type = 'application/ld+json';
script.text = JSON.stringify(data);
this.doc.head.appendChild(script);
} }
private setOrUpdate(tags: Array<{ property?: string; name?: string; content: string }>): void { private setOrUpdate(tags: Array<{ property?: string; name?: string; content: string }>): void {