💳 Page: Тарифы - выбор плана подписки VPN

This commit is contained in:
Umbrix Dev
2026-02-04 05:20:11 +03:00
parent 0be39ced0d
commit 43402a00c7

257
app/plans/page.tsx Normal file
View File

@@ -0,0 +1,257 @@
'use client';
import { useState } from 'react';
import { ArrowLeft, Check } from 'lucide-react';
import { useRouter } from 'next/navigation';
export default function Plans() {
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const handlePurchase = async (planType: string) => {
// Проверяем есть ли уже активная подписка ЧЕРЕЗ API
const telegramWebApp = (window as any).Telegram?.WebApp;
const telegramId = telegramWebApp?.initDataUnsafe?.user?.id;
const telegramUsername = telegramWebApp?.initDataUnsafe?.user?.username;
if (telegramId || telegramUsername) {
const params = new URLSearchParams();
if (telegramId) params.append('telegramId', telegramId.toString());
if (telegramUsername) params.append('telegramUsername', telegramUsername);
const checkResponse = await fetch(`/api/user-subscription?${params.toString()}`);
const checkData = await checkResponse.json();
if (checkData.success && checkData.hasSubscription) {
const confirmOverwrite = confirm('У вас уже есть активная подписка. Создать новую? (старая будет заменена)');
if (!confirmOverwrite) {
return;
}
}
}
setIsLoading(true);
try {
const telegramWebApp = (window as any).Telegram?.WebApp;
console.log('🔍 Telegram WebApp объект:', telegramWebApp);
console.log('🔍 initData:', telegramWebApp?.initData);
console.log('🔍 initDataUnsafe (FULL):', JSON.stringify(telegramWebApp?.initDataUnsafe, null, 2));
console.log('🔍 user (FULL):', JSON.stringify(telegramWebApp?.initDataUnsafe?.user, null, 2));
const user = telegramWebApp?.initDataUnsafe?.user;
const telegramId = user?.id || null;
const telegramUsername = user?.username || null;
const firstName = user?.first_name || null;
const lastName = user?.last_name || null;
console.log('📤 Отправляем:', { planType, telegramId, telegramUsername, firstName, lastName });
// Вызываем API для создания реального пользователя в Marzban
const response = await fetch('/api/create-user', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
planType: planType,
telegramId: telegramId,
telegramUsername: telegramUsername,
firstName: firstName,
lastName: lastName,
}),
});
const data = await response.json();
if (!data.success) {
throw new Error(data.error || 'Failed to create subscription');
}
// Просто возвращаемся на главную - там данные обновятся автоматически
router.push('/');
} catch (error) {
console.error('Purchase error:', error);
alert('Ошибка при создании подписки. Попробуйте позже.');
} finally {
setIsLoading(false);
}
};
return (
<div
className="min-h-screen"
style={{ background: 'var(--bg-app)', color: 'var(--text-primary)' }}
>
{/* Header */}
<header
className="flex items-center gap-3 p-4 border-b sticky top-0 z-10 backdrop-blur-sm"
style={{ borderColor: 'var(--border)', background: 'var(--bg-card)' }}
>
<button onClick={() => router.push('/')} className="p-2 hover:bg-slate-700 rounded-lg transition-colors">
<ArrowLeft className="w-6 h-6" />
</button>
<h1
className="text-xl font-bold"
style={{ color: 'var(--text-white)' }}
>
Выбрать тариф
</h1>
</header>
<main className="p-4">
{/* Trial - полная ширина */}
<div className="mb-3">
<PlanCard
badge="🎁 Пробный период"
title="7 дней бесплатно"
price="0₽"
features={[
'Все тарифы доступны',
'Безлимитный трафик',
'Любые локации',
]}
buttonText="Попробовать"
isPrimary
onPurchase={() => handlePurchase('trial')}
isLoading={isLoading}
/>
</div>
{/* Grid 2x2 для остальных тарифов */}
<div className="grid grid-cols-2 gap-3">
{/* Start */}
<PlanCard
badge="🌍 Старт"
title="Базовый"
price="100₽/мес"
features={['1 устройство', '1 локация', '50 ГБ']}
buttonText="Выбрать"
onPurchase={() => handlePurchase('start')}
isLoading={isLoading}
/>
{/* Plus */}
<PlanCard
badge="🌎 Плюс"
title="Расширенный"
price="299₽/мес"
features={['3 устройства', '3 локации', '299 ГБ']}
buttonText="Выбрать"
onPurchase={() => handlePurchase('plus')}
isLoading={isLoading}
/>
{/* Max */}
<PlanCard
badge="🌏 Макс"
title="Премиум"
price="350₽/мес"
features={['5 устройств', '15+ стран', 'Безлимит']}
buttonText="Выбрать"
isPopular
onPurchase={() => handlePurchase('max')}
isLoading={isLoading}
/>
{/* Empty slot or promo */}
<div
className="p-4 rounded-xl border flex items-center justify-center"
style={{
background: 'var(--bg-card)',
borderColor: 'var(--border)',
borderStyle: 'dashed',
}}
>
<div className="text-center opacity-50">
<div className="text-2xl mb-1">💰</div>
<div className="text-xs">Скоро новые</div>
<div className="text-xs">тарифы</div>
</div>
</div>
</div>
</main>
</div>
);
}
function PlanCard({
badge,
title,
price,
features,
buttonText,
isPrimary = false,
isPopular = false,
onPurchase,
isLoading = false,
}: {
badge: string;
title: string;
price: string;
features: string[];
buttonText: string;
isPrimary?: boolean;
isPopular?: boolean;
onPurchase: () => void;
isLoading?: boolean;
}) {
return (
<div
className="p-3 rounded-xl border relative"
style={{
background: 'var(--bg-card)',
borderColor: isPrimary
? 'var(--primary)'
: isPopular
? 'var(--primary)'
: 'var(--border)',
}}
>
{isPopular && (
<div
className="absolute -top-2 -right-2 px-2 py-0.5 rounded-full text-xs font-bold"
style={{ background: 'var(--primary)', color: 'var(--text-white)' }}
>
ТОП
</div>
)}
<div className="text-xs mb-1 opacity-70">{badge}</div>
<h3
className="font-bold mb-1 text-sm"
style={{ color: 'var(--text-white)' }}
>
{title}
</h3>
<div
className="text-xl font-bold mb-2"
style={{ color: 'var(--primary)' }}
>
{price}
</div>
<ul className="space-y-1.5 mb-3">
{features.map((feature, i) => (
<li key={i} className="flex items-center gap-1.5 text-xs">
<Check
className="w-3.5 h-3.5 flex-shrink-0"
style={{ color: 'var(--success)' }}
/>
<span>{feature}</span>
</li>
))}
</ul>
<button
onClick={onPurchase}
disabled={isLoading}
className="w-full py-2 rounded-lg font-semibold text-xs transition-all hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed"
style={{
background: isPrimary ? 'var(--primary)' : 'var(--bg-elevated)',
color: 'var(--text-white)',
}}
>
{isLoading ? 'Оформление...' : buttonText}
</button>
</div>
);
}