wip
This commit is contained in:
113
app/[locale]/blog/[slug]/opengraph-image.tsx
Normal file
113
app/[locale]/blog/[slug]/opengraph-image.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { getPostBySlug } from '@/lib/blog';
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
export default async function Image({ params: { locale, slug } }: { params: { locale: string, slug: string } }) {
|
||||
const post = await getPostBySlug(slug, locale);
|
||||
|
||||
if (!post) {
|
||||
return new ImageResponse(
|
||||
<div style={{ display: 'flex', width: '100%', height: '100%', backgroundColor: '#001a4d' }} />
|
||||
);
|
||||
}
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'flex-end',
|
||||
backgroundColor: '#001a4d',
|
||||
padding: '80px',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{/* Background Image Overlay if available */}
|
||||
{post.frontmatter.featuredImage && (
|
||||
<img
|
||||
src={post.frontmatter.featuredImage.startsWith('http') ? post.frontmatter.featuredImage : `https://klz-cables.com${post.frontmatter.featuredImage}`}
|
||||
alt=""
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
opacity: 0.4,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Gradient Overlay */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
background: 'linear-gradient(to top, rgba(0,26,77,1) 0%, rgba(0,26,77,0.4) 100%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', position: 'relative', zIndex: 10 }}>
|
||||
{post.frontmatter.category && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'bold',
|
||||
color: '#00ff99',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.1em',
|
||||
marginBottom: '16px',
|
||||
backgroundColor: 'rgba(0,255,153,0.1)',
|
||||
padding: '4px 12px',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
>
|
||||
{post.frontmatter.category}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
fontSize: '64px',
|
||||
fontWeight: '900',
|
||||
color: 'white',
|
||||
lineHeight: '1.1',
|
||||
maxWidth: '900px',
|
||||
marginBottom: '24px',
|
||||
}}
|
||||
>
|
||||
{post.frontmatter.title}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '24px',
|
||||
color: 'rgba(255,255,255,0.6)',
|
||||
fontWeight: '500',
|
||||
}}
|
||||
>
|
||||
KLZ Cables Blog
|
||||
</div>
|
||||
<div style={{ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: '#00ff99', margin: '0 16px' }} />
|
||||
<div
|
||||
style={{
|
||||
fontSize: '24px',
|
||||
color: 'rgba(255,255,255,0.6)',
|
||||
}}
|
||||
>
|
||||
{new Date(post.frontmatter.date).toLocaleDateString(locale, { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import { getPostBySlug, getAdjacentPosts } from '@/lib/blog';
|
||||
import { Metadata } from 'next';
|
||||
|
||||
interface BlogPostProps {
|
||||
params: {
|
||||
@@ -9,6 +10,32 @@ interface BlogPostProps {
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale, slug } }: BlogPostProps): Promise<Metadata> {
|
||||
const post = await getPostBySlug(slug, locale);
|
||||
|
||||
if (!post) return {};
|
||||
|
||||
const description = post.frontmatter.excerpt || '';
|
||||
|
||||
return {
|
||||
title: post.frontmatter.title,
|
||||
description: description,
|
||||
openGraph: {
|
||||
title: post.frontmatter.title,
|
||||
description: description,
|
||||
type: 'article',
|
||||
publishedTime: post.frontmatter.date,
|
||||
authors: ['KLZ Cables'],
|
||||
url: `https://klz-cables.com/${locale}/blog/${slug}`,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: post.frontmatter.title,
|
||||
description: description,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
import Link from 'next/link';
|
||||
import VisualLinkPreview from '@/components/blog/VisualLinkPreview';
|
||||
import Callout from '@/components/blog/Callout';
|
||||
@@ -253,6 +280,34 @@ export default async function BlogPost({ params: { locale, slug } }: BlogPostPro
|
||||
<MDXRemote source={post.content} components={components} />
|
||||
</div>
|
||||
|
||||
{/* Structured Data */}
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
headline: post.frontmatter.title,
|
||||
datePublished: post.frontmatter.date,
|
||||
image: post.frontmatter.featuredImage ? `https://klz-cables.com${post.frontmatter.featuredImage}` : undefined,
|
||||
author: {
|
||||
'@type': 'Organization',
|
||||
name: 'KLZ Cables',
|
||||
url: 'https://klz-cables.com',
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'KLZ Cables',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: 'https://klz-cables.com/logo.png', // Assuming logo exists
|
||||
},
|
||||
},
|
||||
description: post.frontmatter.excerpt,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Power CTA */}
|
||||
<div className="mt-20">
|
||||
<PowerCTA locale={locale} />
|
||||
|
||||
Reference in New Issue
Block a user