diff --git a/app/(home)/main/reset-password/page.tsx b/app/(auth)/login/reset-password/page.tsx similarity index 100% rename from app/(home)/main/reset-password/page.tsx rename to app/(auth)/login/reset-password/page.tsx diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx new file mode 100644 index 0000000..5a516a7 --- /dev/null +++ b/app/(auth)/register/page.tsx @@ -0,0 +1,411 @@ +"use client" + +import { useState, useEffect, useRef } from "react" +import { Card } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import Link from "next/link" +import { + MessageCircle, + X, + Sparkles, + Shield, + Zap, + CheckCircle, + ArrowRight, + Info +} from "lucide-react" + +interface Particle { + x: number + y: number + vx: number + vy: number + size: number + opacity: number + color: string +} + +export default function RegisterPage() { + const [showModal, setShowModal] = useState(false) + const [particles, setParticles] = useState([]) + const [mousePos, setMousePos] = useState({ x: 0, y: 0 }) + const canvasRef = useRef(null) + + const telegramBotUrl = "https://t.me/School21AnonimousGame_bot" + + // Initialize particles + useEffect(() => { + const initParticles: Particle[] = [] + const colors = ['#06b6d4', '#0ea5e9', '#3b82f6', '#8b5cf6'] + for (let i = 0; i < 50; i++) { + initParticles.push({ + x: Math.random() * (typeof window !== 'undefined' ? window.innerWidth : 1920), + y: Math.random() * (typeof window !== 'undefined' ? window.innerHeight : 1080), + vx: (Math.random() - 0.5) * 0.5, + vy: (Math.random() - 0.5) * 0.5, + size: Math.random() * 2 + 1, + opacity: Math.random() * 0.4 + 0.2, + color: colors[Math.floor(Math.random() * colors.length)] + }) + } + setParticles(initParticles) + }, []) + + // Animate particles + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + + const ctx = canvas.getContext('2d') + if (!ctx) return + + const updateCanvasSize = () => { + canvas.width = window.innerWidth + canvas.height = window.innerHeight + } + updateCanvasSize() + + let animationFrameId: number + + const animate = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height) + + setParticles(prevParticles => { + return prevParticles.map(particle => { + let newX = particle.x + particle.vx + let newY = particle.y + particle.vy + + if (newX < 0 || newX > canvas.width) particle.vx *= -1 + if (newY < 0 || newY > canvas.height) particle.vy *= -1 + + newX = Math.max(0, Math.min(canvas.width, newX)) + newY = Math.max(0, Math.min(canvas.height, newY)) + + ctx.shadowBlur = 8 + ctx.shadowColor = particle.color + ctx.fillStyle = `${particle.color}${Math.floor(particle.opacity * 255).toString(16).padStart(2, '0')}` + ctx.beginPath() + ctx.arc(newX, newY, particle.size, 0, Math.PI * 2) + ctx.fill() + + return { ...particle, x: newX, y: newY } + }) + }) + + animationFrameId = requestAnimationFrame(animate) + } + + animate() + + window.addEventListener('resize', updateCanvasSize) + return () => { + cancelAnimationFrame(animationFrameId) + window.removeEventListener('resize', updateCanvasSize) + } + }, []) + + // Mouse parallax + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + setMousePos({ x: e.clientX, y: e.clientY }) + } + window.addEventListener('mousemove', handleMouseMove) + return () => window.removeEventListener('mousemove', handleMouseMove) + }, []) + + const handleTelegramClick = () => { + setShowModal(true) + setTimeout(() => { + window.open(telegramBotUrl, '_blank') + }, 500) + } + + return ( +
+ {/* Animated particles canvas */} + + + {/* Background grid */} +
+ + {/* Glowing effects */} +
+
+ + + {/* Animated top accent line */} +
+ + {/* Animated corner glow */} +
+ +
+ {/* Header */} +
+
+
+ +
+

+ Registration +

+

+ Join the Cyber Academy +

+
+ + {/* Info Block */} +
+
+
+ +
+
+

+ Регистрация проходит через нашего Telegram бота. + Нажмите кнопку ниже для начала регистрации. +

+
+
+
+ + {/* Features List */} +
+ {[ + { icon: Zap, text: "Быстрая регистрация", color: "blue" }, + { icon: CheckCircle, text: "Мгновенный доступ", color: "purple" } + ].map((feature, index) => ( +
+
+ +
+ + {feature.text} + +
+ ))} +
+ + {/* Telegram Registration Button */} + + + {/* Telegram Bot Username */} + + + {/* Footer Links */} +
+
+
+
+
+
+ + OR + +
+
+ +

+ Already have an account?{" "} + + Login + +

+ + + ← Back to home + +
+
+ + {/* Enhanced corner decorations */} +
+
+
+
+ + + {/* Modal */} + {showModal && ( +
+ + {/* Animated top accent */} +
+ + {/* Close button */} + + +
+ {/* Icon */} +
+
+ +
+ + {/* Title */} +

+ Регистрация через Telegram +

+ + {/* Content */} +
+

+ Вы будете перенаправлены на нашего Telegram бота. +

+ +
+
+
+ +
+ Что нужно сделать: +
+
    +
  1. + 1. + Откройте бота в Telegram +
  2. +
  3. + 2. + Нажмите "Start" или "/start" +
  4. +
  5. + 3. + Следуйте инструкциям бота +
  6. +
  7. + 4. + Получите данные для входа +
  8. +
+
+ + {/* Bot username display */} + +
+ + {/* Buttons */} +
+ + +
+
+ + {/* Corner decorations */} +
+
+
+
+ +
+ )} + + +
+ ) +} \ No newline at end of file diff --git a/app/(home)/main/page.tsx b/app/(home)/main/page.tsx index 1819ca5..7be5eac 100644 --- a/app/(home)/main/page.tsx +++ b/app/(home)/main/page.tsx @@ -21,7 +21,8 @@ import { Clock, Star, Sparkles, - ChevronDown + Award, + TrendingUp } from "lucide-react" import Link from "next/link" @@ -35,23 +36,20 @@ interface Particle { } export default function MainPage() { - const [activeTab, setActiveTab] = useState("main") const [selectedQuest, setSelectedQuest] = useState(null) const [hoveredQuest, setHoveredQuest] = useState(null) const [mousePos, setMousePos] = useState({ x: 0, y: 0 }) const [particles, setParticles] = useState([]) const [searchQuery, setSearchQuery] = useState("") const [filterDifficulty, setFilterDifficulty] = useState("all") - const [viewMode, setViewMode] = useState<"map" | "list">("map") const canvasRef = useRef(null) - const [stats, setStats] = useState({ + const [stats] = useState({ xpToday: 450, timeSpent: "2h 15m", streak: 7, levelProgress: 68 }) - // Квесты/проекты const quests = [ { id: 1, @@ -151,17 +149,28 @@ export default function MainPage() { } ] + const questPositions = [ + { x: 150, y: 250 }, + { x: 300, y: 250 }, + { x: 450, y: 250 }, + { x: 600, y: 250 }, + { x: 750, y: 150 }, + { x: 750, y: 350 }, + { x: 900, y: 250 }, + { x: 1050, y: 250 } + ] + // Initialize particles useEffect(() => { const initParticles: Particle[] = [] - for (let i = 0; i < 50; i++) { + for (let i = 0; i < 80; i++) { initParticles.push({ - x: Math.random() * window.innerWidth, - y: Math.random() * window.innerHeight, - vx: (Math.random() - 0.5) * 0.5, - vy: (Math.random() - 0.5) * 0.5, - size: Math.random() * 2 + 1, - opacity: Math.random() * 0.5 + 0.2 + x: Math.random() * (typeof window !== 'undefined' ? window.innerWidth : 1920), + y: Math.random() * (typeof window !== 'undefined' ? window.innerHeight : 1080), + vx: (Math.random() - 0.5) * 0.8, + vy: (Math.random() - 0.5) * 0.8, + size: Math.random() * 3 + 1, + opacity: Math.random() * 0.6 + 0.2 }) } setParticles(initParticles) @@ -175,8 +184,11 @@ export default function MainPage() { const ctx = canvas.getContext('2d') if (!ctx) return - canvas.width = window.innerWidth - canvas.height = window.innerHeight + const updateCanvasSize = () => { + canvas.width = window.innerWidth + canvas.height = window.innerHeight + } + updateCanvasSize() let animationFrameId: number @@ -194,7 +206,9 @@ export default function MainPage() { newX = Math.max(0, Math.min(canvas.width, newX)) newY = Math.max(0, Math.min(canvas.height, newY)) - // Draw particle + // Draw particle with glow + ctx.shadowBlur = 10 + ctx.shadowColor = 'rgba(6, 182, 212, 0.8)' ctx.fillStyle = `rgba(6, 182, 212, ${particle.opacity})` ctx.beginPath() ctx.arc(newX, newY, particle.size, 0, Math.PI * 2) @@ -209,7 +223,11 @@ export default function MainPage() { animate() - return () => cancelAnimationFrame(animationFrameId) + window.addEventListener('resize', updateCanvasSize) + return () => { + cancelAnimationFrame(animationFrameId) + window.removeEventListener('resize', updateCanvasSize) + } }, []) // Mouse parallax @@ -246,42 +264,40 @@ export default function MainPage() { return matchesSearch && matchesDifficulty }) - const questPositions = [ - { x: 150, y: 250 }, - { x: 300, y: 250 }, - { x: 450, y: 250 }, - { x: 600, y: 250 }, - { x: 750, y: 150 }, - { x: 750, y: 350 }, - { x: 900, y: 250 }, - { x: 1050, y: 250 } - ] - return (
{/* Animated particles canvas */} {/* Animated background effects */}
+
@@ -297,25 +313,25 @@ export default function MainPage() {
- - - - - - - -
@@ -326,7 +342,7 @@ export default function MainPage() { {/* Stats Bar */}
- +
@@ -338,7 +354,7 @@ export default function MainPage() {
- +
@@ -350,7 +366,7 @@ export default function MainPage() {
- +
@@ -362,7 +378,7 @@ export default function MainPage() {
- +
@@ -371,9 +387,11 @@ export default function MainPage() {
Level Progress
+ > +
+
@@ -392,10 +410,10 @@ export default function MainPage() { Missions
- -
@@ -408,7 +426,7 @@ export default function MainPage() { placeholder="Search missions..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} - className="bg-[#0a0e1a]/80 border-cyan-500/40 text-cyan-400 placeholder:text-cyan-400/30 font-mono text-sm" + className="bg-[#0a0e1a]/80 border-cyan-500/40 text-cyan-400 placeholder:text-cyan-400/30 font-mono text-sm cursor-text focus:border-cyan-400 transition-all" />
@@ -417,7 +435,7 @@ export default function MainPage() {
@@ -376,20 +633,20 @@ export default function ProfilePage() { -
@@ -402,18 +659,38 @@ export default function ProfilePage() {
+ + {/* Additional Stats */} +
+
+
+
+ + Streak +
+
7 days
+
+
+
+ + Rank +
+
#42
+
+
+
@@ -421,13 +698,13 @@ export default function ProfilePage() { {/* Enhanced Background grid */}
@@ -439,6 +716,32 @@ export default function ProfilePage() { .animate-pulse-slow { animation: pulse-slow 3s ease-in-out infinite; } + @keyframes moveBackground { + 0% { transform: translate(0, 0); } + 100% { transform: translate(30px, 30px); } + } + @keyframes shimmer { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } + } + .animate-shimmer { + animation: shimmer 2s infinite; + } + @keyframes gradient { + 0%, 100% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + } + .animate-gradient { + background-size: 200% 200%; + animation: gradient 3s ease infinite; + } + @keyframes pulse-border { + 0%, 100% { box-shadow: 0 0 20px rgba(234, 179, 8, 0.2); } + 50% { box-shadow: 0 0 30px rgba(234, 179, 8, 0.4); } + } + .animate-pulse-border { + animation: pulse-border 2s ease-in-out infinite; + } `}
) diff --git a/app/(home)/main/progress/page.tsx b/app/(home)/main/progress/page.tsx index 66c168d..8566397 100644 --- a/app/(home)/main/progress/page.tsx +++ b/app/(home)/main/progress/page.tsx @@ -1,51 +1,158 @@ "use client" -import { useState } from "react" +import { useState, useEffect, useRef } from "react" import { Card } from "@/components/ui/card" import { Input } from "@/components/ui/input" -import { Building2, Trophy, User, Search } from "lucide-react" +import { + Building2, + Trophy, + User, + Search, + Medal, + Crown, + Star, + TrendingUp, + Award, + Sparkles, + Zap, + Shield +} from "lucide-react" import Link from "next/link" -export default function RulesPage() { - const [activeTab, setActiveTab] = useState("rules") +interface Particle { + x: number + y: number + vx: number + vy: number + size: number + opacity: number + color: string +} + +export default function ProgressPage() { const [searchQuery, setSearchQuery] = useState("") + const [particles, setParticles] = useState([]) + const [mousePos, setMousePos] = useState({ x: 0, y: 0 }) + const canvasRef = useRef(null) // Mock data для рейтингов const cityLeaderboard = [ - { rank: 1, name: "Moscow", points: 12450 }, - { rank: 2, name: "Kazan", points: 8920 }, - { rank: 3, name: "SPB", points: 7185 }, - { rank: 4, name: "Novosibirsk", points: 5670 }, - { rank: 5, name: "Ekaterinburg", points: 4320 } + { rank: 1, name: "Moscow", points: 12450, growth: "+320" }, + { rank: 2, name: "Kazan", points: 8920, growth: "+180" }, + { rank: 3, name: "SPB", points: 7185, growth: "+150" }, + { rank: 4, name: "Novosibirsk", points: 5670, growth: "+95" }, + { rank: 5, name: "Ekaterinburg", points: 4320, growth: "+70" } ] const playerLeaderboard = [ - { rank: 1, name: "cyber_god", points: 2840 }, - { rank: 2, name: "h4ck3r_pro", points: 2650 }, - { rank: 3, name: "matrix_neo", points: 2420 }, - { rank: 4, name: "data_wizard", points: 2180 }, - { rank: 5, name: "code_ninja", points: 1950 } + { rank: 1, name: "cyber_god", points: 2840, level: 12, streak: 15 }, + { rank: 2, name: "h4ck3r_pro", points: 2650, level: 11, streak: 12 }, + { rank: 3, name: "matrix_neo", points: 2420, level: 10, streak: 8 }, + { rank: 4, name: "data_wizard", points: 2180, level: 9, streak: 6 }, + { rank: 5, name: "code_ninja", points: 1950, level: 8, streak: 5 } ] const kazanLeaderboard = [ - { rank: 1, name: "pennytige", points: 1337 }, - { rank: 2, name: "kzn_hacker", points: 1280 }, - { rank: 3, name: "tatar_coder", points: 1150 }, - { rank: 4, name: "crypto_bear", points: 980 }, - { rank: 5, name: "byte_master", points: 875 } + { rank: 1, name: "pennytige", points: 1337, level: 5, isYou: true }, + { rank: 2, name: "kzn_hacker", points: 1280, level: 6, isYou: false }, + { rank: 3, name: "tatar_coder", points: 1150, level: 5, isYou: false }, + { rank: 4, name: "crypto_bear", points: 980, level: 4, isYou: false }, + { rank: 5, name: "byte_master", points: 875, level: 4, isYou: false } ] const allPlayers = [ - { rank: 1, name: "cyber_god", level: 12, points: 2840, city: "Moscow" }, - { rank: 2, name: "h4ck3r_pro", level: 11, points: 2650, city: "SPB" }, - { rank: 3, name: "matrix_neo", level: 10, points: 2420, city: "Kazan" }, - { rank: 15, name: "pennytige", level: 5, points: 1337, city: "Kazan" } + { rank: 1, name: "cyber_god", level: 12, points: 2840, city: "Moscow", change: "up" }, + { rank: 2, name: "h4ck3r_pro", level: 11, points: 2650, city: "SPB", change: "up" }, + { rank: 3, name: "matrix_neo", level: 10, points: 2420, city: "Kazan", change: "same" }, + { rank: 4, name: "data_wizard", level: 9, points: 2180, city: "Moscow", change: "down" }, + { rank: 5, name: "code_ninja", level: 8, points: 1950, city: "Kazan", change: "up" }, + { rank: 15, name: "pennytige", level: 5, points: 1337, city: "Kazan", change: "up", isYou: true } ] const filteredPlayers = allPlayers.filter(player => player.name.toLowerCase().includes(searchQuery.toLowerCase()) ) + // Initialize particles + useEffect(() => { + const initParticles: Particle[] = [] + const colors = ['#06b6d4', '#eab308', '#8b5cf6', '#3b82f6', '#10b981'] + for (let i = 0; i < 70; i++) { + initParticles.push({ + x: Math.random() * (typeof window !== 'undefined' ? window.innerWidth : 1920), + y: Math.random() * (typeof window !== 'undefined' ? window.innerHeight : 1080), + vx: (Math.random() - 0.5) * 0.7, + vy: (Math.random() - 0.5) * 0.7, + size: Math.random() * 2.5 + 1, + opacity: Math.random() * 0.5 + 0.2, + color: colors[Math.floor(Math.random() * colors.length)] + }) + } + setParticles(initParticles) + }, []) + + // Animate particles + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + + const ctx = canvas.getContext('2d') + if (!ctx) return + + const updateCanvasSize = () => { + canvas.width = window.innerWidth + canvas.height = window.innerHeight + } + updateCanvasSize() + + let animationFrameId: number + + const animate = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height) + + setParticles(prevParticles => { + return prevParticles.map(particle => { + let newX = particle.x + particle.vx + let newY = particle.y + particle.vy + + if (newX < 0 || newX > canvas.width) particle.vx *= -1 + if (newY < 0 || newY > canvas.height) particle.vy *= -1 + + newX = Math.max(0, Math.min(canvas.width, newX)) + newY = Math.max(0, Math.min(canvas.height, newY)) + + ctx.shadowBlur = 10 + ctx.shadowColor = particle.color + ctx.fillStyle = `${particle.color}${Math.floor(particle.opacity * 255).toString(16).padStart(2, '0')}` + ctx.beginPath() + ctx.arc(newX, newY, particle.size, 0, Math.PI * 2) + ctx.fill() + + return { ...particle, x: newX, y: newY } + }) + }) + + animationFrameId = requestAnimationFrame(animate) + } + + animate() + + window.addEventListener('resize', updateCanvasSize) + return () => { + cancelAnimationFrame(animationFrameId) + window.removeEventListener('resize', updateCanvasSize) + } + }, []) + + // Mouse parallax + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + setMousePos({ x: e.clientX, y: e.clientY }) + } + window.addEventListener('mousemove', handleMouseMove) + return () => window.removeEventListener('mousemove', handleMouseMove) + }, []) + const getRankColor = (rank: number) => { if (rank === 1) return "text-yellow-400" if (rank === 2) return "text-gray-300" @@ -53,11 +160,56 @@ export default function RulesPage() { return "text-cyan-400" } + const getRankIcon = (rank: number) => { + if (rank === 1) return + if (rank === 2) return + if (rank === 3) return + return null + } + + const getRankBg = (rank: number) => { + if (rank === 1) return "bg-gradient-to-br from-yellow-500/20 to-orange-500/20 border-yellow-500/40" + if (rank === 2) return "bg-gradient-to-br from-gray-400/20 to-gray-500/20 border-gray-400/40" + if (rank === 3) return "bg-gradient-to-br from-amber-600/20 to-orange-700/20 border-amber-600/40" + return "bg-cyan-500/5 border-cyan-500/20" + } + return (
+ {/* Animated particles canvas */} + + {/* Animated background effects */} -
-
+
+
+
{/* Header */}
@@ -65,24 +217,24 @@ export default function RulesPage() {
- - - - + - - - + +
@@ -92,39 +244,73 @@ export default function RulesPage() { {/* Main Content */}
+ {/* Page Title */} +
+
+ +

+ Leaderboards +

+ +
+

Соревнуйся с лучшими хакерами!

+
+ {/* Top Leaderboards */} -
+
{/* Топ городов */}
+ {/* Animated background pattern */} +
+
+
+
-
-
- +
+
+ +
+
+

+ Топ городов +

+

City Rankings

-

- Топ городов -

- {cityLeaderboard.map((entry) => ( + {cityLeaderboard.map((entry, index) => (
-
- - #{entry.rank} - - +
+
+ {getRankIcon(entry.rank)} + + #{entry.rank} + +
+ {entry.name}
- - {entry.points.toLocaleString()} - +
+ + {entry.points.toLocaleString()} + + + + {entry.growth} + +
))}
@@ -135,29 +321,54 @@ export default function RulesPage() {
+ {/* Animated background pattern */} +
+
+
+
-
-
- +
+
+ +
+
+

+ Топ игроков +

+

Global Rankings

-

- Топ игроков -

- {playerLeaderboard.map((entry) => ( + {playerLeaderboard.map((entry, index) => (
-
- - #{entry.rank} - - - {entry.name} - +
+
+ {getRankIcon(entry.rank)} + + #{entry.rank} + +
+
+ + {entry.name} + +
+ Lvl {entry.level} + + + {entry.streak} + +
+
{entry.points.toLocaleString()} @@ -169,32 +380,61 @@ export default function RulesPage() { {/* Топ Kazan */} - -
+ +
+ + {/* Animated background pattern */} +
+
+
-
-
- +
+
+ +
+
+

+ Топ Kazan +

+

Local Rankings

-

- Топ Kazan -

- {kazanLeaderboard.map((entry) => ( + {kazanLeaderboard.map((entry, index) => (
-
- - #{entry.rank} - - - {entry.name} - +
+
+ {getRankIcon(entry.rank)} + + #{entry.rank} + +
+
+ + {entry.name} + + {entry.isYou && ( + + YOU + + )} +
{entry.points.toLocaleString()} @@ -211,78 +451,121 @@ export default function RulesPage() {
-
- -

- Все игроки -

+
+
+
+ +
+
+

+ Все игроки +

+

Complete Rankings

+
+
+
+ + + {allPlayers.length} игроков + +
{/* Search */}
- + setSearchQuery(e.target.value)} - className="pl-12 bg-[#0a0e1a]/80 backdrop-blur border-cyan-500/40 text-cyan-400 placeholder:text-cyan-400/30 font-mono hover:border-cyan-500/60 transition-all focus:border-cyan-400 focus:shadow-[0_0_20px_rgba(6,182,212,0.2)]" + className="pl-12 bg-[#0a0e1a]/80 backdrop-blur border-cyan-500/40 text-cyan-400 placeholder:text-cyan-400/30 font-mono hover:border-cyan-500/60 transition-all focus:border-cyan-400 focus:shadow-[0_0_20px_rgba(6,182,212,0.2)] cursor-text h-12 rounded-xl" />
{/* Table */} -
+
- - + - - - - + - {filteredPlayers.map((player) => ( + {filteredPlayers.map((player, index) => ( - - - - - + ))} @@ -290,10 +573,16 @@ export default function RulesPage() { {filteredPlayers.length === 0 && ( -
-

+

+
+ +
+

Игрок не найден

+

+ Попробуйте другой запрос +

)}
@@ -302,15 +591,29 @@ export default function RulesPage() { {/* Enhanced Background grid */}
+ +
) } \ No newline at end of file
+
Ранг + Игрок + Уровень + Очки + Город + Тренд +
- - #{player.rank} - + +
+ {getRankIcon(player.rank)} + + #{player.rank} + +
- - {player.name} - + +
+ + {player.name} + + {player.isYou && ( + + YOU + + )} +
- - Lvl {player.level} - + +
+ + + {player.level} + +
+ {player.points.toLocaleString()} + {player.city} +
+ {player.change === 'up' && } + {player.change === 'down' && } + {player.change === 'same' && } +
+