Files
game-marathon/frontend/src/components/layout/Layout.tsx

217 lines
8.1 KiB
TypeScript
Raw Normal View History

2025-12-17 02:03:33 +07:00
import { useState, useEffect } from 'react'
import { Outlet, Link, useNavigate, useLocation } from 'react-router-dom'
2025-12-14 02:38:35 +07:00
import { useAuthStore } from '@/store/auth'
2025-12-17 02:03:33 +07:00
import { Gamepad2, LogOut, Trophy, User, Menu, X } from 'lucide-react'
2025-12-16 20:06:16 +07:00
import { TelegramLink } from '@/components/TelegramLink'
2025-12-17 02:03:33 +07:00
import { clsx } from 'clsx'
2025-12-14 02:38:35 +07:00
export function Layout() {
const { user, isAuthenticated, logout } = useAuthStore()
const navigate = useNavigate()
2025-12-17 02:03:33 +07:00
const location = useLocation()
const [isScrolled, setIsScrolled] = useState(false)
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 10)
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [])
// Close mobile menu on route change
useEffect(() => {
setIsMobileMenuOpen(false)
}, [location])
2025-12-14 02:38:35 +07:00
const handleLogout = () => {
logout()
navigate('/login')
}
2025-12-17 02:03:33 +07:00
const isActiveLink = (path: string) => location.pathname === path
2025-12-14 02:38:35 +07:00
return (
<div className="min-h-screen flex flex-col">
{/* Header */}
2025-12-17 02:03:33 +07:00
<header
className={clsx(
'fixed top-0 left-0 right-0 z-50 transition-all duration-300',
isScrolled
? 'bg-dark-900/80 backdrop-blur-lg border-b border-dark-600/50 shadow-lg'
: 'bg-transparent'
)}
>
2025-12-14 02:38:35 +07:00
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
2025-12-17 02:03:33 +07:00
{/* Logo */}
<Link
to="/"
className="flex items-center gap-3 group"
>
<div className="relative">
<Gamepad2 className="w-8 h-8 text-neon-500 transition-all duration-300 group-hover:text-neon-400 group-hover:drop-shadow-[0_0_8px_rgba(0,240,255,0.8)]" />
</div>
<span className="text-xl font-bold text-white font-display tracking-wider glitch-hover">
МАРАФОН
</span>
2025-12-14 02:38:35 +07:00
</Link>
2025-12-17 02:03:33 +07:00
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center gap-6">
2025-12-14 02:38:35 +07:00
{isAuthenticated ? (
<>
<Link
to="/marathons"
2025-12-17 02:03:33 +07:00
className={clsx(
'flex items-center gap-2 px-3 py-2 rounded-lg transition-all duration-200',
isActiveLink('/marathons')
? 'text-neon-400 bg-neon-500/10'
: 'text-gray-300 hover:text-white hover:bg-dark-700'
)}
2025-12-14 02:38:35 +07:00
>
<Trophy className="w-5 h-5" />
<span>Марафоны</span>
</Link>
2025-12-17 02:03:33 +07:00
<div className="flex items-center gap-3 ml-2 pl-4 border-l border-dark-600">
2025-12-16 22:12:12 +07:00
<Link
to="/profile"
2025-12-17 02:03:33 +07:00
className={clsx(
'flex items-center gap-2 px-3 py-2 rounded-lg transition-all duration-200',
isActiveLink('/profile')
? 'text-neon-400 bg-neon-500/10'
: 'text-gray-300 hover:text-white hover:bg-dark-700'
)}
2025-12-16 22:12:12 +07:00
>
2025-12-14 02:38:35 +07:00
<User className="w-5 h-5" />
<span>{user?.nickname}</span>
2025-12-16 22:12:12 +07:00
</Link>
2025-12-14 02:38:35 +07:00
2025-12-16 20:06:16 +07:00
<TelegramLink />
2025-12-14 02:38:35 +07:00
<button
onClick={handleLogout}
2025-12-17 02:03:33 +07:00
className="p-2 text-gray-400 hover:text-red-400 hover:bg-red-500/10 rounded-lg transition-all duration-200"
2025-12-14 02:38:35 +07:00
title="Выйти"
>
<LogOut className="w-5 h-5" />
</button>
</div>
</>
) : (
<>
2025-12-17 02:03:33 +07:00
<Link
to="/login"
className="text-gray-300 hover:text-white transition-colors px-4 py-2"
>
2025-12-14 02:38:35 +07:00
Войти
</Link>
2025-12-17 02:03:33 +07:00
<Link
to="/register"
className="px-4 py-2 bg-neon-500 hover:bg-neon-400 text-dark-900 font-semibold rounded-lg transition-all duration-200 shadow-[0_0_15px_rgba(0,240,255,0.3)] hover:shadow-[0_0_25px_rgba(0,240,255,0.5)]"
>
2025-12-14 02:38:35 +07:00
Регистрация
</Link>
</>
)}
</nav>
2025-12-17 02:03:33 +07:00
{/* Mobile Menu Button */}
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="md:hidden p-2 text-gray-300 hover:text-white rounded-lg hover:bg-dark-700 transition-colors"
>
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
2025-12-14 02:38:35 +07:00
</div>
2025-12-17 02:03:33 +07:00
{/* Mobile Menu */}
{isMobileMenuOpen && (
<div className="md:hidden bg-dark-800/95 backdrop-blur-lg border-t border-dark-600 animate-slide-in-down">
<div className="container mx-auto px-4 py-4 space-y-2">
{isAuthenticated ? (
<>
<Link
to="/marathons"
className={clsx(
'flex items-center gap-3 px-4 py-3 rounded-lg transition-all',
isActiveLink('/marathons')
? 'text-neon-400 bg-neon-500/10'
: 'text-gray-300 hover:text-white hover:bg-dark-700'
)}
>
<Trophy className="w-5 h-5" />
<span>Марафоны</span>
</Link>
<Link
to="/profile"
className={clsx(
'flex items-center gap-3 px-4 py-3 rounded-lg transition-all',
isActiveLink('/profile')
? 'text-neon-400 bg-neon-500/10'
: 'text-gray-300 hover:text-white hover:bg-dark-700'
)}
>
<User className="w-5 h-5" />
<span>{user?.nickname}</span>
</Link>
<div className="pt-2 border-t border-dark-600">
<button
onClick={handleLogout}
className="flex items-center gap-3 w-full px-4 py-3 text-red-400 hover:bg-red-500/10 rounded-lg transition-all"
>
<LogOut className="w-5 h-5" />
<span>Выйти</span>
</button>
</div>
</>
) : (
<>
<Link
to="/login"
className="block px-4 py-3 text-gray-300 hover:text-white hover:bg-dark-700 rounded-lg transition-all"
>
Войти
</Link>
<Link
to="/register"
className="block px-4 py-3 text-center bg-neon-500 hover:bg-neon-400 text-dark-900 font-semibold rounded-lg transition-all"
>
Регистрация
</Link>
</>
)}
</div>
</div>
)}
2025-12-14 02:38:35 +07:00
</header>
2025-12-17 02:03:33 +07:00
{/* Spacer for fixed header */}
<div className="h-[72px]" />
2025-12-14 02:38:35 +07:00
{/* Main content */}
<main className="flex-1 container mx-auto px-4 py-8">
<Outlet />
</main>
{/* Footer */}
2025-12-17 02:03:33 +07:00
<footer className="bg-dark-800/50 border-t border-dark-600/50 py-6">
<div className="container mx-auto px-4">
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-2 text-gray-500">
<Gamepad2 className="w-5 h-5 text-neon-500/50" />
<span className="text-sm">
Игровой Марафон &copy; {new Date().getFullYear()}
</span>
</div>
<div className="flex items-center gap-4 text-sm text-gray-500">
<span className="text-neon-500/50">v1.0</span>
</div>
</div>
2025-12-14 02:38:35 +07:00
</div>
</footer>
</div>
)
}