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) => ({

View File

@@ -1,8 +1,11 @@
import { Metadata } from "next";
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { ChevronRight, Search } from "lucide-react";
import { ChevronRight, Search, ArrowRight } from "lucide-react";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import { getAllOilForConcernPages, getLocalizedString } from "@/lib/programmatic-seo/dataLoader";
import { isValidLocale, DEFAULT_LOCALE } from "@/lib/i18n/locales";
type Params = Promise<{ locale: string }>;
@@ -12,64 +15,25 @@ export async function generateMetadata({
params: Params;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "Solutions.ByConcern" });
const validLocale = isValidLocale(locale) ? locale : DEFAULT_LOCALE;
const t = await getTranslations({ locale: validLocale, namespace: "Solutions.ByConcern" });
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://manoonoils.com";
const localePrefix = validLocale === DEFAULT_LOCALE ? "/sr" : `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}/solutions/by-concern`;
return {
title: t("metaTitle"),
description: t("metaDescription"),
alternates: {
canonical: canonicalUrl,
},
openGraph: {
url: canonicalUrl,
},
};
}
function groupByConcern(pages: Awaited<ReturnType<typeof getAllOilForConcernPages>>) {
const concerns = new Map<string, typeof pages>();
pages.forEach((page) => {
const concernSlug = page.concernSlug;
if (!concerns.has(concernSlug)) {
concerns.set(concernSlug, []);
}
concerns.get(concernSlug)?.push(page);
});
return concerns;
}
interface ConcernCardProps {
concernSlug: string;
concernName: string;
oilCount: number;
topOils: string[];
locale: string;
}
function ConcernCard({ concernSlug, concernName, oilCount, topOils, locale }: ConcernCardProps) {
return (
<div className="border border-[#e5e5e5] rounded-lg p-6 hover:border-black transition-colors group">
<h3 className="text-lg font-medium text-[#1a1a1a] mb-2">
{concernName}
</h3>
<p className="text-sm text-[#666666] mb-4">
{oilCount} {oilCount === 1 ? "oil solution" : "oil solutions"} available
</p>
<div className="space-y-2 mb-4">
{topOils.slice(0, 3).map((oilName) => (
<div key={oilName} className="flex items-center gap-2 text-sm text-[#666666]">
<div className="w-1.5 h-1.5 rounded-full bg-amber-400" />
{oilName}
</div>
))}
</div>
<Link
href={`/${locale}/solutions/by-concern/${concernSlug}`}
className="inline-flex items-center text-sm font-medium text-[#1a1a1a] group-hover:text-black transition-colors"
>
View All Solutions
<ChevronRight className="ml-1 w-4 h-4 transform group-hover:translate-x-1 transition-transform" />
</Link>
</div>
);
}
export default async function ByConcernPage({
params,
}: {
@@ -80,75 +44,90 @@ export default async function ByConcernPage({
const pageT = await getTranslations({ locale, namespace: "Solutions.ByConcern" });
const pages = await getAllOilForConcernPages();
const concernsMap = groupByConcern(pages);
const concernsList = Array.from(concernsMap.entries())
.map(([slug, pages]) => ({
slug,
name: getLocalizedString(pages[0].concernName, locale),
oilCount: pages.length,
topOils: pages.slice(0, 3).map((p) => getLocalizedString(p.oilName, locale)),
}))
.sort((a, b) => a.name.localeCompare(b.name));
const sortedPages = pages.sort((a, b) => {
const aName = getLocalizedString(a.concernName, locale);
const bName = getLocalizedString(b.concernName, locale);
return aName.localeCompare(bName);
});
return (
<div className="min-h-screen bg-white">
<section className="pt-32 pb-16 lg:pt-40 lg:pb-24">
<div className="container">
<nav className="flex items-center gap-2 text-sm text-[#666666] mb-8">
<Link href={`/${locale}`} className="hover:text-black transition-colors">
{t("breadcrumb.home")}
</Link>
<ChevronRight className="w-4 h-4" />
<Link href={`/${locale}/solutions`} className="hover:text-black transition-colors">
{t("breadcrumb.solutions")}
</Link>
<ChevronRight className="w-4 h-4" />
<span className="text-[#1a1a1a]">{t("breadcrumb.byConcern")}</span>
</nav>
<>
<Header locale={locale} hideLangSwitcher={true} />
<div className="min-h-screen bg-white">
<section className="pt-32 pb-16 lg:pt-40 lg:pb-24">
<div className="container">
<nav className="flex items-center gap-2 text-sm text-[#666666] mb-8">
<Link href={`/${locale}`} className="hover:text-black transition-colors">
{t("breadcrumb.home")}
</Link>
<ChevronRight className="w-4 h-4" />
<Link href={`/${locale}/solutions`} className="hover:text-black transition-colors">
{t("breadcrumb.solutions")}
</Link>
<ChevronRight className="w-4 h-4" />
<span className="text-[#1a1a1a]">{t("breadcrumb.byConcern")}</span>
</nav>
<div className="max-w-3xl mb-12">
<h1 className="text-4xl lg:text-5xl font-medium tracking-tight text-[#1a1a1a] mb-6">
{pageT("title")}
</h1>
<p className="text-lg text-[#666666] leading-relaxed">
{pageT("subtitle")}
</p>
</div>
<div className="bg-[#fafafa] border border-[#e5e5e5] rounded-lg p-6 mb-12">
<div className="flex items-center gap-3 text-[#666666]">
<Search className="w-5 h-5" />
<span className="text-sm">
{pageT("stats.availableConcerns", { count: concernsList.length })}
</span>
<span className="text-[#e5e5e5]">|</span>
<span className="text-sm">
{pageT("stats.totalSolutions", { count: pages.length })}
</span>
<div className="max-w-3xl mb-12">
<h1 className="text-4xl lg:text-5xl font-medium tracking-tight text-[#1a1a1a] mb-6">
{pageT("title")}
</h1>
<p className="text-lg text-[#666666] leading-relaxed">
{pageT("subtitle")}
</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{concernsList.map((concern) => (
<ConcernCard
key={concern.slug}
concernSlug={concern.slug}
concernName={concern.name}
oilCount={concern.oilCount}
topOils={concern.topOils}
locale={locale}
/>
))}
</div>
{concernsList.length === 0 && (
<div className="text-center py-16">
<p className="text-[#666666]">{pageT("noResults")}</p>
<div className="bg-[#fafafa] border border-[#e5e5e5] rounded-lg p-6 mb-12">
<div className="flex items-center gap-3 text-[#666666]">
<Search className="w-5 h-5" />
<span className="text-sm">
{pageT("stats.solutionsAvailable", { count: sortedPages.length })}
</span>
</div>
</div>
)}
</div>
</section>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{sortedPages.map((page) => {
const pageTitle = getLocalizedString(page.pageTitle, locale);
const oilName = getLocalizedString(page.oilName, locale);
const concernName = getLocalizedString(page.concernName, locale);
return (
<Link
key={page.slug}
href={`/${locale}/solutions/${page.localizedSlugs[locale as "sr" | "en" | "de" | "fr"]}`}
className="border border-[#e5e5e5] rounded-lg p-6 hover:border-black transition-colors group"
>
<div className="flex items-center gap-2 mb-3">
<span className="text-xs px-2 py-1 bg-amber-100 text-amber-700 rounded-full">
{concernName}
</span>
</div>
<h3 className="text-lg font-medium text-[#1a1a1a] mb-2 group-hover:text-black transition-colors">
{pageTitle}
</h3>
<p className="text-sm text-[#666666] mb-4">
{oilName}
</p>
<span className="inline-flex items-center text-sm font-medium text-[#1a1a1a] group-hover:text-black transition-colors">
{pageT("viewSolution")}
<ArrowRight className="ml-1 w-4 h-4 transform group-hover:translate-x-1 transition-transform" />
</span>
</Link>
);
})}
</div>
{sortedPages.length === 0 && (
<div className="text-center py-16">
<p className="text-[#666666]">{pageT("noResults")}</p>
</div>
)}
</div>
</section>
</div>
<Footer locale={locale} />
</>
);
}

View File

@@ -2,7 +2,10 @@ import { Metadata } from "next";
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { ChevronRight, Droplets } from "lucide-react";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import { getAllOilForConcernPages, getLocalizedString } from "@/lib/programmatic-seo/dataLoader";
import { isValidLocale, DEFAULT_LOCALE } from "@/lib/i18n/locales";
type Params = Promise<{ locale: string }>;
@@ -12,11 +15,22 @@ export async function generateMetadata({
params: Params;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "Solutions.ByOil" });
const validLocale = isValidLocale(locale) ? locale : DEFAULT_LOCALE;
const t = await getTranslations({ locale: validLocale, namespace: "Solutions.ByOil" });
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://manoonoils.com";
const localePrefix = validLocale === DEFAULT_LOCALE ? "/sr" : `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}/solutions/by-oil`;
return {
title: t("metaTitle"),
description: t("metaDescription"),
alternates: {
canonical: canonicalUrl,
},
openGraph: {
url: canonicalUrl,
},
};
}
@@ -40,9 +54,25 @@ interface OilCardProps {
concernCount: number;
topConcerns: string[];
locale: string;
firstSolutionSlug: string;
bestFor: string;
exploreSolutions: string;
solutionLabel: string;
solutionsLabel: string;
}
function OilCard({ oilSlug, oilName, concernCount, topConcerns, locale }: OilCardProps) {
function OilCard({
oilSlug,
oilName,
concernCount,
topConcerns,
locale,
firstSolutionSlug,
bestFor,
exploreSolutions,
solutionLabel,
solutionsLabel
}: OilCardProps) {
return (
<div className="border border-[#e5e5e5] rounded-lg p-6 hover:border-black transition-colors group">
<div className="flex items-center gap-3 mb-4">
@@ -54,11 +84,11 @@ function OilCard({ oilSlug, oilName, concernCount, topConcerns, locale }: OilCar
</h3>
</div>
<p className="text-sm text-[#666666] mb-4">
{concernCount} {concernCount === 1 ? "concern solution" : "concern solutions"} available
{concernCount} {concernCount === 1 ? solutionLabel : solutionsLabel}
</p>
<div className="space-y-2 mb-4">
<p className="text-xs uppercase tracking-wider text-[#999999] font-medium">
Best for:
{bestFor}
</p>
{topConcerns.slice(0, 3).map((concernName) => (
<div key={concernName} className="flex items-center gap-2 text-sm text-[#666666]">
@@ -68,10 +98,10 @@ function OilCard({ oilSlug, oilName, concernCount, topConcerns, locale }: OilCar
))}
</div>
<Link
href={`/${locale}/solutions/by-oil/${oilSlug}`}
href={`/${locale}/solutions/${firstSolutionSlug}`}
className="inline-flex items-center text-sm font-medium text-[#1a1a1a] group-hover:text-black transition-colors"
>
Explore Oil Solutions
{exploreSolutions}
<ChevronRight className="ml-1 w-4 h-4 transform group-hover:translate-x-1 transition-transform" />
</Link>
</div>
@@ -96,70 +126,80 @@ export default async function ByOilPage({
name: getLocalizedString(pages[0].oilName, locale),
concernCount: pages.length,
topConcerns: pages.slice(0, 3).map((p) => getLocalizedString(p.concernName, locale)),
firstSolutionSlug: getLocalizedString(pages[0].localizedSlugs, locale),
}))
.sort((a, b) => a.name.localeCompare(b.name));
return (
<div className="min-h-screen bg-white">
<section className="pt-32 pb-16 lg:pt-40 lg:pb-24">
<div className="container">
<nav className="flex items-center gap-2 text-sm text-[#666666] mb-8">
<Link href={`/${locale}`} className="hover:text-black transition-colors">
{t("breadcrumb.home")}
</Link>
<ChevronRight className="w-4 h-4" />
<Link href={`/${locale}/solutions`} className="hover:text-black transition-colors">
{t("breadcrumb.solutions")}
</Link>
<ChevronRight className="w-4 h-4" />
<span className="text-[#1a1a1a]">{t("breadcrumb.byOil")}</span>
</nav>
<>
<Header locale={locale} hideLangSwitcher={true} />
<div className="min-h-screen bg-white">
<section className="pt-32 pb-16 lg:pt-40 lg:pb-24">
<div className="container">
<nav className="flex items-center gap-2 text-sm text-[#666666] mb-8">
<Link href={`/${locale}`} className="hover:text-black transition-colors">
{t("breadcrumb.home")}
</Link>
<ChevronRight className="w-4 h-4" />
<Link href={`/${locale}/solutions`} className="hover:text-black transition-colors">
{t("breadcrumb.solutions")}
</Link>
<ChevronRight className="w-4 h-4" />
<span className="text-[#1a1a1a]">{t("breadcrumb.byOil")}</span>
</nav>
<div className="max-w-3xl mb-12">
<h1 className="text-4xl lg:text-5xl font-medium tracking-tight text-[#1a1a1a] mb-6">
{pageT("title")}
</h1>
<p className="text-lg text-[#666666] leading-relaxed">
{pageT("subtitle")}
</p>
</div>
<div className="max-w-3xl mb-12">
<h1 className="text-4xl lg:text-5xl font-medium tracking-tight text-[#1a1a1a] mb-6">
{pageT("title")}
</h1>
<p className="text-lg text-[#666666] leading-relaxed">
{pageT("subtitle")}
</p>
</div>
<div className="bg-gradient-to-r from-amber-50 to-emerald-50 border border-[#e5e5e5] rounded-lg p-6 mb-12">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-white flex items-center justify-center shadow-sm">
<Droplets className="w-6 h-6 text-amber-600" />
</div>
<div>
<p className="text-sm text-[#666666]">
{pageT("stats.availableOils", { count: oilsList.length })}
</p>
<p className="text-sm text-[#666666]">
{pageT("stats.totalSolutions", { count: pages.length })}
</p>
<div className="bg-gradient-to-r from-amber-50 to-emerald-50 border border-[#e5e5e5] rounded-lg p-6 mb-12">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-white flex items-center justify-center shadow-sm">
<Droplets className="w-6 h-6 text-amber-600" />
</div>
<div>
<p className="text-sm text-[#666666]">
{pageT("stats.availableOils", { count: oilsList.length })}
</p>
<p className="text-sm text-[#666666]">
{pageT("stats.totalSolutions", { count: pages.length })}
</p>
</div>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{oilsList.map((oil) => (
<OilCard
key={oil.slug}
oilSlug={oil.slug}
oilName={oil.name}
concernCount={oil.concernCount}
topConcerns={oil.topConcerns}
locale={locale}
/>
))}
</div>
{oilsList.length === 0 && (
<div className="text-center py-16">
<p className="text-[#666666]">{pageT("noResults")}</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{oilsList.map((oil) => (
<OilCard
key={oil.slug}
oilSlug={oil.slug}
oilName={oil.name}
concernCount={oil.concernCount}
topConcerns={oil.topConcerns}
locale={locale}
firstSolutionSlug={oil.firstSolutionSlug}
bestFor={pageT("oilCard.bestFor")}
exploreSolutions={pageT("oilCard.exploreSolutions")}
solutionLabel={pageT("oilCard.solutionLabel")}
solutionsLabel={pageT("oilCard.solutionsLabel")}
/>
))}
</div>
)}
</div>
</section>
</div>
{oilsList.length === 0 && (
<div className="text-center py-16">
<p className="text-[#666666]">{pageT("noResults")}</p>
</div>
)}
</div>
</section>
</div>
<Footer locale={locale} />
</>
);
}

View File

@@ -1,7 +1,10 @@
import { Metadata } from "next";
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { ChevronRight, Sparkles, Heart, Leaf, Sun, Moon, Clock, Globe, Users, Droplets, ArrowRight } from "lucide-react";
import { ChevronRight, Droplets, ArrowRight } from "lucide-react";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import { isValidLocale, DEFAULT_LOCALE } from "@/lib/i18n/locales";
type Params = Promise<{ locale: string }>;
@@ -11,11 +14,22 @@ export async function generateMetadata({
params: Params;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "Solutions.Hub" });
const validLocale = isValidLocale(locale) ? locale : DEFAULT_LOCALE;
const t = await getTranslations({ locale: validLocale, namespace: "Solutions.Hub" });
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://manoonoils.com";
const localePrefix = validLocale === DEFAULT_LOCALE ? "/sr" : `/${validLocale}`;
const canonicalUrl = `${baseUrl}${localePrefix}/solutions`;
return {
title: t("metaTitle"),
description: t("metaDescription"),
alternates: {
canonical: canonicalUrl,
},
openGraph: {
url: canonicalUrl,
},
};
}
@@ -107,185 +121,103 @@ export default async function SolutionsHubPage({
icon: <Droplets className="w-5 h-5" />,
priority: true,
},
{
title: hubT("categories.ageSkinRoutine.title"),
description: hubT("categories.ageSkinRoutine.description"),
href: `/${locale}/solutions/age-skin-routine`,
icon: <Clock className="w-5 h-5" />,
},
{
title: hubT("categories.ingredientPairings.title"),
description: hubT("categories.ingredientPairings.description"),
href: `/${locale}/solutions/ingredient-pairings`,
icon: <Sparkles className="w-5 h-5" />,
},
{
title: hubT("categories.bodyPartConcerns.title"),
description: hubT("categories.bodyPartConcerns.description"),
href: `/${locale}/solutions/body-part-concerns`,
icon: <Heart className="w-5 h-5" />,
},
{
title: hubT("categories.oilComparisons.title"),
description: hubT("categories.oilComparisons.description"),
href: `/${locale}/solutions/oil-comparisons`,
icon: <Users className="w-5 h-5" />,
},
{
title: hubT("categories.routineStepSkinType.title"),
description: hubT("categories.routineStepSkinType.description"),
href: `/${locale}/solutions/routine-step-skin-type`,
icon: <Leaf className="w-5 h-5" />,
},
{
title: hubT("categories.seasonalSkincare.title"),
description: hubT("categories.seasonalSkincare.description"),
href: `/${locale}/solutions/seasonal-skincare`,
icon: <Sun className="w-5 h-5" />,
},
{
title: hubT("categories.timeOfDayConcerns.title"),
description: hubT("categories.timeOfDayConcerns.description"),
href: `/${locale}/solutions/time-of-day-concerns`,
icon: <Moon className="w-5 h-5" />,
},
{
title: hubT("categories.naturalAlternatives.title"),
description: hubT("categories.naturalAlternatives.description"),
href: `/${locale}/solutions/natural-alternatives`,
icon: <Leaf className="w-5 h-5" />,
},
{
title: hubT("categories.culturalBeautySecrets.title"),
description: hubT("categories.culturalBeautySecrets.description"),
href: `/${locale}/solutions/cultural-beauty-secrets`,
icon: <Globe className="w-5 h-5" />,
},
];
return (
<div className="min-h-screen bg-white">
<section className="pt-32 pb-16 lg:pt-40 lg:pb-24">
<div className="container">
<nav className="flex items-center gap-2 text-sm text-[#666666] mb-8">
<Link href={`/${locale}`} className="hover:text-black transition-colors">
{t("breadcrumb.home")}
</Link>
<ChevronRight className="w-4 h-4" />
<span className="text-[#1a1a1a]">{t("breadcrumb.solutions")}</span>
</nav>
<>
<Header locale={locale} hideLangSwitcher={true} />
<div className="min-h-screen bg-white">
<section className="pt-32 pb-16 lg:pt-40 lg:pb-24">
<div className="container">
<nav className="flex items-center gap-2 text-sm text-[#666666] mb-8">
<Link href={`/${locale}`} className="hover:text-black transition-colors">
{t("breadcrumb.home")}
</Link>
<ChevronRight className="w-4 h-4" />
<span className="text-[#1a1a1a]">{t("breadcrumb.solutions")}</span>
</nav>
<div className="max-w-3xl">
<h1 className="text-4xl lg:text-5xl font-medium tracking-tight text-[#1a1a1a] mb-6">
{hubT("title")}
</h1>
<p className="text-lg text-[#666666] leading-relaxed">
{hubT("subtitle")}
</p>
</div>
</div>
</section>
<section className="pb-16 lg:pb-24">
<div className="container">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 lg:gap-6">
{categories.map((category) => (
<CategoryCard key={category.href} {...category} />
))}
</div>
</div>
</section>
<section className="pb-16 lg:pb-24">
<div className="container">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12">
<div className="border border-[#e5e5e5] rounded-lg overflow-hidden">
<div className="p-6 bg-[#fafafa] border-b border-[#e5e5e5]">
<h2 className="text-lg font-medium text-[#1a1a1a]">
{hubT("quickAccess.byConcern")}
</h2>
<p className="text-sm text-[#666666] mt-1">
{hubT("quickAccess.byConcernDesc")}
</p>
</div>
<div className="divide-y divide-[#e5e5e5]">
<QuickLink
title={hubT("quickAccess.links.wrinkles")}
href={`/${locale}/solutions/by-concern/wrinkles`}
/>
<QuickLink
title={hubT("quickAccess.links.acne")}
href={`/${locale}/solutions/by-concern/acne`}
/>
<QuickLink
title={hubT("quickAccess.links.drySkin")}
href={`/${locale}/solutions/by-concern/dry-skin`}
/>
<QuickLink
title={hubT("quickAccess.links.darkSpots")}
href={`/${locale}/solutions/by-concern/dark-spots`}
/>
<QuickLink
title={hubT("quickAccess.links.viewAll")}
href={`/${locale}/solutions/by-concern`}
/>
</div>
<div className="max-w-3xl">
<h1 className="text-4xl lg:text-5xl font-medium tracking-tight text-[#1a1a1a] mb-6">
{hubT("title")}
</h1>
<p className="text-lg text-[#666666] leading-relaxed">
{hubT("subtitle")}
</p>
</div>
</div>
</section>
<div className="border border-[#e5e5e5] rounded-lg overflow-hidden">
<div className="p-6 bg-[#fafafa] border-b border-[#e5e5e5]">
<h2 className="text-lg font-medium text-[#1a1a1a]">
{hubT("quickAccess.byOil")}
</h2>
<p className="text-sm text-[#666666] mt-1">
{hubT("quickAccess.byOilDesc")}
</p>
<section className="pb-16 lg:pb-24">
<div className="container">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 lg:gap-6">
{categories.map((category) => (
<CategoryCard key={category.href} {...category} />
))}
</div>
</div>
</section>
<section className="pb-16 lg:pb-24">
<div className="container">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12">
<div className="border border-[#e5e5e5] rounded-lg overflow-hidden">
<div className="p-6 bg-[#fafafa] border-b border-[#e5e5e5]">
<h2 className="text-lg font-medium text-[#1a1a1a]">
{hubT("quickAccess.byConcern")}
</h2>
<p className="text-sm text-[#666666] mt-1">
{hubT("quickAccess.byConcernDesc")}
</p>
</div>
<div className="divide-y divide-[#e5e5e5]">
<QuickLink
title={hubT("quickAccess.links.viewAll")}
href={`/${locale}/solutions/by-concern`}
/>
</div>
</div>
<div className="divide-y divide-[#e5e5e5]">
<QuickLink
title={hubT("quickAccess.links.rosehipOil")}
href={`/${locale}/solutions/by-oil/rosehip-oil`}
/>
<QuickLink
title={hubT("quickAccess.links.arganOil")}
href={`/${locale}/solutions/by-oil/argan-oil`}
/>
<QuickLink
title={hubT("quickAccess.links.jojobaOil")}
href={`/${locale}/solutions/by-oil/jojoba-oil`}
/>
<QuickLink
title={hubT("quickAccess.links.seaBuckthornOil")}
href={`/${locale}/solutions/by-oil/sea-buckthorn-oil`}
/>
<QuickLink
title={hubT("quickAccess.links.viewAll")}
href={`/${locale}/solutions/by-oil`}
/>
<div className="border border-[#e5e5e5] rounded-lg overflow-hidden">
<div className="p-6 bg-[#fafafa] border-b border-[#e5e5e5]">
<h2 className="text-lg font-medium text-[#1a1a1a]">
{hubT("quickAccess.byOil")}
</h2>
<p className="text-sm text-[#666666] mt-1">
{hubT("quickAccess.byOilDesc")}
</p>
</div>
<div className="divide-y divide-[#e5e5e5]">
<QuickLink
title={hubT("quickAccess.links.viewAll")}
href={`/${locale}/solutions/by-oil`}
/>
</div>
</div>
</div>
</div>
</div>
</section>
</section>
<section className="pb-16 lg:pb-24">
<div className="container">
<div className="bg-[#1a1a1a] rounded-2xl p-8 lg:p-12 text-center">
<h2 className="text-2xl lg:text-3xl font-medium text-white mb-4">
{hubT("cta.title")}
</h2>
<p className="text-[#999999] max-w-xl mx-auto mb-8">
{hubT("cta.description")}
</p>
<Link
href={`/${locale}/products`}
className="inline-flex items-center justify-center px-8 py-3 bg-white text-[#1a1a1a] font-medium rounded-full hover:bg-[#f5f5f5] transition-colors"
>
{hubT("cta.button")}
</Link>
<section className="pb-16 lg:pb-24">
<div className="container">
<div className="bg-[#1a1a1a] rounded-2xl p-8 lg:p-12 text-center">
<h2 className="text-2xl lg:text-3xl font-medium text-white mb-4">
{hubT("cta.title")}
</h2>
<p className="text-[#999999] max-w-xl mx-auto mb-8">
{hubT("cta.description")}
</p>
<Link
href={`/${locale}/products`}
className="inline-flex items-center justify-center px-8 py-3 bg-white text-[#1a1a1a] font-medium rounded-full hover:bg-[#f5f5f5] transition-colors"
>
{hubT("cta.button")}
</Link>
</div>
</div>
</div>
</section>
</div>
</section>
</div>
<Footer locale={locale} />
</>
);
}