/** Safe for double-quoted HTML attributes */
const escAttr = (str) =>
String(str ?? "")
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/ {
const root = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
const categoryUrl = `${root}/Kategorie/${category.seoName}`;
const name = category.name || `Kategorie ${category.seoName}`;
const site = config.siteName || config.brandName;
const desc = `${name} bei ${config.brandName}: Growshop-Sortiment online kaufen. Schnelle Lieferung, Laden Dresden.`;
const descShort = desc.length > 160 ? `${desc.slice(0, 157)}...` : desc;
const e = escAttr;
const logoUrl =
config.images && config.images.logo
? `${root}${config.images.logo}`
: `${root}/assets/images/nopicture.jpg`;
return `
`;
};
const generateCategoryJsonLd = (category, products = [], baseUrl, config) => {
// Category IDs to skip (seeds, plants, headshop items)
const skipCategoryIds = [689, 706, 709, 711, 714, 748, 749, 896, 710, 924, 923, 922, 921, 916, 278, 259, 258];
// Check if category ID is in skip list
if (category.id && skipCategoryIds.includes(parseInt(category.id))) {
return '';
}
const root = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
const categoryUrl = `${root}/Kategorie/${category.seoName}`;
const id = {
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",
name: category.name,
url: categoryUrl,
description: `${category.name} - Entdecken Sie unsere Auswahl an hochwertigen Produkten`,
isPartOf: { "@id": id.website },
breadcrumb: { "@id": id.breadcrumb },
};
const graph = [businessNode, websiteNode, breadcrumbNode, collectionPageNode];
// ItemList: URLs only — full Product/Offer markup belongs on each /Artikel/… page (Google guidelines).
const withUrls = (products || []).filter((p) => p && p.seoName);
if (withUrls.length > 0) {
collectionPageNode.mainEntity = { "@id": id.itemList };
graph.push({
"@id": id.itemList,
"@type": "ItemList",
numberOfItems: withUrls.length,
itemListElement: withUrls.map((product, index) => ({
"@type": "ListItem",
position: index + 1,
item: `${root}/Artikel/${product.seoName}`,
})),
});
}
const categoryGraph = {
"@context": "https://schema.org",
"@graph": graph,
};
return ``;
};
module.exports = {
generateCategoryMetaTags,
generateCategoryJsonLd,
};