🏷️ added tags system
This commit is contained in:
44
components/blog/popular-tags.tsx
Normal file
44
components/blog/popular-tags.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import Link from 'next/link';
|
||||
import { getPopularTags } from '@/lib/tags';
|
||||
import { TagBadge } from './tag-badge';
|
||||
|
||||
export async function PopularTags({ limit = 5 }: { limit?: number }) {
|
||||
const tags = await getPopularTags(limit);
|
||||
|
||||
if (tags.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="border-2 border-slate-700 bg-slate-900 p-6">
|
||||
<div className="border-b-2 border-slate-700 pb-3 mb-4">
|
||||
<h3 className="font-mono text-sm font-bold uppercase text-cyan-400">
|
||||
POPULAR TAGS
|
||||
</h3>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{tags.map((tag, index) => (
|
||||
<Link
|
||||
key={tag.slug}
|
||||
href={`/tags/${tag.slug}`}
|
||||
className="flex items-center justify-between group p-2 border border-slate-700 hover:border-cyan-400 hover:bg-slate-800 transition"
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<span className="font-mono text-xs text-zinc-500">
|
||||
[{index + 1}]
|
||||
</span>
|
||||
<span className="font-mono text-xs uppercase group-hover:text-cyan-400 transition">
|
||||
#{tag.name}
|
||||
</span>
|
||||
</div>
|
||||
<TagBadge count={tag.count} />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<Link
|
||||
href="/tags"
|
||||
className="block mt-4 text-center font-mono text-xs text-cyan-400 hover:text-cyan-300 transition uppercase"
|
||||
>
|
||||
> VIEW ALL TAGS
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
20
components/blog/tag-badge.tsx
Normal file
20
components/blog/tag-badge.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
interface TagBadgeProps {
|
||||
count: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function TagBadge({ count, className = '' }: TagBadgeProps) {
|
||||
return (
|
||||
<span
|
||||
className={`
|
||||
inline-flex items-center justify-center
|
||||
px-2 py-1 font-mono text-xs font-bold
|
||||
bg-cyan-900 border border-cyan-700
|
||||
text-cyan-300
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{count}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
36
components/blog/tag-cloud.tsx
Normal file
36
components/blog/tag-cloud.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import Link from 'next/link';
|
||||
import { TagInfo } from '@/lib/tags';
|
||||
|
||||
interface TagCloudProps {
|
||||
tags: Array<TagInfo & { size: 'sm' | 'md' | 'lg' | 'xl' }>;
|
||||
}
|
||||
|
||||
export function TagCloud({ tags }: TagCloudProps) {
|
||||
const sizeClasses = {
|
||||
sm: 'text-xs opacity-70',
|
||||
md: 'text-sm',
|
||||
lg: 'text-base font-bold',
|
||||
xl: 'text-lg font-bold',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-4 items-baseline">
|
||||
{tags.map(tag => (
|
||||
<Link
|
||||
key={tag.slug}
|
||||
href={`/tags/${tag.slug}`}
|
||||
className={`
|
||||
${sizeClasses[tag.size]}
|
||||
font-mono uppercase
|
||||
text-zinc-400
|
||||
hover:text-cyan-400
|
||||
transition-colors
|
||||
`}
|
||||
title={`${tag.count} ${tag.count === 1 ? 'articol' : 'articole'}`}
|
||||
>
|
||||
#{tag.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
37
components/blog/tag-list.tsx
Normal file
37
components/blog/tag-list.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import Link from 'next/link';
|
||||
import { slugifyTag } from '@/lib/tags';
|
||||
|
||||
interface TagListProps {
|
||||
tags: (string | undefined)[];
|
||||
variant?: 'default' | 'minimal' | 'colored';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function TagList({ tags, variant = 'default', className = '' }: TagListProps) {
|
||||
const validTags = tags.filter(Boolean) as string[];
|
||||
|
||||
if (validTags.length === 0) return null;
|
||||
|
||||
const baseClasses = 'inline-flex items-center font-mono text-xs uppercase border transition-colors';
|
||||
|
||||
const variants = {
|
||||
default: 'px-3 py-1 bg-zinc-900 border-slate-700 text-zinc-400 hover:border-cyan-400 hover:text-cyan-400',
|
||||
minimal: 'px-2 py-0.5 border-transparent text-zinc-500 hover:text-cyan-400',
|
||||
colored: 'px-3 py-1 bg-cyan-900 border-cyan-700 text-cyan-300 hover:bg-cyan-800 hover:border-cyan-600',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`flex flex-wrap gap-2 ${className}`}>
|
||||
{validTags.map(tag => (
|
||||
<Link
|
||||
key={tag}
|
||||
href={`/tags/${slugifyTag(tag)}`}
|
||||
className={`${baseClasses} ${variants[variant]}`}
|
||||
>
|
||||
<span className="mr-1">#</span>
|
||||
{tag}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user