hero_web_try1/src/pages/BlogPost.jsx
2025-08-03 10:01:35 +02:00

221 lines
8.1 KiB
JavaScript

import React, { useEffect, useState } from 'react';
import { useParams, Link, useLocation } from 'react-router-dom';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import matter from 'gray-matter';
import { ArrowLeft, Calendar, User, Tag } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Buffer } from 'buffer'; // Explicitly import Buffer
const imageModules = import.meta.glob('../assets/*.jpg', { eager: true, import: 'default' });
const BlogPost = () => {
const { category, slug } = useParams(); // category will be undefined for /component/:slug
const location = useLocation();
const [post, setPost] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const loadPost = async () => {
try {
let contentModule;
let basePath;
let currentSlug = slug; // Use slug from useParams
// Determine content type based on pathname
const pathSegments = location.pathname.split('/').filter(Boolean);
let contentType = '';
if (pathSegments[0] === 'blog' && pathSegments.length >= 3) {
contentType = 'blog';
// For blog posts, category is the second segment, slug is the third
currentSlug = pathSegments[2];
basePath = `../content/blog/`; // blog/:category/:slug
} else if (pathSegments[0] === 'component' && pathSegments.length >= 2) {
contentType = 'component';
currentSlug = pathSegments[1]; // component/:slug
basePath = '../content/component/';
} else if (pathSegments[0] === 'technology' && pathSegments.length >= 2) {
contentType = 'tech';
currentSlug = pathSegments[1];
basePath = '../content/tech/';
} else if (pathSegments[0] === 'freezone' && pathSegments.length >= 2) {
contentType = 'freezone';
currentSlug = pathSegments[1];
basePath = '../content/freezone/';
} else if (pathSegments[0] === 'home' && pathSegments.length >= 2) {
contentType = 'home';
currentSlug = pathSegments[1];
basePath = '../content/home/';
}
else {
setError('Invalid URL path for content.');
setLoading(false);
return;
}
try {
const modules = import.meta.glob('../content/**/*.md', { as: 'raw', eager: true });
const fullPath = `${basePath}${currentSlug}.md`;
contentModule = modules[fullPath];
if (!contentModule) {
throw new Error(`Markdown file not found at ${fullPath}`);
}
const { data: frontmatter, content: markdownContent } = matter(contentModule);
// Resolve image path
const resolvedImage = frontmatter.image ? imageModules[`../assets/${frontmatter.image}`] : null;
setPost({ frontmatter: { ...frontmatter, image: resolvedImage }, content: markdownContent, contentType }); // Store contentType and resolved image with post
} catch (err) {
setError(`Failed to load content: ${err.message}. Please ensure the file exists and is correctly formatted.`);
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
loadPost();
}, [location.pathname, slug]); // Depend on pathname and slug
if (loading) {
return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="text-white text-xl">Loading...</div>
</div>
);
}
if (error) {
return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="text-white text-xl">Error: {error}</div>
</div>
);
}
const getBackLink = () => {
// Use post.contentType to determine the back link
if (!post || !post.contentType) {
return '/'; // Default to home if content type is unknown
}
switch (post.contentType) {
case 'component':
return '/how';
case 'blog':
return '/blog';
case 'tech':
return '/technology';
case 'freezone':
return '/freezone';
case 'home':
return '/'; // Or a more specific home content listing page if it exists
default:
return '/';
}
};
return (
<div className="min-h-screen bg-gray-900">
<div className="container mx-auto px-4 py-8 max-w-4xl">
<div className="mb-8">
<Link to={getBackLink()}>
<Button className="bg-gray-800 hover:bg-gray-700 text-white">
<ArrowLeft className="mr-2 h-4 w-4" />
Back to {post?.contentType ? post.contentType.charAt(0).toUpperCase() + post.contentType.slice(1) : 'Previous Page'}
</Button>
</Link>
</div>
{post.frontmatter.image && (
<div className="mb-8">
<img
src={post.frontmatter.image}
alt={post.frontmatter.title}
className="w-full h-64 md:h-96 object-cover rounded-lg"
/>
</div>
)}
<article className="bg-gray-800 rounded-lg p-8">
<div className="mb-6">
<h1 className="text-4xl font-bold text-white mb-4">
{post.frontmatter.title}
</h1>
<div className="flex items-center space-x-4 text-gray-400 mb-4">
<div className="flex items-center space-x-2">
<User size={16} />
<span>{post.frontmatter.author}</span>
</div>
<div className="flex items-center space-x-2">
<Calendar size={16} />
<span>{post.frontmatter.date}</span>
</div>
<span>{post.frontmatter.readTime}</span>
</div>
<div className="flex flex-wrap gap-2">
{post.frontmatter.tags?.map((tag, index) => (
<span
key={index}
className="px-3 py-1 bg-blue-600/20 text-blue-400 text-sm rounded-full"
>
<Tag className="inline-block mr-1 h-3 w-3" />
{tag}
</span>
))}
</div>
</div>
<div className="prose prose-invert prose-lg max-w-none">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
h1: ({node, ...props}) => <h1 className="text-3xl font-bold text-white mb-4" {...props} />,
h2: ({node, ...props}) => <h2 className="text-2xl font-semibold text-white mb-3 mt-6" {...props} />,
h3: ({node, ...props}) => <h3 className="text-xl font-semibold text-white mb-2 mt-4" {...props} />,
p: ({node, ...props}) => <p className="text-gray-300 leading-relaxed mb-4" {...props} />,
ul: ({node, ...props}) => <ul className="text-gray-300 list-disc list-inside mb-4" {...props} />,
ol: ({node, ...props}) => <ol className="text-gray-300 list-decimal list-inside mb-4" {...props} />,
li: ({node, ...props}) => <li className="text-gray-300 mb-1" {...props} />,
blockquote: ({node, ...props}) => (
<blockquote
className="border-l-4 border-blue-500 pl-4 italic text-gray-400 mb-4"
{...props}
/>
),
code: ({node, inline, ...props}) => (
<code
className={`${
inline
? 'bg-gray-700 px-1 py-0.5 rounded text-sm'
: 'block bg-gray-700 p-4 rounded text-sm overflow-x-auto'
}`}
{...props}
/>
),
a: ({node, ...props}) => (
<a
className="text-blue-400 hover:text-blue-300 underline"
{...props}
/>
),
}}
>
{post.content}
</ReactMarkdown>
</div>
</article>
</div>
</div>
);
};
export default BlogPost;