Files
gabrielkaszewski-next/app/projects/[projectName]/page.tsx
Gabriel Kaszewski 4d0cbb7fb2
All checks were successful
Build and Deploy Gabriel Kaszewski Portfolio / build-and-deploy-local (push) Successful in 1m46s
Refactor project slug handling and improve project retrieval logic
2026-08-21 15:46:33 +02:00

151 lines
5.0 KiB
TypeScript

import { getProjectBySlug, projects, projectSlug } from "@/lib/data";
import Chip from "@/components/chip";
import MarkdownContent from "@/components/markdown-content";
import { Metadata } from "next";
import Image from "next/image";
import { Github, Eye, CloudDownload } from "lucide-react";
import { notFound } from "next/navigation";
export async function generateStaticParams() {
return projects.map((project) => ({
projectName: projectSlug(project),
}));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ projectName: string }>;
}): Promise<Metadata> {
const { projectName } = await params;
const project = getProjectBySlug(projectName);
if (!project) {
return { title: "Project Not Found" };
}
const slug = projectSlug(project);
return {
title: `${project.name} | Gabriel Kaszewski`,
description: project.short_description,
alternates: {
canonical: `https://gabrielkaszewski.dev/projects/${slug}`,
},
openGraph: {
title: project.name,
description: project.short_description,
url: `https://gabrielkaszewski.dev/projects/${slug}`,
images:
project.thumbnails && project.thumbnails.length > 0
? [
{
url: project.thumbnails[0],
width: 800,
height: 600,
alt: project.name,
},
]
: undefined,
},
twitter: {
card: "summary",
title: project.name,
description: project.short_description,
},
};
}
export default async function ProjectDetailPage({
params,
}: {
params: Promise<{ projectName: string }>;
}) {
const { projectName } = await params;
const project = getProjectBySlug(projectName);
if (!project) {
notFound();
}
const hasLinks =
project.github_url || project.visit_url || project.download_url;
return (
<div className="flex flex-col w-full h-full min-h-screen gap-4 p-4 pt-24">
<div className="max-w-4xl mx-auto w-full">
<h1 className="text-4xl font-extrabold mb-6 bg-gradient-to-r from-yellow-400 to-blue-400 bg-clip-text text-transparent tracking-tight">
{project.name}
</h1>
<MarkdownContent content={project.description} />
<section className="mt-12 flex flex-col items-center">
<h2 className="text-xl font-bold text-white mb-4">Technologies</h2>
<div className="flex flex-wrap justify-center gap-2">
{project.technologies.map((tech) => (
<Chip key={tech} text={tech} />
))}
</div>
</section>
{project.thumbnails && project.thumbnails.length > 0 && (
<section className="mt-12 flex flex-col items-center">
<h2 className="text-xl font-bold text-white mb-4">Gallery</h2>
<div className="flex flex-col gap-4">
{project.thumbnails.map((thumb, index) => (
<Image
key={index}
src={thumb}
alt={`${project.name} thumbnail ${index + 1}`}
width={1024}
height={768}
className="rounded-lg shadow-lg"
/>
))}
</div>
</section>
)}
{hasLinks && (
<section className="mt-12 flex flex-col items-center">
<h2 className="text-xl font-bold text-white mb-4">Links</h2>
<div className="flex flex-col sm:flex-row gap-4">
{project.github_url && (
<a
href={project.github_url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 p-2 px-4 text-center glass-effect glossy-effect bottom gloss-highlight rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
>
<Github size={20} /> CODE
</a>
)}
{project.visit_url && (
<a
href={project.visit_url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 p-2 px-4 text-center glass-effect glossy-effect bottom gloss-highlight rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
>
<Eye size={20} /> LIVE
</a>
)}
{project.download_url && (
<a
href={project.download_url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 p-2 px-4 text-center glass-effect glossy-effect bottom gloss-highlight rounded-2xl bg-yellow-400 hover:bg-yellow-500 text-black font-bold"
>
<CloudDownload size={20} /> DOWNLOAD
</a>
)}
</div>
</section>
)}
</div>
</div>
);
}