forked from admin/zeropost-tool
feat: billing complete — plans page, admin billing, credit cost hints
/plans: страница тарифов с карточками, стоимостью операций, FAQ /system → Биллинг: таблица пользователей с кредитами, ручное начисление ChannelView: badge стоимости (2кр текст + 5кр картинка) под кнопкой генерации Ошибка INSUFFICIENT_CREDITS → понятное сообщение После генерации — event credits-updated → обновление badge в header Header: подписка на credits-updated event API роуты: /api/billing/plans, /api/billing/admin/users, /api/billing/admin/credit
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
'use client';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Loader2, Save, Eye, EyeOff, RefreshCw, Check, AlertCircle, BarChart3 } from 'lucide-react';
|
||||
import { Loader2, Save, Eye, EyeOff, RefreshCw, Check, AlertCircle, BarChart3, Coins } from 'lucide-react';
|
||||
import AdminBilling from './admin/AdminBilling';
|
||||
|
||||
const TABS_SYS = [
|
||||
{ id: 'settings', label: 'Настройки API' },
|
||||
{ id: 'billing', label: 'Биллинг' },
|
||||
];
|
||||
|
||||
// Категории, которые управляются здесь (в админке tool, а не в админке блога).
|
||||
// Категория `engine` (TELEGRAM_API_BASE и т.п.) намеренно живёт в zeropost.ru/admin.
|
||||
const CATEGORIES = [
|
||||
{ slug: 'ai_providers', title: 'AI провайдеры',
|
||||
hint: 'Ключи, URL и модели для текстовой и картиночной генерации. Меняются на лету — рестарт engine не нужен. Курс USD↔РУБ и наценка реселлера тут же — влияют на расчёт стоимости в блоке «Расход AI» выше.' },
|
||||
@@ -12,6 +16,7 @@ const CATEGORIES = [
|
||||
];
|
||||
|
||||
export default function SystemSettings() {
|
||||
const [sysTab, setSysTab] = useState('settings');
|
||||
const [byCategory, setByCategory] = useState({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
@@ -60,6 +65,21 @@ export default function SystemSettings() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Вкладки системной страницы */}
|
||||
<div className="flex gap-1 border-b border-border pb-0">
|
||||
{TABS_SYS.map(t => (
|
||||
<button key={t.id} onClick={() => setSysTab(t.id)}
|
||||
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors -mb-px ${
|
||||
sysTab === t.id ? 'border-accent text-accent' : 'border-transparent text-gray-400 hover:text-gray-200'
|
||||
}`}>
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{sysTab === 'billing' && <AdminBilling />}
|
||||
|
||||
{sysTab === 'settings' && (<>
|
||||
<UsageSummary />
|
||||
{CATEGORIES.map(cat => (
|
||||
<CategoryBlock
|
||||
@@ -69,6 +89,7 @@ export default function SystemSettings() {
|
||||
onSaved={() => load()}
|
||||
/>
|
||||
))}
|
||||
</>)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user