diff --git a/app/(marketing)/blog/page.tsx b/app/(marketing)/blog/page.tsx deleted file mode 100644 index e50f785..0000000 --- a/app/(marketing)/blog/page.tsx +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Blog Overview Page - * - * Landing page with series cards and recent posts - */ - -import Link from 'next/link' -import { getAllPosts, getSeriesWithCounts, type BlogSeries } from '@/lib/mdx/blog' - -export const metadata = { - title: 'Blog - AI Speedrun', - description: 'Artikelen over AI-gestuurde software ontwikkeling, healthcare IT en het bouwen van een EPD.', - openGraph: { - title: 'Blog - AI Speedrun', - description: 'Artikelen over AI-gestuurde software ontwikkeling, healthcare IT en het bouwen van een EPD.', - type: 'website', - siteName: 'AI Speedrun', - locale: 'nl_NL', - }, - twitter: { - card: 'summary', - title: 'Blog - AI Speedrun', - description: 'Artikelen over AI-gestuurde software ontwikkeling, healthcare IT en het bouwen van een EPD.', - }, - alternates: { - canonical: `${process.env.NEXT_PUBLIC_APP_URL || 'https://aispeedrun.vercel.app'}/blog`, - }, -} - -export default async function BlogPage() { - const series = await getSeriesWithCounts() - const posts = await getAllPosts() - - return ( -
-
- {/* Header */} -
-

- Blog -

-

- Artikelen over AI-gestuurde ontwikkeling en de reis van het bouwen van een EPD. -

-
- - {/* Series Section */} - {series.length > 0 && ( -
-

- Series -

-
- {series.map((serie) => ( - - ))} -
-
- )} - - {/* Recent Posts Section */} -
-

- Recente Posts -

- {posts.length === 0 ? ( -

- Nog geen blogposts gepubliceerd. -

- ) : ( -
- {posts.map((post) => ( - -
- - {post.seriesId.replace('-', ' ')} - - - - - {post.readingTime} min leestijd -
-

- {post.frontmatter.title} -

-

- {post.frontmatter.description} -

- {post.frontmatter.tags && post.frontmatter.tags.length > 0 && ( -
- {post.frontmatter.tags.map((tag) => ( - - {tag} - - ))} -
- )} - - ))} -
- )} -
-
-
- ) -} - -function SeriesCard({ series }: { series: BlogSeries & { postCount: number } }) { - const colorStyles = { - teal: { - bg: 'bg-teal-50', - border: 'border-teal-200 hover:border-teal-400', - badge: 'bg-teal-100 text-teal-700', - dot: 'bg-teal-500', - }, - amber: { - bg: 'bg-amber-50', - border: 'border-amber-200 hover:border-amber-400', - badge: 'bg-amber-100 text-amber-700', - dot: 'bg-amber-500', - }, - slate: { - bg: 'bg-slate-50', - border: 'border-slate-200 hover:border-slate-400', - badge: 'bg-slate-100 text-slate-700', - dot: 'bg-slate-500', - }, - } - - const styles = colorStyles[series.color] || colorStyles.slate - const statusLabels = { - completed: 'Voltooid', - active: 'Actief', - planned: 'Gepland', - } - - return ( - -
-
- - {statusLabels[series.status]} - -
-

{series.title}

-

{series.description}

-
- {series.postCount} {series.postCount === 1 ? 'deel' : 'delen'} -
- - ) -} - diff --git a/app/(marketing)/blog/serie/[serieId]/[slug]/page.tsx b/app/(marketing)/blog/serie/[serieId]/[slug]/page.tsx deleted file mode 100644 index 0c9932f..0000000 --- a/app/(marketing)/blog/serie/[serieId]/[slug]/page.tsx +++ /dev/null @@ -1,324 +0,0 @@ -/** - * Individual Blog Post Page - * - * Renders MDX content with series navigation - */ - -import { notFound } from 'next/navigation' -import Link from 'next/link' -import { MDXRemote } from 'next-mdx-remote/rsc' -import { getPost, getSeries, getPostsBySeries, getSeriesNavigation, getAllPosts } from '@/lib/mdx/blog' -import { mdxComponents } from '../../../../documentatie/components/mdx-components' - -interface PostPageProps { - params: Promise<{ serieId: string; slug: string }> -} - -export async function generateStaticParams() { - const posts = await getAllPosts() - return posts.map((post) => ({ - serieId: post.seriesId, - slug: post.slug, - })) -} - -export async function generateMetadata({ params }: PostPageProps) { - const { serieId, slug } = await params - const post = await getPost(serieId, slug) - const series = await getSeries(serieId) - - if (!post || !series) { - return { title: 'Post niet gevonden' } - } - - const siteUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://aispeedrun.vercel.app' - const postUrl = `${siteUrl}/blog/serie/${serieId}/${slug}` - // Use post-specific image if provided, otherwise fall back to default - const ogImageUrl = post.frontmatter.image - ? `${siteUrl}${post.frontmatter.image.startsWith('/') ? '' : '/'}${post.frontmatter.image}` - : `${siteUrl}/og-blog-default.png` - - return { - title: `${post.frontmatter.title} - ${series.title}`, - description: post.frontmatter.description, - authors: [{ name: 'Colin van der Heijden', url: 'https://ikbenlit.nl' }], - keywords: post.frontmatter.tags || [], - openGraph: { - title: post.frontmatter.title, - description: post.frontmatter.description, - type: 'article', - publishedTime: post.frontmatter.date, - authors: ['Colin van der Heijden'], - siteName: 'AI Speedrun', - locale: 'nl_NL', - images: [ - { - url: ogImageUrl, - width: 1200, - height: 630, - alt: post.frontmatter.title, - }, - ], - }, - twitter: { - card: 'summary_large_image', - title: post.frontmatter.title, - description: post.frontmatter.description, - images: [ogImageUrl], - }, - alternates: { - canonical: postUrl, - }, - } -} - -export default async function PostPage({ params }: PostPageProps) { - const { serieId, slug } = await params - const post = await getPost(serieId, slug) - const series = await getSeries(serieId) - - if (!post || !series) { - notFound() - } - - const navigation = await getSeriesNavigation(serieId, slug) - const { frontmatter, content, readingTime } = post - - // Generate Article structured data for SEO - const siteUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://aispeedrun.vercel.app' - const postUrl = `${siteUrl}/blog/serie/${serieId}/${slug}` - // Use post-specific image if provided, otherwise fall back to default - const articleImage = frontmatter.image - ? `${siteUrl}${frontmatter.image.startsWith('/') ? '' : '/'}${frontmatter.image}` - : `${siteUrl}/og-blog-default.png` - const articleSchema = { - '@context': 'https://schema.org', - '@type': 'Article', - headline: frontmatter.title, - description: frontmatter.description, - image: articleImage, - datePublished: frontmatter.date, - dateModified: frontmatter.date, - author: { - '@type': 'Person', - name: 'Colin van der Heijden', - url: 'https://ikbenlit.nl', - }, - publisher: { - '@type': 'Organization', - name: 'AI Speedrun', - url: siteUrl, - logo: { - '@type': 'ImageObject', - url: `${siteUrl}/images/aispeedrun-logo.webp`, - }, - }, - mainEntityOfPage: { - '@type': 'WebPage', - '@id': postUrl, - }, - articleSection: series.title, - keywords: frontmatter.tags?.join(', ') || '', - wordCount: content.split(/\s+/).length, - timeRequired: `PT${readingTime}M`, - } - - // Generate BreadcrumbList structured data - const breadcrumbSchema = { - '@context': 'https://schema.org', - '@type': 'BreadcrumbList', - itemListElement: [ - { - '@type': 'ListItem', - position: 1, - name: 'Home', - item: siteUrl, - }, - { - '@type': 'ListItem', - position: 2, - name: 'Blog', - item: `${siteUrl}/blog`, - }, - { - '@type': 'ListItem', - position: 3, - name: series.title, - item: `${siteUrl}/blog/serie/${serieId}`, - }, - { - '@type': 'ListItem', - position: 4, - name: frontmatter.title, - item: postUrl, - }, - ], - } - - const colorStyles = { - teal: { - badge: 'bg-teal-100 text-teal-700 border-teal-200', - link: 'text-teal-600 hover:text-teal-700', - }, - amber: { - badge: 'bg-amber-100 text-amber-700 border-amber-200', - link: 'text-amber-600 hover:text-amber-700', - }, - slate: { - badge: 'bg-slate-100 text-slate-700 border-slate-200', - link: 'text-slate-600 hover:text-slate-700', - }, - } - - const styles = colorStyles[series.color] || colorStyles.slate - - return ( -
- {/* Structured Data for SEO */} -