diff --git a/app/page.js b/app/page.js index 600a796..e31eb6f 100644 --- a/app/page.js +++ b/app/page.js @@ -6,21 +6,23 @@ import HeroImage from '@/components/HeroImage'; import Stats from '@/components/Stats'; import NowBlock from '@/components/NowBlock'; import NotesBlock from '@/components/NotesBlock'; +import SeriesGrid from '@/components/SeriesGrid'; import Reveal from '@/components/Reveal'; -import { listArticles, listTags, getStats, getLive, listNotes } from '@/lib/engine'; +import { listArticles, listTags, getStats, getLive, listNotes, listSeries } from '@/lib/engine'; import { Sparkles, ArrowRight } from 'lucide-react'; export const dynamic = 'force-dynamic'; export default async function HomePage() { - let articles = [], tags = [], stats = null, live = null, notes = []; + let articles = [], tags = [], stats = null, live = null, notes = [], series = []; try { - [articles, tags, stats, live, notes] = await Promise.all([ + [articles, tags, stats, live, notes, series] = await Promise.all([ listArticles({ limit: 13 }), listTags(), getStats(), getLive(), listNotes({ limit: 6 }), + listSeries(), ]); } catch (err) { console.error('Home load failed:', err.message); @@ -88,6 +90,15 @@ export default async function HomePage() { )} + {/* Серии */} + {series.length > 0 && ( + +
+ +
+
+ )} + {/* Rest */} {rest.length > 0 && ( diff --git a/app/series/[slug]/page.js b/app/series/[slug]/page.js new file mode 100644 index 0000000..e31d5e1 --- /dev/null +++ b/app/series/[slug]/page.js @@ -0,0 +1,83 @@ +import { notFound } from 'next/navigation'; +import Link from 'next/link'; +import Header from '@/components/Header'; +import Footer from '@/components/Footer'; +import ArticleCard from '@/components/ArticleCard'; +import { getSeries } from '@/lib/engine'; +import { Sparkles, Plug, Zap, Layers, ArrowLeft } from 'lucide-react'; + +const ICONS = { Sparkles, Plug, Zap, Layers }; +const COLORS = { + emerald: { bg: 'rgb(16 185 129 / 0.1)', text: '#10b981' }, + teal: { bg: 'rgb(20 184 166 / 0.1)', text: '#14b8a6' }, + amber: { bg: 'rgb(245 158 11 / 0.1)', text: '#f59e0b' }, + indigo: { bg: 'rgb(99 102 241 / 0.1)', text: '#6366f1' }, +}; + +export const dynamic = 'force-dynamic'; + +export async function generateMetadata({ params }) { + const { slug } = await params; + const s = await getSeries(slug); + if (!s) return { title: 'Серия не найдена' }; + return { + title: s.title, + description: s.intro, + }; +} + +export default async function SeriesPage({ params }) { + const { slug } = await params; + const s = await getSeries(slug); + if (!s) notFound(); + const Icon = ICONS[s.icon] || Layers; + const color = COLORS[s.color] || COLORS.emerald; + + return ( + <> +
+
+
+ + Все статьи + + +
+
+ +
+
Серия
+
+ +

+ {s.title} +

+ {s.intro && ( +

{s.intro}

+ )} +
+ {s.articles?.length || 0} {(s.articles?.length || 0) === 1 ? 'материал' : 'материалов'} в серии +
+
+ + {s.articles?.length > 0 ? ( +
+
+ {s.articles.map(a => )} +
+
+ ) : ( +
+
+ Скоро здесь появятся статьи. Серия только формируется. +
+
+ )} +
+