// Главная страница приложения (Home Page) // Показывает статус подписки пользователя и кнопки действий // URL: https://app.umbrix.net/ 'use client'; // Next.js 13: это клиентский компонент (с useState, useEffect) import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import QRCodeModal from '@/components/QRCodeModal'; import ReferralModal from '@/components/ReferralModal'; import { marzbanApi } from '@/lib/marzban-api'; import { getSubscriptionUrl, MARZBAN_SUBSCRIPTION_URL } from '@/lib/constants'; import { Shield, Settings, Gift, DollarSign, Wrench, User, HelpCircle, ExternalLink, X, Key, CreditCard, Lock, Eye, Ban, MessageCircle, UserPlus, QrCode, Copy, ChevronRight, Share2, } from 'lucide-react'; export default function Home() { const router = useRouter(); const [hasSubscription, setHasSubscription] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false); const [isKeyMenuOpen, setIsKeyMenuOpen] = useState(false); const [isQROpen, setIsQROpen] = useState(false); const [isReferralOpen, setIsReferralOpen] = useState(false); const [subscriptionStatus, setSubscriptionStatus] = useState<'active' | 'expired' | 'none'>('none'); const [expiryDate, setExpiryDate] = useState(''); const [showToast, setShowToast] = useState(false); const [toastMessage, setToastMessage] = useState(''); const [subscriptionToken, setSubscriptionToken] = useState(null); const [isLoading, setIsLoading] = useState(true); const subscriptionUrl = subscriptionToken ? getSubscriptionUrl(subscriptionToken) : MARZBAN_SUBSCRIPTION_URL; const showToastNotification = (message: string) => { setToastMessage(message); setShowToast(true); setTimeout(() => setShowToast(false), 3000); }; // Загружаем подписку при монтировании - ЧЕРЕЗ API, НЕ localStorage! useEffect(() => { const loadSubscription = async () => { // Получаем Telegram данные const telegramWebApp = (window as any).Telegram?.WebApp; const telegramId = telegramWebApp?.initDataUnsafe?.user?.id; const telegramUsername = telegramWebApp?.initDataUnsafe?.user?.username; console.log('🔍 Loading subscription for:', { telegramId, telegramUsername }); if (!telegramId && !telegramUsername) { console.log('⚠️ No Telegram data - probably opened in browser'); setIsLoading(false); return; } try { // Запрашиваем подписку по Telegram ID через API const params = new URLSearchParams(); if (telegramId) params.append('telegramId', telegramId.toString()); if (telegramUsername) params.append('telegramUsername', telegramUsername); const response = await fetch(`/api/user-subscription?${params.toString()}`); const data = await response.json(); console.log('📥 API response:', data); if (data.success && data.hasSubscription) { setSubscriptionToken(data.token); setHasSubscription(true); const status = data.status === 'active' ? 'active' : 'expired'; setSubscriptionStatus(status); if (data.expire) { setExpiryDate(marzbanApi.formatExpireDate(data.expire)); } } else { console.log('❌ No subscription found'); } } catch (error) { console.error('Failed to load subscription:', error); } setIsLoading(false); }; loadSubscription(); }, []); // Загружаем при каждом монтировании компонента // ОТКЛЮЧАЕМ автоматическую проверку API при загрузке (чтобы не зависала страница) // API проверка будет только на странице /subscription/[token] // useEffect(() => { // async function checkSubscription() { // try { // const userData = await marzbanApi.getUserInfo(subscriptionToken); // // if (userData.status === 'active') { // setHasSubscription(true); // setSubscriptionStatus('active'); // setExpiryDate(marzbanApi.formatExpireDate(userData.expire)); // } else if (userData.status === 'expired') { // setHasSubscription(false); // setSubscriptionStatus('expired'); // setExpiryDate(marzbanApi.formatExpireDate(userData.expire)); // } else { // setHasSubscription(false); // setSubscriptionStatus('none'); // } // } catch (error) { // console.error('Failed to check subscription:', error); // setHasSubscription(false); // setSubscriptionStatus('none'); // } // } // // checkSubscription(); // }, []); const copyToClipboard = async (text: string) => { try { await navigator.clipboard.writeText(text); showToastNotification('✅ Скопировано в буфер обмена!'); } catch (err) { console.error('Failed to copy:', err); showToastNotification('❌ Ошибка копирования'); } }; const shareReferralLink = async () => { // Генерируем реферальную ссылку (TODO: заменить на реальный user ID после авторизации) const userId = subscriptionToken?.split('_')[0] || 'DEMO'; const referralUrl = `https://t.me/umbrix_bot?start=ref_${userId}`; const shareText = `🚀 Попробуй Umbrix VPN - быстрый и безопасный VPN!\n\n✨ Получи 7 дней бесплатно по моей ссылке:\n${referralUrl}`; // Проверяем поддержку Web Share API if (navigator.share) { try { await navigator.share({ title: 'Umbrix VPN - Пригласи друга!', text: shareText, url: referralUrl, }); } catch (err) { // Пользователь отменил или ошибка if ((err as Error).name !== 'AbortError') { console.error('Share failed:', err); // Fallback: копируем в буфер copyToClipboard(shareText); } } } else { // Fallback для браузеров без поддержки Share API copyToClipboard(shareText); } }; return (
{/* Header */}
Umbrix
{/* Бегущая строка с преимуществами */}
Без логов
Без регистрации
Не отслеживаем
Не продаём ваш трафик
{/* Дублируем для бесшовной анимации */}
Без логов
Без регистрации
Не отслеживаем
Не продаём ваш трафик
{/* Main Content */}
{/* Hero Section - Логотип и статус */}
🛡️

Umbrix VPN

Быстрый и безопасный VPN

{/* Status */}
{hasSubscription ? ( Моя подписка ✅ Активна до {expiryDate || '...'} ) : subscriptionStatus === 'expired' ? (
⚠️ Подписка истекла {expiryDate}
) : (
⚠️ Нет подписки
)}
{/* Action Buttons */}
{/* Показываем trial только если нет активной подписки */} {!hasSubscription && ( } text="Попробовать 7 дней бесплатно" onClick={() => (window.location.href = '/plans')} /> )} } text="Купить подписку от 99₽" onClick={() => (window.location.href = '/plans')} /> } text="Настроить VPN" onClick={() => {}} />
{/* Bottom Navigation */} {/* Account Menu Modal */} {isMenuOpen && (
setIsMenuOpen(false)} >
e.stopPropagation()} > {/* Header */}

Меню аккаунта

{/* Menu Items */}
{hasSubscription ? ( <> } label="📊 Моя подписка" onClick={() => setIsMenuOpen(false)} /> } label="🔑 Ключ подписки" onClick={() => { setIsMenuOpen(false); setIsKeyMenuOpen(true); }} /> ) : ( } label="🛒 Купить подписку" onClick={() => setIsMenuOpen(false)} /> )} } label="💳 Тарифы" onClick={() => setIsMenuOpen(false)} /> } label="⚙️ Настройки" onClick={() => setIsMenuOpen(false)} /> } label="💬 Поддержка" onClick={() => { setIsMenuOpen(false); router.push('/help'); }} /> } label="🎁 Пригласить друга" onClick={() => { setIsMenuOpen(false); setIsReferralOpen(true); }} />
)} {/* Key Subscription Menu Modal */} {isKeyMenuOpen && (
setIsKeyMenuOpen(false)} >
e.stopPropagation()} > {/* Header */}

Ключ подписки

{/* Menu Items */}
} label="Скопировать ссылку" onClick={() => { copyToClipboard(subscriptionUrl); setIsKeyMenuOpen(false); }} /> } label="Показать QR код" onClick={() => { setIsKeyMenuOpen(false); setIsQROpen(true); }} /> } label="Поделиться подпиской" onClick={() => { // TODO: Проверка тарифа, затем share sheet if (navigator.share) { navigator.share({ title: 'VPN подписка', text: 'Моя VPN подписка', url: subscriptionUrl, }); } else { copyToClipboard(subscriptionUrl); } setIsKeyMenuOpen(false); }} />
)} {/* QR Code Modal */} setIsQROpen(false)} url={subscriptionUrl} title="QR код подписки" /> {/* Referral Modal */} setIsReferralOpen(false)} referralUrl={`https://t.me/umbrix_bot?start=ref_${subscriptionToken?.split('_')[0] || 'DEMO'}`} onShare={() => { shareReferralLink(); setIsReferralOpen(false); }} onCopy={() => { const userId = subscriptionToken?.split('_')[0] || 'DEMO'; const referralUrl = `https://t.me/umbrix_bot?start=ref_${userId}`; copyToClipboard(referralUrl); setIsReferralOpen(false); }} /> {/* Toast Notification */} {showToast && (
{toastMessage}
)}
); } function ActionButton({ icon, text, onClick, }: { icon: React.ReactNode; text: string; onClick: () => void; }) { return ( ); } function NavButton({ icon, label, href, onClick, }: { icon: React.ReactNode; label: string; href?: string; onClick?: () => void; }) { if (onClick) { return ( ); } return (
{icon} {label}
); } function MenuButton({ icon, label, onClick, }: { icon: React.ReactNode; label: string; onClick: () => void; }) { return ( ); }