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,79 @@
import { readFile } from "fs/promises";
import { join } from "path";
import type {
OilForConcernPage,
LocalizedSEOKeywords
} from "./types";
const DATA_DIR = join(process.cwd(), "data");
export function getLocalizedString(
localized: { sr: string; en: string; de: string; fr: string },
locale: string
): string {
return localized[locale as keyof typeof localized] || localized.en;
}
export function getLocalizedArray(
localized: { sr: string[]; en: string[]; de: string[]; fr: string[] },
locale: string
): string[] {
return localized[locale as keyof typeof localized] || localized.en;
}
export function getLocalizedKeywords(
seoKeywords: LocalizedSEOKeywords,
locale: string
): string[] {
const keywords = seoKeywords[locale as keyof LocalizedSEOKeywords] || seoKeywords.en;
return [...keywords.primary, ...keywords.secondary, ...keywords.longTail];
}
export async function getOilForConcernPage(slug: string): Promise<OilForConcernPage | null> {
try {
const filePath = join(DATA_DIR, "oil-for-concern", `${slug}.json`);
const content = await readFile(filePath, "utf-8");
return JSON.parse(content) as OilForConcernPage;
} catch (error) {
console.error(`Failed to load oil-for-concern page: ${slug}`, error);
return null;
}
}
export async function getAllOilForConcernSlugs(): Promise<string[]> {
try {
const { readdir } = await import("fs/promises");
const dirPath = join(DATA_DIR, "oil-for-concern");
const files = await readdir(dirPath);
return files
.filter((file) => file.endsWith(".json"))
.map((file) => file.replace(".json", ""));
} catch (error) {
console.error("Failed to load oil-for-concern slugs:", error);
return [];
}
}
export async function getAllOilForConcernPages(): Promise<OilForConcernPage[]> {
const slugs = await getAllOilForConcernSlugs();
const pages = await Promise.all(
slugs.map((slug) => getOilForConcernPage(slug))
);
return pages.filter((page): page is OilForConcernPage => page !== null);
}
export async function getAllSolutionSlugs(): Promise<Array<{ locale: string; slug: string }>> {
const slugs = await getAllOilForConcernSlugs();
const result: Array<{ locale: string; slug: string }> = [];
for (const slug of slugs) {
for (const locale of ["sr", "en", "de", "fr"] as const) {
result.push({
locale,
slug
});
}
}
return result;
}