game update
This commit is contained in:
+265
-163
@@ -1,57 +1,86 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { motion, AnimatePresence, type Variants } from "framer-motion";
|
||||
import {
|
||||
Shield,
|
||||
Zap,
|
||||
Copy,
|
||||
Loader2,
|
||||
ArrowUpCircle,
|
||||
ArrowRight,
|
||||
Users,
|
||||
Activity,
|
||||
CheckCircle2,
|
||||
Globe,
|
||||
Terminal,
|
||||
Cpu,
|
||||
Database,
|
||||
} from "lucide-react";
|
||||
import { activateTrial, buySubscription, fetchProfile } from "./api";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
// 🔥 ИСПРАВЛЕНО: Полная локализация интерфейса
|
||||
// Mock API functions for standalone preview compilation
|
||||
const fetchProfile = async () => {
|
||||
return new Promise((resolve) =>
|
||||
setTimeout(
|
||||
() =>
|
||||
resolve({
|
||||
user: {
|
||||
tg_id: "0x1A4F",
|
||||
tg_username: "GhostRunner",
|
||||
balance: 8450,
|
||||
game_tickets: 5,
|
||||
has_used_trial: false,
|
||||
},
|
||||
subscription: {
|
||||
plan_name: "NETRUNNER",
|
||||
expires_at: new Date(
|
||||
Date.now() + 30 * 24 * 60 * 60 * 1000,
|
||||
).toISOString(),
|
||||
},
|
||||
recruits: 12,
|
||||
earned: 1200,
|
||||
}),
|
||||
1500,
|
||||
),
|
||||
);
|
||||
};
|
||||
const activateTrial = async () => Promise.resolve();
|
||||
const buySubscription = async (plan: string) => Promise.resolve();
|
||||
|
||||
const DICT = {
|
||||
en: {
|
||||
auth_ready: "App Authorization Ready",
|
||||
operative: "Operative",
|
||||
balance: "Balance",
|
||||
tickets: "Tickets",
|
||||
uplink_status: "Uplink Status",
|
||||
expires: "Expires",
|
||||
offline: "OFFLINE",
|
||||
no_active_sub: "No active subscription.",
|
||||
run_breach: "Run Breach Protocol",
|
||||
system_override: "System Override",
|
||||
manage_plan: "Manage Plan",
|
||||
acquire: "Acquire Tier",
|
||||
activate_trial: "Activate Trial",
|
||||
auth_ready: "SYSTEM ONLINE",
|
||||
operative: "Operative ID",
|
||||
balance: "Credits",
|
||||
tickets: "Access Tokens",
|
||||
uplink_status: "Node Status",
|
||||
expires: "Valid Until",
|
||||
offline: "DISCONNECTED",
|
||||
no_active_sub: "No active grid connection.",
|
||||
run_breach: "Execute Breach Protocol",
|
||||
system_override: "Sys.Override()",
|
||||
manage_plan: "Manage Uplink",
|
||||
acquire: "Acquire",
|
||||
activate_trial: "Initialize Trial",
|
||||
trial_used: "Trial Exhausted",
|
||||
syndicate: "Syndicate Network",
|
||||
recruits: "Recruits",
|
||||
earned: "Eddies Earned",
|
||||
ref_desc: "Expand the grid. Earn 100 E$ per initialized uplink.",
|
||||
copied: "Copied!",
|
||||
syndicate: "Syndicate Grid",
|
||||
recruits: "Nodes Hacked",
|
||||
earned: "Eddies Extracted",
|
||||
ref_desc: "Expand the network. Extract 100 E$ per initialized node.",
|
||||
copied: "Data copied to clipboard.",
|
||||
plans: {
|
||||
TRIAL: {
|
||||
tag: "Free Trial",
|
||||
tag: "Trial Run",
|
||||
desc: "Experience the Grid. 3 days of unrestricted access.",
|
||||
features: ["5GB Bandwidth", "No logs", "Standard Nodes"],
|
||||
},
|
||||
GHOST: {
|
||||
tag: "Personal Uplink",
|
||||
tag: "Stealth Node",
|
||||
desc: "The ideal choice for personal use. Minimal digital footprint.",
|
||||
features: ["1 device", "ChaCha20-Poly1305", "DPI-Analysis Protection"],
|
||||
features: ["1 device", "ChaCha20-Poly1305", "DPI Protection"],
|
||||
},
|
||||
NETRUNNER: {
|
||||
tag: "Professional Interface",
|
||||
desc: "For professionals working with multiple devices simultaneously.",
|
||||
features: ["5 devices", "Priority Support", "Built-in FakeDNS"],
|
||||
tag: "Pro Interface",
|
||||
desc: "For professionals working with multiple rigs simultaneously.",
|
||||
features: ["5 devices", "Priority Support", "FakeDNS Module"],
|
||||
},
|
||||
MAINFRAME: {
|
||||
tag: "Command Center",
|
||||
@@ -65,44 +94,44 @@ const DICT = {
|
||||
},
|
||||
},
|
||||
ru: {
|
||||
auth_ready: "Авторизация Успешна",
|
||||
operative: "Оперативник",
|
||||
balance: "Баланс",
|
||||
tickets: "Билеты",
|
||||
auth_ready: "СИСТЕМА АКТИВНА",
|
||||
operative: "ID Оперативника",
|
||||
balance: "Кредиты",
|
||||
tickets: "Токены Доступа",
|
||||
uplink_status: "Статус Узла",
|
||||
expires: "Истекает",
|
||||
expires: "Действует до",
|
||||
offline: "ОТКЛЮЧЕНО",
|
||||
no_active_sub: "Нет активной подписки.",
|
||||
run_breach: "Запустить Протокол Взлома",
|
||||
system_override: "Терминал Доступа",
|
||||
manage_plan: "Управление подпиской",
|
||||
acquire: "Арендовать",
|
||||
activate_trial: "Начать Триал",
|
||||
no_active_sub: "Нет активного подключения к Сети.",
|
||||
run_breach: "Протокол Взлома",
|
||||
system_override: "Sys.Override()",
|
||||
manage_plan: "Управление Узлом",
|
||||
acquire: "Интегрировать",
|
||||
activate_trial: "Запуск Триала",
|
||||
trial_used: "Триал Использован",
|
||||
syndicate: "Синдикат",
|
||||
recruits: "Рекруты",
|
||||
earned: "Заработано (E$)",
|
||||
ref_desc: "Расширяй сеть. Получай 100 E$ за каждый новый узел.",
|
||||
copied: "Скопировано!",
|
||||
recruits: "Взломано узлов",
|
||||
earned: "Извлечено (E$)",
|
||||
ref_desc: "Расширяй сеть. Извлекай 100 E$ за каждый новый узел.",
|
||||
copied: "Данные скопированы.",
|
||||
plans: {
|
||||
TRIAL: {
|
||||
tag: "Бесплатный Тест",
|
||||
tag: "Тестовый Прогон",
|
||||
desc: "Испытай Сеть. 3 дня безлимитного доступа.",
|
||||
features: ["5GB Трафика", "Без логов", "Стандартные узлы"],
|
||||
},
|
||||
GHOST: {
|
||||
tag: "Личный Узел",
|
||||
tag: "Стелс-Узел",
|
||||
desc: "Идеально для себя. Минимальный цифровой след.",
|
||||
features: ["1 устройство", "ChaCha20-Poly1305", "Защита от DPI"],
|
||||
},
|
||||
NETRUNNER: {
|
||||
tag: "Про-Интерфейс",
|
||||
desc: "Для работы со смартфоном, ноутом и планшетом одновременно.",
|
||||
features: ["5 устройств", "Приоритетная база", "FakeDNS Защита"],
|
||||
desc: "Для работы со смартфоном, декой и планшетом одновременно.",
|
||||
features: ["5 устройств", "Приоритетная база", "Модуль FakeDNS"],
|
||||
},
|
||||
MAINFRAME: {
|
||||
tag: "Командный Центр",
|
||||
desc: "Ультимативное решение для команд и независимости.",
|
||||
desc: "Ультимативное решение для команд и полной независимости.",
|
||||
features: ["Безлимит устройств", "High-Load сервера", "Поддержка 24/7"],
|
||||
},
|
||||
},
|
||||
@@ -121,6 +150,20 @@ const getLang = (): "en" | "ru" => {
|
||||
return l === "ru" ? "ru" : "en";
|
||||
};
|
||||
|
||||
// Анимационные варианты для элементов HUD
|
||||
const containerVariants: Variants = {
|
||||
hidden: { opacity: 0 },
|
||||
show: {
|
||||
opacity: 1,
|
||||
transition: { staggerChildren: 0.1 },
|
||||
},
|
||||
};
|
||||
|
||||
const itemVariants: Variants = {
|
||||
hidden: { opacity: 0, y: 10 },
|
||||
show: { opacity: 1, y: 0, transition: { type: "spring", stiffness: 100 } },
|
||||
};
|
||||
|
||||
export default function Profile() {
|
||||
const [data, setData] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -131,7 +174,7 @@ export default function Profile() {
|
||||
|
||||
const [lang, setLang] = useState<"en" | "ru">(getLang);
|
||||
const t = DICT[lang];
|
||||
const botUsername = import.meta.env.VITE_BOT_USERNAME || "";
|
||||
const botUsername = "NetrunnerBot";
|
||||
|
||||
useEffect(() => {
|
||||
fetchProfile()
|
||||
@@ -151,37 +194,43 @@ export default function Profile() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col items-center justify-center bg-[#05050A]">
|
||||
<Loader2 className="w-12 h-12 animate-spin mb-4 text-secondary drop-shadow-[0_0_15px_rgba(0,229,242,0.5)]" />
|
||||
<p className="tracking-[0.3em] uppercase text-sm animate-pulse text-secondary">
|
||||
Authenticating...
|
||||
</p>
|
||||
<div className="min-h-screen flex flex-col items-center justify-center bg-[#05050A] text-secondary font-mono">
|
||||
<div className="relative flex items-center justify-center mb-6">
|
||||
<div className="absolute inset-0 border-t-2 border-secondary rounded-full animate-spin w-16 h-16 opacity-50" />
|
||||
<Loader2 className="w-8 h-8 animate-spin" />
|
||||
</div>
|
||||
<div className="typewriter">
|
||||
<p className="tracking-[0.3em] uppercase text-xs animate-pulse opacity-80">
|
||||
Establishing secure uplink...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let currentData = data;
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col items-center justify-center bg-[#05050A] text-red-500 font-mono p-6 text-center">
|
||||
<Shield className="w-16 h-16 mb-4 text-red-600 opacity-80" />
|
||||
<h1 className="text-2xl font-black tracking-widest mb-2">
|
||||
ACCESS DENIED
|
||||
<div className="bg-red-500/10 p-4 rounded-full mb-6 border border-red-500/30">
|
||||
<Shield className="w-12 h-12 text-red-500" />
|
||||
</div>
|
||||
<h1 className="text-xl font-black tracking-widest mb-2 uppercase">
|
||||
[ CONNECTION REFUSED ]
|
||||
</h1>
|
||||
<p className="text-sm opacity-70 mb-6">
|
||||
{error || "No data received from core."}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Пожалуйста, откройте терминал через интерфейс Телеграм-бота.
|
||||
<p className="text-xs opacity-70 max-w-xs mt-2 text-muted-foreground">
|
||||
Handshake failed. Authenticate via secure Telegram terminal.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const user = data.user || {};
|
||||
const subscription = data.subscription;
|
||||
const user = currentData.user || {};
|
||||
const subscription = currentData.subscription;
|
||||
const hasSub = subscription && subscription.plan_name;
|
||||
const recruits = data.recruits || 0;
|
||||
const earned = data.earned || 0;
|
||||
const recruits = currentData.recruits || 0;
|
||||
const earned = currentData.earned || 0;
|
||||
|
||||
const copyRef = () => {
|
||||
navigator.clipboard.writeText(
|
||||
@@ -202,7 +251,7 @@ export default function Profile() {
|
||||
const newData = await fetchProfile();
|
||||
setData(newData);
|
||||
} catch (e: any) {
|
||||
alert(`[ERROR] ${e.message}`);
|
||||
alert(`[SYS.ERR] ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -212,100 +261,144 @@ export default function Profile() {
|
||||
const newData = await fetchProfile();
|
||||
setData(newData);
|
||||
} catch (e: any) {
|
||||
alert(`[ERROR] ${e.message}`);
|
||||
alert(`[SYS.ERR] ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-[#131124] via-[#08080C] to-black p-4 sm:p-6 pb-24 text-foreground font-mono">
|
||||
<header className="mb-8 text-center pt-8 relative z-10 flex flex-col items-center">
|
||||
<div className="min-h-screen bg-[#05050A] p-4 sm:p-6 pb-24 text-foreground font-mono relative overflow-hidden">
|
||||
{/* Cyberpunk Grid Background */}
|
||||
<div
|
||||
className="fixed inset-0 pointer-events-none z-0 opacity-20"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(to right, rgba(255,255,255,0.05) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(255,255,255,0.05) 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: "40px 40px",
|
||||
maskImage:
|
||||
"radial-gradient(ellipse at 50% 0%, black 10%, transparent 80%)",
|
||||
WebkitMaskImage:
|
||||
"radial-gradient(ellipse at 50% 0%, black 10%, transparent 80%)",
|
||||
}}
|
||||
/>
|
||||
|
||||
<header className="flex justify-between items-start mb-8 relative z-10 pt-2">
|
||||
<div>
|
||||
<div className="inline-flex items-center gap-2 text-secondary text-[10px] font-bold uppercase tracking-widest mb-1 opacity-80">
|
||||
<Activity className="w-3 h-3 animate-pulse" /> [{t.auth_ready}]
|
||||
</div>
|
||||
<h1 className="text-2xl font-black tracking-tight text-white/90 drop-shadow-[0_0_10px_rgba(255,255,255,0.1)] uppercase">
|
||||
Netrunner<span className="text-primary opacity-80">_OS</span>
|
||||
</h1>
|
||||
</div>
|
||||
<button
|
||||
onClick={toggleLang}
|
||||
className="absolute top-4 right-4 bg-white/5 border border-white/10 p-2 rounded-full text-muted-foreground hover:text-white transition-colors"
|
||||
className="bg-black/40 border border-white/10 p-2.5 rounded-lg text-muted-foreground hover:text-secondary hover:border-secondary/40 transition-colors backdrop-blur-md"
|
||||
>
|
||||
<Globe className="w-4 h-4" />
|
||||
</button>
|
||||
<div className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full border border-secondary/30 bg-secondary/5 text-secondary text-xs font-bold uppercase tracking-widest mb-4 shadow-[0_0_15px_rgba(0,229,242,0.1)]">
|
||||
<Shield className="w-4 h-4" /> {t.auth_ready}
|
||||
</div>
|
||||
<h1 className="text-3xl font-black tracking-tight drop-shadow-md">
|
||||
NETRUNNER_OS
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="grid gap-6 max-w-2xl mx-auto relative z-10"
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
className="grid gap-5 max-w-2xl mx-auto relative z-10"
|
||||
>
|
||||
<div className="flex justify-between items-start mb-8">
|
||||
<div>
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-widest mb-1">
|
||||
{t.operative}
|
||||
</p>
|
||||
<p className="text-sm font-bold opacity-80 text-primary">
|
||||
@{user.tg_username || user.tg_id || "Anon"}
|
||||
</p>
|
||||
{/* User Stats Card */}
|
||||
<motion.div
|
||||
variants={itemVariants}
|
||||
className="bg-white/[0.02] border border-white/10 rounded-xl p-5 backdrop-blur-md relative overflow-hidden"
|
||||
>
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-primary/10 rounded-full blur-[40px] -mr-10 -mt-10" />
|
||||
<div className="flex justify-between items-start mb-6 relative z-10">
|
||||
<div>
|
||||
<p className="text-muted-foreground text-[10px] uppercase tracking-widest mb-1 flex items-center gap-1">
|
||||
<Cpu className="w-3 h-3" /> {t.operative}
|
||||
</p>
|
||||
<p className="text-sm font-bold text-white/90 uppercase">
|
||||
@{user.tg_username || user.tg_id || "Anon"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-muted-foreground text-[10px] uppercase tracking-widest mb-1 flex justify-end items-center gap-1">
|
||||
<Database className="w-3 h-3" /> {t.balance}
|
||||
</p>
|
||||
<p className="text-2xl font-black text-secondary drop-shadow-[0_0_10px_rgba(0,229,242,0.4)]">
|
||||
{user.balance || 0}{" "}
|
||||
<span className="text-xs opacity-60">E$</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-widest mb-1">
|
||||
{t.balance} | {t.tickets}:{" "}
|
||||
<span className="text-primary">{user.game_tickets || 0}</span>
|
||||
</p>
|
||||
<p className="text-3xl font-black text-secondary drop-shadow-[0_0_12px_rgba(0,229,242,0.6)]">
|
||||
{user.balance || 0} <span className="text-sm opacity-70">E$</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-black/40 border border-white/5 rounded-2xl p-4">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<div className="bg-black/50 border border-white/5 rounded-lg p-3.5 flex justify-between items-center relative z-10">
|
||||
<div className="flex items-center gap-2">
|
||||
<Activity className="w-4 h-4 text-primary" />
|
||||
<span className="text-sm uppercase tracking-widest text-muted-foreground">
|
||||
<div
|
||||
className={`w-2 h-2 rounded-full ${hasSub ? "bg-secondary animate-pulse shadow-[0_0_8px_rgba(0,229,242,0.8)]" : "bg-red-500"}`}
|
||||
/>
|
||||
<span className="text-xs uppercase tracking-widest text-muted-foreground">
|
||||
{t.uplink_status}
|
||||
</span>
|
||||
</div>
|
||||
{hasSub ? (
|
||||
<span className="px-3 py-1 rounded-full text-xs font-bold uppercase bg-primary/20 text-primary border border-primary/30 shadow-[0_0_10px_rgba(139,61,255,0.3)]">
|
||||
{subscription.plan_name}
|
||||
</span>
|
||||
<div className="text-right">
|
||||
<span className="text-xs font-bold uppercase text-primary">
|
||||
[{subscription.plan_name}]
|
||||
</span>
|
||||
<p className="text-[9px] text-muted-foreground mt-0.5 tracking-widest uppercase">
|
||||
{t.expires}:{" "}
|
||||
{new Date(subscription.expires_at).toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<span className="px-3 py-1 rounded-full text-xs font-bold uppercase bg-white/10 text-muted-foreground">
|
||||
{t.offline}
|
||||
<span className="text-xs font-bold uppercase text-red-500/80">
|
||||
[{t.offline}]
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{hasSub ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t.expires}:{" "}
|
||||
{new Date(subscription.expires_at).toLocaleDateString()}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground mt-2">
|
||||
{t.no_active_sub}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
<div className="mt-3 flex justify-between items-center text-[10px] uppercase tracking-widest text-muted-foreground px-1">
|
||||
<span>
|
||||
{t.tickets}:{" "}
|
||||
<span className="text-white/70">{user.game_tickets || 0}</span>
|
||||
</span>
|
||||
{!hasSub && (
|
||||
<span className="text-red-400/60">{t.no_active_sub}</span>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Breach Protocol Action */}
|
||||
<motion.button
|
||||
variants={itemVariants}
|
||||
onClick={() => navigate("/hack")}
|
||||
className="w-full relative z-10 bg-primary/10 hover:bg-primary/30 border border-primary/50 text-primary font-bold tracking-widest uppercase p-4 rounded-3xl transition-all flex justify-center items-center gap-3 shadow-[0_0_20px_rgba(139,61,255,0.15)] hover:shadow-[0_0_30px_rgba(139,61,255,0.4)] group overflow-hidden"
|
||||
className="w-full relative bg-primary/10 hover:bg-primary/20 border border-primary/40 text-primary font-bold tracking-widest uppercase p-4 rounded-xl transition-all flex justify-center items-center gap-3 shadow-[0_0_15px_rgba(139,61,255,0.1)] hover:shadow-[0_0_25px_rgba(139,61,255,0.3)] group overflow-hidden"
|
||||
>
|
||||
<div className="absolute inset-0 bg-[linear-gradient(45deg,transparent_25%,rgba(139,61,255,0.2)_50%,transparent_75%)] bg-[length:250%_250%,100%_100%] animate-[shimmer_2s_infinite] opacity-0 group-hover:opacity-100" />
|
||||
<Terminal className="w-5 h-5 group-hover:animate-pulse relative z-10" />
|
||||
<span className="relative z-10">{t.run_breach}</span>
|
||||
</button>
|
||||
<div className="absolute inset-0 bg-[linear-gradient(90deg,transparent_0%,rgba(139,61,255,0.1)_50%,transparent_100%)] translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000" />
|
||||
<Terminal className="w-5 h-5 group-hover:text-white transition-colors" />
|
||||
<span className="group-hover:text-white transition-colors">
|
||||
[ {t.run_breach} ]
|
||||
</span>
|
||||
</motion.button>
|
||||
|
||||
{/* SUBSCRIPTIONS */}
|
||||
<div className="bg-white/[0.03] backdrop-blur-xl border border-white/10 rounded-3xl p-6 shadow-[0_8px_32px_0_rgba(0,0,0,0.3)]">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="font-bold text-lg">{t.system_override}</h3>
|
||||
{/* System Override / Subscriptions */}
|
||||
<motion.div
|
||||
variants={itemVariants}
|
||||
className="bg-white/[0.02] border border-white/10 rounded-xl p-5 backdrop-blur-md"
|
||||
>
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<h3 className="font-bold text-sm uppercase tracking-widest text-white/90">
|
||||
_ {t.system_override}
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => setShowPlans(!showPlans)}
|
||||
className="text-xs flex items-center gap-1 bg-secondary/10 hover:bg-secondary/20 text-secondary border border-secondary/30 px-3 py-1.5 rounded-full transition-colors"
|
||||
className="text-[10px] flex items-center gap-1.5 bg-secondary/10 hover:bg-secondary/20 text-secondary border border-secondary/20 px-3 py-1.5 rounded-md uppercase tracking-widest transition-colors"
|
||||
>
|
||||
<ArrowUpCircle className="w-4 h-4" /> {t.manage_plan}
|
||||
{t.manage_plan}{" "}
|
||||
<ArrowRight
|
||||
className={`w-3 h-3 transition-transform ${showPlans ? "rotate-90" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -317,7 +410,7 @@ export default function Profile() {
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="grid sm:grid-cols-1 gap-4 mt-4 pb-2">
|
||||
<div className="grid gap-3 mt-5 pb-1">
|
||||
{UI_PLANS.map((plan: any) => {
|
||||
const planData = t.plans[
|
||||
plan.name as keyof typeof t.plans
|
||||
@@ -325,58 +418,61 @@ export default function Profile() {
|
||||
const isTrial = plan.name === "TRIAL";
|
||||
const isTrialUsed = isTrial && user.has_used_trial;
|
||||
|
||||
// 🔥 ИСПРАВЛЕНО: Улучшенный дизайн карточек и кнопки Триала
|
||||
return (
|
||||
<div
|
||||
key={plan.name}
|
||||
className={`bg-black/50 border rounded-3xl p-5 flex flex-col transition-all duration-300 group relative overflow-hidden ${
|
||||
className={`bg-black/60 border rounded-xl p-4 flex flex-col transition-all duration-300 group relative overflow-hidden ${
|
||||
isTrial
|
||||
? isTrialUsed
|
||||
? "border-white/5 opacity-50 grayscale"
|
||||
: "border-secondary/50 shadow-[0_0_15px_rgba(0,229,242,0.15)] hover:shadow-[0_0_25px_rgba(0,229,242,0.3)] hover:border-secondary"
|
||||
: "border-secondary/40 shadow-[0_0_10px_rgba(0,229,242,0.1)] hover:border-secondary"
|
||||
: "border-white/10 hover:border-primary/50"
|
||||
}`}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<div className="absolute top-0 left-0 w-1 h-full bg-gradient-to-b from-transparent via-white/10 to-transparent group-hover:via-primary/50 transition-colors" />
|
||||
|
||||
<div className="flex justify-between items-start mb-3 ml-2">
|
||||
<div>
|
||||
<h4 className="font-bold text-primary uppercase text-lg">
|
||||
<h4 className="font-bold text-primary uppercase text-sm tracking-widest mb-0.5">
|
||||
{plan.name}
|
||||
</h4>
|
||||
<p className="text-[10px] uppercase tracking-widest text-secondary">
|
||||
<p className="text-[9px] uppercase tracking-widest text-secondary/80">
|
||||
{planData.tag}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-2xl font-black text-white">
|
||||
<p className="text-xl font-black text-white/90 tracking-tight">
|
||||
{getPrice(
|
||||
plan.price_usd || plan.price,
|
||||
plan.price_rub || plan.price * 100,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mb-4 relative z-10">
|
||||
|
||||
<p className="text-[11px] text-muted-foreground mb-4 ml-2 leading-relaxed">
|
||||
{planData.desc}
|
||||
</p>
|
||||
<ul className="text-xs text-muted-foreground space-y-2 mb-6 relative z-10">
|
||||
|
||||
<ul className="text-[10px] text-muted-foreground/80 space-y-2 mb-5 ml-2 uppercase tracking-wide">
|
||||
{planData.features.map((f: string, i: number) => (
|
||||
<li key={i} className="flex items-center gap-2">
|
||||
<div className="w-1 h-1 bg-secondary rounded-full" />{" "}
|
||||
<span className="text-secondary/50">{">"}</span>{" "}
|
||||
{f}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<button
|
||||
onClick={() =>
|
||||
isTrial ? handleTrial() : handleBuy(plan.name)
|
||||
}
|
||||
disabled={isTrialUsed}
|
||||
className={`mt-auto w-full py-3 rounded-2xl text-xs font-bold uppercase tracking-widest transition-colors relative z-10
|
||||
className={`mt-auto w-full py-2.5 rounded-lg text-[10px] font-bold uppercase tracking-widest transition-all
|
||||
${
|
||||
isTrialUsed
|
||||
? "bg-white/5 text-muted-foreground cursor-not-allowed"
|
||||
: isTrial
|
||||
? "bg-secondary/20 hover:bg-secondary/40 text-secondary border border-secondary/50"
|
||||
: "bg-white/5 hover:bg-primary text-white border border-white/10 hover:border-primary"
|
||||
? "bg-secondary/10 hover:bg-secondary/20 text-secondary border border-secondary/30"
|
||||
: "bg-white/5 hover:bg-primary/20 text-white border border-white/10 hover:border-primary/50"
|
||||
}`}
|
||||
>
|
||||
{isTrial
|
||||
@@ -392,45 +488,51 @@ export default function Profile() {
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* SYNDICATE NETWORK */}
|
||||
<div className="bg-white/[0.03] backdrop-blur-xl border border-white/10 rounded-3xl p-6 relative overflow-hidden group">
|
||||
<Zap className="absolute -right-4 -bottom-4 w-32 h-32 text-secondary opacity-5 group-hover:opacity-10 transition-opacity blur-[2px]" />
|
||||
<h3 className="font-bold text-lg mb-4 relative z-10 flex items-center gap-2">
|
||||
<Users className="w-5 h-5 text-secondary" /> {t.syndicate}
|
||||
{/* Syndicate Network */}
|
||||
<motion.div
|
||||
variants={itemVariants}
|
||||
className="bg-white/[0.02] border border-white/10 rounded-xl p-5 relative overflow-hidden group backdrop-blur-md"
|
||||
>
|
||||
<Zap className="absolute -right-4 -bottom-4 w-32 h-32 text-primary opacity-5 group-hover:opacity-[0.08] transition-opacity" />
|
||||
<h3 className="font-bold text-sm mb-4 relative z-10 flex items-center gap-2 uppercase tracking-widest text-white/90">
|
||||
<Users className="w-4 h-4 text-primary" /> _ {t.syndicate}
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-4 mb-6 relative z-10">
|
||||
<div className="bg-black/40 border border-white/5 rounded-2xl p-4 text-center">
|
||||
<p className="text-2xl font-black text-white">{recruits}</p>
|
||||
<p className="text-[10px] uppercase tracking-widest text-muted-foreground mt-1">
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 mb-5 relative z-10">
|
||||
<div className="bg-black/50 border border-white/5 rounded-lg p-3 text-center hover:border-primary/20 transition-colors">
|
||||
<p className="text-xl font-black text-white/90">{recruits}</p>
|
||||
<p className="text-[9px] uppercase tracking-widest text-muted-foreground mt-1">
|
||||
{t.recruits}
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-black/40 border border-white/5 rounded-2xl p-4 text-center">
|
||||
<p className="text-2xl font-black text-secondary">{earned}</p>
|
||||
<p className="text-[10px] uppercase tracking-widest text-muted-foreground mt-1">
|
||||
<div className="bg-black/50 border border-white/5 rounded-lg p-3 text-center hover:border-secondary/20 transition-colors">
|
||||
<p className="text-xl font-black text-secondary">{earned}</p>
|
||||
<p className="text-[9px] uppercase tracking-widest text-muted-foreground mt-1">
|
||||
{t.earned}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mb-4 max-w-[90%] relative z-10">
|
||||
|
||||
<p className="text-[10px] text-muted-foreground mb-3 max-w-[90%] relative z-10 uppercase tracking-wide">
|
||||
{t.ref_desc}
|
||||
</p>
|
||||
|
||||
<button
|
||||
onClick={copyRef}
|
||||
className="w-full relative z-10 bg-black/60 backdrop-blur-md border border-white/10 hover:border-secondary/50 flex items-center justify-between p-4 rounded-2xl transition-all"
|
||||
className="w-full relative z-10 bg-black/60 border border-white/10 hover:border-secondary/40 flex items-center justify-between p-3.5 rounded-lg transition-all"
|
||||
>
|
||||
<span className="text-xs text-muted-foreground truncate mr-4 transition-colors">
|
||||
<span className="text-[11px] text-muted-foreground truncate mr-4 font-mono">
|
||||
t.me/{botUsername}?start={user.tg_id || user.id}
|
||||
</span>
|
||||
{copied ? (
|
||||
<CheckCircle2 className="w-4 h-4 text-secondary shrink-0" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4 text-secondary shrink-0" />
|
||||
<Copy className="w-4 h-4 text-secondary/60 hover:text-secondary shrink-0 transition-colors" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user