skills
This commit is contained in:
@@ -4,18 +4,41 @@ import { getPublishedPostBySlug, getAuthorBySlug, getRelatedPosts, getPublishedP
|
||||
import { AnswerBox } from "@/components/aeo/AnswerBox";
|
||||
import { StepList } from "@/components/aeo/StepList";
|
||||
import { FAQSection } from "@/components/aeo/FAQSection";
|
||||
import { SourcesList } from "@/components/aeo/SourcesList";
|
||||
import { AuthorCard } from "@/components/author/AuthorCard";
|
||||
import { RelatedPosts } from "@/components/blog/RelatedPosts";
|
||||
import { blogPostingSchema, howToSchema, faqPageSchema } from "@/lib/schema";
|
||||
import { blogPostingSchema, howToSchema, faqPageSchema, breadcrumbSchema } from "@/lib/schema";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
export function generateMetadata({ params }: { params: { slug: string } }) {
|
||||
const post = getPublishedPostBySlug(params.slug);
|
||||
if (!post) return {};
|
||||
|
||||
const ogImage = post.heroImage ? `https://www.qrmaster.net${post.heroImage}` : undefined;
|
||||
|
||||
return {
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
alternates: {
|
||||
canonical: `https://www.qrmaster.net/blog/${post.slug}`,
|
||||
},
|
||||
openGraph: {
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
type: 'article',
|
||||
publishedTime: post.datePublished,
|
||||
modifiedTime: post.dateModified || post.datePublished,
|
||||
authors: ['https://www.qrmaster.net'],
|
||||
tags: post.keywords,
|
||||
images: ogImage ? [{ url: ogImage, alt: post.imageAlt || post.title }] : undefined,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: post.title,
|
||||
description: post.description,
|
||||
images: ogImage ? [ogImage] : undefined,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,6 +61,15 @@ export default function BlogPostPage({ params }: { params: { slug: string } }) {
|
||||
const howtoLd = post.keySteps?.length ? howToSchema(post, author) : null;
|
||||
const faqLd = post.faq ? faqPageSchema(post.faq) : null;
|
||||
|
||||
// Generate breadcrumb schema: Home → Learn → Pillar → Post
|
||||
const pillarName = post.pillar ? post.pillar.charAt(0).toUpperCase() + post.pillar.slice(1) : 'Blog';
|
||||
const breadcrumbLd = breadcrumbSchema([
|
||||
{ name: 'Home', url: '/' },
|
||||
{ name: 'Learn', url: '/learn' },
|
||||
{ name: pillarName, url: `/learn/${post.pillar}` },
|
||||
{ name: post.title, url: `/blog/${post.slug}` },
|
||||
]);
|
||||
|
||||
return (
|
||||
<main className="container mx-auto max-w-4xl py-12 px-4">
|
||||
<Script id="ld-blogposting" type="application/ld+json" strategy="afterInteractive"
|
||||
@@ -50,6 +82,8 @@ export default function BlogPostPage({ params }: { params: { slug: string } }) {
|
||||
<Script id="ld-faq" type="application/ld+json" strategy="afterInteractive"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqLd) }} />
|
||||
)}
|
||||
<Script id="ld-breadcrumb" type="application/ld+json" strategy="afterInteractive"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbLd) }} />
|
||||
|
||||
<header className="space-y-6 text-center max-w-3xl mx-auto mb-10">
|
||||
<div className="flex justify-center gap-2 text-sm text-gray-500 font-medium">
|
||||
@@ -71,7 +105,12 @@ export default function BlogPostPage({ params }: { params: { slug: string } }) {
|
||||
)}
|
||||
<div className="text-left text-sm">
|
||||
<div className="font-bold text-gray-900">{author.name}</div>
|
||||
<div className="text-gray-500">Updated {post.updatedAt || post.date}</div>
|
||||
<div className="text-gray-500 text-xs mt-0.5">
|
||||
Published {post.date}
|
||||
{post.updatedAt && (
|
||||
<> <span className="mx-1">•</span> Updated {post.updatedAt}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -97,6 +136,9 @@ export default function BlogPostPage({ params }: { params: { slug: string } }) {
|
||||
{/* AEO BLOCK: FAQ */}
|
||||
{!!post.faq?.length && <div className="mt-12"><FAQSection items={post.faq} /></div>}
|
||||
|
||||
{/* AEO BLOCK: SOURCES */}
|
||||
{!!post.sources?.length && <SourcesList sources={post.sources} />}
|
||||
|
||||
<div className="border-t border-gray-100 my-12"></div>
|
||||
|
||||
{author && <AuthorCard author={author} />}
|
||||
|
||||
Reference in New Issue
Block a user