// ======================================================================= // MariusVPN Franchise — App shell (auth, sidebar, router). // ======================================================================= const { frApi } = window.FR_API; function FrShell() { const [me, setMe] = React.useState(null); const [booted, setBooted] = React.useState(false); const [tab, setTab] = React.useState('dashboard'); React.useEffect(() => { (async () => { // Telegram Mini App: if opened inside Telegram, auth via initData. const tg = window.Telegram && window.Telegram.WebApp; if (tg) { try { tg.ready(); tg.expand(); } catch (e) {} } if (tg && tg.initData) { try { await fetch('/api/tg/franchise-auth', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ init_data: tg.initData }), }); } catch (e) {} } try { setMe(await frApi('/whoami')); } catch (e) {} setBooted(true); })(); }, []); if (!booted) return
; if (!me) return setMe(m)}/>; return setMe(null)} tab={tab} setTab={setTab}/>; } const NAV = [ { id: 'dashboard', label: 'Дашборд', icon: ICONS.dashboard }, { id: 'users', label: 'Пользователи', icon: ICONS.users }, { id: 'broadcast', label: 'Рассылка', icon: ICONS.broadcast }, { id: 'settings', label: 'Настройки', icon: ICONS.settings }, { id: 'payouts', label: 'Выплаты', icon: ICONS.wallet }, ]; function FrApp({ me, onLogout, tab, setTab }) { const t = useToast(); const logout = async () => { try { await frApi('/logout', { method: 'POST' }); } catch (e) {} onLogout(); }; return (
{/* sidebar */} {/* main */}
{/* mobile topbar */}
{me.franchise_name || 'VPN'}
{tab === 'dashboard' && setTab('payouts')}/>} {tab === 'users' && } {tab === 'broadcast' && } {tab === 'settings' && } {tab === 'payouts' && }
); } // --------------- Mount --------------------------------------------------- const root = ReactDOM.createRoot(document.getElementById('root')); root.render( );