Compare commits

...

2 Commits

3 changed files with 396 additions and 278 deletions

View File

@@ -7,41 +7,82 @@ const generateCategoryJsonLd = (category, products = [], baseUrl, config) => {
return ''; return '';
} }
const categoryUrl = `${baseUrl}/Kategorie/${category.seoName}`; const root = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
const categoryUrl = `${root}/Kategorie/${category.seoName}`;
// Calculate price valid date (current date + 3 months) // Calculate price valid date (current date + 3 months)
const priceValidDate = new Date(); const priceValidDate = new Date();
priceValidDate.setMonth(priceValidDate.getMonth() + 3); priceValidDate.setMonth(priceValidDate.getMonth() + 3);
const priceValidUntil = priceValidDate.toISOString().split("T")[0]; const priceValidUntil = priceValidDate.toISOString().split("T")[0];
const jsonLd = { const id = {
"@context": "https://schema.org/", business: `${root}#business`,
website: `${root}#website`,
breadcrumb: `${categoryUrl}#breadcrumb`,
itemList: `${categoryUrl}#itemlist`,
};
const logoUrl =
config.images && config.images.logo
? `${root}${config.images.logo}`
: undefined;
const businessNode = {
"@id": id.business,
"@type": ["GardenStore", "LocalBusiness", "Organization"],
name: config.brandName,
url: root,
...(logoUrl && {
logo: { "@type": "ImageObject", url: logoUrl },
image: { "@type": "ImageObject", url: logoUrl },
}),
};
const websiteNode = {
"@id": id.website,
"@type": "WebSite",
name: config.siteName || config.brandName,
url: root,
publisher: { "@id": id.business },
};
const breadcrumbNode = {
"@id": id.breadcrumb,
"@type": "BreadcrumbList",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "Home",
item: root,
},
{
"@type": "ListItem",
position: 2,
name: category.name,
item: categoryUrl,
},
],
};
const collectionPageNode = {
"@id": categoryUrl,
"@type": "CollectionPage", "@type": "CollectionPage",
name: category.name, name: category.name,
url: categoryUrl, url: categoryUrl,
description: `${category.name} - Entdecken Sie unsere Auswahl an hochwertigen Produkten`, description: `${category.name} - Entdecken Sie unsere Auswahl an hochwertigen Produkten`,
breadcrumb: { isPartOf: { "@id": id.website },
"@type": "BreadcrumbList", breadcrumb: { "@id": id.breadcrumb },
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "Home",
item: baseUrl,
},
{
"@type": "ListItem",
position: 2,
name: category.name,
item: categoryUrl,
},
],
},
}; };
const graph = [businessNode, websiteNode, breadcrumbNode, collectionPageNode];
// Add product list if products are available // Add product list if products are available
if (products && products.length > 0) { if (products && products.length > 0) {
jsonLd.mainEntity = { collectionPageNode.mainEntity = { "@id": id.itemList };
graph.push({
"@id": id.itemList,
"@type": "ItemList", "@type": "ItemList",
numberOfItems: products.length, numberOfItems: products.length,
itemListElement: products.slice(0, 20).map((product, index) => ({ itemListElement: products.slice(0, 20).map((product, index) => ({
@@ -50,14 +91,14 @@ const generateCategoryJsonLd = (category, products = [], baseUrl, config) => {
item: { item: {
"@type": "Product", "@type": "Product",
name: product.name, name: product.name,
url: `${baseUrl}/Artikel/${product.seoName}`, url: `${root}/Artikel/${product.seoName}`,
image: image:
product.pictureList && product.pictureList.trim() product.pictureList && product.pictureList.trim()
? `${baseUrl}/assets/images/prod${product.pictureList ? `${root}/assets/images/prod${product.pictureList
.split(",")[0] .split(",")[0]
.trim()}.avif` .trim()}.avif`
: `${baseUrl}/assets/images/nopicture.jpg`, : `${root}/assets/images/nopicture.jpg`,
description: product.description description: product.description
? product.description.replace(/<[^>]*>/g, "").substring(0, 200) ? product.description.replace(/<[^>]*>/g, "").substring(0, 200)
: `${product.name} - Hochwertiges Growshop Produkt`, : `${product.name} - Hochwertiges Growshop Produkt`,
sku: product.articleNumber || product.seoName, sku: product.articleNumber || product.seoName,
@@ -67,22 +108,23 @@ const generateCategoryJsonLd = (category, products = [], baseUrl, config) => {
}, },
offers: { offers: {
"@type": "Offer", "@type": "Offer",
url: `${baseUrl}/Artikel/${product.seoName}`, url: `${root}/Artikel/${product.seoName}`,
price: product.price && !isNaN(product.price) ? product.price.toString() : "0.00", price:
product.price && !isNaN(product.price)
? product.price.toString()
: "0.00",
priceCurrency: config.currency, priceCurrency: config.currency,
priceValidUntil: priceValidUntil, priceValidUntil: priceValidUntil,
availability: product.available availability: product.available
? "https://schema.org/InStock" ? "https://schema.org/InStock"
: "https://schema.org/OutOfStock", : "https://schema.org/OutOfStock",
seller: { seller: { "@id": id.business },
"@type": "Organization",
name: config.brandName,
},
itemCondition: "https://schema.org/NewCondition", itemCondition: "https://schema.org/NewCondition",
hasMerchantReturnPolicy: { hasMerchantReturnPolicy: {
"@type": "MerchantReturnPolicy", "@type": "MerchantReturnPolicy",
applicableCountry: "DE", applicableCountry: "DE",
returnPolicyCategory: "https://schema.org/MerchantReturnFiniteReturnWindow", returnPolicyCategory:
"https://schema.org/MerchantReturnFiniteReturnWindow",
merchantReturnDays: 14, merchantReturnDays: 14,
returnMethod: "https://schema.org/ReturnByMail", returnMethod: "https://schema.org/ReturnByMail",
returnFees: "https://schema.org/FreeReturn", returnFees: "https://schema.org/FreeReturn",
@@ -91,7 +133,7 @@ const generateCategoryJsonLd = (category, products = [], baseUrl, config) => {
"@type": "OfferShippingDetails", "@type": "OfferShippingDetails",
shippingRate: { shippingRate: {
"@type": "MonetaryAmount", "@type": "MonetaryAmount",
value: 5.90, value: 5.9,
currency: "EUR", currency: "EUR",
}, },
shippingDestination: { shippingDestination: {
@@ -117,11 +159,16 @@ const generateCategoryJsonLd = (category, products = [], baseUrl, config) => {
}, },
}, },
})), })),
}; });
} }
const categoryGraph = {
"@context": "https://schema.org",
"@graph": graph,
};
return `<script type="application/ld+json">${JSON.stringify( return `<script type="application/ld+json">${JSON.stringify(
jsonLd categoryGraph
)}</script>`; )}</script>`;
}; };

View File

@@ -36,177 +36,198 @@ const generateHomepageJsonLd = (baseUrl, config, categories = []) => {
const canonicalUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; const canonicalUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
const logoUrl = `${canonicalUrl}${config.images.logo}`; const logoUrl = `${canonicalUrl}${config.images.logo}`;
const websiteJsonLd = { const id = {
"@context": "https://schema.org/", business: `${canonicalUrl}#business`,
website: `${canonicalUrl}#website`,
faq: `${canonicalUrl}#faq`,
categoryList: `${canonicalUrl}#category-list`,
sitemapPage: `${canonicalUrl}/sitemap#webpage`,
};
const organizationNode = {
"@id": id.business,
"@type": ["GardenStore", "LocalBusiness", "Organization"],
name: config.brandName,
alternateName: config.siteName,
description: config.descriptions.de.long,
url: canonicalUrl,
logo: {
"@type": "ImageObject",
url: logoUrl,
},
image: {
"@type": "ImageObject",
url: logoUrl,
},
telephone: "015208491860",
email: "service@growheads.de",
address: {
"@type": "PostalAddress",
streetAddress: "Trachenberger Strasse 14",
addressLocality: "Dresden",
postalCode: "01129",
addressCountry: "DE",
addressRegion: "Sachsen",
},
geo: {
"@type": "GeoCoordinates",
latitude: "51.083675",
longitude: "13.727215",
},
openingHours: [
"Mo-Fr 10:00:00-20:00:00",
"Sa 11:00:00-19:00:00",
],
paymentAccepted: "Cash, Credit Card, PayPal, Bank Transfer",
currenciesAccepted: "EUR",
priceRange: "€€",
areaServed: {
"@type": "Country",
name: "Germany",
},
contactPoint: [
{
"@type": "ContactPoint",
telephone: "015208491860",
contactType: "customer service",
availableLanguage: "German",
hoursAvailable: {
"@type": "OpeningHoursSpecification",
dayOfWeek: [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
],
opens: "10:00:00",
closes: "20:00:00",
},
},
{
"@type": "ContactPoint",
email: "service@growheads.de",
contactType: "customer service",
availableLanguage: "German",
},
],
sameAs: [],
};
const sitemapWebPageNode = {
"@id": id.sitemapPage,
"@type": "WebPage",
name: "Sitemap",
url: `${canonicalUrl}/sitemap`,
description: "Vollständige Sitemap mit allen Kategorien und Seiten",
isPartOf: { "@id": id.website },
};
const websiteNode = {
"@id": id.website,
"@type": "WebSite", "@type": "WebSite",
name: config.brandName, name: config.brandName,
url: canonicalUrl, url: canonicalUrl,
description: config.descriptions.de.long, description: config.descriptions.de.long,
publisher: { publisher: { "@id": id.business },
"@type": "Organization",
name: config.brandName,
url: canonicalUrl,
logo: {
"@type": "ImageObject",
url: logoUrl,
},
},
potentialAction: { potentialAction: {
"@type": "SearchAction", "@type": "SearchAction",
target: `${canonicalUrl}/search?q={search_term_string}`, target: `${canonicalUrl}/search?q={search_term_string}`,
query: "required name=search_term_string" query: "required name=search_term_string",
}, },
mainEntity: { mainEntity: { "@id": id.sitemapPage },
"@type": "WebPage", sameAs: [],
name: "Sitemap",
url: `${canonicalUrl}/sitemap`,
description: "Vollständige Sitemap mit allen Kategorien und Seiten",
},
sameAs: [
// Add your social media URLs here if available
],
}; };
// Organization/LocalBusiness Schema for rich results const faqMainEntity = [
const organizationJsonLd = { {
"@context": "https://schema.org", "@type": "Question",
"@type": "LocalBusiness", name: "Welche Zahlungsmethoden akzeptiert GrowHeads?",
"name": config.brandName, acceptedAnswer: {
"alternateName": config.siteName, "@type": "Answer",
"description": config.descriptions.de.long, text: "Wir akzeptieren Kreditkarten, PayPal, Banküberweisung, Nachnahme und Barzahlung bei Abholung in unserem Laden in Dresden.",
"url": canonicalUrl,
"logo": logoUrl,
"image": logoUrl,
"telephone": "015208491860",
"email": "service@growheads.de",
"address": {
"@type": "PostalAddress",
"streetAddress": "Trachenberger Strasse 14",
"addressLocality": "Dresden",
"postalCode": "01129",
"addressCountry": "DE",
"addressRegion": "Sachsen"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": "51.083675",
"longitude": "13.727215"
},
"openingHours": [
"Mo-Fr 10:00:00-20:00:00",
"Sa 11:00:00-19:00:00"
],
"paymentAccepted": "Cash, Credit Card, PayPal, Bank Transfer",
"currenciesAccepted": "EUR",
"priceRange": "€€",
"areaServed": {
"@type": "Country",
"name": "Germany"
},
"contactPoint": [
{
"@type": "ContactPoint",
"telephone": "015208491860",
"contactType": "customer service",
"availableLanguage": "German",
"hoursAvailable": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "10:00:00",
"closes": "20:00:00"
}
}, },
{ },
"@type": "ContactPoint", {
"email": "service@growheads.de", "@type": "Question",
"contactType": "customer service", name: "Liefert GrowHeads deutschlandweit?",
"availableLanguage": "German" acceptedAnswer: {
} "@type": "Answer",
], text: "Ja, wir liefern deutschlandweit. Zusätzlich haben wir einen Laden in Dresden (Trachenberger Strasse 14) für lokale Kunden.",
"sameAs": [ },
// Add social media URLs when available },
// "https://www.facebook.com/growheads", {
// "https://www.instagram.com/growheads" "@type": "Question",
] name: "Welche Produkte bietet GrowHeads?",
}; acceptedAnswer: {
"@type": "Answer",
text: "Wir bieten ein komplettes Sortiment für den Indoor-Anbau: Beleuchtung, Belüftung, Dünger, Töpfe, Zelte, Messgeräte und vieles mehr für professionelle Zuchtanlagen.",
},
},
{
"@type": "Question",
name: "Hat GrowHeads einen physischen Laden?",
acceptedAnswer: {
"@type": "Answer",
text: "Ja, unser Laden befindet sich in Dresden, Trachenberger Strasse 14. Öffnungszeiten: Mo-Fr 10-20 Uhr, Sa 11-19 Uhr. Sie können auch online bestellen.",
},
},
{
"@type": "Question",
name: "Bietet GrowHeads Beratung zum Indoor-Anbau?",
acceptedAnswer: {
"@type": "Answer",
text: "Ja, unser erfahrenes Team berät Sie gerne zu allen Aspekten des Indoor-Anbaus. Kontaktieren Sie uns telefonisch unter 015208491860 oder besuchen Sie unseren Laden.",
},
},
];
// FAQPage Schema for common questions const faqNode = {
const faqJsonLd = { "@id": id.faq,
"@context": "https://schema.org",
"@type": "FAQPage", "@type": "FAQPage",
"mainEntity": [ url: canonicalUrl,
{ publisher: { "@id": id.business },
"@type": "Question", isPartOf: { "@id": id.website },
"name": "Welche Zahlungsmethoden akzeptiert GrowHeads?", mainEntity: faqMainEntity,
"acceptedAnswer": {
"@type": "Answer",
"text": "Wir akzeptieren Kreditkarten, PayPal, Banküberweisung, Nachnahme und Barzahlung bei Abholung in unserem Laden in Dresden."
}
},
{
"@type": "Question",
"name": "Liefert GrowHeads deutschlandweit?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Ja, wir liefern deutschlandweit. Zusätzlich haben wir einen Laden in Dresden (Trachenberger Strasse 14) für lokale Kunden."
}
},
{
"@type": "Question",
"name": "Welche Produkte bietet GrowHeads?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Wir bieten ein komplettes Sortiment für den Indoor-Anbau: Beleuchtung, Belüftung, Dünger, Töpfe, Zelte, Messgeräte und vieles mehr für professionelle Zuchtanlagen."
}
},
{
"@type": "Question",
"name": "Hat GrowHeads einen physischen Laden?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Ja, unser Laden befindet sich in Dresden, Trachenberger Strasse 14. Öffnungszeiten: Mo-Fr 10-20 Uhr, Sa 11-19 Uhr. Sie können auch online bestellen."
}
},
{
"@type": "Question",
"name": "Bietet GrowHeads Beratung zum Indoor-Anbau?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Ja, unser erfahrenes Team berät Sie gerne zu allen Aspekten des Indoor-Anbaus. Kontaktieren Sie uns telefonisch unter 015208491860 oder besuchen Sie unseren Laden."
}
}
]
}; };
// Generate ItemList for all categories (more appropriate for homepage) const filteredCategories = categories.filter((c) => c.seoName);
const categoriesListJsonLd = {
"@context": "https://schema.org", const graph = [
"@type": "ItemList", organizationNode,
"name": "Produktkategorien", websiteNode,
"description": "Alle verfügbaren Produktkategorien in unserem Online-Shop", sitemapWebPageNode,
"numberOfItems": categories.filter(category => category.seoName).length, faqNode,
"itemListElement": categories ];
.filter(category => category.seoName) // Only include categories with seoName
.map((category, index) => ({ if (filteredCategories.length > 0) {
graph.push({
"@id": id.categoryList,
"@type": "ItemList",
name: "Produktkategorien",
description: "Alle verfügbaren Produktkategorien in unserem Online-Shop",
numberOfItems: filteredCategories.length,
isPartOf: { "@id": id.website },
itemListElement: filteredCategories.map((category, index) => ({
"@type": "ListItem", "@type": "ListItem",
"position": index + 1, position: index + 1,
"item": { item: {
"@type": "Thing", "@type": "Thing",
"name": category.name, name: category.name,
"url": `${canonicalUrl}/Kategorie/${category.seoName}` url: `${canonicalUrl}/Kategorie/${category.seoName}`,
} },
})) })),
});
}
const homepageGraph = {
"@context": "https://schema.org",
"@graph": graph,
}; };
// Return all JSON-LD scripts return `<script type="application/ld+json">${JSON.stringify(
const websiteScript = `<script type="application/ld+json">${JSON.stringify(websiteJsonLd)}</script>`; homepageGraph
const organizationScript = `<script type="application/ld+json">${JSON.stringify(organizationJsonLd)}</script>`; )}</script>`;
const faqScript = `<script type="application/ld+json">${JSON.stringify(faqJsonLd)}</script>`;
const categoriesScript = categories.length > 0
? `<script type="application/ld+json">${JSON.stringify(categoriesListJsonLd)}</script>`
: '';
return websiteScript + '\n' + organizationScript + '\n' + faqScript + (categoriesScript ? '\n' + categoriesScript : '');
}; };
module.exports = { module.exports = {

View File

@@ -68,14 +68,15 @@ const generateProductMetaTags = (product, baseUrl, config) => {
}; };
const generateProductJsonLd = (product, baseUrl, config, categoryInfo = null) => { const generateProductJsonLd = (product, baseUrl, config, categoryInfo = null) => {
const productUrl = `${baseUrl}/Artikel/${product.seoName}`; const root = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
const productUrl = `${root}/Artikel/${product.seoName}`;
const pictureFirstId = const pictureFirstId =
product.pictureList && product.pictureList.trim() product.pictureList && product.pictureList.trim()
? product.pictureList.split(",")[0].trim() ? product.pictureList.split(",")[0].trim()
: null; : null;
const imageUrl = pictureFirstId const imageUrl = pictureFirstId
? `${baseUrl}/assets/images/prod${pictureFirstId}.avif` ? `${root}/assets/images/prod${pictureFirstId}.avif`
: `${baseUrl}/assets/images/nopicture.jpg`; : `${root}/assets/images/nopicture.jpg`;
// Clean description for JSON-LD (remove HTML tags) // Clean description for JSON-LD (remove HTML tags)
const cleanDescription = product.description const cleanDescription = product.description
@@ -86,8 +87,87 @@ const generateProductJsonLd = (product, baseUrl, config, categoryInfo = null) =>
const priceValidDate = new Date(); const priceValidDate = new Date();
priceValidDate.setMonth(priceValidDate.getMonth() + 3); priceValidDate.setMonth(priceValidDate.getMonth() + 3);
const productJsonLd = { const id = {
"@context": "https://schema.org/", business: `${root}#business`,
website: `${root}#website`,
product: `${productUrl}#product`,
breadcrumb: `${productUrl}#breadcrumb`,
};
const logoUrl =
config.images && config.images.logo
? `${root}${config.images.logo}`
: undefined;
const businessNode = {
"@id": id.business,
"@type": ["GardenStore", "LocalBusiness", "Organization"],
name: config.brandName,
url: root,
...(logoUrl && {
logo: { "@type": "ImageObject", url: logoUrl },
image: { "@type": "ImageObject", url: logoUrl },
}),
};
const websiteNode = {
"@id": id.website,
"@type": "WebSite",
name: config.siteName || config.brandName,
url: root,
publisher: { "@id": id.business },
};
const offer = {
"@type": "Offer",
url: productUrl,
priceCurrency: config.currency,
price: product.price.toString(),
priceValidUntil: priceValidDate.toISOString().split("T")[0],
itemCondition: "https://schema.org/NewCondition",
availability: product.available
? "https://schema.org/InStock"
: "https://schema.org/OutOfStock",
seller: { "@id": id.business },
hasMerchantReturnPolicy: {
"@type": "MerchantReturnPolicy",
applicableCountry: "DE",
returnPolicyCategory: "https://schema.org/MerchantReturnFiniteReturnWindow",
merchantReturnDays: 14,
returnMethod: "https://schema.org/ReturnByMail",
returnFees: "https://schema.org/FreeReturn",
},
shippingDetails: {
"@type": "OfferShippingDetails",
shippingRate: {
"@type": "MonetaryAmount",
value: 5.9,
currency: "EUR",
},
shippingDestination: {
"@type": "DefinedRegion",
addressCountry: "DE",
},
deliveryTime: {
"@type": "ShippingDeliveryTime",
handlingTime: {
"@type": "QuantitativeValue",
minValue: 0,
maxValue: 1,
unitCode: "DAY",
},
transitTime: {
"@type": "QuantitativeValue",
minValue: 2,
maxValue: 3,
unitCode: "DAY",
},
},
},
};
const productNode = {
"@id": id.product,
"@type": "Product", "@type": "Product",
name: product.name, name: product.name,
image: [imageUrl], image: [imageUrl],
@@ -98,95 +178,65 @@ const generateProductJsonLd = (product, baseUrl, config, categoryInfo = null) =>
"@type": "Brand", "@type": "Brand",
name: product.manufacturer || "Unknown", name: product.manufacturer || "Unknown",
}, },
offers: { offers: offer,
"@type": "Offer",
url: productUrl,
priceCurrency: config.currency,
price: product.price.toString(),
priceValidUntil: priceValidDate.toISOString().split("T")[0],
itemCondition: "https://schema.org/NewCondition",
availability: product.available
? "https://schema.org/InStock"
: "https://schema.org/OutOfStock",
seller: {
"@type": "Organization",
name: config.brandName,
},
hasMerchantReturnPolicy: {
"@type": "MerchantReturnPolicy",
applicableCountry: "DE",
returnPolicyCategory: "https://schema.org/MerchantReturnFiniteReturnWindow",
merchantReturnDays: 14,
returnMethod: "https://schema.org/ReturnByMail",
returnFees: "https://schema.org/FreeReturn",
},
shippingDetails: {
"@type": "OfferShippingDetails",
shippingRate: {
"@type": "MonetaryAmount",
value: 5.90,
currency: "EUR",
},
shippingDestination: {
"@type": "DefinedRegion",
addressCountry: "DE",
},
deliveryTime: {
"@type": "ShippingDeliveryTime",
handlingTime: {
"@type": "QuantitativeValue",
minValue: 0,
maxValue: 1,
unitCode: "DAY",
},
transitTime: {
"@type": "QuantitativeValue",
minValue: 2,
maxValue: 3,
unitCode: "DAY",
},
},
},
},
}; };
const productScript = `<script type="application/ld+json">${JSON.stringify( const hasBreadcrumb =
productJsonLd categoryInfo && categoryInfo.name && categoryInfo.seoName;
const breadcrumbList = hasBreadcrumb
? {
"@id": id.breadcrumb,
"@type": "BreadcrumbList",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "Home",
item: root,
},
{
"@type": "ListItem",
position: 2,
name: categoryInfo.name,
item: `${root}/Kategorie/${categoryInfo.seoName}`,
},
{
"@type": "ListItem",
position: 3,
name: product.name,
item: productUrl,
},
],
}
: null;
const itemPageNode = {
"@id": productUrl,
"@type": "ItemPage",
url: productUrl,
name: product.name,
isPartOf: { "@id": id.website },
mainEntity: { "@id": id.product },
...(hasBreadcrumb && { breadcrumb: { "@id": id.breadcrumb } }),
};
const graph = [
businessNode,
websiteNode,
itemPageNode,
...(breadcrumbList ? [breadcrumbList] : []),
productNode,
];
const productGraph = {
"@context": "https://schema.org",
"@graph": graph,
};
return `<script type="application/ld+json">${JSON.stringify(
productGraph
)}</script>`; )}</script>`;
// BreadcrumbList is not a valid property on Product; emit as its own JSON-LD block (WebPage path context).
if (categoryInfo && categoryInfo.name && categoryInfo.seoName) {
const breadcrumbJsonLd = {
"@context": "https://schema.org/",
"@type": "BreadcrumbList",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "Home",
item: baseUrl,
},
{
"@type": "ListItem",
position: 2,
name: categoryInfo.name,
item: `${baseUrl}/Kategorie/${categoryInfo.seoName}`,
},
{
"@type": "ListItem",
position: 3,
name: product.name,
item: productUrl,
},
],
};
const breadcrumbScript = `<script type="application/ld+json">${JSON.stringify(
breadcrumbJsonLd
)}</script>`;
return `${productScript}\n${breadcrumbScript}`;
}
return productScript;
}; };
module.exports = { module.exports = {