🖼️ added images support

- Should investigate how to resize the image from .md specs
This commit is contained in:
RJ
2025-11-21 16:15:15 +02:00
committed by Rares J
parent 41b32b13f2
commit 1042a43dfa
26 changed files with 871 additions and 274 deletions

View File

@@ -24,7 +24,7 @@ export default async function BlogPostBreadcrumb({
}) {
const { slug } = await params
const slugPath = slug.join('/')
const post = getPostBySlug(slugPath)
const post = await getPostBySlug(slugPath)
const items: BreadcrumbItem[] = [
{

View File

@@ -20,7 +20,7 @@ export async function generateMetadata({
}): Promise<Metadata> {
const { slug } = await params
const slugPath = slug.join('/')
const post = getPostBySlug(slugPath)
const post = await getPostBySlug(slugPath)
if (!post) {
return { title: 'Articol negăsit' }
@@ -68,7 +68,7 @@ function extractHeadings(content: string) {
export default async function BlogPostPage({ params }: { params: Promise<{ slug: string[] }> }) {
const { slug } = await params
const slugPath = slug.join('/')
const post = getPostBySlug(slugPath)
const post = await getPostBySlug(slugPath)
if (!post) {
notFound()

View File

@@ -1,4 +1,4 @@
import Link from 'next/link';
import Link from 'next/link'
export default function TagNotFound() {
return (
@@ -36,5 +36,5 @@ export default function TagNotFound() {
</div>
</div>
</div>
);
)
}

View File

@@ -1,30 +1,25 @@
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import Link from 'next/link';
import {
getAllTags,
getPostsByTag,
getTagInfo,
getRelatedTags
} from '@/lib/tags';
import { TagList } from '@/components/blog/tag-list';
import { formatDate } from '@/lib/utils';
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
import Link from 'next/link'
import { getAllTags, getPostsByTag, getTagInfo, getRelatedTags } from '@/lib/tags'
import { TagList } from '@/components/blog/tag-list'
import { formatDate } from '@/lib/utils'
export async function generateStaticParams() {
const tags = await getAllTags();
return tags.map(tag => ({ tag: tag.slug }));
const tags = await getAllTags()
return tags.map(tag => ({ tag: tag.slug }))
}
export async function generateMetadata({
params,
}: {
params: Promise<{ tag: string }>;
params: Promise<{ tag: string }>
}): Promise<Metadata> {
const { tag } = await params;
const tagInfo = await getTagInfo(tag);
const { tag } = await params
const tagInfo = await getTagInfo(tag)
if (!tagInfo) {
return { title: 'Tag negăsit' };
return { title: 'Tag negăsit' }
}
return {
@@ -34,7 +29,7 @@ export async function generateMetadata({
title: `Tag: ${tagInfo.name}`,
description: `Explorează ${tagInfo.count} articole despre ${tagInfo.name}`,
},
};
}
}
function PostCard({ post }: { post: any }) {
@@ -49,47 +44,34 @@ function PostCard({ post }: { post: any }) {
)}
<div className="flex items-center gap-2 font-mono text-xs text-zinc-500 mb-3 uppercase">
<time dateTime={post.frontmatter.date}>
{formatDate(post.frontmatter.date)}
</time>
<time dateTime={post.frontmatter.date}>{formatDate(post.frontmatter.date)}</time>
<span>&gt;</span>
<span>{post.readingTime} min</span>
</div>
<h2 className="font-mono text-lg font-bold mb-3 uppercase">
<Link
href={`/blog/${post.slug}`}
className="text-cyan-400 hover:text-cyan-300 transition"
>
<Link href={`/blog/${post.slug}`} className="text-cyan-400 hover:text-cyan-300 transition">
{post.frontmatter.title}
</Link>
</h2>
<p className="text-zinc-400 mb-4 line-clamp-3">
{post.frontmatter.description}
</p>
<p className="text-zinc-400 mb-4 line-clamp-3">{post.frontmatter.description}</p>
{post.frontmatter.tags && (
<TagList tags={post.frontmatter.tags} variant="minimal" />
)}
{post.frontmatter.tags && <TagList tags={post.frontmatter.tags} variant="minimal" />}
</article>
);
)
}
export default async function TagPage({
params,
}: {
params: Promise<{ tag: string }>;
}) {
const { tag } = await params;
const tagInfo = await getTagInfo(tag);
export default async function TagPage({ params }: { params: Promise<{ tag: string }> }) {
const { tag } = await params
const tagInfo = await getTagInfo(tag)
if (!tagInfo) {
notFound();
notFound()
}
const posts = await getPostsByTag(tag);
const relatedTags = await getRelatedTags(tag);
const posts = await getPostsByTag(tag)
const relatedTags = await getRelatedTags(tag)
return (
<div className="min-h-screen bg-slate-950 text-slate-100">
@@ -122,9 +104,7 @@ export default async function TagPage({
<div className="lg:col-span-3">
{posts.length === 0 ? (
<div className="border-4 border-slate-800 bg-slate-900 p-12 text-center">
<p className="font-mono text-zinc-400 mb-6 uppercase">
&gt; NO DOCUMENTS FOUND
</p>
<p className="font-mono text-zinc-400 mb-6 uppercase">&gt; NO DOCUMENTS FOUND</p>
<Link
href="/blog"
className="inline-block px-6 py-3 font-mono text-sm uppercase border-2 border-cyan-400 bg-cyan-900 text-cyan-100 hover:bg-cyan-800 transition"
@@ -156,12 +136,8 @@ export default async function TagPage({
href={`/tags/${tag.slug}`}
className="flex items-center justify-between p-2 border border-slate-700 hover:border-cyan-400 hover:bg-slate-800 transition"
>
<span className="font-mono text-xs uppercase text-zinc-300">
#{tag.name}
</span>
<span className="font-mono text-xs text-zinc-500">
[{tag.count}]
</span>
<span className="font-mono text-xs uppercase text-zinc-300">#{tag.name}</span>
<span className="font-mono text-xs text-zinc-500">[{tag.count}]</span>
</Link>
))}
</div>
@@ -170,9 +146,7 @@ export default async function TagPage({
<div className="border-2 border-slate-700 bg-slate-900 p-6">
<div className="border-b-2 border-slate-700 pb-3 mb-4">
<h2 className="font-mono text-sm font-bold uppercase text-cyan-400">
QUICK NAV
</h2>
<h2 className="font-mono text-sm font-bold uppercase text-cyan-400">QUICK NAV</h2>
</div>
<div className="space-y-2">
<Link
@@ -199,5 +173,5 @@ export default async function TagPage({
</div>
</div>
</div>
);
)
}

View File

@@ -1,17 +1,17 @@
import { Metadata } from 'next';
import Link from 'next/link';
import { getAllTags, getTagCloud } from '@/lib/tags';
import { TagCloud } from '@/components/blog/tag-cloud';
import { TagBadge } from '@/components/blog/tag-badge';
import { Metadata } from 'next'
import Link from 'next/link'
import { getAllTags, getTagCloud } from '@/lib/tags'
import { TagCloud } from '@/components/blog/tag-cloud'
import { TagBadge } from '@/components/blog/tag-badge'
export const metadata: Metadata = {
title: 'Tag-uri',
description: 'Explorează articolele după tag-uri',
};
}
export default async function TagsPage() {
const allTags = await getAllTags();
const tagCloud = await getTagCloud();
const allTags = await getAllTags()
const tagCloud = await getTagCloud()
if (allTags.length === 0) {
return (
@@ -21,9 +21,7 @@ export default async function TagsPage() {
<h1 className="font-mono text-3xl font-bold uppercase mb-4 text-cyan-400">
TAG DATABASE
</h1>
<p className="font-mono text-zinc-400 mb-8">
&gt; NO TAGS AVAILABLE
</p>
<p className="font-mono text-zinc-400 mb-8">&gt; NO TAGS AVAILABLE</p>
<Link
href="/blog"
className="inline-block px-6 py-3 font-mono text-sm uppercase border-2 border-cyan-400 bg-cyan-900 text-cyan-100 hover:bg-cyan-800 transition"
@@ -33,19 +31,22 @@ export default async function TagsPage() {
</div>
</div>
</div>
);
)
}
const groupedTags = allTags.reduce((acc, tag) => {
const firstLetter = tag.name[0].toUpperCase();
if (!acc[firstLetter]) {
acc[firstLetter] = [];
}
acc[firstLetter].push(tag);
return acc;
}, {} as Record<string, typeof allTags>);
const groupedTags = allTags.reduce(
(acc, tag) => {
const firstLetter = tag.name[0].toUpperCase()
if (!acc[firstLetter]) {
acc[firstLetter] = []
}
acc[firstLetter].push(tag)
return acc
},
{} as Record<string, typeof allTags>
)
const sortedLetters = Object.keys(groupedTags).sort();
const sortedLetters = Object.keys(groupedTags).sort()
return (
<div className="min-h-screen bg-slate-950 text-slate-100">
@@ -57,9 +58,7 @@ export default async function TagsPage() {
<h1 className="font-mono text-4xl font-bold uppercase text-cyan-400 mb-4">
TAG REGISTRY
</h1>
<p className="font-mono text-lg text-zinc-400">
&gt; TOTAL TAGS: {allTags.length}
</p>
<p className="font-mono text-lg text-zinc-400">&gt; TOTAL TAGS: {allTags.length}</p>
</div>
<section className="mb-12">
@@ -109,38 +108,28 @@ export default async function TagsPage() {
<p className="font-mono text-xs text-cyan-600 uppercase tracking-widest mb-2">
DOCUMENT STATISTICS
</p>
<h2 className="font-mono text-2xl font-bold uppercase text-cyan-400">
TAG METRICS
</h2>
<h2 className="font-mono text-2xl font-bold uppercase text-cyan-400">TAG METRICS</h2>
</div>
<div className="grid gap-6 sm:grid-cols-3">
<div className="border-2 border-cyan-700 bg-slate-900 p-6 text-center">
<div className="font-mono text-3xl font-bold text-cyan-400">
{allTags.length}
</div>
<div className="font-mono text-xs text-zinc-400 uppercase mt-2">
TOTAL TAGS
</div>
<div className="font-mono text-3xl font-bold text-cyan-400">{allTags.length}</div>
<div className="font-mono text-xs text-zinc-400 uppercase mt-2">TOTAL TAGS</div>
</div>
<div className="border-2 border-cyan-700 bg-slate-900 p-6 text-center">
<div className="font-mono text-3xl font-bold text-cyan-400">
{Math.max(...allTags.map(t => t.count))}
</div>
<div className="font-mono text-xs text-zinc-400 uppercase mt-2">
MAX POSTS/TAG
</div>
<div className="font-mono text-xs text-zinc-400 uppercase mt-2">MAX POSTS/TAG</div>
</div>
<div className="border-2 border-cyan-700 bg-slate-900 p-6 text-center">
<div className="font-mono text-3xl font-bold text-cyan-400">
{Math.round(allTags.reduce((sum, t) => sum + t.count, 0) / allTags.length)}
</div>
<div className="font-mono text-xs text-zinc-400 uppercase mt-2">
AVG POSTS/TAG
</div>
<div className="font-mono text-xs text-zinc-400 uppercase mt-2">AVG POSTS/TAG</div>
</div>
</div>
</section>
</div>
</div>
);
)
}