added blog

This commit is contained in:
FerrenF
2026-10-31 03:08:41 +00:00
parent 30c8ad9293
commit c521907512
4 changed files with 177 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
import type { Metadata } from "next";
import { ProjectProvider } from "@/app/context/ProjectContext";
import { getBlogDetails, BLOG_POSTS } from "@/data/content";
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const { slug } = await params;
const post = BLOG_POSTS.find((p) => p.slug === slug);
if (!post) return { title: "Post not found" };
return { title: `${post.title} | Blog`, description: post.description };
}
/**
* Layout for all paths caught under blog/[slug].
* Mirrors app/project/[slug]/layout.tsx: the rendered markdown is resolved
* here on the server and handed to the client page via the shared context.
* (ProjectContext is a generic `any` context despite its name; its value
* contract is `string | null` = rendered HTML.)
*/
export default async function BlogSlugLayout({ children, params }: {
children: React.ReactNode;
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const postData = await getBlogDetails(slug);
return (
<ProjectProvider value={postData}>
<main>
{children}
</main>
</ProjectProvider>
);
}

76
app/blog/[slug]/page.tsx Normal file
View File

@@ -0,0 +1,76 @@
'use client'
import { use } from 'react'
import { notFound } from 'next/navigation'
import { useSharedData } from '@/app/context/ProjectContext';
import { useMountTransition } from '@/hooks/useAnimations';
import "../../../styles/markdown-container.css";
import Link from 'next/link';
import { BLOG_POSTS } from '@/data/content';
import SimpleGallery from '@/components/SimpleGallery';
export default function BlogPostPage({params}: { params: Promise<{ slug: string }> })
{
const mounted = useMountTransition(50)
const { slug } = use(params);
const hasValidPost = BLOG_POSTS.find((v)=>v.slug == slug);
if (!slug || !hasValidPost) notFound();
const hasValidDetails = hasValidPost.details
const postDetails = useSharedData();
return (
<div
id="blog-markdown-container"
className={"markdown-container"}
style={{
opacity: mounted ? 1 : 0,
transition: "all 0.5s ease",
translate: mounted ? "0 2em" : "0 0"
}}
>
{
hasValidDetails ?
(
<main dangerouslySetInnerHTML={{ __html: postDetails }}>
</main>
) : (
<p>No post details available.</p>
)
}
{hasValidPost.images && hasValidPost.images.length > 0 && (
<section className="project-details-gallery">
<h2>Gallery</h2>
<SimpleGallery images={hasValidPost.images} title={hasValidPost.title} gallerySize="large"/>
</section>
)}
<div style={{
display: "flex",
flexDirection: "row",
gap: "1em",
marginBottom: "2em"
}}>
<Link onClick={(e)=>{
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});
}}
className="expand-on-hover-button"
href={"#"}>Back To Top</Link>
<Link
className="expand-on-hover-button"
href={"/blog"}>All Posts</Link>
</div>
</div>
);
}