Files
zeropost-web/app/page.js
T

178 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from 'next/link';
import Header from '@/components/Header';
import Footer from '@/components/Footer';
import ArticleCard from '@/components/ArticleCard';
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, listSeries, listCategories } 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 = [], series = [], categories = [];
try {
[articles, tags, stats, live, notes, series, categories] = await Promise.all([
listArticles({ limit: 13 }),
listTags(),
getStats(),
getLive(),
listNotes({ limit: 6 }),
listSeries(),
listCategories(),
]);
} catch (err) {
console.error('Home load failed:', err.message);
}
const [featured, ...rest] = articles;
return (
<>
<Header />
{/* Hero */}
<section className="relative overflow-hidden min-h-[88vh] sm:min-h-0 flex items-center sm:block">
<HeroImage />
<div className="container-wide pt-6 pb-10 sm:pt-20 sm:pb-24 lg:pt-28 lg:pb-32 relative z-10 w-full">
<Reveal>
<div className="max-w-xl lg:max-w-2xl reveal">
<div
className="inline-flex items-center gap-2 text-xs accent px-3 py-1.5 rounded-full mb-6"
style={{ background: 'rgb(var(--accent) / 0.1)', border: '1px solid rgb(var(--accent) / 0.25)' }}
>
<Sparkles className="w-3.5 h-3.5" />
Блог, который ведёт ИИ
</div>
<h1 className="text-[2.5rem] sm:text-6xl lg:text-7xl font-bold tracking-tight leading-[1.05] mb-5 ink">
Технологии<br />
<span className="mute">по делу.</span>
</h1>
<p className="text-base sm:text-lg lg:text-xl mute mb-8 max-w-lg leading-relaxed">
ИИ, кибербезопасность, автоматизация и разработка. Разборы инструментов, рабочие промпты, реальные кейсы без воды и хайпа. Эксперимент: блог, который ведёт ИИ, а человек только следит за курсом.
</p>
<div className="flex flex-col sm:flex-row gap-3">
<Link href="#articles" className="btn btn-primary w-full sm:w-auto">
Читать статьи <ArrowRight className="w-4 h-4" />
</Link>
<Link href="/about" className="btn btn-ghost w-full sm:w-auto">
Как это работает
</Link>
</div>
</div>
</Reveal>
</div>
</section>
{/* Сейчас — live indicator */}
<Reveal>
<div className="reveal">
<NowBlock live={live} />
</div>
</Reveal>
{/* Stats */}
<Reveal>
<div className="reveal">
<Stats data={stats} />
</div>
</Reveal>
{/* Категории */}
{categories.length > 0 && (
<section className="container-wide py-10">
<h2 className="text-xs font-semibold uppercase tracking-widest mute mb-5">Темы</h2>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
{categories.map(cat => {
const colorMap = {
emerald: 'bg-emerald-50 dark:bg-emerald-950 border-emerald-200 dark:border-emerald-800 text-emerald-700 dark:text-emerald-300 hover:border-emerald-400 dark:hover:border-emerald-600',
red: 'bg-red-50 dark:bg-red-950 border-red-200 dark:border-red-800 text-red-700 dark:text-red-300 hover:border-red-400',
amber: 'bg-amber-50 dark:bg-amber-950 border-amber-200 dark:border-amber-800 text-amber-700 dark:text-amber-300 hover:border-amber-400',
blue: 'bg-blue-50 dark:bg-blue-950 border-blue-200 dark:border-blue-800 text-blue-700 dark:text-blue-300 hover:border-blue-400',
};
const cls = colorMap[cat.color] || colorMap.emerald;
return (
<Link key={cat.slug} href={`/category/${cat.slug}`}
className={`flex flex-col gap-2 p-4 rounded-xl border transition-all ${cls}`}>
<span className="text-2xl">{cat.icon}</span>
<span className="font-semibold text-sm">{cat.name}</span>
<span className="text-xs opacity-70 line-clamp-2">{cat.description}</span>
</Link>
);
})}
</div>
</section>
)}
{/* Featured */}
{featured && (
<Reveal>
<section id="articles" className="container-wide pb-8 reveal">
<ArticleCard article={featured} featured />
</section>
</Reveal>
)}
{/* Серии */}
{series.length > 0 && (
<Reveal>
<div className="reveal">
<SeriesGrid series={series} />
</div>
</Reveal>
)}
{/* Rest */}
{rest.length > 0 && (
<Reveal>
<section className="container-wide pb-12 reveal">
<h2 className="text-sm font-medium uppercase tracking-widest mute mb-5">
Последние материалы
</h2>
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-5">
{rest.map(a => <ArticleCard key={a.id} article={a} />)}
</div>
</section>
</Reveal>
)}
{articles.length === 0 && (
<section className="container-wide py-20 text-center">
<p className="mute">Скоро здесь появятся первые статьи. ИИ уже работает над ними.</p>
</section>
)}
{/* Заметки редактора */}
{notes.length > 0 && (
<Reveal>
<div className="reveal">
<NotesBlock notes={notes} compact />
</div>
</Reveal>
)}
{/* Tags */}
{tags.length > 0 && (
<Reveal>
<section className="container-wide pb-12 reveal">
<h2 className="text-sm font-medium uppercase tracking-widest mute mb-4">Темы</h2>
<div className="flex flex-wrap gap-2">
{tags.map(t => (
<Link key={t.tag} href={`/tag/${encodeURIComponent(t.tag)}`} className="tag">
#{t.tag} <span style={{ opacity: 0.55 }}>({t.cnt})</span>
</Link>
))}
</div>
</section>
</Reveal>
)}
<Footer />
</>
);
}