🖼️ update blog post styles
This commit is contained in:
@@ -3,6 +3,10 @@ import { notFound } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { getAllPosts, getPostBySlug, getRelatedPosts } from '@/lib/markdown'
|
||||
import { formatDate, formatRelativeDate } from '@/lib/utils'
|
||||
import { TableOfContents } from '@/components/blog/table-of-contents'
|
||||
import { ReadingProgress } from '@/components/blog/reading-progress'
|
||||
import { StickyFooter } from '@/components/blog/sticky-footer'
|
||||
import MarkdownRenderer from '@/components/blog/markdown-renderer'
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const posts = await getAllPosts()
|
||||
@@ -39,41 +43,19 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
|
||||
}
|
||||
}
|
||||
|
||||
function AuthorInfo({ author, date }: { author: string; date: string }) {
|
||||
return (
|
||||
<div className="flex items-center space-x-4 py-6 border-y border-gray-200 dark:border-gray-700">
|
||||
<div className="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-full flex items-center justify-center">
|
||||
<span className="text-xl font-bold text-primary-600 dark:text-primary-400">
|
||||
{author.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold">{author}</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
Publicat {formatRelativeDate(date)} • {formatDate(date)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
function extractHeadings(content: string) {
|
||||
const headingRegex = /^(#{2,3})\s+(.+)$/gm
|
||||
const headings: { id: string; text: string; level: number }[] = []
|
||||
let match
|
||||
|
||||
function RelatedPosts({ posts }: { posts: any[] }) {
|
||||
if (posts.length === 0) return null
|
||||
while ((match = headingRegex.exec(content)) !== null) {
|
||||
const level = match[1].length
|
||||
const text = match[2]
|
||||
const id = text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '')
|
||||
headings.push({ id, text, level })
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="mt-12 pt-8 border-t border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-2xl font-bold mb-6">Articole similare</h2>
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
{posts.map((post) => (
|
||||
<Link key={post.slug} href={`/blog/${post.slug}`} className="block p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:shadow-lg transition">
|
||||
<h3 className="font-semibold mb-2 line-clamp-2">{post.frontmatter.title}</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">{post.frontmatter.description}</p>
|
||||
<p className="text-xs text-gray-500 mt-2">{formatDate(post.frontmatter.date)}</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
return headings
|
||||
}
|
||||
|
||||
export default async function BlogPostPage({ params }: { params: Promise<{ slug: string[] }> }) {
|
||||
@@ -86,44 +68,118 @@ export default async function BlogPostPage({ params }: { params: Promise<{ slug:
|
||||
}
|
||||
|
||||
const relatedPosts = await getRelatedPosts(slugPath)
|
||||
const headings = extractHeadings(post.content)
|
||||
const fullUrl = `https://yourdomain.com/blog/${slugPath}`
|
||||
|
||||
return (
|
||||
<article className="max-w-4xl mx-auto">
|
||||
<header className="mb-8">
|
||||
{post.frontmatter.image && (
|
||||
<img src={post.frontmatter.image} alt={post.frontmatter.title} className="w-full h-64 md:h-96 object-cover rounded-lg mb-8" />
|
||||
)}
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-4">{post.frontmatter.title}</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-400 mb-6">{post.frontmatter.description}</p>
|
||||
{post.frontmatter.tags && post.frontmatter.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 mb-6">
|
||||
{post.frontmatter.tags.map((tag: string) => (
|
||||
<Link key={tag} href={`/tags/${tag.toLowerCase().replace(/\s+/g, '-')}`} className="px-3 py-1 bg-primary-100 dark:bg-primary-900 text-primary-700 dark:text-primary-300 rounded-full text-sm hover:bg-primary-200 dark:hover:bg-primary-800 transition">
|
||||
#{tag}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<AuthorInfo author={post.frontmatter.author} date={post.frontmatter.date} />
|
||||
</header>
|
||||
<>
|
||||
<ReadingProgress />
|
||||
|
||||
<div className="prose dark:prose-invert max-w-none">
|
||||
<div className="flex items-center justify-between text-sm text-gray-500 mb-6">
|
||||
<span>Timp estimat de citire: {post.readingTime} minute</span>
|
||||
<div className="max-w-7xl mx-auto px-6 py-16">
|
||||
<div className="flex gap-12">
|
||||
<TableOfContents headings={headings} />
|
||||
|
||||
<article className="flex-1 min-w-0">
|
||||
<header className="mb-16 border border-[var(--neon-cyan)] bg-[rgb(var(--bg-primary))] p-8 relative">
|
||||
<div className="border-b border-[var(--neon-pink)] pb-4 mb-6 relative">
|
||||
<div className="flex items-center gap-3 mb-3 justify-end">
|
||||
<p className="font-mono text-xs text-[var(--neon-cyan)] uppercase tracking-widest">
|
||||
>> CLASSIFIED_DOC://PUBLIC_ACCESS
|
||||
</p>
|
||||
<div className="flex gap-1.5">
|
||||
<div className="w-4 h-4 border border-[rgb(var(--border-primary))] hover:bg-red-500/10 cursor-pointer" />
|
||||
<div className="w-4 h-4 border border-[rgb(var(--border-primary))] hover:bg-yellow-500/10 cursor-pointer" />
|
||||
<div className="w-4 h-4 border border-[rgb(var(--border-primary))] hover:bg-green-500/10 cursor-pointer" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mb-2">
|
||||
{post.frontmatter.tags.map((tag: string) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="px-3 py-1 bg-cyan-500/5 border border-[var(--neon-cyan)] text-cyan-400 text-xs font-mono uppercase shadow-[0_0_8px_rgba(90,139,149,0.3)] hover:shadow-[0_0_12px_rgba(90,139,149,0.5)] transition-all"
|
||||
>
|
||||
#{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-l border-[var(--neon-magenta)] pl-6 relative">
|
||||
<h1 className="text-4xl md:text-5xl font-mono font-bold text-[var(--neon-cyan)] uppercase tracking-tight leading-tight mb-6">
|
||||
{post.frontmatter.title}
|
||||
</h1>
|
||||
|
||||
<p className="text-lg text-[rgb(var(--text-secondary))] leading-relaxed mb-6 font-mono">
|
||||
<span className="text-[var(--neon-pink)]">>></span> {post.frontmatter.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 pt-6 border-t border-[var(--neon-purple)] relative">
|
||||
<div className="w-12 h-12 border border-[var(--neon-cyan)] bg-[rgb(var(--bg-secondary))] flex items-center justify-center">
|
||||
<span className="font-mono text-[var(--neon-cyan)] text-xs">
|
||||
{post.frontmatter.author.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-mono font-bold text-[var(--neon-cyan)] uppercase text-sm">{post.frontmatter.author}</p>
|
||||
<div className="flex items-center gap-2 text-xs text-[rgb(var(--text-muted))] font-mono">
|
||||
<time className="text-[var(--neon-magenta)]">{formatDate(post.frontmatter.date)}</time>
|
||||
<span className="text-[var(--neon-pink)]">//</span>
|
||||
<span className="text-[var(--neon-cyan)]">{post.readingTime}min READ</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{post.frontmatter.image && (
|
||||
<div className="relative aspect-video mb-16 border border-[var(--neon-pink)] overflow-hidden">
|
||||
<img
|
||||
src={post.frontmatter.image}
|
||||
alt={post.frontmatter.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="prose prose-invert prose-lg max-w-none cyberpunk-prose">
|
||||
<MarkdownRenderer content={post.content} />
|
||||
</div>
|
||||
|
||||
{relatedPosts.length > 0 && (
|
||||
<section className="mt-12 pt-8 border-t border-zinc-800">
|
||||
<h2 className="text-2xl font-mono font-bold uppercase text-[var(--neon-cyan)] mb-6">// Articole similare</h2>
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
{relatedPosts.map((relatedPost) => (
|
||||
<Link
|
||||
key={relatedPost.slug}
|
||||
href={`/blog/${relatedPost.slug}`}
|
||||
className="block p-4 border border-zinc-800 bg-zinc-950 hover:border-[var(--neon-cyan)] transition-all hover:shadow-[0_0_8px_rgba(90,139,149,0.2)]"
|
||||
>
|
||||
<h3 className="font-mono font-semibold text-cyan-400 mb-2 line-clamp-2">{relatedPost.frontmatter.title}</h3>
|
||||
<p className="text-sm text-zinc-400 line-clamp-2">{relatedPost.frontmatter.description}</p>
|
||||
<p className="text-xs text-zinc-600 mt-2 font-mono">{formatDate(relatedPost.frontmatter.date)}</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<nav className="flex justify-between items-center mt-12 pt-8 border-t border-zinc-800">
|
||||
<Link
|
||||
href="/blog"
|
||||
className="flex items-center text-[var(--neon-pink)] hover:text-[var(--neon-magenta)] transition-all font-mono text-sm uppercase border border-[var(--neon-pink)] px-4 py-2 hover:shadow-[0_0_6px_rgba(155,90,110,0.3)]"
|
||||
>
|
||||
<svg className="mr-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
[BACK TO BLOG]
|
||||
</Link>
|
||||
</nav>
|
||||
</article>
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{ __html: post.content }} />
|
||||
</div>
|
||||
|
||||
<nav className="flex justify-between items-center mt-12 pt-8 border-t border-gray-200 dark:border-gray-700">
|
||||
<Link href="/blog" className="flex items-center text-primary-600 hover:text-primary-700 transition">
|
||||
<svg className="mr-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Înapoi la blog
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<RelatedPosts posts={relatedPosts} />
|
||||
</article>
|
||||
<StickyFooter url={fullUrl} title={post.frontmatter.title} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user