feat(AutogenPanel): «Очередь тем» → «Планируется завтра»

Блок теперь показывает черновики сегодняшней генерации:
  - миниатюра обложки + название + категория
  - кнопка «Редактировать» → /admin/articles/:id
  - кнопка удалить черновик
  - предупреждение если сгенерировано < 4 статей
  - пустое состояние «появятся в 17:00 МСК»
  - ссылка «Все черновики →» на /admin/drafts
This commit is contained in:
Aleksei Pavlov
2026-06-21 21:17:10 +03:00
parent acc5c63b0a
commit d3f13f906f
+39 -47
View File
@@ -1,4 +1,5 @@
'use client'; 'use client';
import Link from 'next/link';
import { useState } from 'react'; import { useState } from 'react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { Play, Plus, Trash2, RefreshCw, Clock, CheckCircle, XCircle, Zap } from 'lucide-react'; import { Play, Plus, Trash2, RefreshCw, Clock, CheckCircle, XCircle, Zap } from 'lucide-react';
@@ -291,74 +292,65 @@ export default function AutogenPanel({ status, queue, topics, categories = [] })
})} })}
</div> </div>
{/* Очередь тем */} {/* Планируется завтра — черновики сегодняшней генерации */}
<div className="bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800"> <div className="bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800">
<div className="flex items-center justify-between px-5 py-4 border-b border-neutral-100 dark:border-neutral-800"> <div className="flex items-center justify-between px-5 py-4 border-b border-neutral-100 dark:border-neutral-800">
<div>
<h2 className="text-sm font-semibold text-neutral-900 dark:text-neutral-100"> <h2 className="text-sm font-semibold text-neutral-900 dark:text-neutral-100">
Очередь тем <span className="text-neutral-400 font-normal">({pendingQueue.length})</span> Планируется завтра
<span className="ml-2 text-neutral-400 font-normal text-xs">
{pendingQueue.length > 0 ? `${pendingQueue.length} из 4 готово` : 'генерация в 17:00 МСК'}
</span>
</h2> </h2>
<button <p className="text-xs text-neutral-400 mt-0.5">
onClick={() => setShowAddForm(v => !v)} Черновики которые выйдут завтра. Редактируй до 07:00 потом авто-публикация.
className="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg border border-neutral-200 dark:border-neutral-700 text-sm hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors" </p>
>
<Plus className="w-3.5 h-3.5" /> Добавить тему
</button>
</div> </div>
<Link href="/admin/drafts"
{showAddForm && ( className="inline-flex items-center gap-1 px-3 py-1.5 rounded-lg border border-neutral-200 dark:border-neutral-700 text-sm hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors text-neutral-600 dark:text-neutral-400">
<div className="px-5 py-4 border-b border-neutral-100 dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-800/50"> Все черновики
<div className="flex gap-3"> </Link>
<select
value={newCat}
onChange={e => setNewCat(e.target.value)}
className="px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-800 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500 shrink-0"
>
{Object.entries(CAT_LABELS).map(([v, l]) => (
<option key={v} value={v}>{l.icon} {l.name}</option>
))}
</select>
<input
type="text"
value={newTopic}
onChange={e => setNewTopic(e.target.value)}
onKeyDown={e => e.key === 'Enter' && addTopic()}
placeholder="Тема статьи..."
className="flex-1 px-3 py-2 rounded-lg border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-800 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500"
autoFocus
/>
<button
onClick={addTopic}
disabled={addingTopic || !newTopic.trim()}
className="px-4 py-2 rounded-lg bg-emerald-500 hover:bg-emerald-600 disabled:opacity-50 text-white text-sm font-medium transition-colors shrink-0"
>
{addingTopic ? '...' : 'Добавить'}
</button>
</div> </div>
</div>
)}
<div className="divide-y divide-neutral-100 dark:divide-neutral-800"> <div className="divide-y divide-neutral-100 dark:divide-neutral-800">
{pendingQueue.length === 0 && !showAddForm && ( {pendingQueue.length === 0 && (
<div className="px-5 py-8 text-center text-sm text-neutral-400"> <div className="px-5 py-8 text-center text-sm text-neutral-400">
Очередь пуста темы берутся из банка автоматически <div className="text-2xl mb-2"></div>
Черновики появятся сегодня в 17:00 МСК
</div> </div>
)} )}
{pendingQueue.map(item => { {pendingQueue.map(item => {
const cat = CAT_LABELS[item.category]; const cat = CAT_LABELS[item.category] || { icon: '📝', name: item.category, color: 'neutral' };
return ( return (
<div key={item.id} className="flex items-center gap-3 px-5 py-3"> <div key={item.id} className="flex items-center gap-3 px-5 py-3.5 hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-colors">
<span className="text-lg shrink-0">{cat?.icon || '📝'}</span> {item.cover_url
? <img src={item.cover_url} alt="" className="w-12 h-9 rounded-md object-cover shrink-0 bg-neutral-100" />
: <div className="w-12 h-9 rounded-md bg-neutral-100 dark:bg-neutral-800 flex items-center justify-center text-xl shrink-0">{cat.icon}</div>
}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="text-sm text-neutral-900 dark:text-neutral-100 truncate">{item.topic}</div> <div className="text-sm font-medium text-neutral-900 dark:text-neutral-100 truncate">{item.title || '—'}</div>
<div className="text-xs text-neutral-400">{cat?.name} · приоритет {item.priority}</div> <div className="text-xs text-neutral-400 mt-0.5">{cat.icon} {cat.name}</div>
</div> </div>
<button onClick={() => removeTopic(item.id)} className="p-1.5 rounded text-neutral-300 hover:text-red-500 transition-colors"> <div className="flex items-center gap-1 shrink-0">
<Link href={`/admin/articles/${item.id}`}
className="px-2.5 py-1.5 rounded-lg text-xs border border-neutral-200 dark:border-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors">
Редактировать
</Link>
<button onClick={() => removeTopic(item.id)}
className="p-1.5 rounded text-neutral-300 hover:text-red-500 transition-colors" title="Удалить черновик">
<Trash2 className="w-3.5 h-3.5" /> <Trash2 className="w-3.5 h-3.5" />
</button> </button>
</div> </div>
</div>
); );
})} })}
</div> </div>
{pendingQueue.length > 0 && pendingQueue.length < 4 && (
<div className="px-5 py-3 bg-amber-50 dark:bg-amber-950/20 border-t border-amber-100 dark:border-amber-900/30 text-xs text-amber-700 dark:text-amber-400 rounded-b-xl">
Сгенерировано {pendingQueue.length} из 4 статей. Запусти генерацию вручную для недостающих категорий.
</div>
)}
</div> </div>
{/* Банк тем */} {/* Банк тем */}