import { notFound } from "next/navigation"; import Link from "next/link"; import { getBlogPostBySlug, getAllBlogPosts as getAllPosts, } from "../../../lib/content"; import ContentBanner from "../../components/ContentBanner"; import RelatedArticles from "../../components/RelatedArticles"; import AskOrganizer from "../../components/AskOrganizer"; import { getAssetPath, ASSETS } from "../../../lib/assetUtils"; // AskOrganizer data - same as index page const askOrganizerData = { title: "Still have questions?", subtitle: "Get answers from an experienced organizer", buttonText: "Ask an organizer", buttonHref: "#contact", }; /** * Generate static params for all blog posts * This enables static generation for all blog posts at build time */ export async function generateStaticParams() { try { const posts = getAllPosts(); return posts.map((post) => ({ slug: post.slug, })); } catch (error) { console.error("Error generating static params:", error); return []; } } /** * Generate metadata for each blog post */ export async function generateMetadata({ params }) { try { const { slug } = await params; const post = getBlogPostBySlug(slug); if (!post) { return { title: "Post Not Found", description: "The requested blog post could not be found.", }; } return { title: post.frontmatter.title, description: post.frontmatter.description, authors: [{ name: post.frontmatter.author }], openGraph: { title: post.frontmatter.title, description: post.frontmatter.description, type: "article", publishedTime: post.frontmatter.date, authors: [post.frontmatter.author], url: `https://communityrule.com/blog/${slug}`, siteName: "CommunityRule", }, twitter: { card: "summary_large_image", title: post.frontmatter.title, description: post.frontmatter.description, creator: "@communityrule", }, }; } catch (error) { console.error("Error generating metadata:", error); return { title: "Blog Post", description: "A blog post from our community.", }; } } /** * Dynamic blog post page */ export default async function BlogPostPage({ params }) { // Get the blog post data const { slug } = await params; const post = getBlogPostBySlug(slug); // If post doesn't exist, show 404 if (!post) { notFound(); } // Get related articles with improved algorithm const allPosts = getAllPosts(); // Simple related articles algorithm based on content similarity const getRelatedArticles = (currentPost, allPosts, limit = 3) => { const otherPosts = allPosts.filter((p) => p.slug !== currentPost.slug); // Score posts based on content similarity const scoredPosts = otherPosts.map((post) => { let score = 0; // Check for similar keywords in title and description const currentTitle = currentPost.frontmatter.title.toLowerCase(); const currentDesc = currentPost.frontmatter.description.toLowerCase(); const postTitle = post.frontmatter.title.toLowerCase(); const postDesc = post.frontmatter.description.toLowerCase(); // Common keywords that indicate similarity const keywords = [ "community", "conflict", "decision", "governance", "security", "trust", "collaboration", "organization", ]; keywords.forEach((keyword) => { if (currentTitle.includes(keyword) && postTitle.includes(keyword)) score += 3; if (currentDesc.includes(keyword) && postDesc.includes(keyword)) score += 2; if (currentTitle.includes(keyword) && postDesc.includes(keyword)) score += 1; if (currentDesc.includes(keyword) && postTitle.includes(keyword)) score += 1; }); return { ...post, score }; }); // Sort by score and return top posts return scoredPosts .sort((a, b) => b.score - a.score) .slice(0, limit) .map(({ score, ...post }) => post); // Remove score from final result }; const relatedArticles = getRelatedArticles(post, allPosts); // Generate structured data for search engines const structuredData = { "@context": "https://schema.org", "@type": "Article", headline: post.frontmatter.title, description: post.frontmatter.description, author: { "@type": "Person", name: post.frontmatter.author, }, publisher: { "@type": "Organization", name: "CommunityRule", url: "https://communityrule.com", logo: { "@type": "ImageObject", url: "https://communityrule.com/assets/Logo.svg", }, }, datePublished: post.frontmatter.date, dateModified: post.frontmatter.date, mainEntityOfPage: { "@type": "WebPage", "@id": `https://communityrule.com/blog/${post.slug}`, }, url: `https://communityrule.com/blog/${post.slug}`, articleSection: "Community Building", keywords: ["community", "governance", "decision making", "collaboration"], }; // Breadcrumb structured data const breadcrumbData = { "@context": "https://schema.org", "@type": "BreadcrumbList", itemListElement: [ { "@type": "ListItem", position: 1, name: "Home", item: "https://communityrule.com", }, { "@type": "ListItem", position: 2, name: "Blog", item: "https://communityrule.com/blog", }, { "@type": "ListItem", position: 3, name: post.frontmatter.title, item: `https://communityrule.com/blog/${post.slug}`, }, ], }; return ( <> {/* Structured Data */}