89 lines
3.0 KiB
TypeScript
89 lines
3.0 KiB
TypeScript
// app/Projects/[slug]/page.tsx
|
|
"use client";
|
|
|
|
import {useParams, useRouter} from "next/navigation";
|
|
import {useEffect, useState} from "react";
|
|
import Header from "../../components/Header";
|
|
import Footer_Back from "../../components/Footer_Back";
|
|
import ReactMarkdown from "react-markdown";
|
|
import remarkGfm from "remark-gfm";
|
|
import rehypeRaw from "rehype-raw";
|
|
import matter from "gray-matter";
|
|
|
|
interface Project {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
text: string;
|
|
slug: string;
|
|
image: string;
|
|
}
|
|
|
|
export default function ProjectDetail() {
|
|
const params = useParams();
|
|
const router = useRouter();
|
|
const {slug} = params as { slug: string };
|
|
const [project, setProject] = useState<Project | null>(null);
|
|
const [isVisible, setIsVisible] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setTimeout(() => {
|
|
setIsVisible(true);
|
|
}, 150); // Delay to start the animation
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (slug) {
|
|
fetch(`/projects/${slug}.md`)
|
|
.then((res) => res.text())
|
|
.then((content) => {
|
|
const {data, content: markdownContent} = matter(content);
|
|
setProject({
|
|
id: data.id,
|
|
title: data.title,
|
|
description: data.description,
|
|
text: markdownContent,
|
|
slug: slug,
|
|
image: data.image,
|
|
});
|
|
|
|
// Log the project view
|
|
fetch("/api/stats", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
type: "project_view",
|
|
projectId: data.id,
|
|
}),
|
|
}).catch((err) => console.error("Failed to log project view", err));
|
|
})
|
|
.catch(() => {
|
|
// Redirect to 404 if project not found
|
|
router.replace("/not-found");
|
|
});
|
|
}
|
|
}, [slug, router]);
|
|
|
|
if (!project) {
|
|
return <div className="p-10 text-center">Loading...</div>;
|
|
}
|
|
|
|
return (
|
|
<div className={`min-h-screen flex flex-col bg-radiant ${isVisible ? 'animate-fly-in' : 'opacity-0'}`}>
|
|
<Header/>
|
|
<div className="flex-grow p-10 pt-24">
|
|
<div
|
|
className="flex flex-col p-8 bg-gradient-to-br from-white/60 to-white/30 backdrop-blur-lg rounded-2xl shadow-xl">
|
|
<div className="mt-4 text-gray-600 dark:text-gray-300 markdown">
|
|
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
|
|
{project.text}
|
|
</ReactMarkdown>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Footer_Back/>
|
|
</div>
|
|
);
|
|
} |