Files
netrunner-landing/components/sections/BlogPreview.tsx
T
Трапезников Кирилл Иванович 4b11fec8cc base with cms integrated
2026-04-18 21:16:55 +10:00

134 lines
4.2 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import Link from "next/link";
import Image from "next/image";
import {
Card,
CardHeader,
CardTitle,
CardContent,
CardFooter,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { ArrowRight, Loader2 } from "lucide-react";
import { pb, getPbImage } from "@/lib/pb";
export interface PBPost {
id: string;
collectionId: string;
title: string;
slug: string;
excerpt: string;
content: string;
image: string;
created: string;
}
export function BlogPreview({ lang }: { lang: string }) {
const [posts, setPosts] = useState<PBPost[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!lang) return;
async function fetchPosts() {
try {
setLoading(true);
// Берем только 3 последние опубликованные статьи для текущего языка
const result = await pb.collection("posts").getList<PBPost>(1, 3, {
filter: `lang = "${lang}" && published = true`,
sort: "-created", // Свежие сверху
requestKey: null,
});
setPosts(result.items);
} catch (error) {
console.error("Ошибка загрузки логов:", error);
} finally {
setLoading(false);
}
}
fetchPosts();
}, [lang]);
if (loading) {
return (
<div className="flex justify-center py-20 text-primary opacity-50">
<Loader2 className="w-8 h-8 animate-spin" />
</div>
);
}
if (posts.length === 0) return null; // Скрываем секцию, если постов нет
return (
<section id="blog" className="py-24 px-4 container mx-auto max-w-7xl">
<div className="flex flex-col md:flex-row justify-between items-end mb-12 gap-6">
<div>
<Badge
variant="outline"
className="mb-4 text-primary border-primary/30"
>
Data Logs
</Badge>
<h2 className="text-4xl font-bold tracking-tighter">
Сетевые Хроники
</h2>
</div>
<Button
asChild
variant="ghost"
className="text-muted-foreground hover:text-primary"
>
<Link href={`/${lang}/blog`} className="flex items-center gap-2">
Все записи <ArrowRight size={16} />
</Link>
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{posts.map((post) => (
<Card
key={post.id}
className="group overflow-hidden border-border/50 bg-card/30 hover:border-primary/50 transition-all duration-300"
>
{post.image && (
<div className="relative w-full h-48 overflow-hidden border-b border-border/50">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={getPbImage(post, post.image)}
alt={post.title}
className="object-cover w-full h-full group-hover:scale-105 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-background to-transparent opacity-80" />
</div>
)}
<CardHeader>
<div className="text-xs font-mono text-muted-foreground mb-2">
{new Date(post.created).toLocaleDateString(
lang === "ru" ? "ru-RU" : "en-US",
)}
</div>
<CardTitle className="text-xl group-hover:text-primary transition-colors line-clamp-2">
{post.title}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground line-clamp-3">
{post.excerpt}
</p>
</CardContent>
<CardFooter>
<Button asChild variant="link" className="px-0 text-primary">
<Link href={`/${lang}/blog/${post.slug}`}>Читать далее</Link>
</Button>
</CardFooter>
</Card>
))}
</div>
</section>
);
}