This commit is contained in:
5t4l1n
2025-07-30 07:43:06 +05:30
parent c7065d31fe
commit c751a49d8f
10 changed files with 1995 additions and 609 deletions
+52
View File
@@ -0,0 +1,52 @@
# Git
.git
.gitignore
README.md
# Node modules
node_modules
frontend/node_modules
frontend/.next
# Python
__pycache__
*.pyc
*.pyo
*.pyd
venv
.pytest_cache
# Environment files
.env*
!.env.example
# IDE
.vscode
.idea
*.swp
*.swo
# Logs
*.log
logs
backend/openlearnx.log
# OS
.DS_Store
Thumbs.db
# Docker
Dockerfile*
docker-compose*.yml
.dockerignore
# Temporary
tmp/
temp/
cache/
backend/cache/
# Build artifacts
dist/
build/
out/
+160
View File
@@ -0,0 +1,160 @@
# Multi-stage Dockerfile for OpenLearnX Platform
FROM node:18-alpine AS frontend-builder
# Build Frontend
WORKDIR /app/frontend
COPY frontend/package.json frontend/pnpm-lock.yaml* ./
RUN corepack enable pnpm && pnpm install --frozen-lockfile
COPY frontend/ ./
ENV NEXT_TELEMETRY_DISABLED=1
RUN pnpm run build
# Main Python image with everything
FROM python:3.10-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV TF_CPP_MIN_LOG_LEVEL=2
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Install system dependencies (Python + Node.js)
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
gcc \
g++ \
libpq-dev \
curl \
wget \
ca-certificates \
gnupg \
nginx \
supervisor \
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \
&& npm install -g pnpm \
&& rm -rf /var/lib/apt/lists/*
# Set work directory
WORKDIR /app
# Copy and install Python dependencies
COPY backend/requirements.txt ./backend/
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -r backend/requirements.txt
# Copy backend code
COPY backend/ ./backend/
# Copy built frontend from previous stage
COPY --from=frontend-builder /app/frontend/.next/standalone ./frontend/
COPY --from=frontend-builder /app/frontend/.next/static ./frontend/.next/static
COPY --from=frontend-builder /app/frontend/public ./frontend/public
# Create necessary directories
RUN mkdir -p /var/log/supervisor \
&& mkdir -p /app/logs \
&& mkdir -p /app/uploads
# Configure Nginx
COPY <<EOF /etc/nginx/sites-available/openlearnx
server {
listen 80;
server_name localhost;
# Frontend
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
# Backend API
location /api/ {
proxy_pass http://127.0.0.1:5000/;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
# Health check
location /health {
access_log off;
return 200 "healthy\\n";
add_header Content-Type text/plain;
}
}
EOF
RUN ln -s /etc/nginx/sites-available/openlearnx /etc/nginx/sites-enabled/ \
&& rm /etc/nginx/sites-enabled/default
# Configure Supervisor
COPY <<EOF /etc/supervisor/conf.d/openlearnx.conf
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
[program:nginx]
command=nginx -g "daemon off;"
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/nginx.err.log
stdout_logfile=/var/log/supervisor/nginx.out.log
[program:backend]
command=gunicorn --bind 127.0.0.1:5000 --workers 4 --timeout 120 main:app
directory=/app/backend
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/backend.err.log
stdout_logfile=/var/log/supervisor/backend.out.log
environment=PYTHONPATH="/app/backend"
[program:frontend]
command=node server.js
directory=/app/frontend
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/frontend.err.log
stdout_logfile=/var/log/supervisor/frontend.out.log
environment=PORT="3000",HOSTNAME="127.0.0.1"
EOF
# Create non-root user
RUN adduser --disabled-password --gecos '' appuser \
&& chown -R appuser:appuser /app \
&& chown -R appuser:appuser /var/log/supervisor
# Health check script
COPY <<EOF /app/healthcheck.sh
#!/bin/bash
curl -f http://127.0.0.1/health && \
curl -f http://127.0.0.1:3000/ && \
curl -f http://127.0.0.1:5000/api/health
EOF
RUN chmod +x /app/healthcheck.sh
# Switch to non-root user for app files
USER appuser
# Expose port
EXPOSE 80
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD /app/healthcheck.sh || exit 1
# Switch back to root for supervisor
USER root
# Start all services with supervisor
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/openlearnx.conf"]
+43
View File
@@ -0,0 +1,43 @@
FROM python:3.10-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV TF_CPP_MIN_LOG_LEVEL=2
# Set work directory
WORKDIR /app
# Install system dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
gcc \
g++ \
libpq-dev \
curl \
wget \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create non-root user
RUN adduser --disabled-password --gecos '' appuser \
&& chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/api/health || exit 1
# Run with Gunicorn for production
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "--timeout", "120", "main:app"]
+128
View File
@@ -0,0 +1,128 @@
#!/bin/bash
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
print_status() { echo -e "${BLUE}[INFO]${NC} $1"; }
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
echo "🚀 Deploying OpenLearnX Single Container Platform"
echo "================================================="
# Check prerequisites
print_status "Checking prerequisites..."
command -v docker >/dev/null 2>&1 || { print_error "Docker not found. Please install Docker."; exit 1; }
command -v docker-compose >/dev/null 2>&1 || { print_error "docker-compose not found. Please install docker-compose."; exit 1; }
# Check if Docker is running
if ! docker info >/dev/null 2>&1; then
print_error "Docker is not running. Please start Docker first."
exit 1
fi
print_success "Prerequisites check passed!"
# Create necessary directories
print_status "Creating necessary directories..."
mkdir -p backend/uploads backend/logs
# Stop existing containers
print_status "Stopping existing containers..."
docker-compose down -v 2>/dev/null || true
# Clean up old images
print_status "Cleaning up old images..."
docker system prune -f
# Build the single container
print_status "Building OpenLearnX single container..."
docker-compose build --no-cache openlearnx
# Start all services
print_status "Starting all services..."
docker-compose up -d
# Wait for services to be ready
print_status "Waiting for services to start..."
sleep 60
# Health checks
print_status "Performing health checks..."
check_service() {
local service=$1
local url=$2
local max_attempts=30
local attempt=1
while [ $attempt -le $max_attempts ]; do
if curl -f "$url" >/dev/null 2>&1; then
print_success "$service is healthy!"
return 0
fi
print_status "Waiting for $service... (attempt $attempt/$max_attempts)"
sleep 5
((attempt++))
done
print_warning "$service health check failed after $max_attempts attempts"
return 1
}
# Check all services
check_service "Main Application" "http://localhost/health"
check_service "Frontend" "http://localhost/"
check_service "Backend API" "http://localhost/api/health"
check_service "Database" "http://localhost:5432" || print_warning "Database connection check skipped"
# Display service status
print_status "Service Status:"
docker-compose ps
# Display logs for debugging if needed
print_status "Recent logs:"
docker-compose logs --tail=10 openlearnx
# Display URLs and information
echo ""
echo "🎉 OpenLearnX Platform Deployed Successfully!"
echo "============================================="
echo "🌐 Main Application: http://localhost"
echo "🔧 Backend API: http://localhost/api/"
echo "📱 Frontend: http://localhost/"
echo "🗄️ Database: postgresql://postgres:openlearnx123@localhost:5432/openlearnx"
echo "📊 Redis: redis://localhost:6379"
echo ""
echo "📋 Useful Commands:"
echo " - View logs: docker-compose logs -f openlearnx"
echo " - View all logs: docker-compose logs -f"
echo " - Stop services: docker-compose down"
echo " - Restart: docker-compose restart openlearnx"
echo " - Enter container: docker-compose exec openlearnx bash"
echo " - View processes: docker-compose exec openlearnx supervisorctl status"
echo ""
echo "🔍 Monitoring:"
echo " - Application logs: docker-compose exec openlearnx tail -f /var/log/supervisor/backend.out.log"
echo " - Frontend logs: docker-compose exec openlearnx tail -f /var/log/supervisor/frontend.out.log"
echo " - Nginx logs: docker-compose exec openlearnx tail -f /var/log/supervisor/nginx.out.log"
echo ""
# Optional: Open browser
if command -v xdg-open &> /dev/null; then
print_status "Opening application in browser..."
xdg-open http://localhost
elif command -v open &> /dev/null; then
print_status "Opening application in browser..."
open http://localhost
fi
print_success "Single container deployment completed! 🐳🚀"
print_status "Your OpenLearnX platform is running in a single Docker container with all services!"
+69
View File
@@ -0,0 +1,69 @@
version: '3.8'
services:
# Main OpenLearnX Application
openlearnx:
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80"
environment:
- DATABASE_URL=postgresql://postgres:openlearnx123@postgres:5432/openlearnx
- REDIS_URL=redis://redis:6379
- FLASK_ENV=production
- NODE_ENV=production
- TF_CPP_MIN_LOG_LEVEL=2
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
networks:
- openlearnx-network
volumes:
- app_uploads:/app/uploads
- app_logs:/app/logs
# PostgreSQL Database
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: openlearnx
POSTGRES_USER: postgres
POSTGRES_PASSWORD: openlearnx123
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- openlearnx-network
# Redis Cache
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
restart: unless-stopped
networks:
- openlearnx-network
networks:
openlearnx-network:
driver: bridge
volumes:
postgres_data:
redis_data:
app_uploads:
app_logs:
+694 -178
View File
@@ -1,7 +1,7 @@
'use client' 'use client'
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { Play, Lock, Shield, AlertTriangle, Users, Trophy, Clock } from 'lucide-react' import { Play, Lock, Shield, AlertTriangle, Users, Trophy, Clock, Code, Zap, Sparkles, Star, Brain, CheckCircle, XCircle } from 'lucide-react'
type UserRole = 'selector' | 'host' | 'participant' type UserRole = 'selector' | 'host' | 'participant'
type ExamStatus = 'waiting' | 'active' | 'completed' type ExamStatus = 'waiting' | 'active' | 'completed'
@@ -139,7 +139,6 @@ export default function CodingExamPlatform() {
exam_details: data.exam_details || {} exam_details: data.exam_details || {}
})) }))
// ✅ ENHANCED SUCCESS MESSAGE WITH REDIRECT INFO
alert(`✅ Exam Created Successfully! alert(`✅ Exam Created Successfully!
📝 Exam Code: ${participantCode} 📝 Exam Code: ${participantCode}
@@ -251,6 +250,7 @@ Redirecting to exam interface...`)
} }
const submitSolution = async () => { const submitSolution = async () => {
setIsExecuting(true)
try { try {
const response = await fetch('http://127.0.0.1:5000/api/exam/submit-solution', { const response = await fetch('http://127.0.0.1:5000/api/exam/submit-solution', {
method: 'POST', method: 'POST',
@@ -265,6 +265,8 @@ Redirecting to exam interface...`)
} }
} catch (error) { } catch (error) {
alert('Failed to submit solution') alert('Failed to submit solution')
} finally {
setIsExecuting(false)
} }
} }
@@ -284,108 +286,315 @@ Redirecting to exam interface...`)
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}` return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
} }
// Role Selection Screen // Role Selection Screen with Enhanced Animations
if (userRole === 'selector') { if (userRole === 'selector') {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-blue-900 to-purple-900 flex items-center justify-center"> <div className="min-h-screen bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 flex items-center justify-center relative overflow-hidden animate-fade-in">
<div className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full"> {/* Animated Background Elements */}
<h1 className="text-2xl font-bold text-center mb-8">OpenLearnX Coding Exam</h1> <div className="absolute inset-0 opacity-10">
<div className="absolute top-1/4 left-1/4 w-32 h-32 bg-white rounded-full animate-float"></div>
<div className="absolute top-3/4 right-1/4 w-24 h-24 bg-white rounded-full animate-float animate-delay-500"></div>
<div className="absolute top-1/2 right-1/3 w-16 h-16 bg-white rounded-full animate-float animate-delay-1000"></div>
<div className="space-y-4"> {/* Floating particles */}
<button <div className="absolute top-1/6 left-1/6 w-2 h-2 bg-white rounded-full animate-pulse opacity-60"></div>
onClick={() => setUserRole('host')} <div className="absolute top-2/3 left-3/4 w-1 h-1 bg-white rounded-full animate-pulse animate-delay-300 opacity-40"></div>
className="w-full bg-blue-600 hover:bg-blue-700 text-white py-3 px-4 rounded-lg flex items-center justify-center space-x-2" <div className="absolute top-1/3 right-1/2 w-1.5 h-1.5 bg-white rounded-full animate-pulse animate-delay-700 opacity-50"></div>
> </div>
<Users className="h-5 w-5" />
<span>Host an Exam</span> {/* Floating sparkles */}
</button> <div className="absolute top-1/4 right-1/3 animate-float animate-delay-200">
<Sparkles className="w-6 h-6 text-white opacity-60 animate-pulse" />
</div>
<div className="absolute bottom-1/4 left-1/4 animate-float animate-delay-800">
<Star className="w-4 h-4 text-white opacity-50 animate-spin-slow" />
</div>
<div className="bg-white/95 backdrop-blur-sm rounded-2xl shadow-2xl p-10 max-w-lg w-full transform animate-scale-in hover:scale-105 transition-all duration-500 relative overflow-hidden group">
{/* Card shine effect */}
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent transition-transform duration-1000"></div>
<div className="relative z-10">
<div className="text-center mb-10">
<div className="flex justify-center mb-4 animate-bounce">
<Code className="h-16 w-16 text-blue-600 animate-pulse" />
</div>
<h1 className="text-3xl font-bold text-gray-800 mb-3 animate-slide-down">
OpenLearnX Coding Exam
</h1>
<p className="text-gray-600 animate-fade-in animate-delay-300">
Choose your role to get started
</p>
</div>
<button <div className="space-y-6">
onClick={() => setUserRole('participant')} <button
className="w-full bg-green-600 hover:bg-green-700 text-white py-3 px-4 rounded-lg flex items-center justify-center space-x-2" onClick={() => setUserRole('host')}
> className="w-full bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 text-white py-4 px-6 rounded-xl flex items-center justify-center space-x-3 transform transition-all duration-300 hover:scale-105 hover:shadow-2xl active:scale-95 animate-slide-up group relative overflow-hidden"
<Play className="h-5 w-5" /> style={{ animationDelay: '0.1s' }}
<span>Join an Exam</span> >
</button> {/* Button background animation */}
<div className="absolute inset-0 bg-gradient-to-r from-blue-500 to-purple-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative z-10 flex items-center space-x-3">
<Users className="h-6 w-6 transition-transform duration-300 group-hover:rotate-12 group-hover:scale-110" />
<span className="text-lg font-semibold group-hover:tracking-wider transition-all duration-300">Host an Exam</span>
</div>
{/* Floating particles on hover */}
<div className="absolute -top-1 -right-1 opacity-0 group-hover:opacity-100 group-hover:animate-ping transition-all duration-300">
<div className="w-3 h-3 bg-white rounded-full"></div>
</div>
</button>
<button
onClick={() => setUserRole('participant')}
className="w-full bg-gradient-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800 text-white py-4 px-6 rounded-xl flex items-center justify-center space-x-3 transform transition-all duration-300 hover:scale-105 hover:shadow-2xl active:scale-95 animate-slide-up group relative overflow-hidden"
style={{ animationDelay: '0.2s' }}
>
{/* Button background animation */}
<div className="absolute inset-0 bg-gradient-to-r from-green-500 to-teal-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative z-10 flex items-center space-x-3">
<Play className="h-6 w-6 transition-transform duration-300 group-hover:translate-x-1 group-hover:scale-110" />
<span className="text-lg font-semibold group-hover:tracking-wider transition-all duration-300">Join an Exam</span>
</div>
{/* Floating particles on hover */}
<div className="absolute -top-1 -right-1 opacity-0 group-hover:opacity-100 group-hover:animate-ping transition-all duration-300">
<div className="w-3 h-3 bg-white rounded-full"></div>
</div>
</button>
</div>
{/* Animated footer */}
<div className="mt-8 text-center animate-fade-in animate-delay-500">
<p className="text-sm text-gray-500 hover:text-gray-700 transition-colors duration-300">
Secure Real-time Professional
</p>
</div>
</div> </div>
</div> </div>
</div> </div>
) )
} }
// Host Setup Screen // Host Setup Screen with Enhanced UI
if (userRole === 'host' && !examId) { if (userRole === 'host' && !examId) {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-blue-900 to-purple-900 flex items-center justify-center"> <div className="min-h-screen bg-gradient-to-br from-blue-900 via-indigo-900 to-purple-900 flex items-center justify-center relative overflow-hidden animate-fade-in">
<div className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full"> {/* Enhanced background animations */}
<h1 className="text-2xl font-bold text-center mb-8">Host Coding Exam</h1> <div className="absolute inset-0 opacity-5">
<div className="absolute top-0 left-0 w-96 h-96 bg-white rounded-full mix-blend-overlay animate-blob"></div>
<div className="absolute top-0 right-0 w-96 h-96 bg-white rounded-full mix-blend-overlay animate-blob animation-delay-2000"></div>
<div className="absolute -bottom-8 left-20 w-96 h-96 bg-white rounded-full mix-blend-overlay animate-blob animation-delay-4000"></div>
</div>
{/* Floating icons */}
<div className="absolute top-1/5 right-1/5 animate-float animate-delay-1000">
<Brain className="w-8 h-8 text-white opacity-30 animate-pulse" />
</div>
<div className="absolute bottom-1/5 left-1/5 animate-float animate-delay-2000">
<Zap className="w-6 h-6 text-white opacity-20 animate-bounce" />
</div>
<div className="bg-white/95 backdrop-blur-lg rounded-3xl shadow-2xl p-12 max-w-xl w-full transform animate-scale-in hover:scale-105 transition-all duration-500 relative overflow-hidden group">
{/* Enhanced shine effect */}
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-blue-200/30 to-transparent transition-transform duration-1000"></div>
<div className="space-y-4"> <div className="relative z-10">
<input <div className="text-center mb-8">
type="text" <div className="flex justify-center mb-6 relative">
placeholder="Enter your name" <div className="p-4 bg-blue-100 rounded-full animate-bounce">
value={participantName} <Users className="h-12 w-12 text-blue-600 animate-pulse" />
onChange={(e) => setParticipantName(e.target.value)} </div>
className="w-full p-3 border border-gray-300 rounded-lg" {/* Floating particles around icon */}
/> <div className="absolute -top-2 -right-2 w-3 h-3 bg-blue-400 rounded-full animate-ping"></div>
<div className="absolute -bottom-2 -left-2 w-2 h-2 bg-blue-300 rounded-full animate-ping animation-delay-500"></div>
</div>
<h1 className="text-4xl font-bold text-gray-800 mb-4 animate-slide-down">
Host Coding Exam
</h1>
<p className="text-gray-600 text-lg animate-fade-in animate-delay-300">
Create a secure coding environment for your participants
</p>
</div>
<button <div className="space-y-6">
onClick={createExam} <div className="relative animate-slide-up" style={{ animationDelay: '0.1s' }}>
disabled={!participantName} <input
className="w-full bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white py-3 px-4 rounded-lg" type="text"
> placeholder="Enter your name"
Create Exam value={participantName}
</button> onChange={(e) => setParticipantName(e.target.value)}
</div> className="w-full p-4 border-2 border-gray-200 rounded-xl text-lg transition-all duration-300 focus:ring-4 focus:ring-blue-200 focus:border-blue-500 hover:border-blue-300 bg-gray-50 hover:bg-white focus:bg-white group"
/>
{/* Debug Info */} {/* Input decoration */}
<div className="mt-4 p-3 bg-gray-50 rounded text-xs text-gray-600"> <div className="absolute right-4 top-1/2 transform -translate-y-1/2 opacity-0 group-focus-within:opacity-100 transition-opacity duration-300">
<p>Will create with host_name: "{participantName}"</p> <CheckCircle className="w-5 h-5 text-green-500 animate-pulse" />
<p> Will display exam_code (6 chars), not exam_id</p> </div>
<p>🔄 After creation redirect to /coding/host/[examCode]</p> </div>
<button
onClick={createExam}
disabled={!participantName}
className="w-full bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 disabled:from-gray-400 disabled:to-gray-500 text-white py-4 px-6 rounded-xl text-lg font-semibold transform transition-all duration-300 hover:scale-105 hover:shadow-2xl active:scale-95 disabled:hover:scale-100 animate-slide-up group relative overflow-hidden"
style={{ animationDelay: '0.2s' }}
>
{/* Button animation background */}
<div className="absolute inset-0 bg-gradient-to-r from-purple-600 to-pink-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative z-10 flex items-center justify-center space-x-3">
<span className="group-hover:tracking-wider transition-all duration-300">Create Exam</span>
<Sparkles className="w-5 h-5 group-hover:animate-spin transition-transform duration-300" />
</div>
{/* Ripple effect */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-20 group-hover:animate-ping transition-all duration-300 bg-white rounded-xl"></div>
</button>
</div>
{/* Enhanced Debug Info */}
<div className="mt-8 p-6 bg-gradient-to-r from-gray-50 to-blue-50 rounded-xl text-sm text-gray-600 animate-fade-in border border-gray-200 hover:border-blue-300 transition-colors duration-300" style={{ animationDelay: '0.3s' }}>
<div className="flex items-center space-x-2 mb-3">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
<span className="font-semibold">System Status</span>
</div>
<div className="space-y-2">
<p className="flex items-center space-x-2">
<CheckCircle className="w-4 h-4 text-green-500" />
<span>Will create with host_name: "{participantName}"</span>
</p>
<p className="flex items-center space-x-2">
<CheckCircle className="w-4 h-4 text-green-500" />
<span>Will display exam_code (6 chars), not exam_id</span>
</p>
<p className="flex items-center space-x-2">
<Zap className="w-4 h-4 text-blue-500 animate-pulse" />
<span>After creation redirect to /coding/host/[examCode]</span>
</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
) )
} }
// Join Exam Screen // Join Exam Screen with Enhanced Animations
if (userRole === 'participant' && !examInfo) { if (userRole === 'participant' && !examInfo) {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-green-900 to-blue-900 flex items-center justify-center"> <div className="min-h-screen bg-gradient-to-br from-green-900 via-emerald-900 to-blue-900 flex items-center justify-center relative overflow-hidden animate-fade-in">
<div className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full"> {/* Enhanced background effects */}
<h1 className="text-2xl font-bold text-center mb-8">Join Coding Exam</h1> <div className="absolute inset-0 opacity-10">
<div className="absolute top-1/4 left-1/4 w-40 h-40 bg-white rounded-full animate-float hover:scale-150 transition-transform duration-500"></div>
<div className="absolute top-3/4 right-1/4 w-32 h-32 bg-white rounded-full animate-float animate-delay-500 hover:scale-125 transition-transform duration-500"></div>
<div className="absolute top-1/2 right-1/3 w-24 h-24 bg-white rounded-full animate-float animate-delay-1000 hover:scale-200 transition-transform duration-500"></div>
</div>
{/* Animated particles */}
<div className="absolute inset-0 pointer-events-none">
<div className="absolute top-1/6 left-1/6 w-3 h-3 bg-white rounded-full animate-pulse opacity-60 shadow-lg"></div>
<div className="absolute top-2/3 left-3/4 w-2 h-2 bg-white rounded-full animate-pulse animate-delay-300 opacity-40"></div>
<div className="absolute top-1/2 left-1/5 w-2.5 h-2.5 bg-white rounded-full animate-pulse animate-delay-700 opacity-50"></div>
</div>
<div className="bg-white/95 backdrop-blur-lg rounded-3xl shadow-2xl p-12 max-w-xl w-full transform animate-scale-in hover:scale-105 transition-all duration-500 relative overflow-hidden group">
{/* Enhanced card effects */}
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-green-200/30 to-transparent transition-transform duration-1000"></div>
<div className="space-y-4"> <div className="relative z-10">
<input <div className="text-center mb-10">
type="text" <div className="flex justify-center mb-6 relative">
placeholder="Enter exam code (e.g., 3BPIBZ)" <div className="p-4 bg-green-100 rounded-full animate-bounce">
value={examId} <Play className="h-12 w-12 text-green-600 animate-pulse" />
onChange={(e) => setExamId(e.target.value.toUpperCase())} </div>
className="w-full p-3 border border-gray-300 rounded-lg text-center font-mono text-lg tracking-widest uppercase" {/* Animated ring around icon */}
maxLength={6} <div className="absolute inset-0 border-4 border-green-300 rounded-full animate-ping opacity-30"></div>
/> <div className="absolute inset-2 border-2 border-green-400 rounded-full animate-ping opacity-40 animation-delay-500"></div>
</div>
<h1 className="text-4xl font-bold text-gray-800 mb-4 animate-slide-down">
Join Coding Exam
</h1>
<p className="text-gray-600 text-lg animate-fade-in animate-delay-300">
Enter the exam code to participate in the coding challenge
</p>
</div>
<input <div className="space-y-6">
type="text" <div className="relative animate-slide-up" style={{ animationDelay: '0.1s' }}>
placeholder="Enter your name" <input
value={participantName} type="text"
onChange={(e) => setParticipantName(e.target.value)} placeholder="Enter exam code (e.g., 3BPIBZ)"
className="w-full p-3 border border-gray-300 rounded-lg" value={examId}
/> onChange={(e) => setExamId(e.target.value.toUpperCase())}
className="w-full p-4 border-2 border-gray-200 rounded-xl text-center font-mono text-2xl tracking-widest uppercase transition-all duration-300 focus:ring-4 focus:ring-green-200 focus:border-green-500 hover:border-green-300 bg-gray-50 hover:bg-white focus:bg-white relative group"
<button maxLength={6}
onClick={joinExam} />
disabled={!examId || !participantName} {/* Input decorations */}
className="w-full bg-green-600 hover:bg-green-700 disabled:bg-gray-400 text-white py-3 px-4 rounded-lg" <div className="absolute inset-x-0 bottom-0 h-1 bg-gradient-to-r from-green-400 to-blue-400 transform scale-x-0 group-focus-within:scale-x-100 transition-transform duration-300 rounded-full"></div>
> {examId.length === 6 && (
Join Exam <div className="absolute right-4 top-1/2 transform -translate-y-1/2 animate-bounce">
</button> <CheckCircle className="w-6 h-6 text-green-500 animate-pulse" />
</div>
{/* Debug Info */} )}
<div className="text-xs text-gray-500 p-3 bg-gray-50 rounded"> </div>
<p>Will send: exam_code="{examId}" student_name="{participantName}"</p>
<p> After join redirect to /coding/exam</p> <div className="relative animate-slide-up" style={{ animationDelay: '0.2s' }}>
<input
type="text"
placeholder="Enter your name"
value={participantName}
onChange={(e) => setParticipantName(e.target.value)}
className="w-full p-4 border-2 border-gray-200 rounded-xl text-lg transition-all duration-300 focus:ring-4 focus:ring-green-200 focus:border-green-500 hover:border-green-300 bg-gray-50 hover:bg-white focus:bg-white group"
/>
{/* Name validation indicator */}
{participantName.length > 2 && (
<div className="absolute right-4 top-1/2 transform -translate-y-1/2 animate-bounce">
<CheckCircle className="w-5 h-5 text-green-500 animate-pulse" />
</div>
)}
</div>
<button
onClick={joinExam}
disabled={!examId || !participantName}
className="w-full bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-700 hover:to-emerald-700 disabled:from-gray-400 disabled:to-gray-500 text-white py-4 px-6 rounded-xl text-lg font-semibold transform transition-all duration-300 hover:scale-105 hover:shadow-2xl active:scale-95 disabled:hover:scale-100 animate-slide-up group relative overflow-hidden"
style={{ animationDelay: '0.3s' }}
>
{/* Enhanced button animations */}
<div className="absolute inset-0 bg-gradient-to-r from-blue-600 to-purple-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative z-10 flex items-center justify-center space-x-3">
<span className="group-hover:tracking-wider transition-all duration-300">Join Exam</span>
<div className="flex space-x-1">
<div className="w-1 h-1 bg-white rounded-full animate-bounce"></div>
<div className="w-1 h-1 bg-white rounded-full animate-bounce animation-delay-200"></div>
<div className="w-1 h-1 bg-white rounded-full animate-bounce animation-delay-400"></div>
</div>
</div>
{/* Button ripple effect */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-20 group-hover:animate-ping transition-all duration-300 bg-white rounded-xl"></div>
</button>
{/* Enhanced Debug Info */}
<div className="text-sm text-gray-500 p-6 bg-gradient-to-r from-gray-50 to-green-50 rounded-xl animate-fade-in border border-gray-200 hover:border-green-300 transition-colors duration-300" style={{ animationDelay: '0.4s' }}>
<div className="flex items-center space-x-2 mb-3">
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></div>
<span className="font-semibold">Connection Status</span>
</div>
<div className="space-y-2">
<p className="flex items-center space-x-2">
<CheckCircle className="w-4 h-4 text-green-500" />
<span>Will send: exam_code="{examId}" student_name="{participantName}"</span>
</p>
<p className="flex items-center space-x-2">
<Zap className="w-4 h-4 text-blue-500 animate-pulse" />
<span>After join redirect to /coding/exam</span>
</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -393,139 +602,446 @@ Redirecting to exam interface...`)
) )
} }
// System Requirements Check // Enhanced System Requirements Check
if (!systemChecked) { if (!systemChecked) {
return ( return (
<div className="min-h-screen bg-gray-900 text-white flex items-center justify-center"> <div className="min-h-screen bg-gradient-to-br from-gray-900 via-red-900 to-black text-white flex items-center justify-center relative overflow-hidden animate-fade-in">
<div className="bg-gray-800 rounded-lg p-8 max-w-lg w-full"> {/* Animated warning elements */}
<h1 className="text-2xl font-bold mb-6">System Requirements Check</h1> <div className="absolute inset-0 opacity-5">
<div className="absolute top-1/4 left-1/4 w-32 h-32 bg-red-500 rounded-full animate-pulse"></div>
<div className="absolute top-3/4 right-1/4 w-24 h-24 bg-yellow-500 rounded-full animate-pulse animate-delay-500"></div>
<div className="absolute top-1/2 right-1/3 w-16 h-16 bg-orange-500 rounded-full animate-pulse animate-delay-1000"></div>
</div>
{/* Floating warning icons */}
<div className="absolute top-1/5 right-1/5 animate-float animate-delay-1000">
<AlertTriangle className="w-8 h-8 text-red-400 opacity-60 animate-pulse" />
</div>
<div className="absolute bottom-1/5 left-1/5 animate-float animate-delay-2000">
<Shield className="w-6 h-6 text-yellow-400 opacity-40 animate-bounce" />
</div>
<div className="bg-gray-800/95 backdrop-blur-lg rounded-3xl p-12 max-w-2xl w-full transform animate-scale-in hover:scale-105 transition-all duration-500 border border-red-500/30 relative overflow-hidden group">
{/* Security-themed background */}
<div className="absolute inset-0 bg-gradient-to-r from-red-900/20 to-yellow-900/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
<div className="space-y-4 mb-6"> <div className="relative z-10">
<div className="flex items-center space-x-2"> <div className="text-center mb-8">
<Shield className="h-5 w-5 text-green-400" /> <div className="flex justify-center mb-6 relative">
<span>Fullscreen mode support</span> <div className="p-4 bg-red-900/50 rounded-full animate-pulse">
<Shield className="h-16 w-16 text-red-400 animate-bounce" />
</div>
{/* Security rings */}
<div className="absolute inset-0 border-2 border-red-500 rounded-full animate-ping opacity-30"></div>
<div className="absolute inset-2 border border-yellow-500 rounded-full animate-ping opacity-40 animation-delay-500"></div>
</div>
<h1 className="text-4xl font-bold mb-6 animate-slide-down">
System Requirements Check
</h1>
<p className="text-xl text-gray-300 animate-fade-in animate-delay-300">
Preparing secure exam environment
</p>
</div> </div>
<div className="flex items-center space-x-2">
<Lock className="h-5 w-5 text-yellow-400" /> <div className="space-y-6 mb-10">
<span>Copy/paste will be disabled</span> <div className="flex items-center space-x-4 p-4 bg-gray-700/50 rounded-xl animate-slide-up hover:bg-gray-600/50 transition-colors duration-300" style={{ animationDelay: '0.1s' }}>
<Shield className="h-8 w-8 text-green-400 animate-pulse" />
<div className="flex-1">
<span className="text-lg font-medium">Fullscreen mode support</span>
<p className="text-sm text-gray-400">Required for secure examination</p>
</div>
<CheckCircle className="h-6 w-6 text-green-400 animate-bounce" />
</div>
<div className="flex items-center space-x-4 p-4 bg-gray-700/50 rounded-xl animate-slide-up hover:bg-gray-600/50 transition-colors duration-300" style={{ animationDelay: '0.2s' }}>
<Lock className="h-8 w-8 text-yellow-400 animate-bounce" />
<div className="flex-1">
<span className="text-lg font-medium">Copy/paste will be disabled</span>
<p className="text-sm text-gray-400">Prevents unauthorized assistance</p>
</div>
<XCircle className="h-6 w-6 text-yellow-400 animate-pulse" />
</div>
<div className="flex items-center space-x-4 p-4 bg-gray-700/50 rounded-xl animate-slide-up hover:bg-gray-600/50 transition-colors duration-300" style={{ animationDelay: '0.3s' }}>
<AlertTriangle className="h-8 w-8 text-red-400 animate-pulse" />
<div className="flex-1">
<span className="text-lg font-medium">Virtual environments will be detected</span>
<p className="text-sm text-gray-400">Ensures exam integrity</p>
</div>
<Shield className="h-6 w-6 text-red-400 animate-bounce" />
</div>
</div> </div>
<div className="flex items-center space-x-2">
<AlertTriangle className="h-5 w-5 text-red-400" /> <button
<span>Virtual environments will be detected</span> onClick={acceptSystemRequirements}
className="w-full bg-gradient-to-r from-red-600 to-red-700 hover:from-red-700 hover:to-red-800 text-white py-4 px-6 rounded-xl text-lg font-semibold transform transition-all duration-300 hover:scale-105 hover:shadow-2xl active:scale-95 animate-slide-up group relative overflow-hidden"
style={{ animationDelay: '0.4s' }}
>
{/* Button warning effect */}
<div className="absolute inset-0 bg-gradient-to-r from-orange-600 to-yellow-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative z-10 flex items-center justify-center space-x-3">
<Lock className="h-6 w-6 group-hover:animate-bounce transition-transform duration-300" />
<span className="group-hover:tracking-wider transition-all duration-300">Accept & Enter Secure Mode</span>
<Shield className="h-6 w-6 group-hover:animate-pulse transition-transform duration-300" />
</div>
{/* Warning pulse effect */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-20 group-hover:animate-ping transition-all duration-300 bg-white rounded-xl"></div>
</button>
{/* Security notice */}
<div className="mt-6 p-4 bg-yellow-900/30 border border-yellow-500/50 rounded-xl animate-fade-in animate-delay-500">
<div className="flex items-center space-x-2 mb-2">
<AlertTriangle className="w-5 h-5 text-yellow-400 animate-pulse" />
<span className="font-semibold text-yellow-300">Security Notice</span>
</div>
<p className="text-sm text-yellow-200">
This exam uses advanced security measures. Browser restrictions will be enforced during the examination period.
</p>
</div> </div>
</div> </div>
<button
onClick={acceptSystemRequirements}
className="w-full bg-red-600 hover:bg-red-700 text-white py-3 px-4 rounded-lg"
>
Accept & Enter Secure Mode
</button>
</div> </div>
</div> </div>
) )
} }
// Main Exam Interface // Enhanced Main Exam Interface
return ( return (
<div className="min-h-screen bg-gray-900 text-white"> <div className="min-h-screen bg-gradient-to-br from-gray-900 via-slate-900 to-black text-white animate-fade-in relative overflow-hidden">
{/* Security Status Bar */} {/* Animated background elements */}
<div className="bg-red-900 text-white p-2 flex items-center justify-between"> <div className="absolute inset-0 opacity-5">
<div className="flex items-center space-x-4"> <div className="absolute top-0 left-0 w-96 h-96 bg-blue-500 rounded-full mix-blend-overlay animate-blob"></div>
<Shield className="h-4 w-4" /> <div className="absolute top-0 right-0 w-96 h-96 bg-purple-500 rounded-full mix-blend-overlay animate-blob animation-delay-2000"></div>
<span className="text-sm font-medium">SECURE MODE ACTIVE</span> <div className="absolute -bottom-8 left-20 w-96 h-96 bg-green-500 rounded-full mix-blend-overlay animate-blob animation-delay-4000"></div>
<Lock className="h-4 w-4" /> </div>
<span className="text-sm">Copy/Paste Disabled</span>
{/* Enhanced Security Status Bar */}
<div className="bg-gradient-to-r from-red-900 via-red-800 to-red-900 text-white p-4 flex items-center justify-between animate-slide-down shadow-2xl relative overflow-hidden">
{/* Security bar background animation */}
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-red-600/20 to-transparent animate-pulse"></div>
<div className="flex items-center space-x-6 relative z-10">
<div className="flex items-center space-x-2 px-3 py-1 bg-red-800/50 rounded-full">
<Shield className="h-5 w-5 animate-pulse" />
<span className="text-sm font-bold tracking-wider">SECURE MODE ACTIVE</span>
</div>
<div className="flex items-center space-x-2 px-3 py-1 bg-red-700/50 rounded-full">
<Lock className="h-5 w-5 animate-bounce" />
<span className="text-sm font-medium">Copy/Paste Disabled</span>
</div>
<div className="flex items-center space-x-2 px-3 py-1 bg-red-600/50 rounded-full">
<AlertTriangle className="h-5 w-5 animate-pulse" />
<span className="text-sm font-medium">VM Detection Active</span>
</div>
</div> </div>
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-6 relative z-10">
{timeRemaining > 0 && ( {timeRemaining > 0 && (
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-3 px-4 py-2 bg-gradient-to-r from-red-700 to-red-600 rounded-full shadow-lg">
<Clock className="h-4 w-4" /> <Clock className="h-5 w-5 animate-pulse" />
<span className="font-mono">{formatTime(timeRemaining)}</span> <span className="font-mono text-lg font-bold tracking-wider">{formatTime(timeRemaining)}</span>
{timeRemaining < 300 && ( // Last 5 minutes warning
<div className="w-2 h-2 bg-yellow-400 rounded-full animate-ping"></div>
)}
</div> </div>
)} )}
<span>Exam: {examId}</span>
<div className="px-4 py-2 bg-gradient-to-r from-gray-700 to-gray-600 rounded-full shadow-lg">
<span className="font-mono tracking-widest text-lg font-bold">Exam: {examId}</span>
</div>
</div> </div>
</div> </div>
<div className="flex h-screen"> <div className="flex h-screen">
{/* Main Coding Area */} {/* Enhanced Main Coding Area */}
<div className="flex-1 p-6"> <div className="flex-1 p-8 animate-slide-right relative">
{/* Problem Description */} {/* Problem Description with Enhanced Styling */}
<div className="bg-gray-800 rounded-lg p-6 mb-6"> <div className="bg-gradient-to-br from-gray-800 via-gray-800 to-gray-700 rounded-2xl p-8 mb-8 transform transition-all duration-300 hover:shadow-2xl hover:shadow-blue-500/20 hover:scale-105 relative overflow-hidden group">
<h2 className="text-xl font-bold mb-4">Problem: String Capitalizer</h2> {/* Card background animation */}
<p className="mb-4">Write a function that converts a string to uppercase.</p> <div className="absolute inset-0 bg-gradient-to-r from-blue-900/20 to-purple-900/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
<div className="bg-gray-900 p-4 rounded">
<code> <div className="relative z-10">
<div className="flex items-center space-x-4 mb-6">
<div className="p-3 bg-blue-600/20 rounded-xl animate-pulse">
<Code className="h-8 w-8 text-blue-400" />
</div>
<h2 className="text-3xl font-bold animate-slide-up">Problem: String Capitalizer</h2>
<div className="flex-1 h-1 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full animate-pulse"></div>
</div>
<p className="mb-6 text-lg text-gray-300 animate-slide-up" style={{ animationDelay: '0.1s' }}>
Write a function that converts a string to uppercase.
</p>
<div className="bg-black/50 p-6 rounded-xl transform transition-all duration-300 hover:bg-black/60 animate-slide-up border border-gray-600 hover:border-blue-500/50" style={{ animationDelay: '0.2s' }}>
<pre className="text-green-400 font-mono text-lg">
{`def capitalize_string(text): {`def capitalize_string(text):
# Your code here # Your code here
pass pass
# Test: capitalize_string("hello") should return "HELLO"`} # Test: capitalize_string("hello") should return "HELLO"`}
</code> </pre>
</div>
</div> </div>
</div>
{/* Code Editor */}
<div className="bg-gray-800 rounded-lg p-4">
<h3 className="text-lg font-bold mb-4">Code Editor</h3>
<textarea
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="def capitalize_string(text):\n # Your code here\n pass"
className="w-full h-64 bg-gray-900 text-green-400 font-mono p-4 rounded border border-gray-600 resize-none"
style={{ userSelect: 'none', WebkitUserSelect: 'none' }}
/>
<div className="flex space-x-4 mt-4"> {/* Hover shine effect */}
<button <div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-white/5 to-transparent transition-transform duration-1000"></div>
onClick={submitSolution}
className="bg-green-600 hover:bg-green-700 px-6 py-2 rounded flex items-center space-x-2"
>
<Play className="h-4 w-4" />
<span>Submit Solution</span>
</button>
{userRole === 'host' && examInfo?.status === 'waiting' && (
<button
onClick={startExam}
className="bg-blue-600 hover:bg-blue-700 px-6 py-2 rounded"
>
Start Exam
</button>
)}
</div>
</div> </div>
</div>
{/* Leaderboard Sidebar */} {/* Enhanced Code Editor */}
<div className="w-80 bg-gray-800 p-6"> <div className="bg-gradient-to-br from-gray-800 via-gray-800 to-gray-700 rounded-2xl p-8 transform transition-all duration-300 hover:shadow-2xl hover:shadow-purple-500/20 relative overflow-hidden group">
<div className="flex items-center space-x-2 mb-4"> {/* Editor background animation */}
<Trophy className="h-6 w-6 text-yellow-400" /> <div className="absolute inset-0 bg-gradient-to-r from-purple-900/20 to-green-900/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
<h3 className="text-xl font-bold">Leaderboard</h3>
</div> <div className="relative z-10">
<div className="flex items-center justify-between mb-6">
<div className="space-y-2"> <div className="flex items-center space-x-4">
{leaderboard.map((participant, index) => ( <div className="p-3 bg-purple-600/20 rounded-xl animate-pulse">
<div key={index} className={`p-3 rounded ${index === 0 ? 'bg-yellow-900' : index === 1 ? 'bg-gray-700' : index === 2 ? 'bg-orange-900' : 'bg-gray-700'}`}> <Zap className="h-8 w-8 text-purple-400" />
<div className="flex justify-between items-center"> </div>
<span className="font-medium"> <h3 className="text-2xl font-bold animate-slide-up">Code Editor</h3>
{index + 1}. {participant.name} </div>
</span>
<span className="font-bold">{participant.score}%</span> {/* Editor status indicators */}
<div className="flex items-center space-x-3">
<div className="flex items-center space-x-2 px-3 py-1 bg-green-900/30 rounded-full">
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
<span className="text-sm text-green-300">Ready</span>
</div>
<div className="text-sm text-gray-400 font-mono">
Lines: {code.split('\n').length} | Chars: {code.length}
</div>
</div> </div>
</div> </div>
))}
<div className="relative">
<textarea
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="def capitalize_string(text):\n # Your code here\n pass"
className="w-full h-80 bg-black/70 text-green-400 font-mono p-6 rounded-xl border-2 border-gray-600 resize-none transition-all duration-300 focus:border-green-500 focus:ring-4 focus:ring-green-500/20 hover:border-gray-500 animate-slide-up backdrop-blur-sm"
style={{
userSelect: 'none',
WebkitUserSelect: 'none',
animationDelay: '0.1s',
lineHeight: '1.6',
fontSize: '16px'
}}
/>
{/* Editor enhancements */}
<div className="absolute top-4 right-4 flex space-x-2">
{code.length > 0 && (
<div className="px-2 py-1 bg-blue-900/50 rounded text-xs text-blue-300 animate-fade-in">
Modified
</div>
)}
</div>
{/* Line numbers overlay */}
<div className="absolute left-2 top-6 text-gray-500 font-mono text-sm select-none pointer-events-none">
{Array.from({ length: code.split('\n').length }, (_, i) => (
<div key={i} className="h-6 leading-6">
{i + 1}
</div>
))}
</div>
</div>
<div className="flex items-center justify-between mt-6">
<div className="flex space-x-4">
<button
onClick={submitSolution}
disabled={isExecuting}
className="bg-gradient-to-r from-green-600 to-emerald-600 hover:from-green-700 hover:to-emerald-700 disabled:from-gray-600 disabled:to-gray-700 px-8 py-3 rounded-xl flex items-center space-x-3 transform transition-all duration-300 hover:scale-105 hover:shadow-2xl active:scale-95 animate-slide-up group relative overflow-hidden"
style={{ animationDelay: '0.2s' }}
>
{/* Button background animation */}
<div className="absolute inset-0 bg-gradient-to-r from-blue-600 to-purple-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative z-10 flex items-center space-x-3">
{isExecuting ? (
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
) : (
<Play className="h-5 w-5 transition-transform duration-300 group-hover:translate-x-1" />
)}
<span className="text-lg font-semibold">
{isExecuting ? 'Submitting...' : 'Submit Solution'}
</span>
</div>
{/* Sparkle effect */}
<div className="absolute -top-1 -right-1 opacity-0 group-hover:opacity-100 group-hover:animate-ping transition-all duration-300">
<Sparkles className="w-4 h-4 text-white" />
</div>
</button>
{userRole === 'host' && examInfo?.status === 'waiting' && (
<button
onClick={startExam}
className="bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 px-8 py-3 rounded-xl text-lg font-semibold transform transition-all duration-300 hover:scale-105 hover:shadow-2xl active:scale-95 animate-slide-up group relative overflow-hidden"
style={{ animationDelay: '0.3s' }}
>
{/* Button animation */}
<div className="absolute inset-0 bg-gradient-to-r from-purple-600 to-pink-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative z-10 flex items-center space-x-2">
<span>Start Exam</span>
<Zap className="h-5 w-5 group-hover:animate-bounce" />
</div>
</button>
)}
</div>
{/* Code statistics */}
<div className="flex items-center space-x-4 text-sm text-gray-400">
<div className="flex items-center space-x-2">
<div className="w-2 h-2 bg-blue-400 rounded-full animate-pulse"></div>
<span>Python 3.9</span>
</div>
<div className="flex items-center space-x-2">
<CheckCircle className="w-4 h-4 text-green-400" />
<span>Syntax OK</span>
</div>
</div>
</div>
</div>
{/* Editor shine effect */}
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-white/5 to-transparent transition-transform duration-1000"></div>
</div> </div>
<button
onClick={fetchLeaderboard}
className="w-full mt-4 bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded text-sm"
>
Refresh Leaderboard
</button>
</div> </div>
{/* Enhanced Leaderboard Sidebar */}
<div className="w-96 bg-gradient-to-b from-gray-800 via-gray-800 to-gray-700 p-8 animate-slide-left relative overflow-hidden">
{/* Sidebar background animation */}
<div className="absolute inset-0 bg-gradient-to-b from-yellow-900/10 to-orange-900/10 opacity-50"></div>
<div className="relative z-10">
<div className="flex items-center space-x-4 mb-8 animate-slide-down">
<div className="p-3 bg-yellow-600/20 rounded-xl animate-bounce">
<Trophy className="h-8 w-8 text-yellow-400 animate-pulse" />
</div>
<h3 className="text-2xl font-bold">Leaderboard</h3>
<div className="flex-1 h-1 bg-gradient-to-r from-yellow-500 to-orange-500 rounded-full animate-pulse"></div>
</div>
{/* Leaderboard stats */}
<div className="mb-6 p-4 bg-black/30 rounded-xl border border-gray-600">
<div className="flex justify-between items-center mb-2">
<span className="text-sm text-gray-400">Total Participants</span>
<span className="font-bold text-blue-400">{leaderboard.length}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-sm text-gray-400">Completed</span>
<span className="font-bold text-green-400">
{leaderboard.filter(p => p.completed).length}
</span>
</div>
</div>
<div className="space-y-3 max-h-96 overflow-y-auto custom-scrollbar">
{leaderboard.length > 0 ? leaderboard.map((participant, index) => (
<div
key={index}
className={`p-4 rounded-xl transform transition-all duration-300 hover:scale-105 hover:shadow-lg animate-slide-up relative overflow-hidden group cursor-pointer ${
index === 0 ? 'bg-gradient-to-r from-yellow-900/50 to-orange-900/50 border border-yellow-500/30' :
index === 1 ? 'bg-gradient-to-r from-gray-700/50 to-gray-600/50 border border-gray-400/30' :
index === 2 ? 'bg-gradient-to-r from-orange-900/50 to-red-900/50 border border-orange-500/30' :
'bg-gradient-to-r from-gray-700/50 to-gray-600/50 border border-gray-500/30'
}`}
style={{ animationDelay: `${0.1 * index}s` }}
>
{/* Rank indicator */}
<div className="absolute top-2 left-2">
{index < 3 ? (
<div className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold ${
index === 0 ? 'bg-yellow-500 text-black' :
index === 1 ? 'bg-gray-400 text-black' :
'bg-orange-500 text-white'
}`}>
{index + 1}
</div>
) : (
<div className="w-6 h-6 rounded-full bg-gray-600 flex items-center justify-center text-xs font-bold text-white">
{index + 1}
</div>
)}
</div>
{/* Participant info */}
<div className="ml-8">
<div className="flex justify-between items-start mb-2">
<span className="font-semibold text-lg group-hover:text-white transition-colors duration-300">
{participant.name}
</span>
<div className="flex items-center space-x-2">
<span className="font-bold text-xl animate-pulse">
{participant.score}%
</span>
{participant.completed && (
<CheckCircle className="w-5 h-5 text-green-400 animate-bounce" />
)}
</div>
</div>
{/* Progress bar */}
<div className="w-full bg-gray-600 rounded-full h-2 mb-2">
<div
className={`h-2 rounded-full transition-all duration-1000 ${
index === 0 ? 'bg-gradient-to-r from-yellow-400 to-orange-400' :
index === 1 ? 'bg-gradient-to-r from-gray-300 to-gray-400' :
index === 2 ? 'bg-gradient-to-r from-orange-400 to-red-400' :
'bg-gradient-to-r from-blue-400 to-purple-400'
}`}
style={{ width: `${participant.score}%` }}
></div>
</div>
{/* Submission time */}
{participant.submitted_at && (
<div className="text-xs text-gray-400">
Submitted: {new Date(participant.submitted_at).toLocaleTimeString()}
</div>
)}
</div>
{/* Hover effect */}
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-white/5 to-transparent transition-transform duration-700"></div>
</div>
)) : (
<div className="text-center py-8 text-gray-400 animate-pulse">
<Users className="h-12 w-12 mx-auto mb-3 opacity-50" />
<p>No participants yet</p>
</div>
)}
</div>
<button
onClick={fetchLeaderboard}
className="w-full mt-6 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 px-6 py-3 rounded-xl text-lg font-semibold transform transition-all duration-300 hover:scale-105 hover:shadow-2xl active:scale-95 animate-slide-up group relative overflow-hidden"
style={{ animationDelay: '0.4s' }}
>
{/* Button background animation */}
<div className="absolute inset-0 bg-gradient-to-r from-purple-600 to-pink-600 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div className="relative z-10 flex items-center justify-center space-x-2">
<span className="group-hover:tracking-wider transition-all duration-300">Refresh Leaderboard</span>
<div className="w-2 h-2 bg-white rounded-full animate-bounce group-hover:animate-ping"></div>
</div>
</button>
</div>
</div>
</div>
{/* Floating action button for help */}
<div className="fixed bottom-8 right-8 animate-bounce">
<button className="bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 p-4 rounded-full shadow-2xl transform transition-all duration-300 hover:scale-110 group">
<AlertTriangle className="h-6 w-6 text-white group-hover:animate-spin" />
</button>
</div> </div>
</div> </div>
) )
+452 -365
View File
@@ -2,7 +2,7 @@
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { useRouter, useParams } from "next/navigation" import { useRouter, useParams } from "next/navigation"
import { Loader2, Play, Clock, BookOpen, ChevronDown, ChevronRight, User, Users, Star } from "lucide-react" import { Loader2, Play, Clock, BookOpen, ChevronDown, ChevronRight, User, Users, Star, Award, TrendingUp, CheckCircle, ArrowRight } from "lucide-react"
import { toast } from "react-hot-toast" import { toast } from "react-hot-toast"
import api from "@/lib/api" import api from "@/lib/api"
import { useAuth } from "@/context/auth-context" import { useAuth } from "@/context/auth-context"
@@ -62,7 +62,7 @@ export default function CoursePage() {
const [expandedModules, setExpandedModules] = useState<{ [moduleId: string]: boolean }>({}) const [expandedModules, setExpandedModules] = useState<{ [moduleId: string]: boolean }>({})
const [completed, setCompleted] = useState(false) const [completed, setCompleted] = useState(false)
// Certificate Modal State // Certificate Modal State
const [showCertificateModal, setShowCertificateModal] = useState(false) const [showCertificateModal, setShowCertificateModal] = useState(false)
useEffect(() => { useEffect(() => {
@@ -83,13 +83,11 @@ export default function CoursePage() {
try { try {
console.log('🔍 Starting to fetch course data for:', courseId) console.log('🔍 Starting to fetch course data for:', courseId)
// Fetch course details
const courseResponse = await api.get<Course>(`/api/courses/${courseId}?t=${Date.now()}`) const courseResponse = await api.get<Course>(`/api/courses/${courseId}?t=${Date.now()}`)
const courseData = courseResponse.data const courseData = courseResponse.data
console.log('✅ Course data loaded:', courseData) console.log('✅ Course data loaded:', courseData)
setCourse(courseData) setCourse(courseData)
// ✅ FIXED: Better module fetching with multiple endpoint attempts
await fetchModulesAndLessons(courseId) await fetchModulesAndLessons(courseId)
} catch (err: any) { } catch (err: any) {
@@ -107,11 +105,9 @@ export default function CoursePage() {
try { try {
console.log('🔍 Fetching modules for course:', courseId) console.log('🔍 Fetching modules for course:', courseId)
// Try multiple endpoints for modules
let modulesData = null let modulesData = null
let modulesResponse = null let modulesResponse = null
// Try admin endpoint first (most likely to work based on previous conversation)
try { try {
modulesResponse = await fetch(`http://127.0.0.1:5000/api/admin/courses/${courseId}/modules`, { modulesResponse = await fetch(`http://127.0.0.1:5000/api/admin/courses/${courseId}/modules`, {
headers: { headers: {
@@ -128,7 +124,6 @@ export default function CoursePage() {
console.log('⚠️ Admin endpoint failed, trying public endpoint') console.log('⚠️ Admin endpoint failed, trying public endpoint')
} }
// If admin endpoint failed, try public endpoint
if (!modulesData || !modulesResponse?.ok) { if (!modulesData || !modulesResponse?.ok) {
try { try {
modulesResponse = await fetch(`http://127.0.0.1:5000/api/courses/${courseId}/modules`, { modulesResponse = await fetch(`http://127.0.0.1:5000/api/courses/${courseId}/modules`, {
@@ -149,7 +144,6 @@ export default function CoursePage() {
if (modulesData) { if (modulesData) {
let modulesList: Module[] = [] let modulesList: Module[] = []
// Handle different response formats
if (modulesData.success && modulesData.modules && Array.isArray(modulesData.modules)) { if (modulesData.success && modulesData.modules && Array.isArray(modulesData.modules)) {
modulesList = modulesData.modules modulesList = modulesData.modules
} else if (modulesData.modules && Array.isArray(modulesData.modules)) { } else if (modulesData.modules && Array.isArray(modulesData.modules)) {
@@ -160,13 +154,11 @@ export default function CoursePage() {
modulesList = modulesData.data modulesList = modulesData.data
} }
// Sort modules by order
modulesList = modulesList.sort((a, b) => a.order - b.order) modulesList = modulesList.sort((a, b) => a.order - b.order)
console.log('🔍 Processed modules list:', modulesList) console.log('🔍 Processed modules list:', modulesList)
setModules(modulesList) setModules(modulesList)
// Fetch lessons for all modules
if (modulesList.length > 0) { if (modulesList.length > 0) {
await fetchLessonsForAllModules(modulesList) await fetchLessonsForAllModules(modulesList)
} }
@@ -193,7 +185,6 @@ export default function CoursePage() {
try { try {
console.log('🔍 Fetching lessons for module:', module.id) console.log('🔍 Fetching lessons for module:', module.id)
// Try admin endpoint first
let lessonsResponse = await fetch(`http://127.0.0.1:5000/api/admin/modules/${module.id}/lessons`, { let lessonsResponse = await fetch(`http://127.0.0.1:5000/api/admin/modules/${module.id}/lessons`, {
headers: { headers: {
'Authorization': 'Bearer admin-secret-key', 'Authorization': 'Bearer admin-secret-key',
@@ -201,7 +192,6 @@ export default function CoursePage() {
} }
}) })
// If admin fails, try public endpoint
if (!lessonsResponse.ok) { if (!lessonsResponse.ok) {
lessonsResponse = await fetch(`http://127.0.0.1:5000/api/modules/${module.id}/lessons`, { lessonsResponse = await fetch(`http://127.0.0.1:5000/api/modules/${module.id}/lessons`, {
headers: { headers: {
@@ -225,11 +215,9 @@ export default function CoursePage() {
lessonsList = lessonData.data lessonsList = lessonData.data
} }
// Sort lessons by order
lessonsList = lessonsList.sort((a, b) => a.order - b.order) lessonsList = lessonsList.sort((a, b) => a.order - b.order)
lessonsData[module.id] = lessonsList lessonsData[module.id] = lessonsList
// Auto-expand first module with lessons
if (!selectedModuleId && lessonsList.length > 0) { if (!selectedModuleId && lessonsList.length > 0) {
expandedState[module.id] = true expandedState[module.id] = true
} }
@@ -246,7 +234,6 @@ export default function CoursePage() {
setLessons(lessonsData) setLessons(lessonsData)
setExpandedModules(expandedState) setExpandedModules(expandedState)
// Auto-select first lesson if available
if (!selectedModuleId && modulesList.length > 0) { if (!selectedModuleId && modulesList.length > 0) {
const firstModule = modulesList[0] const firstModule = modulesList[0]
const firstModuleLessons = lessonsData[firstModule.id] || [] const firstModuleLessons = lessonsData[firstModule.id] || []
@@ -258,7 +245,6 @@ export default function CoursePage() {
} }
} }
// Helper: embed URL
function getEmbedUrl(url?: string): string | undefined { function getEmbedUrl(url?: string): string | undefined {
if (!url) return undefined if (!url) return undefined
const regExp = /(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|embed\/))([^#&?]{11})/ const regExp = /(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|embed\/))([^#&?]{11})/
@@ -279,7 +265,6 @@ export default function CoursePage() {
const selectLesson = (moduleId: string, lessonId: string) => { const selectLesson = (moduleId: string, lessonId: string) => {
setSelectedModuleId(moduleId) setSelectedModuleId(moduleId)
setSelectedLessonId(lessonId) setSelectedLessonId(lessonId)
// Auto-expand the module when lesson is selected
setExpandedModules(prev => ({ setExpandedModules(prev => ({
...prev, ...prev,
[moduleId]: true [moduleId]: true
@@ -324,10 +309,9 @@ export default function CoursePage() {
return allLessons.length > 0 && allLessons[allLessons.length - 1].id === selectedLessonId return allLessons.length > 0 && allLessons[allLessons.length - 1].id === selectedLessonId
} }
// ✅ Updated markComplete function to show certificate modal
const markComplete = () => { const markComplete = () => {
setCompleted(true) setCompleted(true)
setShowCertificateModal(true) // Show certificate modal instead of just toast setShowCertificateModal(true)
} }
const getTotalLessons = () => { const getTotalLessons = () => {
@@ -338,10 +322,24 @@ export default function CoursePage() {
if (authLoading || loading) { if (authLoading || loading) {
return ( return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center"> <div className="min-h-screen bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 flex items-center justify-center relative overflow-hidden">
<div className="text-center"> {/* Animated background elements */}
<Loader2 className="h-12 w-12 animate-spin text-indigo-600 mx-auto mb-4" /> <div className="absolute inset-0">
<p className="text-lg text-gray-700">Loading course...</p> <div className="absolute top-1/4 left-1/4 w-96 h-96 bg-purple-500 rounded-full mix-blend-multiply filter blur-xl opacity-20 animate-pulse"></div>
<div className="absolute top-1/3 right-1/4 w-96 h-96 bg-yellow-500 rounded-full mix-blend-multiply filter blur-xl opacity-20 animate-pulse animation-delay-1000"></div>
<div className="absolute bottom-1/4 left-1/3 w-96 h-96 bg-pink-500 rounded-full mix-blend-multiply filter blur-xl opacity-20 animate-pulse animation-delay-2000"></div>
</div>
<div className="text-center z-10">
<div className="relative">
<Loader2 className="h-16 w-16 animate-spin text-white mx-auto mb-6 drop-shadow-lg" />
<div className="absolute inset-0 h-16 w-16 border-4 border-transparent border-t-purple-400 rounded-full animate-ping mx-auto"></div>
</div>
<p className="text-xl text-white font-semibold tracking-wide animate-pulse">Loading your learning journey...</p>
<div className="mt-4 flex justify-center space-x-1">
<div className="w-2 h-2 bg-white rounded-full animate-bounce"></div>
<div className="w-2 h-2 bg-white rounded-full animate-bounce animation-delay-200"></div>
<div className="w-2 h-2 bg-white rounded-full animate-bounce animation-delay-400"></div>
</div>
</div> </div>
</div> </div>
) )
@@ -349,14 +347,17 @@ export default function CoursePage() {
if (error) { if (error) {
return ( return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center"> <div className="min-h-screen bg-gradient-to-br from-red-900 via-pink-900 to-purple-900 flex items-center justify-center p-4">
<div className="text-center max-w-md mx-auto px-4"> <div className="text-center max-w-md mx-auto px-6">
<div className="bg-red-50 border border-red-200 rounded-lg p-6"> <div className="bg-white/10 backdrop-blur-lg border border-red-300/30 rounded-3xl p-10 shadow-2xl animate-bounce">
<h2 className="text-lg font-semibold text-red-800 mb-2">Error Loading Course</h2> <div className="w-20 h-20 bg-red-500 rounded-full flex items-center justify-center mx-auto mb-6 animate-pulse">
<p className="text-red-600 mb-4">{error}</p> <span className="text-3xl"></span>
</div>
<h2 className="text-2xl font-bold text-white mb-4">Oops! Something went wrong</h2>
<p className="text-red-200 mb-8 leading-relaxed">{error}</p>
<button <button
onClick={fetchCourseData} onClick={fetchCourseData}
className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 transition-colors" className="px-8 py-4 bg-gradient-to-r from-red-500 to-pink-500 text-white rounded-2xl hover:from-red-600 hover:to-pink-600 shadow-lg transition-all duration-300 transform hover:scale-105 font-semibold text-lg"
> >
Try Again Try Again
</button> </button>
@@ -368,382 +369,429 @@ export default function CoursePage() {
if (!course) { if (!course) {
return ( return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center"> <div className="min-h-screen bg-gradient-to-br from-gray-800 to-gray-900 flex items-center justify-center p-4">
<div className="text-center"> <div className="text-center max-w-sm bg-white/10 backdrop-blur-lg rounded-3xl shadow-2xl p-10 animate-fadeIn">
<h2 className="text-xl font-semibold text-gray-800 mb-2">Course Not Found</h2> <div className="w-24 h-24 bg-gray-500 rounded-full flex items-center justify-center mx-auto mb-6 animate-bounce">
<p className="text-gray-600">The course you're looking for doesn't exist.</p> <span className="text-4xl">🔍</span>
</div>
<h2 className="text-3xl font-bold text-white mb-4">Course Not Found</h2>
<p className="text-gray-300 leading-relaxed">The course you're looking for doesn't exist or may have been removed.</p>
</div> </div>
</div> </div>
) )
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50">
{/* Animated Background Elements */}
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute -top-40 -right-40 w-96 h-96 bg-purple-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-float"></div>
<div className="absolute -bottom-40 -left-40 w-96 h-96 bg-yellow-300 rounded-full mix-blend-multiply filter blur-xl opacity-30 animate-float animation-delay-2000"></div>
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-pink-300 rounded-full mix-blend-multiply filter blur-xl opacity-20 animate-pulse"></div>
</div>
{/* Header */} {/* Header */}
<header className="bg-white shadow-sm border-b"> <header className="bg-white/80 backdrop-blur-lg shadow-xl border-b border-purple-200 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="w-full px-6 sm:px-10 lg:px-16 xl:px-20">
<div className="flex items-center justify-between h-16"> <div className="flex items-center justify-between h-20">
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-6 animate-slideInLeft">
<div className="w-10 h-10 bg-indigo-600 rounded-lg flex items-center justify-center"> <div className="w-14 h-14 bg-gradient-to-br from-purple-600 to-indigo-600 rounded-2xl flex items-center justify-center shadow-lg transform hover:scale-110 transition-transform duration-300">
<span className="text-white font-bold text-lg">OL</span> <span className="text-white font-extrabold text-2xl">OL</span>
</div> </div>
<div> <div>
<h1 className="text-xl font-bold text-gray-900">{course.title}</h1> <h1 className="text-2xl font-extrabold bg-gradient-to-r from-purple-600 to-indigo-600 bg-clip-text text-transparent tracking-tight">{course.title}</h1>
<p className="text-sm text-gray-600">by {course.mentor}</p> <p className="text-sm text-purple-700 font-semibold tracking-wide">by {course.mentor}</p>
</div> </div>
</div> </div>
<div className="hidden md:flex items-center space-x-6 text-sm"> <div className="hidden md:flex items-center space-x-8 text-sm text-purple-700 animate-slideInRight">
<div className="flex items-center text-gray-600"> <div className="flex items-center space-x-2 bg-purple-100 px-4 py-2 rounded-full">
<BookOpen className="w-4 h-4 mr-2" /> <BookOpen className="w-5 h-5 text-purple-600" />
<span>{modules.length} modules</span> <span className="font-semibold">{modules.length} modules</span>
</div> </div>
<div className="flex items-center text-gray-600"> <div className="flex items-center space-x-2 bg-indigo-100 px-4 py-2 rounded-full">
<Play className="w-4 h-4 mr-2" /> <Play className="w-5 h-5 text-indigo-600" />
<span>{getTotalLessons()} lessons</span> <span className="font-semibold">{getTotalLessons()} lessons</span>
</div> </div>
<div className="flex items-center text-gray-600"> <div className="flex items-center space-x-2 bg-pink-100 px-4 py-2 rounded-full">
<Users className="w-4 h-4 mr-2" /> <Users className="w-5 h-5 text-pink-600" />
<span>{course.students} students</span> <span className="font-semibold">{course.students.toLocaleString()} students</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</header> </header>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <main className="w-full px-6 sm:px-10 lg:px-16 xl:px-20 py-12 grid grid-cols-1 lg:grid-cols-5 gap-12 relative z-10">
<div className="lg:grid lg:grid-cols-12 lg:gap-8">
{/* Sidebar - Now takes up 2 columns on large screens */}
{/* Sidebar */} <aside className="lg:col-span-2 animate-slideInLeft">
<aside className="lg:col-span-4 xl:col-span-3"> <div className="bg-white/80 backdrop-blur-lg rounded-3xl shadow-2xl border border-purple-200 p-10 sticky top-28">
<div className="bg-white rounded-xl shadow-sm border p-6 sticky top-8"> <div className="flex items-center justify-between mb-8">
<h2 className="text-lg font-semibold text-gray-900 mb-4">Course Content</h2> <h2 className="text-2xl font-extrabold bg-gradient-to-r from-purple-600 to-indigo-600 bg-clip-text text-transparent">Course Content</h2>
<div className="w-8 h-8 bg-gradient-to-r from-purple-500 to-indigo-500 rounded-full flex items-center justify-center">
{/* Debug Info - Enhanced */} <BookOpen className="w-4 h-4 text-white" />
<div className="mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<h3 className="text-sm font-semibold text-blue-800 mb-2">🔍 Debug Info:</h3>
<div className="text-xs space-y-1 text-blue-700">
<p><strong>Course ID:</strong> {courseId}</p>
<p><strong>Modules Loaded:</strong> {modules.length}</p>
<p><strong>Total Lessons:</strong> {getTotalLessons()}</p>
<p><strong>Modules Loading:</strong> {modulesLoading ? 'Yes' : 'No'}</p>
<p><strong>Selected Module:</strong> {selectedModuleId || 'None'}</p>
<p><strong>Selected Lesson:</strong> {currentLesson?.title || 'None'}</p>
<p><strong>Expanded Modules:</strong> {Object.keys(expandedModules).length}</p>
</div>
{modules.length > 0 && (
<details className="mt-2">
<summary className="text-xs cursor-pointer text-blue-600">Show Raw Data</summary>
<pre className="text-xs mt-2 p-2 bg-gray-100 rounded overflow-auto max-h-32">
{JSON.stringify({ modules, lessons }, null, 2)}
</pre>
</details>
)}
</div> </div>
</div>
{/* Loading State */} {/* Enhanced Progress Bar */}
{modulesLoading && ( <div className="mb-8 p-4 bg-gradient-to-r from-purple-50 to-indigo-50 rounded-2xl border border-purple-200">
<div className="text-center py-8"> <div className="flex items-center justify-between mb-3">
<Loader2 className="h-8 w-8 animate-spin text-indigo-600 mx-auto mb-2" /> <span className="text-sm font-semibold text-purple-700">Progress</span>
<p className="text-sm text-gray-600">Loading modules...</p> <span className="text-sm font-bold text-indigo-600">25%</span>
</div> </div>
)} <div className="w-full bg-gray-200 rounded-full h-3 overflow-hidden">
<div className="bg-gradient-to-r from-purple-500 to-indigo-500 h-3 rounded-full transition-all duration-1000 ease-out animate-pulse" style={{width: '25%'}}></div>
</div>
</div>
{/* No Modules State */} {/* Debug Info - Enhanced */}
{!modulesLoading && modules.length === 0 && ( <div className="mb-8 p-5 bg-gradient-to-r from-blue-50 to-cyan-50 border border-blue-200 rounded-2xl animate-fadeIn">
<div className="text-center py-8"> <h3 className="text-sm font-bold text-blue-800 mb-4 flex items-center">
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4"> <TrendingUp className="w-4 h-4 mr-2" />
<h3 className="text-sm font-semibold text-yellow-800 mb-2">No Modules Found</h3> 🔍 Debug Info:
<p className="text-xs text-yellow-700 mb-3"> </h3>
This could mean: <div className="text-xs space-y-2 text-blue-700">
</p> <p><strong>Course ID:</strong> {courseId}</p>
<ul className="text-xs text-yellow-700 text-left space-y-1"> <p><strong>Modules Loaded:</strong> {modules.length}</p>
<li> No modules created for this course yet</li> <p><strong>Total Lessons:</strong> {getTotalLessons()}</p>
<li> API endpoint issues</li> <p><strong>Modules Loading:</strong> {modulesLoading ? 'Yes' : 'No'}</p>
<li> Course ID mismatch</li> <p><strong>Selected Module:</strong> {selectedModuleId || 'None'}</p>
</ul> <p><strong>Selected Lesson:</strong> {currentLesson?.title || 'None'}</p>
<button <p><strong>Expanded Modules:</strong> {Object.keys(expandedModules).length}</p>
onClick={() => fetchModulesAndLessons(courseId)} </div>
className="mt-3 px-3 py-1 bg-yellow-600 text-white text-xs rounded hover:bg-yellow-700" {modules.length > 0 && (
> <details className="mt-4 border-t border-blue-200 pt-4">
Retry Loading Modules <summary className="text-xs cursor-pointer text-blue-600 font-semibold hover:text-blue-800 transition-colors">Show Raw Data</summary>
</button> <pre className="mt-3 text-xs p-4 bg-white rounded-xl shadow max-h-40 overflow-auto">
</div> {JSON.stringify({ modules, lessons }, null, 2)}
</div> </pre>
)} </details>
{/* Modules List */}
{!modulesLoading && modules.length > 0 && (
<div className="space-y-2">
{modules.map((module, index) => (
<div key={module.id} className="border border-gray-200 rounded-lg overflow-hidden">
{/* Module Header */}
<button
onClick={() => toggleModule(module.id)}
className={`w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center justify-between transition-colors ${
selectedModuleId === module.id ? 'bg-indigo-50 border-indigo-200' : 'bg-white'
}`}
>
<div className="flex-1 min-w-0">
<div className="flex items-center space-x-2">
<span className="flex-shrink-0 w-6 h-6 bg-indigo-100 text-indigo-800 rounded-full flex items-center justify-center text-xs font-semibold">
{index + 1}
</span>
<h3 className="font-medium text-sm text-gray-900 truncate">{module.title}</h3>
</div>
<p className="text-xs text-gray-500 mt-1 ml-8">
{lessons[module.id]?.length || 0} lessons
</p>
</div>
<div className="flex-shrink-0 ml-2">
{expandedModules[module.id] ? (
<ChevronDown className="w-4 h-4 text-gray-400" />
) : (
<ChevronRight className="w-4 h-4 text-gray-400" />
)}
</div>
</button>
{/* Lessons */}
{expandedModules[module.id] && (
<div className="bg-gray-50 border-t border-gray-200">
{lessons[module.id] && lessons[module.id].length > 0 ? (
lessons[module.id].map((lesson, lessonIndex) => (
<button
key={lesson.id}
onClick={() => selectLesson(module.id, lesson.id)}
className={`w-full px-6 py-3 text-left hover:bg-gray-100 transition-colors border-l-4 ${
selectedLessonId === lesson.id
? 'border-indigo-500 bg-indigo-50 text-indigo-900'
: 'border-transparent text-gray-700'
}`}
>
<div className="flex items-center space-x-3">
<div className={`flex-shrink-0 w-5 h-5 rounded-full flex items-center justify-center text-xs ${
selectedLessonId === lesson.id
? 'bg-indigo-500 text-white'
: 'bg-gray-300 text-gray-600'
}`}>
<Play className="w-2.5 h-2.5" />
</div>
<div className="flex-1 min-w-0">
<p className="font-medium text-sm truncate">{lesson.title}</p>
{lesson.duration && (
<p className={`text-xs flex items-center mt-1 ${
selectedLessonId === lesson.id ? 'text-indigo-600' : 'text-gray-500'
}`}>
<Clock className="w-3 h-3 mr-1" />
{lesson.duration}
</p>
)}
</div>
</div>
</button>
))
) : (
<div className="px-6 py-4 text-center">
<p className="text-xs text-gray-500">No lessons in this module</p>
</div>
)}
</div>
)}
</div>
))}
</div>
)} )}
</div> </div>
</aside>
{/* Main Content */} {/* Loading State */}
<main className="mt-8 lg:mt-0 lg:col-span-8 xl:col-span-9"> {modulesLoading && (
<div className="bg-white rounded-xl shadow-sm border overflow-hidden"> <div className="text-center py-10 animate-pulse">
{currentLesson ? ( <Loader2 className="h-12 w-12 animate-spin text-purple-500 mx-auto mb-4" />
<> <p className="text-lg text-purple-700 font-semibold">Loading modules...</p>
{/* Video Player */} </div>
{(currentLesson.embed_url || currentLesson.video_url) && ( )}
<div className="aspect-video bg-black">
<iframe
src={getEmbedUrl(currentLesson.embed_url || currentLesson.video_url)}
title={currentLesson.title}
allowFullScreen
className="w-full h-full"
/>
</div>
)}
{/* Lesson Content */} {/* No Modules State */}
<div className="p-8"> {!modulesLoading && modules.length === 0 && (
{/* Lesson Header */} <div className="text-center py-8 animate-bounce">
<div className="mb-6"> <div className="bg-gradient-to-r from-yellow-50 to-orange-50 border border-yellow-300 rounded-2xl p-6 text-yellow-800">
<div className="flex items-center text-sm text-gray-500 mb-2"> <div className="w-16 h-16 bg-yellow-500 rounded-full flex items-center justify-center mx-auto mb-4">
<User className="w-4 h-4 mr-1" /> <span className="text-2xl">📚</span>
<span>by {course.mentor}</span> </div>
<span className="mx-2"></span> <h3 className="text-lg font-bold mb-3">No Modules Found</h3>
<span className="bg-indigo-100 text-indigo-800 px-2 py-1 rounded-full text-xs font-medium"> <p className="text-sm mb-4 leading-relaxed">
{course.difficulty} This could mean:<br />
</span> &bull; No modules created yet<br />
</div> &bull; API endpoint issues<br />
<h1 className="text-3xl font-bold text-gray-900 mb-2">{currentLesson.title}</h1> &bull; Course ID mismatch
{currentLesson.duration && ( </p>
<div className="flex items-center text-sm text-gray-600"> <button
<Clock className="w-4 h-4 mr-1" /> onClick={() => fetchModulesAndLessons(courseId)}
<span>{currentLesson.duration}</span> className="px-6 py-3 bg-gradient-to-r from-yellow-500 to-orange-500 rounded-2xl text-white font-bold hover:from-yellow-600 hover:to-orange-600 transition-all duration-300 transform hover:scale-105 shadow-lg"
>
Retry Loading Modules
</button>
</div>
</div>
)}
{/* Modules List */}
{!modulesLoading && modules.length > 0 && (
<div className="space-y-4">
{modules.map((module, index) => (
<div key={module.id} className="border border-purple-200 rounded-2xl overflow-hidden shadow-lg bg-white/60 backdrop-blur-sm hover:shadow-xl transition-all duration-300 animate-fadeInUp" style={{animationDelay: `${index * 100}ms`}}>
{/* Module Header */}
<button
onClick={() => toggleModule(module.id)}
className={`w-full px-6 py-5 text-left hover:bg-gradient-to-r hover:from-purple-50 hover:to-indigo-50 flex items-center justify-between transition-all duration-300 ${
selectedModuleId === module.id ? 'bg-gradient-to-r from-purple-100 to-indigo-100 border-purple-300' : 'bg-white/80'
}`}
>
<div className="flex-1 min-w-0">
<div className="flex items-center space-x-4">
<span className="flex-shrink-0 w-10 h-10 bg-gradient-to-r from-purple-500 to-indigo-500 text-white rounded-full flex items-center justify-center font-bold text-sm shadow-lg transform hover:scale-110 transition-transform duration-300">
{index + 1}
</span>
<h3 className="font-bold text-purple-900 truncate text-lg">{module.title}</h3>
</div> </div>
)} <p className="text-sm text-purple-600 mt-2 ml-14 flex items-center">
</div> <CheckCircle className="w-4 h-4 mr-2" />
{(lessons[module.id]?.length ?? 0) + (lessons[module.id]?.length === 1 ? ' lesson' : ' lessons')}
{/* Lesson Description */} </p>
{currentLesson.description && ( </div>
<div className="prose max-w-none mb-8"> <div className="flex-shrink-0 ml-4">
<h2 className="text-xl font-semibold text-gray-900 mb-4">About this lesson</h2> <div className={`transform transition-transform duration-300 ${expandedModules[module.id] ? 'rotate-180' : ''}`}>
<div className="text-gray-700 leading-relaxed whitespace-pre-line bg-gray-50 p-4 rounded-lg"> {expandedModules[module.id] ? (
{currentLesson.description} <ChevronDown className="w-6 h-6 text-purple-500" />
) : (
<ChevronRight className="w-6 h-6 text-purple-400" />
)}
</div> </div>
</div> </div>
)} </button>
{/* Lesson Content */} {/* Lessons */}
{currentLesson.content && ( {expandedModules[module.id] && (
<div className="prose max-w-none mb-8"> <div className="bg-gradient-to-r from-purple-50 to-indigo-50 border-t border-purple-200 animate-slideDown">
<h2 className="text-xl font-semibold text-gray-900 mb-4">Lesson Content</h2> {lessons[module.id] && lessons[module.id].length > 0 ? (
<div className="text-gray-700 leading-relaxed whitespace-pre-line bg-gray-50 p-4 rounded-lg"> lessons[module.id].map((lesson, lessonIndex) => (
{currentLesson.content} <button
</div> key={lesson.id}
</div> onClick={() => selectLesson(module.id, lesson.id)}
)} className={`w-full px-8 py-4 text-left hover:bg-gradient-to-r hover:from-purple-100 hover:to-indigo-100 transition-all duration-300 border-l-4 group ${
selectedLessonId === lesson.id
{/* Navigation */} ? 'border-purple-500 bg-gradient-to-r from-purple-100 to-indigo-100 text-purple-900 font-bold shadow-inner'
<div className="flex justify-between items-center pt-8 border-t border-gray-200"> : 'border-transparent text-purple-700 hover:border-purple-300'
<button }`}
onClick={() => navigateLesson('prev')} >
disabled={isFirstLesson()} <div className="flex items-center space-x-4">
className="px-6 py-3 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium" <div className={`flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center text-xs transition-all duration-300 group-hover:scale-110 ${
> selectedLessonId === lesson.id
Previous Lesson ? 'bg-gradient-to-r from-purple-500 to-indigo-500 text-white shadow-lg'
</button> : 'bg-purple-200 text-purple-700 group-hover:bg-purple-300'
}`}>
{!isLastLesson() ? ( <Play className="w-4 h-4" />
<button </div>
onClick={() => navigateLesson('next')} <div className="flex-1 min-w-0">
className="px-6 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors font-medium" <p className="truncate font-semibold">{lesson.title}</p>
> {lesson.duration && (
Next Lesson <p className={`text-xs flex items-center mt-1 ${
</button> selectedLessonId === lesson.id ? 'text-purple-700 font-semibold' : 'text-purple-500'
) : ( }`}>
<button <Clock className="w-4 h-4 mr-2" />
onClick={markComplete} {lesson.duration}
disabled={completed} </p>
className={`px-6 py-3 rounded-lg font-medium transition-colors ${ )}
completed </div>
? "bg-green-600 text-white cursor-not-allowed" <ArrowRight className={`w-4 h-4 transition-all duration-300 ${
: "bg-purple-600 text-white hover:bg-purple-700" selectedLessonId === lesson.id ? 'text-purple-600 transform scale-110' : 'text-transparent group-hover:text-purple-400'
}`} }`} />
> </div>
{completed ? "✓ Course Completed" : "Mark as Complete"} </button>
</button> ))
)} ) : (
</div> <p className="px-8 py-6 text-purple-600 text-sm italic text-center">No lessons in this module</p>
)}
{/* ✅ Updated Completion Message */}
{completed && !showCertificateModal && (
<div className="mt-8 bg-green-50 border border-green-200 rounded-lg p-6 text-center">
<div className="text-green-700">
<div className="text-4xl mb-2">🎉</div>
<h3 className="text-xl font-bold mb-2">Congratulations!</h3>
<p className="mb-4">You have successfully completed this course!</p>
<button
onClick={() => setShowCertificateModal(true)}
className="px-6 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors font-medium"
>
Get Your Certificate 🏆
</button>
</div>
</div> </div>
)} )}
</div> </div>
</> ))}
) : ( </div>
/* Course Overview */ )}
<div className="p-8 text-center"> </div>
<div className="max-w-3xl mx-auto"> </aside>
<h1 className="text-4xl font-bold text-gray-900 mb-4">{course.title}</h1>
{/* Main Content - Now takes up 3 columns on large screens for full width */}
<div className="flex items-center justify-center space-x-4 mb-6 text-sm"> <section className="lg:col-span-3 animate-slideInRight">
<div className="flex items-center text-gray-600"> <div className="bg-white/80 backdrop-blur-lg rounded-3xl shadow-2xl border border-purple-200 overflow-hidden">
<User className="w-4 h-4 mr-1" /> {currentLesson ? (
<span>by {course.mentor}</span> <>
{/* Video Player */}
{(currentLesson.embed_url || currentLesson.video_url) && (
<div className="aspect-video bg-black rounded-t-3xl overflow-hidden relative group">
<iframe
src={getEmbedUrl(currentLesson.embed_url || currentLesson.video_url)}
title={currentLesson.title}
allowFullScreen
className="w-full h-full"
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
</div>
)}
{/* Lesson Content */}
<div className="p-16">
{/* Lesson Header */}
<div className="mb-12 animate-fadeInUp">
<div className="flex items-center text-purple-600 space-x-4 mb-6">
<div className="flex items-center space-x-2 bg-purple-100 px-6 py-3 rounded-full">
<User className="w-6 h-6" />
<span className="font-bold text-lg">{course.mentor}</span>
</div> </div>
<div className="flex items-center"> <span className="text-purple-300"></span>
<Star className="w-4 h-4 text-yellow-400 mr-1" /> <span className="bg-gradient-to-r from-purple-500 to-indigo-500 text-white px-6 py-3 rounded-full text-lg font-bold uppercase tracking-widest shadow-lg">
<span className="text-gray-600">4.8</span>
</div>
<span className="bg-indigo-100 text-indigo-800 px-3 py-1 rounded-full text-sm font-medium">
{course.difficulty} {course.difficulty}
</span> </span>
</div> </div>
<h1 className="text-6xl font-extrabold bg-gradient-to-r from-purple-600 to-indigo-600 bg-clip-text text-transparent leading-tight mb-6 drop-shadow-sm">{currentLesson.title}</h1>
<p className="text-lg text-gray-700 mb-8 leading-relaxed">{course.description}</p> {currentLesson.duration && (
<div className="flex items-center text-purple-600 space-x-3 text-xl font-semibold">
{/* Course Stats */} <div className="flex items-center space-x-2 bg-purple-100 px-6 py-3 rounded-full">
<div className="grid grid-cols-3 gap-8 mb-8"> <Clock className="w-6 h-6" />
<div className="text-center"> <span>{currentLesson.duration}</span>
<div className="text-3xl font-bold text-indigo-600 mb-1">{modules.length}</div> </div>
<div className="text-sm text-gray-600">Modules</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-indigo-600 mb-1">{getTotalLessons()}</div>
<div className="text-sm text-gray-600">Lessons</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold text-indigo-600 mb-1">{course.students}</div>
<div className="text-sm text-gray-600">Students</div>
</div>
</div>
{/* Course Intro Video */}
{(course.embed_url || course.video_url) && (
<div className="aspect-video rounded-xl overflow-hidden mb-8 shadow-lg bg-black">
<iframe
src={getEmbedUrl(course.embed_url || course.video_url)}
title={course.title}
allowFullScreen
className="w-full h-full"
/>
</div>
)}
{getTotalLessons() > 0 ? (
<div>
<p className="text-gray-600 mb-6">
Ready to start learning? Select a lesson from the course content to begin your journey.
</p>
<button
onClick={() => {
const firstModule = modules[0]
const firstLessons = lessons[firstModule?.id] || []
if (firstLessons.length > 0) {
selectLesson(firstModule.id, firstLessons[0].id)
}
}}
className="px-8 py-4 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 font-semibold text-lg transition-colors"
>
Start Learning
</button>
</div>
) : (
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-yellow-800 mb-2">Coming Soon</h3>
<p className="text-yellow-700">Lessons are being prepared for this course. Check back soon!</p>
</div> </div>
)} )}
</div> </div>
</div>
)}
</div>
</main>
</div>
</div>
{/* ✅ Certificate Modal */} {/* Lesson Description */}
{currentLesson.description && (
<section className="mb-16 animate-fadeInUp animation-delay-200">
<h2 className="text-4xl font-bold bg-gradient-to-r from-purple-600 to-indigo-600 bg-clip-text text-transparent mb-8 border-b-2 border-purple-200 pb-4">
About this lesson
</h2>
<article className="bg-gradient-to-r from-purple-50 to-indigo-50 rounded-3xl p-10 text-purple-900 prose max-w-none shadow-inner border border-purple-200 text-lg leading-relaxed">
{currentLesson.description}
</article>
</section>
)}
{/* Lesson Content */}
{currentLesson.content && (
<section className="mb-16 animate-fadeInUp animation-delay-400">
<h2 className="text-4xl font-bold bg-gradient-to-r from-purple-600 to-indigo-600 bg-clip-text text-transparent mb-8 border-b-2 border-purple-200 pb-4">
Lesson Content
</h2>
<article className="bg-gradient-to-r from-purple-50 to-indigo-50 rounded-3xl p-10 text-purple-900 prose max-w-none whitespace-pre-line shadow-inner border border-purple-200 text-lg leading-relaxed">
{currentLesson.content}
</article>
</section>
)}
{/* Navigation */}
<div className="flex justify-between items-center pt-12 border-t-2 border-purple-200 animate-fadeInUp animation-delay-600">
<button
onClick={() => navigateLesson('prev')}
disabled={isFirstLesson()}
className="px-12 py-5 bg-gradient-to-r from-gray-100 to-gray-200 text-gray-700 rounded-3xl hover:from-gray-200 hover:to-gray-300 disabled:opacity-50 disabled:cursor-not-allowed font-bold transition-all duration-300 transform hover:scale-105 shadow-lg text-xl"
>
Previous Lesson
</button>
{!isLastLesson() ? (
<button
onClick={() => navigateLesson('next')}
className="px-12 py-5 bg-gradient-to-r from-purple-600 to-indigo-600 text-white rounded-3xl hover:from-purple-700 hover:to-indigo-700 font-bold transition-all duration-300 transform hover:scale-105 shadow-xl text-xl"
>
Next Lesson
</button>
) : (
<button
onClick={markComplete}
disabled={completed}
className={`px-12 py-5 rounded-3xl font-bold transition-all duration-300 transform hover:scale-105 shadow-xl text-xl ${
completed
? "bg-gradient-to-r from-green-500 to-emerald-500 text-white cursor-not-allowed shadow-inner"
: "bg-gradient-to-r from-purple-600 to-pink-600 text-white hover:from-purple-700 hover:to-pink-700"
}`}
>
{completed ? "✓ Course Completed" : "Mark as Complete"}
</button>
)}
</div>
{/* Completion Message */}
{completed && !showCertificateModal && (
<div className="mt-16 bg-gradient-to-r from-green-50 to-emerald-50 border-2 border-green-300 rounded-3xl p-12 text-center shadow-2xl animate-bounce">
<div className="text-green-700">
<div className="text-8xl mb-8 animate-pulse">🎉</div>
<h3 className="text-4xl font-extrabold mb-6 bg-gradient-to-r from-green-600 to-emerald-600 bg-clip-text text-transparent">Congratulations!</h3>
<p className="mb-10 text-green-800 font-semibold text-2xl">
You have successfully completed this course!
</p>
<button
onClick={() => setShowCertificateModal(true)}
className="px-16 py-6 bg-gradient-to-r from-green-600 to-emerald-600 text-white rounded-3xl hover:from-green-700 hover:to-emerald-700 transition-all duration-300 transform hover:scale-105 font-bold text-xl shadow-xl"
>
Get Your Certificate 🏆
</button>
</div>
</div>
)}
</div>
</>
) : (
/* Course Overview */
<div className="p-20 text-center max-w-5xl mx-auto text-purple-900 animate-fadeIn">
<h1 className="text-7xl font-extrabold mb-10 bg-gradient-to-r from-purple-600 via-pink-600 to-indigo-600 bg-clip-text text-transparent drop-shadow-lg">{course.title}</h1>
<div className="flex flex-wrap justify-center gap-8 mb-16 text-purple-700 font-bold text-xl">
<div className="flex items-center space-x-4 bg-purple-100 px-8 py-4 rounded-full shadow-lg transform hover:scale-105 transition-transform duration-300">
<User className="w-8 h-8" />
<span>by {course.mentor}</span>
</div>
<div className="flex items-center space-x-4 bg-yellow-100 px-8 py-4 rounded-full shadow-lg transform hover:scale-105 transition-transform duration-300">
<Star className="w-8 h-8 text-yellow-500" />
<span>4.8 Rating</span>
</div>
<span className="bg-gradient-to-r from-purple-500 to-indigo-500 text-white px-8 py-4 rounded-full text-xl uppercase font-extrabold tracking-widest shadow-lg transform hover:scale-105 transition-transform duration-300">
{course.difficulty}
</span>
</div>
<p className="text-3xl max-w-5xl mx-auto mb-16 leading-relaxed tracking-wide text-purple-800">{course.description}</p>
<div className="grid grid-cols-3 gap-16 mb-16">
<div className="text-center transform hover:scale-110 transition-transform duration-300">
<div className="w-32 h-32 bg-gradient-to-r from-purple-500 to-indigo-500 rounded-full flex items-center justify-center mx-auto mb-6 shadow-xl">
<span className="text-5xl font-extrabold text-white">{modules.length}</span>
</div>
<div className="uppercase text-purple-700 font-bold tracking-wide text-xl">Modules</div>
</div>
<div className="text-center transform hover:scale-110 transition-transform duration-300">
<div className="w-32 h-32 bg-gradient-to-r from-pink-500 to-purple-500 rounded-full flex items-center justify-center mx-auto mb-6 shadow-xl">
<span className="text-5xl font-extrabold text-white">{getTotalLessons()}</span>
</div>
<div className="uppercase text-purple-700 font-bold tracking-wide text-xl">Lessons</div>
</div>
<div className="text-center transform hover:scale-110 transition-transform duration-300">
<div className="w-32 h-32 bg-gradient-to-r from-indigo-500 to-blue-500 rounded-full flex items-center justify-center mx-auto mb-6 shadow-xl">
<span className="text-5xl font-extrabold text-white">{course.students.toLocaleString()}</span>
</div>
<div className="uppercase text-purple-700 font-bold tracking-wide text-xl">Students</div>
</div>
</div>
{(course.embed_url || course.video_url) && (
<div className="aspect-video rounded-3xl overflow-hidden shadow-2xl mx-auto max-w-6xl bg-black border-4 border-purple-600 mb-16 transform hover:scale-105 transition-transform duration-500">
<iframe
src={getEmbedUrl(course.embed_url || course.video_url)}
title={course.title}
allowFullScreen
className="w-full h-full"
loading="lazy"
/>
</div>
)}
{getTotalLessons() > 0 ? (
<button
onClick={() => {
const firstModule = modules[0]
const firstLessons = lessons[firstModule?.id] || []
if (firstLessons.length > 0) {
selectLesson(firstModule.id, firstLessons[0].id)
}
}}
className="mt-12 px-20 py-8 bg-gradient-to-r from-purple-600 via-pink-600 to-indigo-600 text-white rounded-3xl hover:from-purple-700 hover:via-pink-700 hover:to-indigo-700 font-extrabold text-3xl shadow-2xl transition-all duration-300 transform hover:scale-110 hover:shadow-purple-500/25"
>
🚀 Start Learning Journey
</button>
) : (
<div className="bg-gradient-to-r from-yellow-50 to-orange-50 border-2 border-yellow-300 rounded-3xl p-12 text-yellow-800 text-2xl font-bold max-w-lg mx-auto shadow-xl">
<div className="w-24 h-24 bg-yellow-500 rounded-full flex items-center justify-center mx-auto mb-8">
<span className="text-4xl">🚧</span>
</div>
<h3 className="text-3xl mb-6">Coming Soon</h3>
<p className="font-normal text-yellow-700 text-xl">
Amazing lessons are being crafted for this course. Check back soon!
</p>
</div>
)}
</div>
)}
</div>
</section>
</main>
{/* Certificate Modal */}
{showCertificateModal && course && ( {showCertificateModal && course && (
<CertificateModal <CertificateModal
isOpen={showCertificateModal} isOpen={showCertificateModal}
@@ -752,9 +800,48 @@ export default function CoursePage() {
courseMentor={course.mentor} courseMentor={course.mentor}
courseId={course.id} courseId={course.id}
userId={user?.uid || firebaseUser?.uid || 'anonymous'} userId={user?.uid || firebaseUser?.uid || 'anonymous'}
walletId={user?.wallet || firebaseUser?.uid || 'no-wallet'} // Adjust based on your user structure walletId={user?.wallet || firebaseUser?.uid || 'no-wallet'}
/> />
)} )}
{/* Custom CSS for animations */}
<style jsx>{`
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-20px); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideInLeft {
from { opacity: 0; transform: translateX(-50px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideInRight {
from { opacity: 0; transform: translateX(50px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideDown {
from { opacity: 0; max-height: 0; }
to { opacity: 1; max-height: 500px; }
}
.animate-float { animation: float 6s ease-in-out infinite; }
.animate-fadeIn { animation: fadeIn 1s ease-out; }
.animate-fadeInUp { animation: fadeInUp 0.8s ease-out; }
.animate-slideInLeft { animation: slideInLeft 0.8s ease-out; }
.animate-slideInRight { animation: slideInRight 0.8s ease-out; }
.animate-slideDown { animation: slideDown 0.3s ease-out; }
.animation-delay-200 { animation-delay: 0.2s; }
.animation-delay-400 { animation-delay: 0.4s; }
.animation-delay-600 { animation-delay: 0.6s; }
.animation-delay-1000 { animation-delay: 1s; }
.animation-delay-2000 { animation-delay: 2s; }
`}</style>
</div> </div>
) )
} }
+382 -49
View File
@@ -1,92 +1,425 @@
import Link from "next/link" import Link from "next/link"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Brain, Zap, LinkIcon, BookOpen, Code, Lightbulb } from "lucide-react" import { Brain, Zap, LinkIcon, BookOpen, Code, Lightbulb, Star, Sparkles, Rocket } from "lucide-react"
export default function Home() { export default function Home() {
return ( return (
<div className="flex flex-col min-h-[calc(100vh-64px)]"> <div className="flex flex-col min-h-[calc(100vh-64px)] overflow-hidden">
<section className="relative w-full py-12 md:py-24 lg:py-32 bg-gradient-to-r from-primary-blue to-primary-purple text-white"> {/* Hero Section with Enhanced Animations */}
<div className="container px-4 md:px-6 text-center"> <section className="relative w-full py-12 md:py-24 lg:py-32 bg-gradient-to-br from-primary-blue via-purple-600 to-primary-purple text-white animate-fade-in overflow-hidden cursor-crosshair">
{/* Enhanced Animated geometric shapes with more visual appeal */}
<div className="absolute inset-0 opacity-5">
<div className="absolute top-0 left-0 w-96 h-96 bg-white rounded-full mix-blend-overlay animate-blob"></div>
<div className="absolute top-0 right-0 w-96 h-96 bg-white rounded-full mix-blend-overlay animate-blob animation-delay-2000"></div>
<div className="absolute -bottom-8 left-20 w-96 h-96 bg-white rounded-full mix-blend-overlay animate-blob animation-delay-4000"></div>
{/* New attractive geometric shapes */}
<div className="absolute top-1/3 left-1/2 w-64 h-64 bg-gradient-to-r from-pink-300 to-purple-300 rounded-full mix-blend-overlay animate-pulse opacity-30"></div>
<div className="absolute bottom-1/4 right-1/3 w-48 h-48 bg-gradient-to-l from-yellow-300 to-orange-300 rounded-full mix-blend-overlay animate-bounce opacity-20"></div>
<div className="absolute top-3/4 left-1/4 w-80 h-80 bg-gradient-to-br from-blue-300 to-cyan-300 rounded-full mix-blend-overlay animate-float opacity-25"></div>
</div>
{/* Enhanced animated background elements with more interactions and visual effects */}
<div className="absolute inset-0 opacity-10">
<div className="absolute top-1/4 left-1/4 w-32 h-32 bg-white rounded-full animate-float hover:animate-bounce cursor-pointer transition-all duration-500 hover:scale-150 hover:opacity-30 hover:rotate-45 hover:bg-gradient-to-r hover:from-pink-300 hover:to-yellow-300"></div>
<div className="absolute top-3/4 right-1/4 w-24 h-24 bg-white rounded-full animate-float animate-delay-500 hover:animate-wiggle cursor-pointer transition-all duration-500 hover:scale-125 hover:opacity-40 hover:rotate-90 hover:bg-gradient-to-l hover:from-blue-300 hover:to-green-300"></div>
<div className="absolute top-1/2 right-1/3 w-16 h-16 bg-white rounded-full animate-float animate-delay-1000 hover:animate-pulse cursor-pointer transition-all duration-500 hover:scale-200 hover:opacity-20 hover:rotate-180"></div>
<div className="absolute top-1/3 left-1/2 w-20 h-20 bg-white rounded-full animate-rotate-slow hover:animate-wiggle cursor-pointer transition-all duration-500 hover:scale-150 hover:animate-spin-slow"></div>
<div className="absolute bottom-1/4 left-1/3 w-28 h-28 bg-white rounded-full animate-pulse-subtle hover:animate-bounce cursor-pointer transition-all duration-500 hover:scale-125 hover:shadow-2xl hover:shadow-white/50"></div>
{/* Enhanced triangular shapes with gradient effects */}
<div className="absolute top-1/5 right-1/5 w-0 h-0 border-l-[20px] border-l-transparent border-r-[20px] border-r-transparent border-b-[35px] border-b-white animate-spin-slow hover:animate-bounce hover:border-b-yellow-300 cursor-pointer transition-all duration-700"></div>
<div className="absolute bottom-1/3 right-2/3 w-0 h-0 border-l-[15px] border-l-transparent border-r-[15px] border-r-transparent border-b-[25px] border-b-white animate-ping hover:animate-wiggle hover:border-b-pink-300 cursor-pointer transition-all duration-700"></div>
{/* New visually attractive hexagon shapes */}
<div className="absolute top-1/6 left-3/4 w-12 h-12 bg-gradient-to-r from-purple-300 to-pink-300 transform rotate-45 animate-spin-slow hover:animate-bounce cursor-pointer transition-all duration-700 opacity-60"></div>
<div className="absolute bottom-1/5 left-1/5 w-8 h-8 bg-gradient-to-l from-cyan-300 to-blue-300 transform rotate-12 animate-pulse hover:animate-wiggle cursor-pointer transition-all duration-700 opacity-70"></div>
{/* Animated diamond shapes */}
<div className="absolute top-2/3 left-2/3 w-6 h-6 bg-gradient-to-br from-yellow-300 to-orange-300 transform rotate-45 animate-bounce hover:animate-spin cursor-pointer transition-all duration-500 opacity-50"></div>
<div className="absolute top-1/5 left-1/6 w-10 h-10 bg-gradient-to-tr from-green-300 to-teal-300 transform rotate-45 animate-float hover:animate-pulse cursor-pointer transition-all duration-600 opacity-40"></div>
{/* Glowing orbs with pulsing effect */}
<div className="absolute top-1/2 left-1/6 w-20 h-20 bg-gradient-radial from-pink-400/50 to-transparent rounded-full animate-pulse hover:animate-ping cursor-pointer transition-all duration-500"></div>
<div className="absolute bottom-1/3 right-1/5 w-16 h-16 bg-gradient-radial from-cyan-400/40 to-transparent rounded-full animate-bounce hover:animate-pulse cursor-pointer transition-all duration-600"></div>
{/* Morphing blob shapes */}
<div className="absolute top-3/5 right-3/5 w-24 h-16 bg-gradient-to-r from-purple-300/30 to-pink-300/30 rounded-full animate-morph hover:animate-wobble cursor-pointer transition-all duration-700"></div>
<div className="absolute bottom-2/5 left-3/5 w-18 h-24 bg-gradient-to-l from-blue-300/25 to-cyan-300/25 rounded-full animate-morph-reverse hover:animate-wobble cursor-pointer transition-all duration-800"></div>
</div>
{/* Enhanced interactive particles with trail effects and new visual elements */}
<div className="absolute inset-0 pointer-events-none">
<div className="absolute top-1/6 left-1/6 w-2 h-2 bg-white rounded-full animate-pulse opacity-60 shadow-lg shadow-white/50"></div>
<div className="absolute top-2/3 left-3/4 w-1 h-1 bg-white rounded-full animate-pulse animate-delay-300 opacity-40 shadow-md shadow-white/30"></div>
<div className="absolute top-1/2 left-1/5 w-1.5 h-1.5 bg-white rounded-full animate-pulse animate-delay-700 opacity-50 shadow-lg shadow-white/40"></div>
<div className="absolute top-3/4 right-1/6 w-1 h-1 bg-white rounded-full animate-bounce opacity-70 shadow-sm shadow-white/60"></div>
<div className="absolute top-1/3 right-1/2 w-2 h-2 bg-white rounded-full animate-ping opacity-30 shadow-xl shadow-white/20"></div>
{/* Enhanced floating sparkles with different sizes and animations */}
<div className="absolute top-1/4 right-1/3 animate-float animate-delay-200">
<Sparkles className="w-4 h-4 text-white opacity-60 animate-pulse" />
</div>
<div className="absolute bottom-1/4 left-1/4 animate-float animate-delay-800">
<Star className="w-3 h-3 text-white opacity-50 animate-spin-slow" />
</div>
<div className="absolute top-1/5 left-2/5 animate-float animate-delay-1200">
<Sparkles className="w-2 h-2 text-yellow-300 opacity-70 animate-ping" />
</div>
<div className="absolute bottom-1/5 right-2/5 animate-float animate-delay-1600">
<Star className="w-5 h-5 text-pink-300 opacity-40 animate-pulse" />
</div>
{/* Constellation-like connected dots */}
<div className="absolute top-1/3 left-1/4 w-1 h-1 bg-white rounded-full opacity-60"></div>
<div className="absolute top-1/3 left-1/3 w-1 h-1 bg-white rounded-full opacity-60"></div>
<div className="absolute top-2/5 left-1/4 w-1 h-1 bg-white rounded-full opacity-60"></div>
{/* Connecting lines for constellation effect */}
<div className="absolute top-1/3 left-1/4 w-16 h-px bg-gradient-to-r from-white/30 to-transparent"></div>
<div className="absolute top-1/3 left-1/4 w-px h-8 bg-gradient-to-b from-white/30 to-transparent"></div>
{/* Floating geometric shapes as particles */}
<div className="absolute top-1/8 right-1/8 w-3 h-3 border border-white/40 rotate-45 animate-spin-slow opacity-50"></div>
<div className="absolute bottom-1/8 left-1/8 w-2 h-2 border border-white/30 rounded-full animate-pulse opacity-60"></div>
<div className="absolute top-5/6 right-5/6 w-4 h-1 bg-white/30 animate-float opacity-40"></div>
{/* Ripple effects */}
<div className="absolute top-1/2 left-1/2 w-32 h-32 border border-white/10 rounded-full animate-ping opacity-20"></div>
<div className="absolute top-1/4 right-1/4 w-24 h-24 border border-white/15 rounded-full animate-pulse opacity-25"></div>
<div className="absolute bottom-1/4 left-1/4 w-40 h-40 border border-white/8 rounded-full animate-ping animate-delay-1000 opacity-15"></div>
</div>
<div className="container px-4 md:px-6 text-center relative z-10">
<div className="max-w-3xl mx-auto space-y-4"> <div className="max-w-3xl mx-auto space-y-4">
<h1 className="text-4xl font-bold tracking-tighter sm:text-5xl md:text-6xl"> {/* Enhanced title with typing effect simulation */}
OpenLearnX Decentralized Adaptive Learning <h1 className="text-4xl font-bold tracking-tighter sm:text-5xl md:text-6xl animate-slide-in-up text-shimmer hover:animate-glow cursor-default transition-all duration-700 hover:scale-105 hover:rotate-1 hover:tracking-widest relative">
<span className="inline-block hover:animate-bounce hover:text-yellow-300 transition-all duration-300">O</span>
<span className="inline-block hover:animate-bounce hover:text-pink-300 transition-all duration-300 animation-delay-100">p</span>
<span className="inline-block hover:animate-bounce hover:text-blue-300 transition-all duration-300 animation-delay-200">e</span>
<span className="inline-block hover:animate-bounce hover:text-green-300 transition-all duration-300 animation-delay-300">n</span>
<span className="inline-block hover:animate-bounce hover:text-purple-300 transition-all duration-300 animation-delay-400">L</span>
<span className="inline-block hover:animate-bounce hover:text-red-300 transition-all duration-300 animation-delay-500">e</span>
<span className="inline-block hover:animate-bounce hover:text-indigo-300 transition-all duration-300 animation-delay-600">a</span>
<span className="inline-block hover:animate-bounce hover:text-orange-300 transition-all duration-300 animation-delay-700">r</span>
<span className="inline-block hover:animate-bounce hover:text-teal-300 transition-all duration-300 animation-delay-800">n</span>
<span className="inline-block hover:animate-bounce hover:text-cyan-300 transition-all duration-300 animation-delay-900">X</span>
<span className="ml-4 text-2xl"></span>
<span className="text-3xl sm:text-4xl md:text-5xl ml-2">Decentralized Adaptive Learning</span>
{/* Animated underline */}
<div className="absolute -bottom-2 left-0 right-0 h-1 bg-gradient-to-r from-transparent via-white to-transparent animate-pulse opacity-60"></div>
</h1> </h1>
<p className="text-lg md:text-xl">
<p className="text-lg md:text-xl animate-slide-in-up animate-delay-200 opacity-90 hover:opacity-100 transition-all duration-300 cursor-text hover:scale-105 hover:text-yellow-100 relative">
Unlock your potential with AI-powered adaptive learning, coding practice, and blockchain-secured Unlock your potential with AI-powered adaptive learning, coding practice, and blockchain-secured
credentials. credentials.
{/* Floating accent */}
<div className="absolute -top-2 -right-2 animate-bounce animate-delay-1000">
<Rocket className="w-5 h-5 opacity-70 animate-spin-slow" />
</div>
</p> </p>
<div className="flex flex-col gap-2 sm:flex-row justify-center">
<div className="flex flex-col gap-2 sm:flex-row justify-center animate-slide-in-up animate-delay-300">
<Link href="/courses"> <Link href="/courses">
<Button className="bg-white text-primary-purple hover:bg-gray-100 px-8 py-3 text-lg font-semibold rounded-full shadow-lg"> <Button className="bg-white text-primary-purple hover:bg-gray-100 px-8 py-3 text-lg font-semibold rounded-full shadow-lg btn-animated hover-lift hover-glow transform transition-all duration-300 group cursor-pointer hover:shadow-2xl hover:animate-bounce-in hover:scale-110 hover:rotate-3 hover:bg-gradient-to-r hover:from-white hover:to-gray-100 relative overflow-hidden">
Start Learning {/* Button shine effect */}
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent transition-transform duration-700"></div>
<span className="group-hover:translate-x-1 transition-transform duration-200 group-hover:font-bold relative z-10">Start Learning</span>
<Zap className="ml-2 h-5 w-5 group-hover:rotate-12 group-hover:animate-pulse group-hover:text-yellow-500 transition-all duration-300 relative z-10" />
{/* Floating particles on hover */}
<div className="absolute -top-1 -right-1 opacity-0 group-hover:opacity-100 group-hover:animate-ping transition-all duration-300">
<div className="w-2 h-2 bg-primary-purple rounded-full"></div>
</div>
</Button> </Button>
</Link> </Link>
</div> </div>
</div> </div>
</div> </div>
{/* Enhanced animated gradient overlay with dynamic effects */}
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent animate-gradient opacity-30 hover:opacity-50 transition-opacity duration-500"></div>
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-black/10 animate-pulse"></div>
{/* Enhanced animated wave effect at bottom with more visual appeal */}
<div className="absolute bottom-0 left-0 right-0 h-20 bg-gradient-to-t from-gray-50 dark:from-gray-900 via-transparent to-transparent opacity-80"></div>
{/* Additional floating elements for visual richness */}
<div className="absolute inset-0 opacity-5">
{/* Animated grid pattern */}
<div className="absolute top-0 left-0 w-full h-full bg-grid-pattern animate-grid-float"></div>
{/* Flowing lines */}
<div className="absolute top-1/4 left-0 w-full h-px bg-gradient-to-r from-transparent via-white/20 to-transparent animate-flow-right"></div>
<div className="absolute top-3/4 left-0 w-full h-px bg-gradient-to-r from-transparent via-white/15 to-transparent animate-flow-left"></div>
<div className="absolute top-1/2 left-0 w-full h-px bg-gradient-to-r from-transparent via-white/10 to-transparent animate-flow-right animate-delay-1000"></div>
</div>
</section> </section>
<section className="w-full py-12 md:py-24 lg:py-32 bg-gray-50 dark:bg-gray-900"> {/* Features Section with Enhanced Size and Better Space Utilization */}
<div className="container px-4 md:px-6"> <section className="w-full py-16 md:py-28 lg:py-36 bg-gray-50 dark:bg-gray-900 animate-fade-in animate-delay-500 relative overflow-hidden">
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3"> {/* Enhanced background pattern animation with more visual elements */}
<Card className="bg-white shadow-md rounded-lg p-6 dark:bg-gray-800 dark:text-gray-100"> <div className="absolute inset-0 opacity-5">
<CardHeader className="flex flex-row items-center space-x-4 pb-2"> <div className="absolute inset-0 bg-gradient-to-r from-primary-blue/10 via-transparent to-primary-purple/10 animate-gradient-slow"></div>
<BookOpen className="h-8 w-8 text-primary-blue" />
<CardTitle className="text-xl font-semibold">Interactive Courses</CardTitle> {/* Additional floating background elements */}
<div className="absolute top-1/4 left-1/4 w-64 h-64 bg-gradient-radial from-purple-200/20 to-transparent rounded-full animate-float-slow"></div>
<div className="absolute bottom-1/4 right-1/4 w-48 h-48 bg-gradient-radial from-blue-200/15 to-transparent rounded-full animate-float-slow animate-delay-2000"></div>
<div className="absolute top-3/4 left-3/4 w-32 h-32 bg-gradient-radial from-pink-200/25 to-transparent rounded-full animate-pulse-subtle"></div>
{/* Geometric pattern overlay */}
<div className="absolute inset-0 bg-hex-pattern opacity-10 animate-pattern-shift"></div>
</div>
{/* Wider container for better space utilization */}
<div className="max-w-8xl mx-auto px-6 md:px-8 lg:px-12 relative z-10">
<div className="text-center mb-16 animate-slide-in-up animate-delay-700">
<h2 className="text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl mb-6 text-gradient hover:animate-wiggle cursor-default transition-all duration-500 hover:scale-105 relative">
Powerful Features
{/* Animated accent lines */}
<div className="absolute -top-4 left-1/2 transform -translate-x-1/2 w-16 h-1 bg-gradient-to-r from-primary-blue to-primary-purple animate-pulse"></div>
<div className="absolute -bottom-4 left-1/2 transform -translate-x-1/2 w-24 h-1 bg-gradient-to-r from-primary-purple to-primary-blue animate-pulse animate-delay-500"></div>
</h2>
<p className="text-xl text-muted-foreground max-w-3xl mx-auto hover:text-foreground transition-colors duration-300 cursor-text hover:scale-105 animate-fade-in animate-delay-1000">
Discover the tools and technologies that make OpenLearnX the ultimate learning platform
</p>
</div>
{/* Enhanced grid with larger cards and better spacing */}
<div className="grid gap-10 md:gap-12 lg:gap-14 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-3 stagger-container">
{/* Interactive Courses Card - Enhanced Size */}
<Card className="bg-white shadow-xl rounded-2xl p-8 md:p-10 dark:bg-gray-800 dark:text-gray-100 card-interactive hover-lift group transform transition-all duration-500 hover:shadow-2xl border-0 hover:border-2 hover:border-primary/30 cursor-pointer hover:rotate-1 hover:-translate-y-4 animate-slide-in-left animate-delay-300 relative overflow-hidden min-h-[320px] md:min-h-[360px]">
{/* Card background gradient on hover */}
<div className="absolute inset-0 bg-gradient-to-br from-primary-blue/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-2xl"></div>
<CardHeader className="flex flex-col items-center text-center space-y-6 pb-6 relative z-10">
<div className="p-4 bg-primary-blue/10 rounded-2xl group-hover:bg-primary-blue/20 transition-all duration-500 group-hover:rotate-12 group-hover:scale-110 cursor-pointer group-hover:shadow-2xl group-hover:shadow-primary-blue/30">
<BookOpen className="h-12 w-12 md:h-14 md:w-14 text-primary-blue group-hover:scale-125 group-hover:animate-pulse transition-all duration-500" />
{/* Floating sparkle effect */}
<div className="absolute -top-2 -right-2 opacity-0 group-hover:opacity-100 group-hover:animate-ping transition-all duration-300">
<Sparkles className="w-4 h-4 text-primary-blue" />
</div>
</div>
<CardTitle className="text-2xl md:text-3xl font-bold group-hover:text-primary-blue transition-all duration-500 group-hover:tracking-wide cursor-pointer group-hover:translate-y-1">
Interactive Courses
</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="text-gray-600 dark:text-gray-300"> <CardContent className="text-gray-600 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-all duration-300 relative z-10 group-hover:translate-y-2 text-center text-lg leading-relaxed">
Engage with rich multimedia content, track your progress, and master new subjects at your own pace. Engage with rich multimedia content, track your progress, and master new subjects at your own pace with our comprehensive learning platform.
</CardContent> </CardContent>
{/* Hover shine effect */}
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-white/10 to-transparent transition-transform duration-1000 rounded-2xl"></div>
</Card> </Card>
<Card className="bg-white shadow-md rounded-lg p-6 dark:bg-gray-800 dark:text-gray-100">
<CardHeader className="flex flex-row items-center space-x-4 pb-2"> {/* Coding Practice Card - Enhanced Size */}
<Code className="h-8 w-8 text-primary-purple" /> <Card className="bg-white shadow-xl rounded-2xl p-8 md:p-10 dark:bg-gray-800 dark:text-gray-100 card-interactive hover-lift group transform transition-all duration-500 hover:shadow-2xl border-0 hover:border-2 hover:border-primary/30 cursor-pointer hover:-rotate-1 hover:-translate-y-4 animate-slide-in-up animate-delay-500 relative overflow-hidden min-h-[320px] md:min-h-[360px]">
<CardTitle className="text-xl font-semibold">LeetCode-Style Coding Practice</CardTitle> <div className="absolute inset-0 bg-gradient-to-br from-primary-purple/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-2xl"></div>
<CardHeader className="flex flex-col items-center text-center space-y-6 pb-6 relative z-10">
<div className="p-4 bg-primary-purple/10 rounded-2xl group-hover:bg-primary-purple/20 transition-all duration-500 group-hover:-rotate-12 group-hover:scale-110 cursor-pointer group-hover:shadow-2xl group-hover:shadow-primary-purple/30 relative">
<Code className="h-12 w-12 md:h-14 md:w-14 text-primary-purple group-hover:scale-125 group-hover:animate-bounce transition-all duration-500" />
{/* Code brackets animation */}
<div className="absolute -left-3 top-1/2 transform -translate-y-1/2 opacity-0 group-hover:opacity-100 group-hover:animate-pulse transition-all duration-300 text-primary-purple font-bold text-lg">{'<'}</div>
<div className="absolute -right-3 top-1/2 transform -translate-y-1/2 opacity-0 group-hover:opacity-100 group-hover:animate-pulse transition-all duration-300 text-primary-purple font-bold text-lg">{'>'}</div>
</div>
<CardTitle className="text-2xl md:text-3xl font-bold group-hover:text-primary-purple transition-all duration-500 group-hover:tracking-wide cursor-pointer group-hover:translate-y-1">
LeetCode-Style Coding Practice
</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="text-gray-600 dark:text-gray-300"> <CardContent className="text-gray-600 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-all duration-300 relative z-10 group-hover:translate-y-2 text-center text-lg leading-relaxed">
Sharpen your coding skills with interactive problems, instant feedback, and a built-in code editor. Sharpen your coding skills with interactive problems, instant feedback, and a built-in code editor designed for optimal learning.
</CardContent> </CardContent>
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-white/10 to-transparent transition-transform duration-1000 rounded-2xl"></div>
</Card> </Card>
<Card className="bg-white shadow-md rounded-lg p-6 dark:bg-gray-800 dark:text-gray-100">
<CardHeader className="flex flex-row items-center space-x-4 pb-2"> {/* Advanced Quiz Platform Card - Enhanced Size */}
<Lightbulb className="h-8 w-8 text-primary-blue" /> <Card className="bg-white shadow-xl rounded-2xl p-8 md:p-10 dark:bg-gray-800 dark:text-gray-100 card-interactive hover-lift group transform transition-all duration-500 hover:shadow-2xl border-0 hover:border-2 hover:border-primary/30 cursor-pointer hover:rotate-1 hover:-translate-y-4 animate-slide-in-right animate-delay-700 relative overflow-hidden min-h-[320px] md:min-h-[360px]">
<CardTitle className="text-xl font-semibold">Advanced Quiz Platform</CardTitle> <div className="absolute inset-0 bg-gradient-to-br from-yellow-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-2xl"></div>
<CardHeader className="flex flex-col items-center text-center space-y-6 pb-6 relative z-10">
<div className="p-4 bg-primary-blue/10 rounded-2xl group-hover:bg-yellow-500/20 transition-all duration-500 group-hover:rotate-45 group-hover:scale-110 cursor-pointer group-hover:shadow-2xl group-hover:shadow-yellow-500/30 relative">
<Lightbulb className="h-12 w-12 md:h-14 md:w-14 text-primary-blue group-hover:scale-125 group-hover:animate-pulse-subtle transition-all duration-500 group-hover:text-yellow-500" />
{/* Lightbulb glow effect */}
<div className="absolute inset-0 bg-yellow-400 rounded-2xl opacity-0 group-hover:opacity-20 group-hover:animate-ping transition-all duration-500"></div>
</div>
<CardTitle className="text-2xl md:text-3xl font-bold group-hover:text-yellow-600 transition-all duration-500 group-hover:tracking-wide cursor-pointer group-hover:translate-y-1">
Advanced Quiz Platform
</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="text-gray-600 dark:text-gray-300"> <CardContent className="text-gray-600 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-all duration-300 relative z-10 group-hover:translate-y-2 text-center text-lg leading-relaxed">
Test your knowledge with timed multiple-choice quizzes, detailed explanations, and performance tracking. Test your knowledge with timed multiple-choice quizzes, detailed explanations, and comprehensive performance tracking analytics.
</CardContent> </CardContent>
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-yellow-300/10 to-transparent transition-transform duration-1000 rounded-2xl"></div>
</Card> </Card>
<Card className="bg-white shadow-md rounded-lg p-6 dark:bg-gray-800 dark:text-gray-100">
<CardHeader className="flex flex-row items-center space-x-4 pb-2"> {/* AI-powered Learning Card - Enhanced Size */}
<Brain className="h-8 w-8 text-primary-purple" /> <Card className="bg-white shadow-xl rounded-2xl p-8 md:p-10 dark:bg-gray-800 dark:text-gray-100 card-interactive hover-lift group transform transition-all duration-500 hover:shadow-2xl border-0 hover:border-2 hover:border-primary/30 cursor-pointer hover:-rotate-1 hover:-translate-y-4 animate-slide-in-left animate-delay-900 relative overflow-hidden min-h-[320px] md:min-h-[360px]">
<CardTitle className="text-xl font-semibold">AI-powered Adaptive Learning</CardTitle> <div className="absolute inset-0 bg-gradient-to-br from-pink-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-2xl"></div>
<CardHeader className="flex flex-col items-center text-center space-y-6 pb-6 relative z-10">
<div className="p-4 bg-primary-purple/10 rounded-2xl group-hover:bg-pink-500/20 transition-all duration-500 group-hover:rotate-12 group-hover:scale-110 cursor-pointer group-hover:shadow-2xl group-hover:shadow-pink-500/30 relative">
<Brain className="h-12 w-12 md:h-14 md:w-14 text-primary-purple group-hover:scale-125 group-hover:animate-glow transition-all duration-500 group-hover:text-pink-500" />
{/* Neural network visualization */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500">
<div className="absolute top-1 left-1 w-2 h-2 bg-pink-400 rounded-full animate-ping animation-delay-100"></div>
<div className="absolute bottom-1 right-1 w-2 h-2 bg-pink-400 rounded-full animate-ping animation-delay-300"></div>
<div className="absolute top-1/2 right-1 w-2 h-2 bg-pink-400 rounded-full animate-ping animation-delay-500"></div>
</div>
</div>
<CardTitle className="text-2xl md:text-3xl font-bold group-hover:text-pink-600 transition-all duration-500 group-hover:tracking-wide cursor-pointer group-hover:translate-y-1">
AI-powered Adaptive Learning
</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="text-gray-600 dark:text-gray-300"> <CardContent className="text-gray-600 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-all duration-300 relative z-10 group-hover:translate-y-2 text-center text-lg leading-relaxed">
Our platform intelligently adjusts content and question difficulty based on your performance. Our platform intelligently adjusts content and question difficulty based on your performance, ensuring optimal learning outcomes.
</CardContent> </CardContent>
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-pink-300/10 to-transparent transition-transform duration-1000 rounded-2xl"></div>
</Card> </Card>
<Card className="bg-white shadow-md rounded-lg p-6 dark:bg-gray-800 dark:text-gray-100">
<CardHeader className="flex flex-row items-center space-x-4 pb-2"> {/* Blockchain Credentials Card - Enhanced Size */}
<LinkIcon className="h-8 w-8 text-primary-blue" /> <Card className="bg-white shadow-xl rounded-2xl p-8 md:p-10 dark:bg-gray-800 dark:text-gray-100 card-interactive hover-lift group transform transition-all duration-500 hover:shadow-2xl border-0 hover:border-2 hover:border-primary/30 cursor-pointer hover:rotate-1 hover:-translate-y-4 animate-slide-in-up animate-delay-1100 relative overflow-hidden min-h-[320px] md:min-h-[360px]">
<CardTitle className="text-xl font-semibold">Blockchain-secured Credentials</CardTitle> <div className="absolute inset-0 bg-gradient-to-br from-green-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-2xl"></div>
<CardHeader className="flex flex-col items-center text-center space-y-6 pb-6 relative z-10">
<div className="p-4 bg-primary-blue/10 rounded-2xl group-hover:bg-green-500/20 transition-all duration-500 group-hover:-rotate-45 group-hover:scale-110 cursor-pointer group-hover:shadow-2xl group-hover:shadow-green-500/30 relative">
<LinkIcon className="h-12 w-12 md:h-14 md:w-14 text-primary-blue group-hover:scale-125 group-hover:rotate-180 transition-all duration-700 group-hover:text-green-500" />
{/* Chain link animation */}
<div className="absolute -top-3 -left-3 opacity-0 group-hover:opacity-100 group-hover:animate-bounce transition-all duration-300">
<div className="w-3 h-3 border-2 border-green-500 rounded-full"></div>
</div>
<div className="absolute -bottom-3 -right-3 opacity-0 group-hover:opacity-100 group-hover:animate-bounce animation-delay-200 transition-all duration-300">
<div className="w-3 h-3 border-2 border-green-500 rounded-full"></div>
</div>
</div>
<CardTitle className="text-2xl md:text-3xl font-bold group-hover:text-green-600 transition-all duration-500 group-hover:tracking-wide cursor-pointer group-hover:translate-y-1">
Blockchain-secured Credentials
</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="text-gray-600 dark:text-gray-300"> <CardContent className="text-gray-600 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-all duration-300 relative z-10 group-hover:translate-y-2 text-center text-lg leading-relaxed">
Your achievements are secured on the blockchain, providing verifiable and tamper-proof records. Your achievements are secured on the blockchain, providing verifiable and tamper-proof records that employers trust.
</CardContent> </CardContent>
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-green-300/10 to-transparent transition-transform duration-1000 rounded-2xl"></div>
</Card> </Card>
<Card className="bg-white shadow-md rounded-lg p-6 dark:bg-gray-800 dark:text-gray-100">
<CardHeader className="flex flex-row items-center space-x-4 pb-2"> {/* Personalized Dashboard Card - Enhanced Size - Spans full width on smaller screens */}
<Zap className="h-8 w-8 text-primary-purple" /> <Card className="bg-white shadow-xl rounded-2xl p-8 md:p-10 dark:bg-gray-800 dark:text-gray-100 card-interactive hover-lift group transform transition-all duration-500 hover:shadow-2xl border-0 hover:border-2 hover:border-primary/30 cursor-pointer hover:-rotate-1 hover:-translate-y-4 animate-slide-in-right animate-delay-1300 relative overflow-hidden min-h-[320px] md:min-h-[360px] md:col-span-2 lg:col-span-1">
<CardTitle className="text-xl font-semibold">Personalized Dashboard</CardTitle> <div className="absolute inset-0 bg-gradient-to-br from-yellow-300/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-2xl"></div>
<CardHeader className="flex flex-col items-center text-center space-y-6 pb-6 relative z-10">
<div className="p-4 bg-primary-purple/10 rounded-2xl group-hover:bg-yellow-300/20 transition-all duration-500 group-hover:rotate-180 group-hover:scale-110 cursor-pointer group-hover:shadow-2xl group-hover:shadow-yellow-300/30 relative">
<Zap className="h-12 w-12 md:h-14 md:w-14 text-primary-purple group-hover:scale-125 group-hover:animate-glow transition-all duration-500 group-hover:text-yellow-500" />
{/* Electric effect */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div className="absolute top-0 left-1/2 w-px h-3 bg-yellow-400 animate-pulse"></div>
<div className="absolute bottom-0 left-1/2 w-px h-3 bg-yellow-400 animate-pulse animation-delay-200"></div>
<div className="absolute left-0 top-1/2 w-3 h-px bg-yellow-400 animate-pulse animation-delay-400"></div>
<div className="absolute right-0 top-1/2 w-3 h-px bg-yellow-400 animate-pulse animation-delay-600"></div>
</div>
</div>
<CardTitle className="text-2xl md:text-3xl font-bold group-hover:text-yellow-600 transition-all duration-500 group-hover:tracking-wide cursor-pointer group-hover:translate-y-1">
Personalized Dashboard
</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="text-gray-600 dark:text-gray-300"> <CardContent className="text-gray-600 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-all duration-300 relative z-10 group-hover:translate-y-2 text-center text-lg leading-relaxed">
Track your progress, identify strengths and weaknesses, and visualize your learning journey. Track your progress, identify strengths and weaknesses, and visualize your learning journey with intuitive analytics.
</CardContent> </CardContent>
<div className="absolute inset-0 -translate-x-full group-hover:translate-x-full bg-gradient-to-r from-transparent via-yellow-300/10 to-transparent transition-transform duration-1000 rounded-2xl"></div>
</Card> </Card>
</div> </div>
</div> </div>
</section> </section>
{/* Enhanced Call-to-Action Section with more dynamic effects */}
<section className="w-full py-16 bg-gradient-to-br from-primary-blue/5 via-purple-500/5 to-primary-purple/5 animate-fade-in animate-delay-1000 hover:from-primary-blue/10 hover:via-purple-500/10 hover:to-primary-purple/10 transition-all duration-700 cursor-default relative overflow-hidden">
{/* Enhanced animated background pattern with more visual elements */}
<div className="absolute inset-0 opacity-10">
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-primary-blue to-transparent animate-pulse"></div>
<div className="absolute bottom-0 right-0 w-full h-1 bg-gradient-to-l from-transparent via-primary-purple to-transparent animate-pulse animate-delay-500"></div>
{/* Additional decorative elements */}
<div className="absolute top-1/2 left-1/4 w-8 h-8 bg-gradient-to-r from-primary-blue/20 to-transparent rounded-full animate-ping"></div>
<div className="absolute top-1/3 right-1/4 w-6 h-6 bg-gradient-to-l from-primary-purple/20 to-transparent rounded-full animate-pulse animate-delay-700"></div>
<div className="absolute bottom-1/3 left-1/3 w-4 h-4 bg-gradient-to-br from-purple-400/20 to-transparent rounded-full animate-bounce"></div>
</div>
<div className="container px-4 md:px-6 text-center relative z-10">
<div className="max-w-2xl mx-auto space-y-6 animate-slide-in-up animate-delay-300">
<h3 className="text-2xl font-bold tracking-tight sm:text-3xl hover:animate-pulse cursor-default transition-all duration-500 hover:scale-105 hover:text-primary relative">
Ready to Transform Your Learning Journey?
{/* Floating question mark */}
<div className="absolute -top-2 -right-6 animate-bounce animate-delay-1000 opacity-60">
<div className="w-6 h-6 rounded-full bg-primary text-white flex items-center justify-center text-sm font-bold">?</div>
</div>
</h3>
<p className="text-lg text-muted-foreground hover:text-foreground transition-all duration-300 cursor-text hover:scale-105 animate-fade-in animate-delay-500">
Join thousands of learners who are already experiencing the future of education
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center animate-slide-in-up animate-delay-700">
<Link href="/courses">
<Button className="btn-animated hover-lift hover-glow px-8 py-3 text-lg font-semibold cursor-pointer hover:shadow-2xl hover:animate-wiggle transform transition-all duration-500 hover:scale-110 hover:rotate-1 relative overflow-hidden group">
{/* Ripple effect */}
<div className="absolute inset-0 bg-white opacity-0 group-hover:opacity-20 group-hover:animate-ping transition-all duration-300 rounded-lg"></div>
<span className="group-hover:tracking-wider transition-all duration-300 relative z-10">Explore Courses</span>
{/* Trailing sparkles */}
<div className="absolute -right-1 top-1/2 transform -translate-y-1/2 opacity-0 group-hover:opacity-100 group-hover:animate-bounce transition-all duration-300">
<Sparkles className="w-4 h-4" />
</div>
</Button>
</Link>
<Link href="/coding">
<Button variant="outline" className="btn-animated hover-lift px-8 py-3 text-lg font-semibold border-2 hover:border-primary cursor-pointer hover:shadow-xl hover:animate-bounce transform transition-all duration-500 hover:scale-110 hover:-rotate-1 hover:bg-primary hover:text-primary-foreground relative overflow-hidden group">
{/* Slide-in background */}
<div className="absolute inset-0 bg-primary transform -translate-x-full group-hover:translate-x-0 transition-transform duration-500"></div>
<span className="group-hover:tracking-wider transition-all duration-300 relative z-10">Try Coding Practice</span>
{/* Code symbol animation */}
<div className="absolute -left-1 top-1/2 transform -translate-y-1/2 opacity-0 group-hover:opacity-100 group-hover:animate-pulse transition-all duration-300 text-xs font-bold relative z-10">
{'</>'}
</div>
</Button>
</Link>
</div>
</div>
</div>
{/* Enhanced floating action elements */}
<div className="absolute top-1/4 left-1/4 animate-float animate-delay-2000 opacity-20">
<div className="w-8 h-8 border-2 border-primary rotate-45"></div>
</div>
<div className="absolute bottom-1/4 right-1/4 animate-float animate-delay-3000 opacity-20">
<div className="w-6 h-6 bg-primary-purple rounded-full"></div>
</div>
<div className="absolute top-1/2 right-1/6 animate-pulse opacity-15">
<div className="w-12 h-12 border border-primary-blue rounded-full"></div>
</div>
<div className="absolute bottom-1/6 left-1/6 animate-bounce opacity-25">
<div className="w-4 h-4 bg-gradient-to-r from-purple-400 to-pink-400 transform rotate-45"></div>
</div>
</section>
</div> </div>
) )
} }
+7 -7
View File
@@ -5,19 +5,19 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 transform hover:scale-105 active:scale-95",
{ {
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90", default: "bg-primary text-primary-foreground hover:bg-primary/90 hover:shadow-lg hover:shadow-primary/25",
destructive: destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90", "bg-destructive text-destructive-foreground hover:bg-destructive/90 hover:shadow-lg hover:shadow-destructive/25",
outline: outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground", "border border-input bg-background hover:bg-accent hover:text-accent-foreground hover:border-accent hover:shadow-md",
secondary: secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80", "bg-secondary text-secondary-foreground hover:bg-secondary/80 hover:shadow-md hover:shadow-secondary/25",
ghost: "hover:bg-accent hover:text-accent-foreground", ghost: "hover:bg-accent hover:text-accent-foreground hover:shadow-sm",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline hover:text-primary/80",
}, },
size: { size: {
default: "h-10 px-4 py-2", default: "h-10 px-4 py-2",
+8 -10
View File
@@ -41,7 +41,7 @@ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
const NavigationMenuItem = NavigationMenuPrimitive.Item const NavigationMenuItem = NavigationMenuPrimitive.Item
const navigationMenuTriggerStyle = cva( const navigationMenuTriggerStyle = cva(
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50" "group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-all duration-300 ease-in-out transform hover:bg-accent hover:text-accent-foreground hover:scale-105 hover:shadow-md focus:bg-accent focus:text-accent-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 active:scale-95 disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[active]:shadow-sm data-[state=open]:bg-accent/50 data-[state=open]:scale-105 data-[state=open]:shadow-lg"
) )
const NavigationMenuTrigger = React.forwardRef< const NavigationMenuTrigger = React.forwardRef<
@@ -55,7 +55,7 @@ const NavigationMenuTrigger = React.forwardRef<
> >
{children}{" "} {children}{" "}
<ChevronDown <ChevronDown
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180" className="relative top-[1px] ml-1 h-3 w-3 transition-all duration-300 ease-in-out group-data-[state=open]:rotate-180 group-hover:text-accent-foreground"
aria-hidden="true" aria-hidden="true"
/> />
</NavigationMenuPrimitive.Trigger> </NavigationMenuPrimitive.Trigger>
@@ -69,7 +69,7 @@ const NavigationMenuContent = React.forwardRef<
<NavigationMenuPrimitive.Content <NavigationMenuPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ", "left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:duration-300 data-[motion^=to-]:duration-200 md:absolute md:w-auto backdrop-blur-sm",
className className
)} )}
{...props} {...props}
@@ -86,7 +86,7 @@ const NavigationMenuViewport = React.forwardRef<
<div className={cn("absolute left-0 top-full flex justify-center")}> <div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport <NavigationMenuPrimitive.Viewport
className={cn( className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]", "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 data-[state=open]:fade-in data-[state=closed]:fade-out data-[state=open]:duration-300 data-[state=closed]:duration-200 data-[state=open]:ease-out data-[state=closed]:ease-in md:w-[var(--radix-navigation-menu-viewport-width)] backdrop-blur-md",
className className
)} )}
ref={ref} ref={ref}
@@ -94,8 +94,7 @@ const NavigationMenuViewport = React.forwardRef<
/> />
</div> </div>
)) ))
NavigationMenuViewport.displayName = NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName
NavigationMenuPrimitive.Viewport.displayName
const NavigationMenuIndicator = React.forwardRef< const NavigationMenuIndicator = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>, React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
@@ -104,16 +103,15 @@ const NavigationMenuIndicator = React.forwardRef<
<NavigationMenuPrimitive.Indicator <NavigationMenuPrimitive.Indicator
ref={ref} ref={ref}
className={cn( className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in", "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in data-[state=visible]:slide-in-from-top-1 data-[state=hidden]:slide-out-to-top-1 data-[state=visible]:duration-200 data-[state=hidden]:duration-150",
className className
)} )}
{...props} {...props}
> >
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" /> <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md transition-all duration-200 data-[state=visible]:shadow-lg" />
</NavigationMenuPrimitive.Indicator> </NavigationMenuPrimitive.Indicator>
)) ))
NavigationMenuIndicator.displayName = NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName
NavigationMenuPrimitive.Indicator.displayName
export { export {
navigationMenuTriggerStyle, navigationMenuTriggerStyle,