69 lines
1.8 KiB
TypeScript
69 lines
1.8 KiB
TypeScript
'use client'
|
|
import { useEffect } from 'react'
|
|
import PublicLayout from '@/components/public/PublicLayout'
|
|
import BlogPostPage from '@/components/public/BlogPostPage'
|
|
|
|
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com'
|
|
const SITE_NAME = process.env.NEXT_PUBLIC_SITE_NAME || 'My Personal Site'
|
|
|
|
interface Props {
|
|
slug: string
|
|
}
|
|
|
|
export default function BlogPostPageClient({ slug }: Props) {
|
|
/**
|
|
* Inject JSON-LD for BlogPosting after hydration.
|
|
* In a real server-side CMS you would do this in generateMetadata
|
|
* or in a server component. Here we gracefully degrade.
|
|
*/
|
|
useEffect(() => {
|
|
const existingScript = document.getElementById('blog-post-jsonld')
|
|
if (existingScript) existingScript.remove()
|
|
|
|
const title = slug
|
|
.split('-')
|
|
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
.join(' ')
|
|
|
|
const jsonLd = {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'BlogPosting',
|
|
headline: title,
|
|
url: `${SITE_URL}/blog/${slug}`,
|
|
author: {
|
|
'@type': 'Person',
|
|
name: SITE_NAME,
|
|
url: SITE_URL,
|
|
},
|
|
publisher: {
|
|
'@type': 'Person',
|
|
name: SITE_NAME,
|
|
url: SITE_URL,
|
|
},
|
|
mainEntityOfPage: {
|
|
'@type': 'WebPage',
|
|
'@id': `${SITE_URL}/blog/${slug}`,
|
|
},
|
|
}
|
|
|
|
const script = document.createElement('script')
|
|
script.id = 'blog-post-jsonld'
|
|
script.type = 'application/ld+json'
|
|
script.textContent = JSON.stringify(jsonLd)
|
|
document.head.appendChild(script)
|
|
|
|
return () => {
|
|
const s = document.getElementById('blog-post-jsonld')
|
|
if (s) s.remove()
|
|
}
|
|
}, [slug])
|
|
|
|
return (
|
|
<PublicLayout>
|
|
<main id="main-content" aria-label="Blog post">
|
|
<BlogPostPage slug={slug} />
|
|
</main>
|
|
</PublicLayout>
|
|
)
|
|
}
|