Files
portfolio/app/[locale]/projects/[slug]/page.tsx
denshooter 0766b46cc8
Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Failing after 10m16s
feat: implement dark mode infrastructure, optimize images, and add SEO structured data
2026-02-15 22:20:49 +01:00

90 lines
2.6 KiB
TypeScript

import { prisma } from "@/lib/prisma";
import ProjectDetailClient from "@/app/_ui/ProjectDetailClient";
import { notFound } from "next/navigation";
import type { Metadata } from "next";
import { getLanguageAlternates, toAbsoluteUrl } from "@/lib/seo";
export const revalidate = 300;
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string; slug: string }>;
}): Promise<Metadata> {
const { locale, slug } = await params;
const languages = getLanguageAlternates({ pathWithoutLocale: `projects/${slug}` });
return {
alternates: {
canonical: toAbsoluteUrl(`/${locale}/projects/${slug}`),
languages,
},
};
}
export default async function ProjectPage({
params,
}: {
params: Promise<{ locale: string; slug: string }>;
}) {
const { locale, slug } = await params;
const project = await prisma.project.findFirst({
where: { slug, published: true },
include: {
translations: {
select: { title: true, description: true, content: true, locale: true },
},
},
});
if (!project) return notFound();
const trPreferred = project.translations?.find((t) => t.locale === locale && (t?.title || t?.description));
const trDefault = project.translations?.find(
(t) => t.locale === project.defaultLocale && (t?.title || t?.description),
);
const tr = trPreferred ?? trDefault;
const { translations: _translations, ...rest } = project;
const localizedContent = (() => {
if (typeof tr?.content === "string") return tr.content;
if (tr?.content && typeof tr.content === "object" && "markdown" in tr.content) {
const markdown = (tr.content as Record<string, unknown>).markdown;
if (typeof markdown === "string") return markdown;
}
return project.content;
})();
const localized = {
...rest,
title: tr?.title ?? project.title,
description: tr?.description ?? project.description,
content: localizedContent,
};
const jsonLd = {
"@context": "https://schema.org",
"@type": "SoftwareSourceCode",
"name": localized.title,
"description": localized.description,
"codeRepository": localized.github,
"programmingLanguage": localized.technologies,
"author": {
"@type": "Person",
"name": "Dennis Konkol"
},
"dateCreated": project.date,
"url": toAbsoluteUrl(`/${locale}/projects/${slug}`),
"image": localized.imageUrl ? toAbsoluteUrl(localized.imageUrl) : undefined,
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<ProjectDetailClient project={localized} locale={locale} />
</>
);
}