258 lines
8.2 KiB
TypeScript
258 lines
8.2 KiB
TypeScript
'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>
|
||
);
|
||
}
|