From debdd66458e23ecddf7002d44b4610f6e7b69620 Mon Sep 17 00:00:00 2001 From: Oronemu Date: Wed, 17 Dec 2025 14:53:56 +0700 Subject: [PATCH] Fix UI --- auth-pages-backup.tsx | 434 ++++++++++++++++++++ frontend/src/components/EventBanner.tsx | 4 +- frontend/src/components/SpinWheel.tsx | 6 +- frontend/src/components/layout/Layout.tsx | 4 +- frontend/src/components/ui/Button.tsx | 2 +- frontend/src/components/ui/Card.tsx | 2 +- frontend/src/components/ui/GlassCard.tsx | 14 +- frontend/src/components/ui/GlitchText.tsx | 4 +- frontend/src/components/ui/Input.tsx | 2 +- frontend/src/components/ui/NeonButton.tsx | 14 +- frontend/src/index.css | 113 ++--- frontend/src/pages/AssignmentDetailPage.tsx | 9 +- frontend/src/pages/CreateMarathonPage.tsx | 8 +- frontend/src/pages/HomePage.tsx | 50 +-- frontend/src/pages/LeaderboardPage.tsx | 2 +- frontend/src/pages/LoginPage.tsx | 164 +++++--- frontend/src/pages/MarathonPage.tsx | 13 +- frontend/src/pages/MarathonsPage.tsx | 2 +- frontend/src/pages/ProfilePage.tsx | 3 +- frontend/src/pages/RegisterPage.tsx | 221 ++++++---- frontend/src/pages/UserProfilePage.tsx | 2 +- frontend/tailwind.config.js | 90 ++-- 22 files changed, 853 insertions(+), 310 deletions(-) create mode 100644 auth-pages-backup.tsx diff --git a/auth-pages-backup.tsx b/auth-pages-backup.tsx new file mode 100644 index 0000000..97b4e4d --- /dev/null +++ b/auth-pages-backup.tsx @@ -0,0 +1,434 @@ +// ============================================ +// AUTH PAGES BACKUP - Bento Style +// ============================================ + +// ============================================ +// LOGIN PAGE (LoginPage.tsx) +// ============================================ + +import { useState } from 'react' +import { Link, useNavigate } from 'react-router-dom' +import { useForm } from 'react-hook-form' +import { zodResolver } from '@hookform/resolvers/zod' +import { z } from 'zod' +import { useAuthStore } from '@/store/auth' +import { marathonsApi } from '@/api' +import { NeonButton, Input, GlassCard } from '@/components/ui' +import { Gamepad2, LogIn, AlertCircle, Trophy, Users, Zap, Target } from 'lucide-react' + +const loginSchema = z.object({ + login: z.string().min(3, 'Логин должен быть не менее 3 символов'), + password: z.string().min(6, 'Пароль должен быть не менее 6 символов'), +}) + +type LoginForm = z.infer + +export function LoginPage() { + const navigate = useNavigate() + const { login, isLoading, error, clearError, consumePendingInviteCode } = useAuthStore() + const [submitError, setSubmitError] = useState(null) + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(loginSchema), + }) + + const onSubmit = async (data: LoginForm) => { + setSubmitError(null) + clearError() + try { + await login(data) + + // Check for pending invite code + const pendingCode = consumePendingInviteCode() + if (pendingCode) { + try { + const marathon = await marathonsApi.join(pendingCode) + navigate(`/marathons/${marathon.id}`) + return + } catch { + // If join fails (already member, etc), just go to marathons + } + } + + navigate('/marathons') + } catch { + setSubmitError(error || 'Ошибка входа') + } + } + + const features = [ + { icon: , text: 'Соревнуйтесь с друзьями' }, + { icon: , text: 'Выполняйте челленджи' }, + { icon: , text: 'Зарабатывайте очки' }, + { icon: , text: 'Создавайте марафоны' }, + ] + + return ( +
+ {/* Background effects */} +
+
+
+
+ + {/* Bento Grid */} +
+
+ {/* Branding Block (left) */} + + {/* Background decoration */} +
+
+
+
+ +
+ {/* Logo */} +
+
+ +
+
+ + {/* Title */} +

+ Game Marathon +

+

+ Платформа для игровых соревнований +

+ + {/* Features */} +
+ {features.map((feature, index) => ( +
+
+ {feature.icon} +
+ {feature.text} +
+ ))} +
+
+ + + {/* Form Block (right) */} + + {/* Header */} +
+

Добро пожаловать!

+

Войдите, чтобы продолжить

+
+ + {/* Form */} +
+ {(submitError || error) && ( +
+ + {submitError || error} +
+ )} + + + + + + } + > + Войти + +
+ + {/* Footer */} +
+

+ Нет аккаунта?{' '} + + Зарегистрироваться + +

+
+
+
+ + {/* Decorative elements */} +
+
+
+
+ ) +} + + +// ============================================ +// REGISTER PAGE (RegisterPage.tsx) +// ============================================ + +import { useState } from 'react' +import { Link, useNavigate } from 'react-router-dom' +import { useForm } from 'react-hook-form' +import { zodResolver } from '@hookform/resolvers/zod' +import { z } from 'zod' +import { useAuthStore } from '@/store/auth' +import { marathonsApi } from '@/api' +import { NeonButton, Input, GlassCard } from '@/components/ui' +import { Gamepad2, UserPlus, AlertCircle, Trophy, Users, Zap, Target, Sparkles } from 'lucide-react' + +const registerSchema = z.object({ + login: z + .string() + .min(3, 'Логин должен быть не менее 3 символов') + .max(50, 'Логин должен быть не более 50 символов') + .regex(/^[a-zA-Z0-9_]+$/, 'Логин может содержать только буквы, цифры и подчёркивания'), + nickname: z + .string() + .min(2, 'Никнейм должен быть не менее 2 символов') + .max(50, 'Никнейм должен быть не более 50 символов'), + password: z.string().min(6, 'Пароль должен быть не менее 6 символов'), + confirmPassword: z.string(), +}).refine((data) => data.password === data.confirmPassword, { + message: 'Пароли не совпадают', + path: ['confirmPassword'], +}) + +type RegisterForm = z.infer + +export function RegisterPage() { + const navigate = useNavigate() + const { register: registerUser, isLoading, error, clearError, consumePendingInviteCode } = useAuthStore() + const [submitError, setSubmitError] = useState(null) + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(registerSchema), + }) + + const onSubmit = async (data: RegisterForm) => { + setSubmitError(null) + clearError() + try { + await registerUser({ + login: data.login, + password: data.password, + nickname: data.nickname, + }) + + // Check for pending invite code + const pendingCode = consumePendingInviteCode() + if (pendingCode) { + try { + const marathon = await marathonsApi.join(pendingCode) + navigate(`/marathons/${marathon.id}`) + return + } catch { + // If join fails, just go to marathons + } + } + + navigate('/marathons') + } catch { + setSubmitError(error || 'Ошибка регистрации') + } + } + + const features = [ + { icon: , text: 'Соревнуйтесь с друзьями' }, + { icon: , text: 'Выполняйте челленджи' }, + { icon: , text: 'Зарабатывайте очки' }, + { icon: , text: 'Создавайте марафоны' }, + ] + + return ( +
+ {/* Background effects */} +
+
+
+
+ + {/* Bento Grid */} +
+
+ {/* Branding Block (left) */} + + {/* Background decoration */} +
+
+
+
+ +
+ {/* Logo */} +
+
+ +
+
+ + {/* Title */} +

+ Game Marathon +

+

+ Присоединяйтесь к игровому сообществу +

+ + {/* Benefits */} +
+
+ + Что вас ждет: +
+
    +
  • + + Создавайте игровые марафоны +
  • +
  • + + Выполняйте уникальные челленджи +
  • +
  • + + Соревнуйтесь за первое место +
  • +
+
+ + {/* Features */} +
+ {features.map((feature, index) => ( +
+
+ {feature.icon} +
+ {feature.text} +
+ ))} +
+
+ + + {/* Form Block (right) */} + + {/* Header */} +
+
+
+ +
+
+

Создать аккаунт

+

Начните играть уже сегодня

+
+ + {/* Form */} +
+ {(submitError || error) && ( +
+ + {submitError || error} +
+ )} + + + + + + + + + + } + > + Зарегистрироваться + +
+ + {/* Footer */} +
+

+ Уже есть аккаунт?{' '} + + Войти + +

+
+
+
+ + {/* Decorative elements */} +
+
+
+
+ ) +} diff --git a/frontend/src/components/EventBanner.tsx b/frontend/src/components/EventBanner.tsx index 4c920eb..e052d40 100644 --- a/frontend/src/components/EventBanner.tsx +++ b/frontend/src/components/EventBanner.tsx @@ -42,7 +42,7 @@ const EVENT_COLORS: Record {/* Side arrows */} @@ -198,7 +198,7 @@ export function SpinWheel({ games, onSpin, onSpinComplete, disabled }: SpinWheel w-16 h-16 rounded-xl overflow-hidden flex-shrink-0 border transition-all duration-300 ${isSelected - ? 'border-neon-500/50 shadow-[0_0_15px_rgba(0,240,255,0.3)]' + ? 'border-neon-500/50 shadow-[0_0_12px_rgba(34,211,238,0.25)]' : 'border-dark-600' } `}> diff --git a/frontend/src/components/layout/Layout.tsx b/frontend/src/components/layout/Layout.tsx index 100e10f..8702c49 100644 --- a/frontend/src/components/layout/Layout.tsx +++ b/frontend/src/components/layout/Layout.tsx @@ -50,7 +50,7 @@ export function Layout() { className="flex items-center gap-3 group" >
- +
МАРАФОН @@ -109,7 +109,7 @@ export function Layout() { Регистрация diff --git a/frontend/src/components/ui/Button.tsx b/frontend/src/components/ui/Button.tsx index eeb6589..f346e36 100644 --- a/frontend/src/components/ui/Button.tsx +++ b/frontend/src/components/ui/Button.tsx @@ -18,7 +18,7 @@ export const Button = forwardRef( 'inline-flex items-center justify-center font-medium rounded-lg transition-all duration-200', 'disabled:opacity-50 disabled:cursor-not-allowed', { - 'bg-neon-500 hover:bg-neon-400 text-dark-900 font-semibold shadow-[0_0_10px_rgba(0,240,255,0.3)] hover:shadow-[0_0_20px_rgba(0,240,255,0.5)]': variant === 'primary', + 'bg-neon-500 hover:bg-neon-400 text-dark-900 font-semibold shadow-[0_0_8px_rgba(34,211,238,0.25)] hover:shadow-[0_0_14px_rgba(34,211,238,0.4)]': variant === 'primary', 'bg-dark-600 hover:bg-dark-500 text-white border border-dark-500': variant === 'secondary', 'bg-red-600 hover:bg-red-700 text-white': variant === 'danger', 'bg-transparent hover:bg-dark-700 text-gray-300 hover:text-white': variant === 'ghost', diff --git a/frontend/src/components/ui/Card.tsx b/frontend/src/components/ui/Card.tsx index 8c2627f..bfa154a 100644 --- a/frontend/src/components/ui/Card.tsx +++ b/frontend/src/components/ui/Card.tsx @@ -12,7 +12,7 @@ export function Card({ children, className, hover = false }: CardProps) {
diff --git a/frontend/src/components/ui/GlassCard.tsx b/frontend/src/components/ui/GlassCard.tsx index 76de256..b2bb7dc 100644 --- a/frontend/src/components/ui/GlassCard.tsx +++ b/frontend/src/components/ui/GlassCard.tsx @@ -91,10 +91,10 @@ export function StatsCard({ className )} > -
-
+
+

{label}

-

+

{value}

{trend && ( @@ -111,7 +111,7 @@ export function StatsCard({ {icon && (
@@ -143,17 +143,17 @@ export function FeatureCard({ neon: { icon: 'text-neon-500 bg-neon-500/10 group-hover:bg-neon-500/20', border: 'group-hover:border-neon-500/50', - glow: 'group-hover:shadow-[0_0_30px_rgba(0,240,255,0.15)]', + glow: 'group-hover:shadow-[0_0_20px_rgba(34,211,238,0.12)]', }, purple: { icon: 'text-accent-500 bg-accent-500/10 group-hover:bg-accent-500/20', border: 'group-hover:border-accent-500/50', - glow: 'group-hover:shadow-[0_0_30px_rgba(168,85,247,0.15)]', + glow: 'group-hover:shadow-[0_0_20px_rgba(139,92,246,0.12)]', }, pink: { icon: 'text-pink-500 bg-pink-500/10 group-hover:bg-pink-500/20', border: 'group-hover:border-pink-500/50', - glow: 'group-hover:shadow-[0_0_30px_rgba(236,72,153,0.15)]', + glow: 'group-hover:shadow-[0_0_20px_rgba(244,114,182,0.12)]', }, } diff --git a/frontend/src/components/ui/GlitchText.tsx b/frontend/src/components/ui/GlitchText.tsx index 38aa60e..4fa5290 100644 --- a/frontend/src/components/ui/GlitchText.tsx +++ b/frontend/src/components/ui/GlitchText.tsx @@ -31,8 +31,8 @@ export function GlitchText({ const glowClasses = { neon: 'neon-text', purple: 'neon-text-purple', - pink: '[text-shadow:0_0_10px_#ec4899,0_0_20px_#ec4899,0_0_30px_#ec4899]', - white: '[text-shadow:0_0_10px_rgba(255,255,255,0.5),0_0_20px_rgba(255,255,255,0.3)]', + pink: '[text-shadow:0_0_8px_rgba(244,114,182,0.5),0_0_16px_rgba(244,114,182,0.25)]', + white: '[text-shadow:0_0_8px_rgba(255,255,255,0.4),0_0_16px_rgba(255,255,255,0.2)]', } const intensityClasses = { diff --git a/frontend/src/components/ui/Input.tsx b/frontend/src/components/ui/Input.tsx index a38fb30..d5151fe 100644 --- a/frontend/src/components/ui/Input.tsx +++ b/frontend/src/components/ui/Input.tsx @@ -21,7 +21,7 @@ export const Input = forwardRef( className={clsx( 'w-full px-4 py-3 bg-dark-800 border rounded-lg text-white placeholder-gray-500', 'focus:outline-none focus:border-neon-500', - 'focus:shadow-[0_0_0_3px_rgba(0,240,255,0.1),0_0_10px_rgba(0,240,255,0.2)]', + 'focus:shadow-[0_0_0_3px_rgba(34,211,238,0.1),0_0_8px_rgba(34,211,238,0.15)]', 'transition-all duration-200', error ? 'border-red-500' : 'border-dark-600', className diff --git a/frontend/src/components/ui/NeonButton.tsx b/frontend/src/components/ui/NeonButton.tsx index 0c36c23..dfc12cb 100644 --- a/frontend/src/components/ui/NeonButton.tsx +++ b/frontend/src/components/ui/NeonButton.tsx @@ -38,8 +38,8 @@ export const NeonButton = forwardRef( outline: 'bg-transparent border-2 border-neon-500 text-neon-500 hover:bg-neon-500 hover:text-dark-900', ghost: 'bg-transparent hover:bg-neon-500/10 text-neon-400', danger: 'bg-red-600 hover:bg-red-700 text-white', - glow: '0 0 20px rgba(0, 240, 255, 0.5)', - glowHover: '0 0 30px rgba(0, 240, 255, 0.7)', + glow: '0 0 12px rgba(34, 211, 238, 0.4)', + glowHover: '0 0 18px rgba(34, 211, 238, 0.55)', }, purple: { primary: 'bg-accent-500 hover:bg-accent-400 text-white', @@ -47,8 +47,8 @@ export const NeonButton = forwardRef( outline: 'bg-transparent border-2 border-accent-500 text-accent-500 hover:bg-accent-500 hover:text-white', ghost: 'bg-transparent hover:bg-accent-500/10 text-accent-400', danger: 'bg-red-600 hover:bg-red-700 text-white', - glow: '0 0 20px rgba(168, 85, 247, 0.5)', - glowHover: '0 0 30px rgba(168, 85, 247, 0.7)', + glow: '0 0 12px rgba(139, 92, 246, 0.4)', + glowHover: '0 0 18px rgba(139, 92, 246, 0.55)', }, pink: { primary: 'bg-pink-500 hover:bg-pink-400 text-white', @@ -56,8 +56,8 @@ export const NeonButton = forwardRef( outline: 'bg-transparent border-2 border-pink-500 text-pink-500 hover:bg-pink-500 hover:text-white', ghost: 'bg-transparent hover:bg-pink-500/10 text-pink-400', danger: 'bg-red-600 hover:bg-red-700 text-white', - glow: '0 0 20px rgba(236, 72, 153, 0.5)', - glowHover: '0 0 30px rgba(236, 72, 153, 0.7)', + glow: '0 0 12px rgba(244, 114, 182, 0.4)', + glowHover: '0 0 18px rgba(244, 114, 182, 0.55)', }, } @@ -147,7 +147,7 @@ export const GradientButton = forwardRef 'relative inline-flex items-center justify-center font-semibold rounded-lg', 'bg-gradient-to-r from-neon-500 via-accent-500 to-pink-500', 'text-white transition-all duration-300', - 'hover:shadow-[0_0_30px_rgba(168,85,247,0.5)]', + 'hover:shadow-[0_0_20px_rgba(139,92,246,0.35)]', 'disabled:opacity-50 disabled:cursor-not-allowed', 'focus:outline-none focus:ring-2 focus:ring-accent-500 focus:ring-offset-2 focus:ring-offset-dark-900', sizeClasses[size], diff --git a/frontend/src/index.css b/frontend/src/index.css index 78b346f..98b3a1f 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -6,36 +6,36 @@ CSS Variables ======================================== */ :root { - /* Base colors */ - --color-dark-950: #050508; - --color-dark-900: #0a0a0f; - --color-dark-800: #12121a; - --color-dark-700: #1a1a24; - --color-dark-600: #1e1e2e; - --color-dark-500: #2a2a3a; + /* Base colors - slightly warmer dark tones */ + --color-dark-950: #08090d; + --color-dark-900: #0d0e14; + --color-dark-800: #14161e; + --color-dark-700: #1c1e28; + --color-dark-600: #252732; + --color-dark-500: #2e313d; - /* Neon cyan (primary) */ - --color-neon-500: #00f0ff; - --color-neon-400: #22d3ee; - --color-neon-600: #00d4e4; + /* Soft cyan (primary) - gentler on eyes */ + --color-neon-500: #22d3ee; + --color-neon-400: #67e8f9; + --color-neon-600: #06b6d4; - /* Purple accent */ - --color-accent-500: #a855f7; - --color-accent-600: #9333ea; - --color-accent-700: #7c3aed; + /* Soft violet accent */ + --color-accent-500: #8b5cf6; + --color-accent-600: #7c3aed; + --color-accent-700: #6d28d9; - /* Pink highlight */ - --color-pink-500: #ec4899; + /* Soft pink highlight - used sparingly */ + --color-pink-500: #f472b6; - /* Glow colors */ - --glow-neon: 0 0 5px #00f0ff, 0 0 10px #00f0ff, 0 0 20px #00f0ff; - --glow-neon-lg: 0 0 10px #00f0ff, 0 0 20px #00f0ff, 0 0 40px #00f0ff, 0 0 60px #00f0ff; - --glow-purple: 0 0 5px #a855f7, 0 0 10px #a855f7, 0 0 20px #a855f7; - --glow-pink: 0 0 5px #ec4899, 0 0 10px #ec4899, 0 0 20px #ec4899; + /* Glow colors - reduced intensity */ + --glow-neon: 0 0 8px rgba(34, 211, 238, 0.4), 0 0 16px rgba(34, 211, 238, 0.2); + --glow-neon-lg: 0 0 12px rgba(34, 211, 238, 0.5), 0 0 24px rgba(34, 211, 238, 0.3); + --glow-purple: 0 0 8px rgba(139, 92, 246, 0.4), 0 0 16px rgba(139, 92, 246, 0.2); + --glow-pink: 0 0 8px rgba(244, 114, 182, 0.4), 0 0 16px rgba(244, 114, 182, 0.2); - /* Text glow */ - --text-glow-neon: 0 0 10px #00f0ff, 0 0 20px #00f0ff, 0 0 30px #00f0ff; - --text-glow-purple: 0 0 10px #a855f7, 0 0 20px #a855f7, 0 0 30px #a855f7; + /* Text glow - subtle */ + --text-glow-neon: 0 0 8px rgba(34, 211, 238, 0.5), 0 0 16px rgba(34, 211, 238, 0.25); + --text-glow-purple: 0 0 8px rgba(139, 92, 246, 0.5), 0 0 16px rgba(139, 92, 246, 0.25); } /* ======================================== @@ -49,8 +49,8 @@ body { @apply bg-dark-900 text-gray-100 min-h-screen antialiased; font-family: 'Inter', system-ui, sans-serif; background-image: - linear-gradient(rgba(0, 240, 255, 0.02) 1px, transparent 1px), - linear-gradient(90deg, rgba(0, 240, 255, 0.02) 1px, transparent 1px); + linear-gradient(rgba(34, 211, 238, 0.015) 1px, transparent 1px), + linear-gradient(90deg, rgba(34, 211, 238, 0.015) 1px, transparent 1px); background-size: 50px 50px; background-attachment: fixed; } @@ -69,16 +69,27 @@ body { background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); } +/* Autofill styles - override browser defaults */ +input:-webkit-autofill, +input:-webkit-autofill:hover, +input:-webkit-autofill:focus, +input:-webkit-autofill:active { + -webkit-box-shadow: 0 0 0 30px #14161e inset !important; + -webkit-text-fill-color: #fff !important; + caret-color: #fff; + transition: background-color 5000s ease-in-out 0s; +} + /* ======================================== Selection Styles ======================================== */ ::selection { - background: rgba(0, 240, 255, 0.3); + background: rgba(34, 211, 238, 0.25); color: #fff; } ::-moz-selection { - background: rgba(0, 240, 255, 0.3); + background: rgba(34, 211, 238, 0.25); color: #fff; } @@ -151,14 +162,14 @@ body { .glitch::before { left: 2px; - text-shadow: -2px 0 #ff00ff; + text-shadow: -2px 0 rgba(139, 92, 246, 0.7); clip: rect(44px, 450px, 56px, 0); animation: glitch-anim 5s infinite linear alternate-reverse; } .glitch::after { left: -2px; - text-shadow: -2px 0 #00ffff, 2px 2px #ff00ff; + text-shadow: -2px 0 rgba(34, 211, 238, 0.7), 2px 2px rgba(139, 92, 246, 0.7); clip: rect(44px, 450px, 56px, 0); animation: glitch-anim2 5s infinite linear alternate-reverse; } @@ -272,10 +283,10 @@ body { @keyframes neon-pulse { 0%, 100% { - box-shadow: 0 0 5px #00f0ff, 0 0 10px #00f0ff, 0 0 20px #00f0ff; + box-shadow: 0 0 6px rgba(34, 211, 238, 0.4), 0 0 12px rgba(34, 211, 238, 0.2); } 50% { - box-shadow: 0 0 10px #00f0ff, 0 0 20px #00f0ff, 0 0 40px #00f0ff, 0 0 60px #00f0ff; + box-shadow: 0 0 10px rgba(34, 211, 238, 0.5), 0 0 20px rgba(34, 211, 238, 0.3); } } @@ -297,29 +308,29 @@ body { } .glass-neon { - background: rgba(18, 18, 26, 0.6); + background: rgba(20, 22, 30, 0.6); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); - border: 1px solid rgba(0, 240, 255, 0.2); - box-shadow: inset 0 0 20px rgba(0, 240, 255, 0.05); + border: 1px solid rgba(34, 211, 238, 0.15); + box-shadow: inset 0 0 20px rgba(34, 211, 238, 0.03); } /* ======================================== Gradient Utilities ======================================== */ .gradient-neon { - background: linear-gradient(135deg, #00f0ff, #a855f7); + background: linear-gradient(135deg, #22d3ee, #8b5cf6); } .gradient-neon-text { - background: linear-gradient(135deg, #00f0ff, #a855f7); + background: linear-gradient(135deg, #22d3ee, #8b5cf6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .gradient-pink-purple { - background: linear-gradient(135deg, #ec4899, #a855f7); + background: linear-gradient(135deg, #f472b6, #8b5cf6); } .gradient-dark { @@ -337,7 +348,7 @@ body { content: ''; position: absolute; inset: -2px; - background: linear-gradient(90deg, #00f0ff, #a855f7, #ec4899, #00f0ff); + background: linear-gradient(90deg, #22d3ee, #8b5cf6, #f472b6, #22d3ee); background-size: 300% 300%; border-radius: 14px; z-index: -1; @@ -389,11 +400,11 @@ body { .btn-primary { @apply bg-neon-500 hover:bg-neon-400 text-dark-900 font-semibold; - box-shadow: 0 0 10px rgba(0, 240, 255, 0.3); + box-shadow: 0 0 8px rgba(34, 211, 238, 0.25); } .btn-primary:hover { - box-shadow: 0 0 20px rgba(0, 240, 255, 0.5); + box-shadow: 0 0 14px rgba(34, 211, 238, 0.4); } .btn-secondary { @@ -416,7 +427,7 @@ body { .btn-neon:hover { @apply text-dark-900; background: var(--color-neon-500); - box-shadow: 0 0 20px rgba(0, 240, 255, 0.5); + box-shadow: 0 0 14px rgba(34, 211, 238, 0.4); } /* Inputs */ @@ -426,7 +437,7 @@ body { .input:focus { @apply outline-none border-neon-500; - box-shadow: 0 0 0 3px rgba(0, 240, 255, 0.1), 0 0 10px rgba(0, 240, 255, 0.2); + box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.1), 0 0 8px rgba(34, 211, 238, 0.15); } /* Cards */ @@ -436,7 +447,7 @@ body { .card-glass { @apply rounded-xl p-6; - background: rgba(18, 18, 26, 0.7); + background: rgba(20, 22, 30, 0.7); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1); @@ -448,8 +459,8 @@ body { .card-hover:hover { @apply -translate-y-1; - box-shadow: 0 10px 40px rgba(0, 240, 255, 0.1); - border-color: rgba(0, 240, 255, 0.3); + box-shadow: 0 10px 40px rgba(34, 211, 238, 0.08); + border-color: rgba(34, 211, 238, 0.25); } /* Links */ @@ -481,7 +492,7 @@ body { .divider-glow { @apply border-t border-neon-500/30; - box-shadow: 0 0 10px rgba(0, 240, 255, 0.2); + box-shadow: 0 0 8px rgba(34, 211, 238, 0.15); } } @@ -501,7 +512,7 @@ body { } .hover-glow:hover { - box-shadow: 0 0 20px rgba(0, 240, 255, 0.3); + box-shadow: 0 0 14px rgba(34, 211, 238, 0.25); } .hover-border-glow { @@ -509,8 +520,8 @@ body { } .hover-border-glow:hover { - border-color: rgba(0, 240, 255, 0.5); - box-shadow: 0 0 15px rgba(0, 240, 255, 0.2); + border-color: rgba(34, 211, 238, 0.4); + box-shadow: 0 0 12px rgba(34, 211, 238, 0.15); } /* Stagger children animations */ diff --git a/frontend/src/pages/AssignmentDetailPage.tsx b/frontend/src/pages/AssignmentDetailPage.tsx index 5d61ea6..a976d0e 100644 --- a/frontend/src/pages/AssignmentDetailPage.tsx +++ b/frontend/src/pages/AssignmentDetailPage.tsx @@ -344,14 +344,13 @@ export function AssignmentDetailPage() { {/* Dispute button */} {assignment.can_dispute && !dispute && !showDisputeForm && ( - setShowDisputeForm(true)} - icon={} > + Оспорить выполнение - + )} {/* Dispute creation form */} diff --git a/frontend/src/pages/CreateMarathonPage.tsx b/frontend/src/pages/CreateMarathonPage.tsx index c9e0b7a..2ca9eef 100644 --- a/frontend/src/pages/CreateMarathonPage.tsx +++ b/frontend/src/pages/CreateMarathonPage.tsx @@ -162,7 +162,7 @@ export function CreateMarathonPage() { className={` relative p-4 rounded-xl border-2 transition-all duration-300 text-left group ${!isPublic - ? 'border-neon-500/50 bg-neon-500/10 shadow-[0_0_20px_rgba(0,240,255,0.1)]' + ? 'border-neon-500/50 bg-neon-500/10 shadow-[0_0_14px_rgba(34,211,238,0.08)]' : 'border-dark-600 bg-dark-700/50 hover:border-dark-500' } `} @@ -192,7 +192,7 @@ export function CreateMarathonPage() { className={` relative p-4 rounded-xl border-2 transition-all duration-300 text-left group ${isPublic - ? 'border-accent-500/50 bg-accent-500/10 shadow-[0_0_20px_rgba(168,85,247,0.1)]' + ? 'border-accent-500/50 bg-accent-500/10 shadow-[0_0_14px_rgba(139,92,246,0.08)]' : 'border-dark-600 bg-dark-700/50 hover:border-dark-500' } `} @@ -230,7 +230,7 @@ export function CreateMarathonPage() { className={` relative p-4 rounded-xl border-2 transition-all duration-300 text-left ${gameProposalMode === 'all_participants' - ? 'border-neon-500/50 bg-neon-500/10 shadow-[0_0_20px_rgba(0,240,255,0.1)]' + ? 'border-neon-500/50 bg-neon-500/10 shadow-[0_0_14px_rgba(34,211,238,0.08)]' : 'border-dark-600 bg-dark-700/50 hover:border-dark-500' } `} @@ -260,7 +260,7 @@ export function CreateMarathonPage() { className={` relative p-4 rounded-xl border-2 transition-all duration-300 text-left ${gameProposalMode === 'organizer_only' - ? 'border-accent-500/50 bg-accent-500/10 shadow-[0_0_20px_rgba(168,85,247,0.1)]' + ? 'border-accent-500/50 bg-accent-500/10 shadow-[0_0_14px_rgba(139,92,246,0.08)]' : 'border-dark-600 bg-dark-700/50 hover:border-dark-500' } `} diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index ca7c97e..961d38f 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -7,26 +7,25 @@ export function HomePage() { const isAuthenticated = useAuthStore((state) => state.isAuthenticated) return ( -
+
+ {/* Global animated background - covers entire page */} +
+ {/* Gradient orbs */} +
+
+
+
+
+
+ {/* Hero Section */}
- {/* Animated background */} -
- {/* Gradient orbs */} -
-
-
- - {/* Grid lines */} -
-
- {/* Content */}
{/* Logo */}
- +
@@ -77,22 +76,7 @@ export function HomePage() { )}
- {/* Stats */} -
-
-
100+
-
Марафонов
-
-
-
500+
-
Игроков
-
-
-
2000+
-
Челленджей
-
-
{/* Scroll indicator */}
@@ -139,8 +123,6 @@ export function HomePage() { {/* How it works */}
-
-

@@ -195,9 +177,9 @@ export function HomePage() { bg-dark-800 border-2 transition-all duration-300 flex items-center justify-center group-hover:-translate-y-2 - ${item.color === 'neon' ? 'border-neon-500/50 group-hover:border-neon-500 group-hover:shadow-[0_0_30px_rgba(0,240,255,0.3)]' : ''} - ${item.color === 'accent' ? 'border-accent-500/50 group-hover:border-accent-500 group-hover:shadow-[0_0_30px_rgba(168,85,247,0.3)]' : ''} - ${item.color === 'pink' ? 'border-pink-500/50 group-hover:border-pink-500 group-hover:shadow-[0_0_30px_rgba(236,72,153,0.3)]' : ''} + ${item.color === 'neon' ? 'border-neon-500/50 group-hover:border-neon-500 group-hover:shadow-[0_0_20px_rgba(34,211,238,0.25)]' : ''} + ${item.color === 'accent' ? 'border-accent-500/50 group-hover:border-accent-500 group-hover:shadow-[0_0_20px_rgba(139,92,246,0.25)]' : ''} + ${item.color === 'pink' ? 'border-pink-500/50 group-hover:border-pink-500 group-hover:shadow-[0_0_20px_rgba(244,114,182,0.25)]' : ''} `} style={{ animationDelay: `${index * 100}ms` }} > @@ -244,7 +226,7 @@ export function HomePage() { Готовы к соревнованиям?

- Присоединяйтесь к сотням игроков, которые уже соревнуются в игровых челленджах + Создавайте марафоны, приглашайте друзей и соревнуйтесь в игровых челленджах

{isAuthenticated ? ( diff --git a/frontend/src/pages/LeaderboardPage.tsx b/frontend/src/pages/LeaderboardPage.tsx index ff673c6..6cc25f8 100644 --- a/frontend/src/pages/LeaderboardPage.tsx +++ b/frontend/src/pages/LeaderboardPage.tsx @@ -209,7 +209,7 @@ export function LeaderboardPage() { relative flex items-center gap-4 p-4 rounded-xl transition-all duration-300 group ${isCurrentUser - ? 'bg-neon-500/10 border border-neon-500/30 shadow-[0_0_20px_rgba(0,240,255,0.1)]' + ? 'bg-neon-500/10 border border-neon-500/30 shadow-[0_0_14px_rgba(34,211,238,0.08)]' : `${rankConfig.bg} border ${rankConfig.border} hover:border-neon-500/20` } `} diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 8b71ce3..e5031bd 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -5,8 +5,8 @@ import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { useAuthStore } from '@/store/auth' import { marathonsApi } from '@/api' -import { NeonButton, Input } from '@/components/ui' -import { Gamepad2, LogIn, AlertCircle } from 'lucide-react' +import { NeonButton, Input, GlassCard } from '@/components/ui' +import { Gamepad2, LogIn, AlertCircle, Trophy, Users, Zap, Target } from 'lucide-react' const loginSchema = z.object({ login: z.string().min(3, 'Логин должен быть не менее 3 символов'), @@ -52,6 +52,13 @@ export function LoginPage() { } } + const features = [ + { icon: , text: 'Соревнуйтесь с друзьями' }, + { icon: , text: 'Выполняйте челленджи' }, + { icon: , text: 'Зарабатывайте очки' }, + { icon: , text: 'Создавайте марафоны' }, + ] + return (
{/* Background effects */} @@ -60,74 +67,113 @@ export function LoginPage() {
-
- {/* Card */} -
- {/* Header */} -
-
-
- + {/* Bento Grid */} +
+
+ {/* Branding Block (left) */} + + {/* Background decoration */} +
+
+
+
+ +
+ {/* Logo */} +
+
+ +
+
+ + {/* Title */} +

+ Game Marathon +

+

+ Платформа для игровых соревнований +

+ + {/* Features */} +
+ {features.map((feature, index) => ( +
+
+ {feature.icon} +
+ {feature.text} +
+ ))}
-

Добро пожаловать!

-

Войдите, чтобы продолжить

-
+ - {/* Form */} -
- {(submitError || error) && ( -
- - {submitError || error} -
- )} + {/* Form Block (right) */} + + {/* Header */} +
+

Добро пожаловать!

+

Войдите, чтобы продолжить

+
- + {/* Form */} + + {(submitError || error) && ( +
+ + {submitError || error} +
+ )} - + - } - > - Войти - - + - {/* Footer */} -
-

- Нет аккаунта?{' '} - } > - Зарегистрироваться - -

-
+ Войти + + + + {/* Footer */} +
+

+ Нет аккаунта?{' '} + + Зарегистрироваться + +

+
+
{/* Decorative elements */} -
-
+
+
) diff --git a/frontend/src/pages/MarathonPage.tsx b/frontend/src/pages/MarathonPage.tsx index e1ed8b8..0c9456a 100644 --- a/frontend/src/pages/MarathonPage.tsx +++ b/frontend/src/pages/MarathonPage.tsx @@ -189,7 +189,7 @@ export function MarathonPage() {
{/* Background */}
-
+
@@ -254,15 +254,14 @@ export function MarathonPage() { {marathon.status === 'active' && isOrganizer && ( - } - className="!text-yellow-400 !border-yellow-500/30 hover:!bg-yellow-500/10" + disabled={isFinishing} + className="inline-flex items-center justify-center gap-2 px-4 py-2.5 rounded-lg font-semibold transition-all duration-200 border border-yellow-500/30 bg-dark-600 text-yellow-400 hover:bg-yellow-500/10 hover:border-yellow-500/50 disabled:opacity-50 disabled:cursor-not-allowed" > + {isFinishing ? : } Завершить - + )} {canDelete && ( diff --git a/frontend/src/pages/MarathonsPage.tsx b/frontend/src/pages/MarathonsPage.tsx index f0f4346..d074369 100644 --- a/frontend/src/pages/MarathonsPage.tsx +++ b/frontend/src/pages/MarathonsPage.tsx @@ -216,7 +216,7 @@ export function MarathonsPage() { return (
diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx index 4890da6..fc8616b 100644 --- a/frontend/src/pages/ProfilePage.tsx +++ b/frontend/src/pages/ProfilePage.tsx @@ -263,7 +263,7 @@ export function ProfilePage() {