// Cabinet — dashboard: balance, catalog, recent orders, refill. // SupportChat is mounted at the app root (web/static/components/SupportChat.jsx). const { useState: useCabinetState, useEffect: useCabinetEffect } = React; const SERVICES = [ { id: 'pf', abbr: 'ПФ', name: 'Авито ПФ', desc: 'Просмотры, лайки, контакты для объявлений', price: 'от 6 ₽/ПФ', available: true, route: 'order-pf' }, { id: 'reviews', abbr: 'ОТЗ', name: 'Отзывы', desc: 'Накрутка / удаление: Авито, ВК, Яндекс, 2ГИС, Google', price: 'по тарифу', available: true, route: null }, { id: 'ypf', abbr: 'ЯПФ', name: 'Яндекс ПФ', desc: 'Поведенческие факторы для Яндекс', price: null, badge: 'В разработке', available: false, route: null }, { id: 'seo', abbr: 'SEO', name: 'SEO-буст', desc: 'Ссылочное продвижение и рост позиций', price: null, badge: 'Скоро', available: false, route: null }, { id: 'copy', abbr: 'КП', name: 'Копирайтинг', desc: 'Тексты для объявлений и карточек', price: null, badge: 'Скоро', available: false, route: null }, { id: 'smm', abbr: 'SMM', name: 'SMM', desc: 'Ведение соцсетей и создание контента', price: null, badge: 'Скоро', available: false, route: null }, ]; const PRESETS = [500, 1000, 2000]; function StatusBadge({ status }) { const map = { Posted: 'posted', Completed: 'completed', Cancelled: 'cancelled', Pending: 'pending' }; const labels = { Posted: 'В работе', Completed: 'Завершён', Cancelled: 'Отменён', Pending: 'Ожидание' }; return {labels[status] || status}; } // Backend stores Авито PF orders with position_name like "7/30" (days/views). // Display nicer service name in tables. function displayServiceName(o) { if (/^\d+\/\d+$/.test(String(o.position_name || ''))) return 'Авито ПФ'; return o.position_name || '—'; } function CabinetPage({ user, balance, setBalance, refreshBalance, onNavigate }) { const [recentOrders, setRecentOrders] = useCabinetState([]); const [refillAmount, setRefillAmount] = useCabinetState(1000); const [customMode, setCustomMode] = useCabinetState(false); const [refillStatus, setRefillStatus] = useCabinetState(null); const [refillPaymentId, setRefillPaymentId] = useCabinetState(null); const [refillErrorMessage, setRefillErrorMessage] = useCabinetState(null); const refillBusy = refillStatus === 'pending' || refillStatus === 'polling'; const refillAmountValid = Number(refillAmount) >= 100; const openSupportForRefill = () => { const text = `Хочу пополнить баланс на ${Number(refillAmount).toLocaleString('ru-RU')} ₽, но через сайт не получается. Помогите, пожалуйста.`; window.dispatchEvent(new CustomEvent('support-chat-send', { detail: { text } })); }; useCabinetEffect(() => { api.get('/api/orders?page=1&page_size=5').then(data => { if (!data.__unauthorized) setRecentOrders(data.items || []); }).catch(() => {}); }, []); const handleRefill = async () => { if (!refillAmountValid) return; setRefillErrorMessage(null); setRefillStatus('pending'); try { const data = await api.post('/api/refill', { amount: Number(refillAmount), agreed_privacy: true, agreed_offer: true, }); setRefillPaymentId(data.payment_id); window.open(data.payment_url, '_blank'); setRefillStatus('polling'); } catch (e) { if (e.status >= 400 && e.status < 500 && e.message) { setRefillErrorMessage(e.message); } setRefillStatus('error'); } }; const selectPreset = (p) => { setRefillAmount(p); setCustomMode(false); }; const enterCustomMode = () => { setCustomMode(true); }; const exitCustomMode = () => { setCustomMode(false); setRefillAmount(1000); }; const checkRefillStatus = async () => { if (!refillPaymentId) return; try { const data = await api.get(`/api/refill/${refillPaymentId}/status`); if (data.status === 'succeeded') { setRefillStatus('success'); refreshBalance(); setTimeout(() => { setRefillStatus(null); setRefillPaymentId(null); }, 4000); } else if (data.status === 'failed') { setRefillStatus('error'); } } catch (_) {} }; const handleServiceClick = (service) => { if (!service.available) return; if (service.route) onNavigate(service.route); else alert(`Услуга "${service.name}" — оформление через менеджера в Telegram`); }; return (
Личный кабинет · Управляйте заказами и балансом
| # | Услуга | Сумма | Статус | Дата |
|---|---|---|---|---|
| #{o.order_id} | {displayServiceName(o)} | {o.price.toLocaleString('ru-RU')} ₽ | {formatDisplay(o.date) || '—'} |