Add sitemap and portfolio category SEO routes
CI / quality (push) Waiting to run

This commit is contained in:
MOH
2026-03-11 23:13:36 +01:00
parent cff416dd83
commit 64976d6e12
8 changed files with 360 additions and 81 deletions
@@ -0,0 +1,91 @@
import type { Metadata } from "next";
import { getTranslations } from "next-intl/server";
import { notFound } from "next/navigation";
import { Container } from "@/components/layout/container";
import { PageHero } from "@/components/layout/page-hero";
import { PortfolioCategoryFilter } from "@/components/site/portfolio-category-filter";
import { PortfolioProjectGrid } from "@/components/site/portfolio-project-grid";
import { buildLocalizedMetadata } from "@/lib/metadata";
import { resolveLocale } from "@/lib/locale";
import {
getActivePortfolioCategories,
getActivePortfolioCategoryBySlug,
getLocalizedValue,
getPublishedPortfolioProjects,
} from "@/lib/portfolio";
type PortfolioCategoryPageProps = {
params: {
locale: string;
slug: string;
};
};
export const dynamic = "force-dynamic";
export async function generateMetadata({
params: { locale, slug },
}: PortfolioCategoryPageProps): Promise<Metadata> {
const localeKey = resolveLocale(locale);
const t = await getTranslations({ locale: localeKey, namespace: "portfolioPage" });
const category = await getActivePortfolioCategoryBySlug(slug);
if (!category) {
return await buildLocalizedMetadata({
locale: localeKey,
pathname: `/portfolio/category/${slug}`,
title: t("title"),
description: t("intro"),
});
}
return await buildLocalizedMetadata({
locale: localeKey,
pathname: `/portfolio/category/${slug}`,
title: getLocalizedValue(category.name, localeKey),
description: getLocalizedValue(category.description, localeKey) || t("intro"),
});
}
export default async function PortfolioCategoryPage({
params: { locale, slug },
}: PortfolioCategoryPageProps) {
const localeKey = resolveLocale(locale);
const t = await getTranslations({ locale: localeKey, namespace: "portfolioPage" });
const [categories, category, projects] = await Promise.all([
getActivePortfolioCategories(),
getActivePortfolioCategoryBySlug(slug),
getPublishedPortfolioProjects({ categorySlug: slug }),
]);
if (!category) {
notFound();
}
return (
<>
<PageHero
locale={localeKey}
badge={t("heroBadge")}
title={getLocalizedValue(category.name, localeKey)}
description={getLocalizedValue(category.description, localeKey) || t("intro")}
/>
<Container className="flex flex-col gap-section pb-12 lg:pb-16">
<PortfolioCategoryFilter
locale={localeKey}
categories={categories}
allLabel={t("all")}
activeCategorySlug={category.slug}
/>
<PortfolioProjectGrid
locale={localeKey}
projects={projects}
emptyLabel={t("empty")}
openLabel={t("open")}
/>
</Container>
</>
);
}
@@ -0,0 +1,17 @@
import { permanentRedirect } from "next/navigation";
import { getLocalizedPath, resolveLocale } from "@/lib/locale";
type PortfolioCategoryIndexPageProps = {
params: {
locale: string;
};
};
export default function PortfolioCategoryIndexPage({
params: { locale },
}: PortfolioCategoryIndexPageProps) {
const localeKey = resolveLocale(locale);
permanentRedirect(getLocalizedPath(localeKey, "/portfolio"));
}