Files
mintel.me/apps/web/app/(site)/blog/[slug]/page.tsx
Marc Mintel 95a8b702fe
Some checks failed
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🏗️ Build (push) Failing after 8m32s
Build & Deploy / 🔍 Prepare (push) Successful in 18s
Build & Deploy / 🧪 QA (push) Failing after 1m33s
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
fix(web): correct relative imports in opengraph-image routes
2026-02-23 01:14:16 +01:00

116 lines
3.3 KiB
TypeScript

import * as React from "react";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { getAllPosts } from "@/src/lib/posts";
import { BlogPostHeader } from "@/src/components/blog/BlogPostHeader";
import { Section } from "@/src/components/Section";
import { Reveal } from "@/src/components/Reveal";
import { BlogPostClient } from "@/src/components/BlogPostClient";
import { TextSelectionShare } from "@/src/components/TextSelectionShare";
import { BlogPostStickyBar } from "@/src/components/blog/BlogPostStickyBar";
import { MDXContent } from "@/src/components/MDXContent";
export async function generateStaticParams() {
const allPosts = await getAllPosts();
return allPosts.map((post) => ({
slug: post.slug,
}));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): Promise<Metadata> {
const { slug } = await params;
const allPosts = await getAllPosts();
const post = allPosts.find((p) => p.slug === slug);
if (!post) return {};
return {
title: post.title,
description: post.description,
openGraph: {
title: post.title,
description: post.description,
type: "article",
},
twitter: {
card: "summary_large_image",
title: post.title,
description: post.description,
},
};
}
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const allPosts = await getAllPosts();
const post = allPosts.find((p) => p.slug === slug);
if (!post) {
notFound();
}
const formattedDate = new Date(post.date).toLocaleDateString("de-DE", {
month: "long",
day: "numeric",
year: "numeric",
});
const wordCount = post.description.split(/\s+/).length + 300;
const readingTime = Math.max(1, Math.ceil(wordCount / 200));
return (
<div className="flex flex-col gap-8 md:gap-12 py-8 md:py-24 overflow-hidden">
<BlogPostClient readingTime={readingTime} title={post.title} />
<BlogPostHeader
title={post.title}
description={post.description}
date={formattedDate}
readingTime={readingTime}
slug={slug}
thumbnail={post.thumbnail}
/>
<main id="post-content">
<BlogPostStickyBar
title={post.title}
url={`https://mintel.me/blog/${slug}`}
/>
<Section containerVariant="wide" className="pt-0 md:pt-0">
<div className="max-w-4xl mx-auto px-5 md:px-0">
<Reveal delay={0.4} width="100%">
{post.tags && post.tags.length > 0 && (
<div className="flex flex-wrap gap-2 mb-10 md:mb-12">
{post.tags?.map((tag) => (
<span
key={tag}
className="px-2.5 py-1 bg-slate-50 border border-slate-100 rounded text-[9px] md:text-[10px] font-mono text-slate-500 uppercase tracking-widest"
>
#{tag}
</span>
))}
</div>
)}
<div className="article-content max-w-none">
<MDXContent code={post.body.code} />
</div>
</Reveal>
</div>
</Section>
</main>
<TextSelectionShare />
</div>
);
}