2025-08-26 15:56:33 +02:00

147 lines
4.3 KiB
JavaScript

import { motion } from 'framer-motion';
import devlogData from '../../data/devlog.json';
import Image from 'next/image';
import { supabase } from '../../lib/supabase';
export default function BlogPost({ blog }) {
if (!blog) {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="py-20 text-center"
>
<h1 className="text-4xl font-bold mb-6">Beitrag nicht gefunden</h1>
<p className="text-xl text-gray-600">Der angeforderte Beitrag existiert nicht.</p>
</motion.div>
);
}
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<section className="py-20 bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="relative p-8 mb-16 before:content-[''] before:absolute before:top-0 before:left-0 before:w-8 before:h-8 before:border-t-2 before:border-l-2 before:border-pb-turquoise after:content-[''] after:absolute after:bottom-0 after:right-0 after:w-8 after:h-8 after:border-b-2 after:border-r-2 after:border-pb-turquoise"
>
<h1 className="text-4xl font-bold mb-6">{blog.title}</h1>
<p className="text-xl text-gray-600 mb-4">{blog.date}</p>
</motion.div>
<div className="space-y-8">
{blog.content && blog.content.map((item, index) => {
// Handle both old format (type/value) and new format (direct content)
if (typeof item === 'string') {
return (
<p key={index} className="text-gray-600 text-lg">
{item}
</p>
);
}
return item.type === 'text' ? (
<p key={index} className="text-gray-600 text-lg">
{item.value}
</p>
) : (
<div
key={index}
className="relative w-full h-64 rounded-lg overflow-hidden"
>
{/* Only render image if src is valid */}
{item.value && (item.value.startsWith('/') || item.value.startsWith('http')) && (
<Image
src={item.value}
alt={blog.title}
fill
className="object-cover"
priority
/>
)}
</div>
);
})}
{/* If no structured content, show description */}
{(!blog.content || blog.content.length === 0) && blog.description && (
<p className="text-gray-600 text-lg">
{blog.description}
</p>
)}
</div>
</div>
</section>
</motion.div>
);
}
export async function getServerSideProps({ params }) {
const { slug } = params;
try {
// First, try to find the blog post in Supabase
const { data: supabaseBlog, error } = await supabase
.from('devlog_entries')
.select('*')
.eq('slug', slug)
.single();
if (supabaseBlog && !error) {
// Found in Supabase, format the data
const blog = {
id: supabaseBlog.id,
title: supabaseBlog.title,
date: supabaseBlog.date,
description: supabaseBlog.description,
slug: supabaseBlog.slug,
image: supabaseBlog.image,
content: supabaseBlog.content || []
};
return {
props: {
blog
}
};
}
// If not found in Supabase, try the static JSON file
const staticBlog = devlogData.find((entry) => entry.slug === slug);
if (staticBlog) {
return {
props: {
blog: staticBlog
}
};
}
// Blog post not found
return {
props: {
blog: null
}
};
} catch (error) {
console.error('Error fetching blog post:', error);
// Fallback to static data
const staticBlog = devlogData.find((entry) => entry.slug === slug);
return {
props: {
blog: staticBlog || null
}
};
}
}