feat: implement programmatic SEO solutions hub

- Add /solutions hub page with 10 category cards
- Add /solutions/by-concern directory page
- Add /solutions/by-oil directory page
- Add Solutions section to Footer with navigation links
- Add Breadcrumb component for solution pages
- Add translations for all solution pages (sr, en, de, fr)
- Fix ExitIntentDetector JSON parsing error
- Update sitemap with solution pages
- Create 3 sample solution pages with data files
This commit is contained in:
Unchained
2026-04-05 05:21:57 +02:00
parent 6caefb420a
commit f6609f07d7
22 changed files with 3263 additions and 8 deletions

View File

@@ -0,0 +1,291 @@
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";
type Params = Promise<{ locale: string }>;
export async function generateMetadata({
params,
}: {
params: Params;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "Solutions.Hub" });
return {
title: t("metaTitle"),
description: t("metaDescription"),
};
}
interface CategoryCardProps {
title: string;
description: string;
href: string;
icon: React.ReactNode;
priority?: boolean;
}
function CategoryCard({ title, description, href, icon, priority }: CategoryCardProps) {
return (
<Link
href={href}
className={`group block p-6 lg:p-8 border border-[#e5e5e5] rounded-lg hover:border-black transition-all duration-300 hover:shadow-lg ${
priority ? "bg-gradient-to-br from-amber-50/50 to-white" : "bg-white"
}`}
>
<div className="flex items-start gap-4">
<div className={`flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center ${
priority ? "bg-amber-100 text-amber-700" : "bg-[#f5f5f5] text-[#666666] group-hover:bg-black group-hover:text-white"
} transition-colors`}>
{icon}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-2">
<h3 className="text-lg font-medium text-[#1a1a1a] group-hover:text-black transition-colors">
{title}
</h3>
{priority && (
<span className="px-2 py-0.5 text-[10px] uppercase tracking-wider font-medium bg-amber-100 text-amber-700 rounded-full">
Popular
</span>
)}
</div>
<p className="text-sm text-[#666666] leading-relaxed mb-4">
{description}
</p>
<span className="inline-flex items-center text-sm font-medium text-[#1a1a1a] group-hover:text-black transition-colors">
{priority ? "Explore Solutions" : "Learn More"}
<ArrowRight className="ml-1 w-4 h-4 transform group-hover:translate-x-1 transition-transform" />
</span>
</div>
</div>
</Link>
);
}
interface QuickLinkProps {
title: string;
href: string;
count?: number;
}
function QuickLink({ title, href, count }: QuickLinkProps) {
return (
<Link
href={href}
className="flex items-center justify-between p-4 border-b border-[#e5e5e5] hover:bg-[#fafafa] transition-colors group"
>
<span className="text-[#1a1a1a] group-hover:text-black transition-colors">
{title}
</span>
<div className="flex items-center gap-2">
{count !== undefined && (
<span className="text-xs text-[#999999]">{count} solutions</span>
)}
<ChevronRight className="w-4 h-4 text-[#999999] group-hover:text-black transition-colors" />
</div>
</Link>
);
}
export default async function SolutionsHubPage({
params,
}: {
params: Params;
}) {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "Solutions" });
const hubT = await getTranslations({ locale, namespace: "Solutions.Hub" });
const categories = [
{
title: hubT("categories.oilForConcern.title"),
description: hubT("categories.oilForConcern.description"),
href: `/${locale}/solutions/by-concern`,
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>
<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>
<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.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>
</div>
</div>
</div>
</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>
</div>
</div>
</section>
</div>
);
}