feat: вкладка Подключение в ChannelEdit (bot token, TG channel id, VK token)

This commit is contained in:
Ник (Claude)
2026-06-10 15:22:45 +03:00
parent 80e962463b
commit e330ac3871
+63 -1
View File
@@ -2,7 +2,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import Link from 'next/link'; import Link from 'next/link';
import { ArrowLeft, Save, Trash2, Loader2, Image as ImageIcon, Type, Palette, Plus, X, Sparkles } from 'lucide-react'; import { ArrowLeft, Save, Trash2, Loader2, Image as ImageIcon, Type, Palette, Plus, X, Sparkles, Plug } from 'lucide-react';
const GOALS = [ const GOALS = [
{ v: 'educational', label: 'Обучение', desc: 'Объясняем, разбираем' }, { v: 'educational', label: 'Обучение', desc: 'Объясняем, разбираем' },
@@ -63,6 +63,7 @@ const IMAGE_PALETTES = [
const TABS = [ const TABS = [
{ id: 'content', label: 'Контент', icon: Type }, { id: 'content', label: 'Контент', icon: Type },
{ id: 'images', label: 'Картинки', icon: ImageIcon }, { id: 'images', label: 'Картинки', icon: ImageIcon },
{ id: 'connect', label: 'Подключение', icon: Plug },
]; ];
export default function ChannelEdit({ channel }) { export default function ChannelEdit({ channel }) {
@@ -95,6 +96,14 @@ export default function ChannelEdit({ channel }) {
const [imageCustomColors, setImageCustomColors] = useState(style.image_custom_colors || ''); const [imageCustomColors, setImageCustomColors] = useState(style.image_custom_colors || '');
const [imagePromptInstructions, setImagePromptInstructions] = useState(style.image_prompt_instructions || ''); const [imagePromptInstructions, setImagePromptInstructions] = useState(style.image_prompt_instructions || '');
// Подключение
const [botToken, setBotToken] = useState(channel.bot_token || '');
const [tgChannelId, setTgChannelId] = useState(channel.tg_channel_id || '');
const [tgUsername, setTgUsername] = useState(channel.tg_username || '');
const [vkToken, setVkToken] = useState(channel.vk_access_token || '');
const [tokenVerifying, setTokenVerifying] = useState(false);
const [tokenStatus, setTokenStatus] = useState(null); // null | 'ok' | 'error'
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [deleting, setDeleting] = useState(false); const [deleting, setDeleting] = useState(false);
const [error, setError] = useState(''); const [error, setError] = useState('');
@@ -106,6 +115,10 @@ export default function ChannelEdit({ channel }) {
try { try {
const data = { const data = {
name, niche, audience, goal: goals.join(','), language, name, niche, audience, goal: goals.join(','), language,
bot_token: botToken.trim() || null,
tg_channel_id: tgChannelId.trim() || null,
tg_username: tgUsername.trim() || null,
vk_access_token: vkToken.trim() || null,
style: { style: {
tone, formality, humor, tone, formality, humor,
post_length: postLength, post_length: postLength,
@@ -417,6 +430,55 @@ export default function ChannelEdit({ channel }) {
)} )}
</div> </div>
)} )}
{/* TAB: Подключение */}
{tab === 'connect' && (
<div className="space-y-5">
<div className="card p-5 space-y-4">
<div className="flex items-center gap-2 mb-1">
<span className="text-lg"></span>
<h3 className="font-semibold">Telegram</h3>
</div>
<div className="bg-surface2/50 rounded-lg p-3 text-xs text-gray-400 space-y-1 border border-border">
<div className="font-medium text-gray-300 mb-2">Как подключить:</div>
<div>1. Создай бота через <span className="text-accent">@BotFather</span> <code>/newbot</code> скопируй токен</div>
<div>2. Добавь бота <b>администратором</b> в свой канал (права: публикация сообщений)</div>
<div>3. Узнай ID канала: перешли любое сообщение из канала боту <span className="text-accent">@userinfobot</span></div>
</div>
<div>
<label className="label">Bot Token</label>
<input className="input font-mono text-sm" placeholder="7123456789:AAHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
value={botToken} onChange={e => { setBotToken(e.target.value); setTokenStatus(null); }} type="password" />
</div>
<div className="grid sm:grid-cols-2 gap-4">
<div>
<label className="label">ID канала</label>
<input className="input font-mono text-sm" placeholder="-1001234567890"
value={tgChannelId} onChange={e => setTgChannelId(e.target.value)} />
<div className="hint">Начинается с -100</div>
</div>
<div>
<label className="label">Username канала</label>
<input className="input font-mono text-sm" placeholder="@mychannel"
value={tgUsername} onChange={e => setTgUsername(e.target.value)} />
<div className="hint">Необязательно если заполнен ID</div>
</div>
</div>
</div>
<div className="card p-5 space-y-4">
<div className="flex items-center gap-2 mb-1">
<span className="text-lg">🅱</span>
<h3 className="font-semibold">ВКонтакте</h3>
</div>
<div>
<label className="label">Access Token группы</label>
<input className="input font-mono text-sm" placeholder="vk1.a.xxx..."
value={vkToken} onChange={e => setVkToken(e.target.value)} type="password" />
<div className="hint">Управление API Ключи доступа Создать ключ (права: wall, photos)</div>
</div>
</div>
</div>
)}
</main> </main>
); );
} }