Files
zeropost-web/components/NowBlock.js
T
Alexey Pavlov c27985614e feat: блок «Сейчас» + «Заметки редактора» + ArticleMeta
- NowBlock: live indicator (последняя статья / идёт генерация) + bar-чарт за 7 дней
- NotesBlock: карточки заметок редактора с pin
- /notes: отдельная страница со всеми заметками
- ArticleMeta: раскрывающийся блок «Как сделана эта статья» на странице статьи
- В шапку добавлена ссылка «Заметки» (desktop и mobile)
2026-05-31 10:05:28 +03:00

122 lines
5.2 KiB
JavaScript

import Link from 'next/link';
import { Activity, Cpu, Calendar, Sparkles, Zap } from 'lucide-react';
import { formatDate } from '@/lib/markdown';
// Сколько прошло времени с даты
function timeAgo(iso) {
if (!iso) return null;
const seconds = Math.max(0, Math.floor((Date.now() - new Date(iso).getTime()) / 1000));
const mins = Math.floor(seconds / 60);
const hours = Math.floor(mins / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days} дн назад`;
if (hours > 0) return `${hours} ч назад`;
if (mins > 0) return `${mins} мин назад`;
return 'только что';
}
export default function NowBlock({ live }) {
if (!live) return null;
const { latest, processing, week } = live;
const maxCnt = Math.max(1, ...week.map(d => d.cnt));
return (
<section className="container-wide pb-12">
<div className="flex items-center gap-2 mb-4 sm:mb-5">
<span className="relative flex h-2.5 w-2.5">
<span
className="absolute inline-flex h-full w-full rounded-full opacity-75 animate-ping"
style={{ background: 'rgb(var(--accent))' }}
/>
<span
className="relative inline-flex rounded-full h-2.5 w-2.5"
style={{ background: 'rgb(var(--accent))' }}
/>
</span>
<h2 className="text-xs sm:text-sm font-medium uppercase tracking-widest mute">Сейчас</h2>
</div>
<div className="grid lg:grid-cols-3 gap-4">
{/* Главная карточка — последняя статья / процесс */}
<div className="article-card lg:col-span-2 p-5 sm:p-6">
{processing ? (
<>
<div className="flex items-center gap-2 text-sm accent mb-2">
<Sparkles className="w-4 h-4 animate-pulse" />
<span className="font-medium">ИИ пишет статью</span>
</div>
<div className="text-lg sm:text-xl ink font-semibold leading-snug mb-2 line-clamp-2">
«{processing.topic}»
</div>
<div className="text-xs mute">
Началось {timeAgo(processing.created_at)} · подожди немного, скоро появится
</div>
</>
) : latest ? (
<Link href={`/blog/${latest.slug}`} className="block group">
<div className="flex items-center gap-2 text-xs mute mb-2">
<Activity className="w-3.5 h-3.5" />
<span>Последний материал {timeAgo(latest.published_at)}</span>
</div>
<div className="text-lg sm:text-xl ink font-semibold leading-snug mb-3 group-hover:accent transition-colors line-clamp-2">
{latest.title}
</div>
<div className="flex flex-wrap items-center gap-3 text-xs mute">
<span className="inline-flex items-center gap-1">
<Cpu className="w-3 h-3" /> claude-sonnet-4-6
</span>
{latest.tokens_out && (
<span className="inline-flex items-center gap-1">
<Zap className="w-3 h-3" /> {latest.tokens_out.toLocaleString('ru-RU')} токенов
</span>
)}
</div>
</Link>
) : (
<div className="mute text-sm">Скоро здесь что-нибудь появится</div>
)}
</div>
{/* Bar-чарт за 7 дней */}
<div className="article-card p-5 sm:p-6">
<div className="flex items-center gap-2 text-xs mute mb-3">
<Calendar className="w-3.5 h-3.5" />
<span className="uppercase tracking-wider">Активность за неделю</span>
</div>
<div className="flex items-end gap-1.5 h-20 mb-2">
{week.map((d, i) => {
const h = d.cnt > 0 ? Math.max(10, (d.cnt / maxCnt) * 100) : 4;
const isToday = i === week.length - 1;
return (
<div key={d.day} className="flex-1 flex flex-col items-center gap-1 group">
<div
className="w-full rounded-md transition-all"
style={{
height: `${h}%`,
background: d.cnt > 0
? (isToday ? 'rgb(var(--accent))' : 'rgb(var(--accent) / 0.45)')
: 'rgb(var(--surface-2))',
}}
title={`${d.day}: ${d.cnt} ${d.cnt === 1 ? 'статья' : 'статей'}`}
/>
</div>
);
})}
</div>
<div className="flex justify-between text-[10px] mute">
{week.map((d, i) => {
const date = new Date(d.day);
const dow = ['вс','пн','вт','ср','чт','пт','сб'][date.getDay()];
return (
<span key={d.day} className={i === week.length - 1 ? 'accent font-medium' : ''}>
{dow}
</span>
);
})}
</div>
</div>
</div>
</section>
);
}