Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Failing after 9m19s
Fixed missing types, import errors, and updated test suites to match the new editorial design. Verified Docker container build.
120 lines
3.7 KiB
TypeScript
120 lines
3.7 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";
|
|
import { getProjectBySlug, Project } from "@/lib/directus";
|
|
import { ProjectDetailData } from "@/app/_ui/ProjectDetailClient";
|
|
|
|
export const revalidate = 300;
|
|
|
|
export async function generateMetadata({
|
|
params,
|
|
}: {
|
|
params: Promise<{ locale: string; slug: string }>;
|
|
}): Promise<Metadata> {
|
|
const { locale, slug } = await params;
|
|
|
|
// Try Directus first for metadata
|
|
const directusProject = await getProjectBySlug(slug, locale);
|
|
if (directusProject) {
|
|
return {
|
|
title: directusProject.title,
|
|
description: directusProject.description,
|
|
alternates: {
|
|
canonical: toAbsoluteUrl(`/${locale}/projects/${slug}`),
|
|
languages: getLanguageAlternates({ pathWithoutLocale: `projects/${slug}` }),
|
|
},
|
|
};
|
|
}
|
|
|
|
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;
|
|
|
|
// Try PostgreSQL first
|
|
const dbProject = await prisma.project.findFirst({
|
|
where: { slug, published: true },
|
|
include: {
|
|
translations: {
|
|
select: { title: true, description: true, content: true, locale: true },
|
|
},
|
|
},
|
|
});
|
|
|
|
let projectData: ProjectDetailData | null = null;
|
|
|
|
if (dbProject) {
|
|
const trPreferred = dbProject.translations?.find((t) => t.locale === locale && (t?.title || t?.description));
|
|
const trDefault = dbProject.translations?.find(
|
|
(t) => t.locale === dbProject.defaultLocale && (t?.title || t?.description),
|
|
);
|
|
const tr = trPreferred ?? trDefault;
|
|
const { translations: _translations, ...rest } = dbProject;
|
|
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 dbProject.content;
|
|
})();
|
|
projectData = {
|
|
...rest,
|
|
title: tr?.title ?? dbProject.title,
|
|
description: tr?.description ?? dbProject.description,
|
|
content: localizedContent,
|
|
} as ProjectDetailData;
|
|
} else {
|
|
// Try Directus fallback
|
|
const directusProject = await getProjectBySlug(slug, locale);
|
|
if (directusProject) {
|
|
projectData = {
|
|
...directusProject,
|
|
id: parseInt(directusProject.id) || 0,
|
|
} as ProjectDetailData;
|
|
}
|
|
}
|
|
|
|
if (!projectData) return notFound();
|
|
|
|
const jsonLd = {
|
|
"@context": "https://schema.org",
|
|
"@type": "SoftwareSourceCode",
|
|
"name": projectData.title,
|
|
"description": projectData.description,
|
|
"codeRepository": projectData.github_url || projectData.github,
|
|
"programmingLanguage": projectData.technologies,
|
|
"author": {
|
|
"@type": "Person",
|
|
"name": "Dennis Konkol"
|
|
},
|
|
"dateCreated": projectData.date || projectData.created_at,
|
|
"url": toAbsoluteUrl(`/${locale}/projects/${slug}`),
|
|
"image": (projectData.imageUrl || projectData.image_url) ? toAbsoluteUrl((projectData.imageUrl || projectData.image_url)!) : undefined,
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
|
/>
|
|
<ProjectDetailClient project={projectData} locale={locale} />
|
|
</>
|
|
);
|
|
}
|
|
|