334b2f51df
- Журнальная главная: hero, CategoryRow, PopularBlock, RecentBlock (Сегодня/Вчера/Неделя) - ArticleCard: 3 размера (hero/regular/compact), цветной badge без дублей тегов - ArticleCoverSVG: 6 брендовых палитр, аватар Зеро в углу вместо #ZEROPOST - /about/zero: страница персонажа с галереей 8 поз - Footer: TG-баннер с аватаром Зеро на каждой странице - Конец статьи: блок «Понравилась? → Подписаться на канал» - ChannelEditor: 4 вкладки (Настройки/Расписание/Авто-публикация/Ручная) - AutoPublishTab: toggle, категории, delay, template, live preview - ArticlePicker: typeahead с was_sent_to_channel / next_scheduled_at флагами - /admin/channels/[id]/stats: график роста подписчиков (recharts) - Dashboard: блок TG-статистики (подписчики, delta 24h/7d, постов) - Header: упрощён до 2 пунктов desktop + расширенное мобильное меню - AutogenPanel: корректные time-picker'ы, calcNextRun с учётом last_run_at
62 lines
2.3 KiB
JavaScript
62 lines
2.3 KiB
JavaScript
import Link from 'next/link';
|
|
import ArticleCard from './ArticleCard';
|
|
import { ArrowRight } from 'lucide-react';
|
|
|
|
/**
|
|
* Группировка свежих материалов по дням: «Сегодня», «Вчера», «Эта неделя», «Ранее».
|
|
* Не рендерится, если пусто.
|
|
*/
|
|
function groupByPeriod(articles) {
|
|
const now = new Date();
|
|
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
const startOfYesterday = startOfToday - 24 * 3600 * 1000;
|
|
const startOfWeek = startOfToday - 7 * 24 * 3600 * 1000;
|
|
|
|
const groups = { today: [], yesterday: [], week: [], earlier: [] };
|
|
for (const a of articles) {
|
|
const t = new Date(a.published_at).getTime();
|
|
if (t >= startOfToday) groups.today.push(a);
|
|
else if (t >= startOfYesterday) groups.yesterday.push(a);
|
|
else if (t >= startOfWeek) groups.week.push(a);
|
|
else groups.earlier.push(a);
|
|
}
|
|
return groups;
|
|
}
|
|
|
|
const LABELS = {
|
|
today: 'Сегодня',
|
|
yesterday: 'Вчера',
|
|
week: 'На этой неделе',
|
|
earlier: 'Ранее',
|
|
};
|
|
|
|
export default function RecentBlock({ articles }) {
|
|
if (!articles || articles.length === 0) return null;
|
|
const groups = groupByPeriod(articles);
|
|
const nonEmpty = Object.entries(groups).filter(([, arr]) => arr.length > 0);
|
|
if (nonEmpty.length === 0) return null;
|
|
|
|
return (
|
|
<section className="container-wide pb-12">
|
|
<div className="flex items-end justify-between mb-5">
|
|
<h2 className="text-xl sm:text-2xl font-bold ink">Свежие материалы</h2>
|
|
<Link href="/archive" className="text-sm font-medium inline-flex items-center gap-1 accent hover:opacity-80 transition-opacity">
|
|
Архив <ArrowRight className="w-3.5 h-3.5" />
|
|
</Link>
|
|
</div>
|
|
<div className="space-y-8">
|
|
{nonEmpty.map(([key, arr]) => (
|
|
<div key={key}>
|
|
<h3 className="text-xs font-semibold uppercase tracking-widest mute mb-3">
|
|
{LABELS[key]} <span className="opacity-50">· {arr.length}</span>
|
|
</h3>
|
|
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-5">
|
|
{arr.map(a => <ArticleCard key={a.id} article={a} />)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|