Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Failing after 9m26s
120 lines
3.8 KiB
TypeScript
120 lines
3.8 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 } 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: typeof directusProject.id === 'string' ? (parseInt(directusProject.id) || 0) : directusProject.id,
|
|
} 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} />
|
|
</>
|
|
);
|
|
}
|
|
|