feat(AutogenPanel): показываем следующую тему в карточке категории

Каждая карточка теперь показывает:
  - жанровый бейдж: [ТУТОРИАЛ] синий / [СРАВНЕНИЕ] фиолетовый /
    [МНЕНИЕ] янтарный / [ДАЙДЖЕСТ] зелёный
  - название темы (без маркера, line-clamp-2)
  - если уже генерировали сегодня → '✓ уже сгенерировано сегодня'

parseTopic() разбирает [ЖАНР] префикс из blog_topics.topic.
next_topic приходит из getAutogenStatus() через subquery в SQL.
This commit is contained in:
Aleksei Pavlov
2026-06-21 21:38:13 +03:00
parent d3f13f906f
commit adb4266e5e
+49
View File
@@ -76,6 +76,29 @@ function buildCatMap(categories) {
return map;
}
// Разбирает [ЖАНР] из названия темы → { genre, title, color }
function parseTopic(topic) {
if (!topic) return { genre: null, title: topic, color: 'neutral' };
const m = topic.match(/^\[([^\]]+)\]\s*(.*)/s);
if (!m) return { genre: null, title: topic, color: 'neutral' };
const genres = {
'ТУТОРИАЛ': { label: 'Туториал', color: 'blue' },
'СРАВНЕНИЕ': { label: 'Сравнение', color: 'purple' },
'МНЕНИЕ': { label: 'Мнение', color: 'amber' },
'ДАЙДЖЕСТ': { label: 'Дайджест', color: 'emerald' },
};
const g = genres[m[1]] || { label: m[1], color: 'neutral' };
return { genre: g.label, title: m[2], color: g.color };
}
const GENRE_COLORS = {
blue: 'bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-300',
purple: 'bg-purple-100 text-purple-700 dark:bg-purple-950 dark:text-purple-300',
amber: 'bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-300',
emerald: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-300',
neutral: 'bg-neutral-100 text-neutral-600 dark:bg-neutral-800 dark:text-neutral-400',
};
export default function AutogenPanel({ status, queue, topics, categories = [] }) {
const CAT_LABELS = buildCatMap(categories);
const router = useRouter();
@@ -224,6 +247,32 @@ export default function AutogenPanel({ status, queue, topics, categories = [] })
<div className="text-xs opacity-70">{s.article_count || 0} статей · {s.topic_count_free ?? s.topic_count ?? 0} тем свободно</div>
</div>
</div>
{/* Следующая тема */}
{s.next_topic && (() => {
const { genre, title, color } = parseTopic(s.next_topic);
const drafted = parseInt(s.drafts_today, 10) > 0;
return (
<div className={`mt-2.5 rounded-lg px-3 py-2 text-xs ${drafted ? 'opacity-40' : 'bg-white/50 dark:bg-black/20'}`}>
<div className="flex items-center gap-1.5 mb-1">
{drafted
? <span className="opacity-70"> уже сгенерировано сегодня</span>
: <>
<span className="opacity-60">Следующая тема:</span>
{genre && (
<span className={`px-1.5 py-0.5 rounded text-[10px] font-semibold ${GENRE_COLORS[color]}`}>
{genre}
</span>
)}
</>
}
</div>
{!drafted && (
<div className="font-medium leading-snug opacity-90 line-clamp-2">{title}</div>
)}
</div>
);
})()}
<button
onClick={() => toggleCategory(s.category, !s.enabled)}
className={`text-xs px-2 py-1 rounded-full font-medium border transition-colors ${