feat: implement programmatic SEO for solutions pages

- Add 10 oil-for-concern solution pages with localized slugs
- Support 4 languages: sr, en, de, fr with proper canonical URLs
- Add solutions hub, by-concern, and by-oil directory pages
- Filter bundle products from solutions pages
- Add hideLangSwitcher prop to Header component
- Update translations for all languages
- Fix canonical URLs to include locale prefix
This commit is contained in:
Unchained
2026-04-08 13:29:42 +02:00
parent cca6f44139
commit 9d07a60d3f
26 changed files with 3120 additions and 1046 deletions

View File

@@ -1,10 +1,10 @@
import {
getOilForConcernPage,
getOilForConcernPageBySlug,
getAllSolutionSlugs,
getLocalizedString,
getLocalizedKeywords
} from "@/lib/programmatic-seo/dataLoader";
import { getProducts } from "@/lib/saleor";
import { getProducts, filterOutBundles } from "@/lib/saleor";
import { OilForConcernPageTemplate } from "@/components/programmatic-seo/OilForConcernPage";
import { FAQSchema } from "@/components/programmatic-seo/FAQSchema";
import { isValidLocale, DEFAULT_LOCALE, type Locale } from "@/lib/i18n/locales";
@@ -24,7 +24,7 @@ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://manoonoils.com";
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { locale, slug } = await params;
const validLocale = isValidLocale(locale) ? locale : DEFAULT_LOCALE;
const page = await getOilForConcernPage(slug);
const page = await getOilForConcernPageBySlug(slug, validLocale);
if (!page) {
return {
@@ -36,7 +36,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
const metaDescription = getLocalizedString(page.metaDescription, validLocale);
const keywords = getLocalizedKeywords(page.seoKeywords, validLocale);
const localePrefix = validLocale === DEFAULT_LOCALE ? "" : `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}/solutions/${page.slug}`;
const canonicalUrl = `${baseUrl}${localePrefix}/solutions/${page.localizedSlugs[validLocale as "sr" | "en" | "de" | "fr"]}`;
return {
title: metaTitle,
@@ -45,10 +45,10 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
alternates: {
canonical: canonicalUrl,
languages: {
"sr": `${baseUrl}/solutions/${page.slug}`,
"en": `${baseUrl}/en/solutions/${page.slug}`,
"de": `${baseUrl}/de/solutions/${page.slug}`,
"fr": `${baseUrl}/fr/solutions/${page.slug}`,
"sr": `${baseUrl}/solutions/${page.localizedSlugs.sr}`,
"en": `${baseUrl}/en/solutions/${page.localizedSlugs.en}`,
"de": `${baseUrl}/de/solutions/${page.localizedSlugs.de}`,
"fr": `${baseUrl}/fr/solutions/${page.localizedSlugs.fr}`,
},
},
openGraph: {
@@ -76,15 +76,18 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
export default async function SolutionPage({ params }: PageProps) {
const { locale, slug } = await params;
const validLocale = isValidLocale(locale) ? locale : DEFAULT_LOCALE;
const [page, products] = await Promise.all([
getOilForConcernPage(slug),
getProducts(validLocale === "sr" ? "SR" : "EN", 4)
const [page, allProducts] = await Promise.all([
getOilForConcernPageBySlug(slug, validLocale),
getProducts(validLocale === "sr" ? "SR" : "EN", 20)
]);
if (!page) {
notFound();
}
// Filter out bundle products (2x, 3x versions) - only show original 4 products
const products = filterOutBundles(allProducts).slice(0, 4);
const basePath = validLocale === DEFAULT_LOCALE ? "" : `/${validLocale}`;
const faqQuestions = page.faqs.map((faq) => ({