Files
s21_game2_front_dan/app/(home)/main/page.tsx
2025-12-23 12:35:59 +03:00

850 lines
37 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState, useEffect, useRef } from "react"
import { Card } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
Lock,
CheckCircle2,
Circle,
Zap,
Target,
Shield,
Code,
Database,
Globe,
X,
Search,
Filter,
Trophy,
Clock,
Star,
Sparkles,
ChevronDown
} from "lucide-react"
import Link from "next/link"
interface Particle {
x: number
y: number
vx: number
vy: number
size: number
opacity: number
}
export default function MainPage() {
const [activeTab, setActiveTab] = useState("main")
const [selectedQuest, setSelectedQuest] = useState<number | null>(null)
const [hoveredQuest, setHoveredQuest] = useState<number | null>(null)
const [mousePos, setMousePos] = useState({ x: 0, y: 0 })
const [particles, setParticles] = useState<Particle[]>([])
const [searchQuery, setSearchQuery] = useState("")
const [filterDifficulty, setFilterDifficulty] = useState<string>("all")
const [viewMode, setViewMode] = useState<"map" | "list">("map")
const canvasRef = useRef<HTMLCanvasElement>(null)
const [stats, setStats] = useState({
xpToday: 450,
timeSpent: "2h 15m",
streak: 7,
levelProgress: 68
})
// Квесты/проекты
const quests = [
{
id: 1,
name: "Registration",
status: "completed",
icon: Circle,
color: "cyan",
description: "Создайте свой аккаунт и начните путешествие в мир киберпространства",
reward: 100,
difficulty: "Easy",
estimatedTime: "5 min",
prerequisites: []
},
{
id: 2,
name: "Sanctum",
status: "completed",
icon: Shield,
color: "cyan",
description: "Пройдите базовую защиту системы и изучите основы безопасности",
reward: 150,
difficulty: "Easy",
estimatedTime: "15 min",
prerequisites: [1]
},
{
id: 3,
name: "Memories",
status: "unlocked",
icon: Database,
color: "yellow",
description: "Восстановите утерянные данные из архива и раскройте секреты прошлого",
reward: 200,
difficulty: "Medium",
estimatedTime: "30 min",
prerequisites: [1, 2]
},
{
id: 4,
name: "Cyber toy",
status: "locked",
icon: Code,
color: "red",
description: "Взломайте защищенную систему используя продвинутые техники",
reward: 250,
difficulty: "Medium",
estimatedTime: "45 min",
prerequisites: [3]
},
{
id: 5,
name: "Flood",
status: "locked",
icon: Zap,
color: "red",
description: "Преодолейте мощные потоки данных и найдите путь через хаос",
reward: 300,
difficulty: "Hard",
estimatedTime: "1h",
prerequisites: [3]
},
{
id: 6,
name: "Core",
status: "locked",
icon: Target,
color: "red",
description: "Проникните в самое ядро системы и получите контроль",
reward: 350,
difficulty: "Hard",
estimatedTime: "1h 30m",
prerequisites: [4, 5]
},
{
id: 7,
name: "Access point",
status: "locked",
icon: Globe,
color: "red",
description: "Получите доступ к финальной точке и завершите миссию",
reward: 500,
difficulty: "Expert",
estimatedTime: "2h",
prerequisites: [6]
},
{
id: 8,
name: "???",
status: "locked",
icon: Lock,
color: "gray",
description: "Секретное задание. Требования неизвестны.",
reward: 999,
difficulty: "???",
estimatedTime: "???",
prerequisites: [7]
}
]
// Initialize particles
useEffect(() => {
const initParticles: Particle[] = []
for (let i = 0; i < 50; 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
})
}
setParticles(initParticles)
}, [])
// Animate particles
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext('2d')
if (!ctx) return
canvas.width = window.innerWidth
canvas.height = window.innerHeight
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))
// Draw particle
ctx.fillStyle = `rgba(6, 182, 212, ${particle.opacity})`
ctx.beginPath()
ctx.arc(newX, newY, particle.size, 0, Math.PI * 2)
ctx.fill()
return { ...particle, x: newX, y: newY }
})
})
animationFrameId = requestAnimationFrame(animate)
}
animate()
return () => cancelAnimationFrame(animationFrameId)
}, [])
// Mouse parallax
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
setMousePos({ x: e.clientX, y: e.clientY })
}
window.addEventListener('mousemove', handleMouseMove)
return () => window.removeEventListener('mousemove', handleMouseMove)
}, [])
const getStatusColor = (status: string) => {
switch (status) {
case "completed": return "from-green-500 to-emerald-600"
case "unlocked": return "from-yellow-500 to-amber-600"
case "locked": return "from-red-500 to-rose-600"
default: return "from-gray-500 to-gray-600"
}
}
const getDifficultyColor = (difficulty: string) => {
switch (difficulty) {
case "Easy": return "text-green-400"
case "Medium": return "text-yellow-400"
case "Hard": return "text-orange-400"
case "Expert": return "text-red-400"
default: return "text-gray-400"
}
}
const filteredQuests = quests.filter(quest => {
const matchesSearch = quest.name.toLowerCase().includes(searchQuery.toLowerCase())
const matchesDifficulty = filterDifficulty === "all" || quest.difficulty === filterDifficulty
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 (
<div className="min-h-screen bg-[#0a0e1a] relative overflow-hidden">
{/* Animated particles canvas */}
<canvas
ref={canvasRef}
className="fixed inset-0 pointer-events-none z-0"
style={{ opacity: 0.4 }}
/>
{/* Animated background effects */}
<div
className="fixed w-96 h-96 bg-cyan-500/5 rounded-full blur-[120px] animate-pulse transition-transform duration-1000"
style={{
top: '20%',
left: '20%',
transform: `translate(${(mousePos.x - window.innerWidth / 2) * 0.02}px, ${(mousePos.y - window.innerHeight / 2) * 0.02}px)`
}}
/>
<div
className="fixed w-96 h-96 bg-purple-500/5 rounded-full blur-[120px] animate-pulse transition-transform duration-1000"
style={{
bottom: '20%',
right: '20%',
animationDelay: '1s',
transform: `translate(${-(mousePos.x - window.innerWidth / 2) * 0.02}px, ${-(mousePos.y - window.innerHeight / 2) * 0.02}px)`
}}
/>
{/* Header */}
<header className="bg-[#0d1117]/80 backdrop-blur-xl border-b border-cyan-500/30 sticky top-0 z-50 shadow-lg shadow-cyan-500/5">
<nav className="container mx-auto px-6 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-8">
<div className="flex items-center gap-2">
<Sparkles className="w-5 h-5 text-cyan-400 animate-pulse" />
<span className="text-cyan-400 font-mono font-bold">CYBER ACADEMY</span>
</div>
</div>
<div className="flex items-center gap-12">
<button className="text-sm font-bold tracking-[0.2em] transition-all duration-200 font-mono uppercase text-cyan-400 scale-110 drop-shadow-[0_0_8px_rgba(6,182,212,0.5)]">
Main
</button>
<Link href="/home/progress">
<button className="text-sm font-bold tracking-[0.2em] transition-all duration-200 font-mono uppercase text-cyan-500/50 hover:text-cyan-400/80 hover:scale-105">
Progress
</button>
</Link>
<Link href="/home/profile">
<button className="text-sm font-bold tracking-[0.2em] transition-all duration-200 font-mono uppercase text-cyan-500/50 hover:text-cyan-400/80 hover:scale-105">
Profile
</button>
</Link>
<Link href="/home/rules">
<button className="text-sm font-bold tracking-[0.2em] transition-all duration-200 font-mono uppercase text-cyan-500/50 hover:text-cyan-400/80 hover:scale-105">
Rules
</button>
</Link>
<button className="text-sm font-bold tracking-[0.2em] transition-all duration-200 font-mono uppercase text-cyan-500/50 hover:text-cyan-400/80 hover:scale-105">
Logoff
</button>
</div>
</div>
</nav>
</header>
{/* Stats Bar */}
<div className="container mx-auto px-6 py-4 relative z-10">
<div className="grid grid-cols-4 gap-4">
<Card className="bg-[#0d1117]/50 backdrop-blur-xl border border-cyan-500/30 p-4 shadow-[0_0_20px_rgba(6,182,212,0.1)] hover:shadow-[0_0_30px_rgba(6,182,212,0.2)] transition-all">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-cyan-500/20 rounded-lg flex items-center justify-center">
<Trophy className="w-5 h-5 text-cyan-400" />
</div>
<div>
<div className="text-xs text-cyan-400/60 font-mono">XP Today</div>
<div className="text-lg font-bold text-cyan-400 font-mono">+{stats.xpToday}</div>
</div>
</div>
</Card>
<Card className="bg-[#0d1117]/50 backdrop-blur-xl border border-cyan-500/30 p-4 shadow-[0_0_20px_rgba(6,182,212,0.1)] hover:shadow-[0_0_30px_rgba(6,182,212,0.2)] transition-all">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-cyan-500/20 rounded-lg flex items-center justify-center">
<Clock className="w-5 h-5 text-cyan-400" />
</div>
<div>
<div className="text-xs text-cyan-400/60 font-mono">Time Spent</div>
<div className="text-lg font-bold text-cyan-400 font-mono">{stats.timeSpent}</div>
</div>
</div>
</Card>
<Card className="bg-[#0d1117]/50 backdrop-blur-xl border border-cyan-500/30 p-4 shadow-[0_0_20px_rgba(6,182,212,0.1)] hover:shadow-[0_0_30px_rgba(6,182,212,0.2)] transition-all">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-cyan-500/20 rounded-lg flex items-center justify-center">
<Zap className="w-5 h-5 text-yellow-400 animate-pulse" />
</div>
<div>
<div className="text-xs text-cyan-400/60 font-mono">Streak</div>
<div className="text-lg font-bold text-yellow-400 font-mono">{stats.streak} days</div>
</div>
</div>
</Card>
<Card className="bg-[#0d1117]/50 backdrop-blur-xl border border-cyan-500/30 p-4 shadow-[0_0_20px_rgba(6,182,212,0.1)] hover:shadow-[0_0_30px_rgba(6,182,212,0.2)] transition-all">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-cyan-500/20 rounded-lg flex items-center justify-center">
<Star className="w-5 h-5 text-cyan-400" />
</div>
<div className="flex-1">
<div className="text-xs text-cyan-400/60 font-mono mb-1">Level Progress</div>
<div className="h-2 bg-[#0a0e1a] rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-cyan-500 to-blue-500 rounded-full transition-all duration-500"
style={{ width: `${stats.levelProgress}%` }}
/>
</div>
</div>
</div>
</Card>
</div>
</div>
{/* Main Content */}
<main className="container mx-auto px-6 py-4 relative z-10">
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
{/* Left Sidebar */}
<Card className="lg:col-span-1 bg-[#0d1117]/50 backdrop-blur-xl border-2 border-cyan-500/30 p-6 shadow-[0_0_30px_rgba(6,182,212,0.1)] max-h-[calc(100vh-350px)] overflow-y-auto">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-bold text-cyan-400 font-mono flex items-center gap-2">
<Target className="w-5 h-5 animate-pulse" />
Missions
</h3>
<div className="flex gap-2">
<button className="text-cyan-400 hover:text-cyan-300 transition-colors">
<Search className="w-4 h-4" />
</button>
<button className="text-cyan-400 hover:text-cyan-300 transition-colors">
<Filter className="w-4 h-4" />
</button>
</div>
</div>
{/* Search */}
<div className="mb-4">
<Input
type="text"
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"
/>
</div>
{/* Filter */}
<div className="mb-4">
<select
value={filterDifficulty}
onChange={(e) => setFilterDifficulty(e.target.value)}
className="w-full bg-[#0a0e1a]/80 border border-cyan-500/40 text-cyan-400 font-mono text-sm rounded-lg p-2 focus:border-cyan-400 focus:outline-none"
>
<option value="all">All Difficulties</option>
<option value="Easy">Easy</option>
<option value="Medium">Medium</option>
<option value="Hard">Hard</option>
<option value="Expert">Expert</option>
</select>
</div>
<div className="space-y-2">
{filteredQuests.map((quest) => {
const Icon = quest.icon
return (
<button
key={quest.id}
onClick={() => setSelectedQuest(quest.id)}
className={`w-full text-left p-3 rounded-lg border transition-all duration-300 group ${
selectedQuest === quest.id
? 'bg-cyan-500/20 border-cyan-500/60 shadow-[0_0_20px_rgba(6,182,212,0.2)]'
: quest.status === 'completed'
? 'bg-green-500/5 border-green-500/30 hover:border-green-500/50'
: quest.status === 'unlocked'
? 'bg-yellow-500/5 border-yellow-500/30 hover:border-yellow-500/50'
: 'bg-[#0a0e1a]/50 border-cyan-500/20 hover:border-cyan-500/30'
}`}
>
<div className="flex items-center gap-3">
<div className={`w-8 h-8 rounded-lg bg-gradient-to-br ${getStatusColor(quest.status)}/20 flex items-center justify-center flex-shrink-0`}>
<Icon className={`w-4 h-4 ${
quest.status === 'completed' ? 'text-green-400' :
quest.status === 'unlocked' ? 'text-yellow-400' :
'text-red-400/50'
}`} />
</div>
<div className="flex-1 min-w-0">
<div className="font-mono text-sm text-cyan-400 truncate">
{quest.name}
</div>
<div className="text-xs text-cyan-400/50 font-mono">
{quest.difficulty} {quest.reward} pts
</div>
</div>
{quest.status === 'completed' && <CheckCircle2 className="w-4 h-4 text-green-400" />}
{quest.status === 'unlocked' && <Zap className="w-4 h-4 text-yellow-400 animate-pulse" />}
{quest.status === 'locked' && <Lock className="w-4 h-4 text-red-400/50" />}
</div>
</button>
)
})}
</div>
</Card>
{/* Center - Interactive Quest Map */}
<Card className="lg:col-span-3 bg-[#0d1117]/50 backdrop-blur-xl border-2 border-cyan-500/30 p-8 shadow-[0_0_30px_rgba(6,182,212,0.1)] relative overflow-hidden group min-h-[600px]">
<div className="relative z-10">
<div className="flex items-center justify-between mb-6">
<h3 className="text-2xl font-bold text-cyan-400 font-mono flex items-center gap-3">
<Globe className="w-7 h-7 animate-pulse" />
Quest Network
</h3>
<div className="text-sm text-cyan-400/60 font-mono">
{quests.filter(q => q.status === 'completed').length}/{quests.length} Completed
</div>
</div>
{/* SVG Quest Map */}
<div className="relative min-h-[500px] flex items-center justify-center">
<svg
viewBox="0 0 1200 500"
className="w-full h-full"
style={{ filter: 'drop-shadow(0 0 10px rgba(6, 182, 212, 0.3))' }}
>
<defs>
<linearGradient id="lineGradient1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#10b981" stopOpacity="0.8" />
<stop offset="100%" stopColor="#10b981" stopOpacity="0.3" />
</linearGradient>
<linearGradient id="lineGradient2" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#eab308" stopOpacity="0.6" />
<stop offset="100%" stopColor="#ef4444" stopOpacity="0.3" />
</linearGradient>
<filter id="glow">
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
{/* Animated data flow particles */}
{quests.slice(0, -1).map((quest, index) => {
if (quest.status === 'completed') {
const start = questPositions[index]
const end = questPositions[index + 1]
return (
<circle key={`flow-${index}`} r="3" fill="#10b981" opacity="0.8">
<animateMotion
dur="2s"
repeatCount="indefinite"
path={`M ${start.x} ${start.y} L ${end.x} ${end.y}`}
/>
</circle>
)
}
return null
})}
{/* Connection Lines */}
<path d="M 150 250 L 300 250" stroke="url(#lineGradient1)" strokeWidth="3" strokeLinecap="round" />
<path d="M 300 250 L 450 250" stroke="url(#lineGradient1)" strokeWidth="3" strokeLinecap="round" />
<path d="M 450 250 L 600 250" stroke="url(#lineGradient2)" strokeWidth="3" strokeLinecap="round" strokeDasharray="5,5" />
<path d="M 600 250 L 750 150" stroke="url(#lineGradient2)" strokeWidth="3" strokeLinecap="round" strokeDasharray="5,5" />
<path d="M 600 250 L 750 350" stroke="url(#lineGradient2)" strokeWidth="3" strokeLinecap="round" strokeDasharray="5,5" />
<path d="M 750 150 L 900 250" stroke="url(#lineGradient2)" strokeWidth="3" strokeLinecap="round" strokeDasharray="5,5" />
<path d="M 750 350 L 900 250" stroke="url(#lineGradient2)" strokeWidth="3" strokeLinecap="round" strokeDasharray="5,5" />
<path d="M 900 250 L 1050 250" stroke="url(#lineGradient2)" strokeWidth="3" strokeLinecap="round" strokeDasharray="5,5" />
{/* Quest Nodes - Hexagons */}
{questPositions.map((pos, index) => {
const quest = quests[index]
const isSelected = selectedQuest === quest.id
const isHovered = hoveredQuest === quest.id
const statusColor =
quest.status === 'completed' ? '#10b981' :
quest.status === 'unlocked' ? '#eab308' :
'#ef4444'
const Icon = quest.icon
const hexSize = 30
const hexPoints = Array.from({ length: 6 }, (_, i) => {
const angle = (Math.PI / 3) * i
return `${pos.x + hexSize * Math.cos(angle)},${pos.y + hexSize * Math.sin(angle)}`
}).join(' ')
return (
<g
key={index}
onMouseEnter={() => setHoveredQuest(quest.id)}
onMouseLeave={() => setHoveredQuest(null)}
onClick={() => setSelectedQuest(quest.id)}
className="cursor-pointer transition-all duration-300"
style={{
transform: isHovered ? 'translateY(-10px)' : 'translateY(0)',
transformOrigin: `${pos.x}px ${pos.y}px`
}}
>
{/* Outer glow rings */}
{isSelected && (
<>
<polygon
points={Array.from({ length: 6 }, (_, i) => {
const angle = (Math.PI / 3) * i
return `${pos.x + 45 * Math.cos(angle)},${pos.y + 45 * Math.sin(angle)}`
}).join(' ')}
fill="none"
stroke={statusColor}
strokeWidth="2"
opacity="0.3"
className="animate-ping"
/>
<polygon
points={Array.from({ length: 6 }, (_, i) => {
const angle = (Math.PI / 3) * i
return `${pos.x + 40 * Math.cos(angle)},${pos.y + 40 * Math.sin(angle)}`
}).join(' ')}
fill="none"
stroke={statusColor}
strokeWidth="3"
opacity="0.6"
/>
</>
)}
{/* Hexagon node */}
<polygon
points={hexPoints}
fill={`${statusColor}20`}
stroke={statusColor}
strokeWidth={isSelected ? "4" : "2"}
filter="url(#glow)"
className="transition-all duration-300"
/>
{/* Inner hexagon */}
<polygon
points={Array.from({ length: 6 }, (_, i) => {
const angle = (Math.PI / 3) * i
return `${pos.x + 20 * Math.cos(angle)},${pos.y + 20 * Math.sin(angle)}`
}).join(' ')}
fill={statusColor}
opacity={quest.status === 'locked' ? '0.3' : '0.6'}
/>
{/* Hover tooltip */}
{isHovered && (
<g>
<rect
x={pos.x - 100}
y={pos.y - 80}
width="200"
height="60"
rx="8"
fill="#0d1117"
stroke="#06b6d4"
strokeWidth="2"
opacity="0.95"
/>
<text
x={pos.x}
y={pos.y - 55}
textAnchor="middle"
fill="#06b6d4"
fontSize="14"
fontWeight="bold"
fontFamily="monospace"
>
{quest.name}
</text>
<text
x={pos.x}
y={pos.y - 35}
textAnchor="middle"
fill="#06b6d4"
fontSize="10"
fontFamily="monospace"
opacity="0.7"
>
{quest.difficulty} {quest.estimatedTime}
</text>
<text
x={pos.x}
y={pos.y - 20}
textAnchor="middle"
fill="#10b981"
fontSize="10"
fontWeight="bold"
fontFamily="monospace"
>
+{quest.reward} PTS
</text>
</g>
)}
</g>
)
})}
</svg>
{/* Quest Details Panel with Close Button */}
{selectedQuest && (
<Card className="absolute bottom-4 left-4 right-4 bg-[#0a0e1a]/95 backdrop-blur-xl border-2 border-cyan-500/40 p-6 shadow-[0_0_40px_rgba(6,182,212,0.2)] animate-in fade-in slide-in-from-bottom-4 duration-300">
{(() => {
const quest = quests.find(q => q.id === selectedQuest)
if (!quest) return null
const Icon = quest.icon
return (
<>
{/* Close Button */}
<button
onClick={(e) => {
e.stopPropagation()
setSelectedQuest(null)
}}
className="absolute top-4 right-4 w-8 h-8 bg-red-500/20 hover:bg-red-500/30 rounded-lg flex items-center justify-center transition-all group border border-red-500/40 hover:border-red-500/60"
>
<X className="w-5 h-5 text-red-400 group-hover:text-red-300" />
</button>
<div className="flex items-start gap-6">
<div className={`w-20 h-20 rounded-2xl bg-gradient-to-br ${getStatusColor(quest.status)}/20 border-2 border-cyan-500/40 flex items-center justify-center flex-shrink-0 shadow-lg relative overflow-hidden group`}>
<div className="absolute inset-0 bg-gradient-to-br from-cyan-500/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
<Icon className={`w-10 h-10 relative z-10 ${
quest.status === 'completed' ? 'text-green-400' :
quest.status === 'unlocked' ? 'text-yellow-400 animate-pulse' :
'text-red-400/50'
}`} />
</div>
<div className="flex-1">
<div className="flex items-start justify-between mb-3">
<div>
<h4 className="text-2xl font-bold text-cyan-400 font-mono mb-2">
{quest.name}
</h4>
<div className="flex items-center gap-4 mb-3">
<span className={`text-xs font-mono ${getDifficultyColor(quest.difficulty)} font-bold px-3 py-1 bg-cyan-500/10 rounded-full border border-cyan-500/30`}>
{quest.difficulty}
</span>
<span className="text-xs text-cyan-400 font-mono flex items-center gap-1">
<Clock className="w-3 h-3" />
{quest.estimatedTime}
</span>
<span className="text-xs text-green-400 font-mono font-bold flex items-center gap-1">
<Trophy className="w-3 h-3" />
+{quest.reward} PTS
</span>
</div>
</div>
<div className="flex items-center gap-2 px-4 py-2 rounded-lg bg-cyan-500/10 border border-cyan-500/30">
{quest.status === 'completed' && (
<>
<CheckCircle2 className="w-5 h-5 text-green-400" />
<span className="text-sm font-mono text-green-400 font-bold">Completed</span>
</>
)}
{quest.status === 'unlocked' && (
<>
<Zap className="w-5 h-5 text-yellow-400 animate-pulse" />
<span className="text-sm font-mono text-yellow-400 font-bold">Available</span>
</>
)}
{quest.status === 'locked' && (
<>
<Lock className="w-5 h-5 text-red-400/70" />
<span className="text-sm font-mono text-red-400/70 font-bold">Locked</span>
</>
)}
</div>
</div>
<p className="text-cyan-400/70 font-mono text-sm mb-4 leading-relaxed">
{quest.description}
</p>
{/* Prerequisites */}
{quest.prerequisites.length > 0 && (
<div className="mb-4 p-3 bg-cyan-500/5 border border-cyan-500/20 rounded-lg">
<div className="text-xs text-cyan-400/60 font-mono mb-2 flex items-center gap-2">
<Target className="w-3 h-3" />
Prerequisites:
</div>
<div className="flex flex-wrap gap-2">
{quest.prerequisites.map(prereqId => {
const prereq = quests.find(q => q.id === prereqId)
return prereq ? (
<span
key={prereqId}
className={`text-xs font-mono px-2 py-1 rounded ${
prereq.status === 'completed'
? 'bg-green-500/20 text-green-400 border border-green-500/40'
: 'bg-red-500/20 text-red-400 border border-red-500/40'
}`}
>
{prereq.name}
</span>
) : null
})}
</div>
</div>
)}
<div className="flex gap-3">
<Button
disabled={quest.status === 'locked' || quest.status === 'completed'}
className={`${
quest.status === 'unlocked'
? 'bg-gradient-to-r from-cyan-500 to-blue-500 hover:from-cyan-400 hover:to-blue-400 text-black shadow-[0_0_30px_rgba(6,182,212,0.4)] hover:shadow-[0_0_50px_rgba(6,182,212,0.6)]'
: 'bg-cyan-500/20 text-cyan-400/50 cursor-not-allowed'
} font-mono font-bold transition-all hover:scale-105 active:scale-95 flex items-center gap-2`}
>
{quest.status === 'completed' ? (
<>
<CheckCircle2 className="w-4 h-4" />
Completed
</>
) : quest.status === 'unlocked' ? (
<>
<Zap className="w-4 h-4" />
Start Mission
</>
) : (
<>
<Lock className="w-4 h-4" />
Locked
</>
)}
</Button>
{quest.status === 'completed' && (
<Button
variant="outline"
className="bg-cyan-500/10 border-cyan-500/40 text-cyan-400 hover:bg-cyan-500/20 font-mono"
>
Review
</Button>
)}
</div>
</div>
</div>
</>
)
})()}
</Card>
)}
</div>
</div>
</Card>
</div>
</main>
{/* Enhanced Background grid */}
<div
className="fixed inset-0 pointer-events-none -z-10 opacity-50"
style={{
backgroundImage: `
linear-gradient(rgba(6, 182, 212, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(6, 182, 212, 0.05) 1px, transparent 1px)
`,
backgroundSize: '50px 50px'
}}
/>
{/* Scanline effect */}
<div className="fixed inset-0 pointer-events-none z-50 opacity-5">
<div className="h-full w-full" style={{
backgroundImage: 'repeating-linear-gradient(0deg, rgba(6, 182, 212, 0.1) 0px, transparent 2px, transparent 4px)',
animation: 'scanline 8s linear infinite'
}} />
</div>
<style jsx>{`
@keyframes scanline {
0% { transform: translateY(0); }
100% { transform: translateY(100%); }
}
`}</style>
</div>
)
}