diff --git a/frontend/src/components/common/LoadingSkeleton.tsx b/frontend/src/components/common/LoadingSkeleton.tsx new file mode 100644 index 0000000..1e9518e --- /dev/null +++ b/frontend/src/components/common/LoadingSkeleton.tsx @@ -0,0 +1,240 @@ +import React from 'react'; +import { Box, Skeleton, Card, CardContent, Grid, useTheme, alpha } from '@mui/material'; + +/** + * Professional Loading Skeleton Components + * Provides smooth loading states instead of spinners + */ + +// Lab Card Skeleton +export const LabCardSkeleton: React.FC = () => { + const theme = useTheme(); + + return ( + + + + + + + + + + + + + + + ); +}; + +// Lab Grid Skeleton +export const LabGridSkeleton: React.FC<{ count?: number }> = ({ count = 6 }) => { + return ( + + {Array.from({ length: count }).map((_, index) => ( + + + + ))} + + ); +}; + +// Dashboard Widget Skeleton +export const DashboardWidgetSkeleton: React.FC = () => { + return ( + + + + + + + + + + + ); +}; + +// Table Row Skeleton +export const TableRowSkeleton: React.FC<{ columns?: number }> = ({ columns = 5 }) => { + return ( + + {Array.from({ length: columns }).map((_, index) => ( + + ))} + + ); +}; + +// Profile Skeleton +export const ProfileSkeleton: React.FC = () => { + return ( + + + + + + + + + + + + + + + + + + ); +}; + +// Stats Card Skeleton +export const StatsCardSkeleton: React.FC = () => { + return ( + + `linear-gradient(135deg, ${alpha(theme.palette.primary.main, 0.1)} 0%, ${alpha( + theme.palette.secondary.main, + 0.1 + )} 100%)`, + }} + > + + + + + + + + + + ); +}; + +// List Item Skeleton +export const ListItemSkeleton: React.FC<{ avatar?: boolean }> = ({ avatar = true }) => { + return ( + + {avatar && } + + + + + + + ); +}; + +// Form Skeleton +export const FormSkeleton: React.FC<{ fields?: number }> = ({ fields = 4 }) => { + return ( + + + + {Array.from({ length: fields }).map((_, index) => ( + + + + + ))} + + + + ); +}; + +// Progress Bar Skeleton +export const ProgressSkeleton: React.FC = () => { + return ( + + + + + + + + + ); +}; + +// Full Page Skeleton (combines multiple skeletons) +export const PageSkeleton: React.FC = () => { + return ( + + + + {[1, 2, 3, 4].map((i) => ( + + + + ))} + + + + ); +}; + +// Export all as a single object for convenience +export const LoadingSkeletons = { + LabCard: LabCardSkeleton, + LabGrid: LabGridSkeleton, + DashboardWidget: DashboardWidgetSkeleton, + TableRow: TableRowSkeleton, + Profile: ProfileSkeleton, + StatsCard: StatsCardSkeleton, + ListItem: ListItemSkeleton, + Form: FormSkeleton, + Progress: ProgressSkeleton, + Page: PageSkeleton, +}; + +export default LoadingSkeletons; diff --git a/frontend/src/components/labs/LabCard.tsx b/frontend/src/components/labs/LabCard.tsx index 48a65ca..84ed2c2 100644 --- a/frontend/src/components/labs/LabCard.tsx +++ b/frontend/src/components/labs/LabCard.tsx @@ -9,12 +9,16 @@ import { Box, Button, LinearProgress, + alpha, + useTheme, } from '@mui/material'; import { AccessTime, EmojiEvents, PlayArrow, Lock, + CheckCircle, + TrendingUp, } from '@mui/icons-material'; import { useNavigate } from 'react-router-dom'; import type { Lab, LabDifficulty } from '../../types'; @@ -23,12 +27,14 @@ interface LabCardProps { lab: Lab; progress?: number; isLocked?: boolean; + isNew?: boolean; + isFeatured?: boolean; } const DIFFICULTY_COLORS: Record = { - beginner: '#4caf50', - intermediate: '#ff9800', - advanced: '#f44336', + beginner: '#00ff88', + intermediate: '#ffb020', + advanced: '#ff5f56', expert: '#9c27b0', }; @@ -42,18 +48,28 @@ const CATEGORY_LABELS: Record = { }; /** - * LabCard Component - * Displays individual lab information with progress and actions + * Modern LabCard Component + * Features: Glassmorphism, neon borders, smooth animations, professional design */ -export const LabCard: React.FC = ({ lab, progress = 0, isLocked = false }) => { +export const LabCard: React.FC = ({ + lab, + progress = 0, + isLocked = false, + isNew = false, + isFeatured = false, +}) => { const navigate = useNavigate(); + const theme = useTheme(); const handleStartLab = () => { - navigate(`/labs/${lab.id}`); + if (!isLocked) { + navigate(`/labs/${lab.id}`); + } }; const difficultyColor = DIFFICULTY_COLORS[lab.difficulty]; const categoryLabel = CATEGORY_LABELS[lab.category] || lab.category; + const isCompleted = progress === 100; return ( = ({ lab, progress = 0, isLocked = display: 'flex', flexDirection: 'column', position: 'relative', - transition: 'transform 0.2s, box-shadow 0.2s', + overflow: 'hidden', + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + cursor: isLocked ? 'not-allowed' : 'pointer', + border: `2px solid ${isCompleted ? '#00ff88' : alpha(difficultyColor, 0.3)}`, + background: + theme.palette.mode === 'dark' + ? `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.9)} 0%, ${alpha( + theme.palette.background.default, + 0.9 + )} 100%)` + : theme.palette.background.paper, + backdropFilter: 'blur(10px)', + opacity: isLocked ? 0.6 : 1, '&:hover': { - transform: isLocked ? 'none' : 'translateY(-4px)', - boxShadow: isLocked ? 1 : 8, + transform: isLocked ? 'none' : 'translateY(-8px) scale(1.02)', + boxShadow: isLocked + ? theme.shadows[2] + : `0 12px 40px ${alpha(difficultyColor, 0.3)}, 0 0 20px ${alpha(difficultyColor, 0.2)}`, + border: `2px solid ${isLocked ? alpha(difficultyColor, 0.3) : difficultyColor}`, + }, + '&::before': !isLocked && { + content: '""', + position: 'absolute', + top: 0, + left: 0, + right: 0, + height: '4px', + background: `linear-gradient(90deg, ${difficultyColor}, ${alpha(difficultyColor, 0.5)})`, + opacity: 0, + transition: 'opacity 0.3s ease', + }, + '&:hover::before': !isLocked && { + opacity: 1, }, - opacity: isLocked ? 0.7 : 1, }} + onClick={handleStartLab} > - {/* Lab Image */} + {/* Status Badges */} + {(isNew || isFeatured || isCompleted) && ( + + {isNew && ( + + )} + {isFeatured && ( + + )} + {isCompleted && ( + } + label="COMPLETED" + size="small" + sx={{ + background: `linear-gradient(135deg, #00ff88 0%, #00cc6d 100%)`, + color: 'white', + fontWeight: 700, + fontSize: '0.65rem', + height: 22, + }} + /> + )} + + )} + + {/* Lab Image with Overlay Gradient */} {/* Difficulty Badge */} @@ -92,10 +208,14 @@ export const LabCard: React.FC = ({ lab, progress = 0, isLocked = position: 'absolute', top: 12, right: 12, - backgroundColor: difficultyColor, - color: 'white', - fontWeight: 'bold', - fontSize: '0.75rem', + zIndex: 2, + background: difficultyColor, + color: theme.palette.mode === 'dark' ? '#000' : '#fff', + fontWeight: 800, + fontSize: '0.7rem', + letterSpacing: '0.05em', + boxShadow: `0 4px 12px ${alpha(difficultyColor, 0.4)}`, + border: `2px solid ${theme.palette.mode === 'dark' ? '#000' : '#fff'}`, }} /> @@ -109,28 +229,63 @@ export const LabCard: React.FC = ({ lab, progress = 0, isLocked = right: 0, bottom: 0, display: 'flex', + flexDirection: 'column', alignItems: 'center', justifyContent: 'center', - backgroundColor: 'rgba(0, 0, 0, 0.6)', + background: 'rgba(0, 0, 0, 0.75)', + backdropFilter: 'blur(8px)', + zIndex: 3, }} > - + + + Complete previous labs to unlock + )} {/* Lab Info */} - - {/* Category */} + + {/* Category Badge */} {/* Lab Name */} - + {lab.name} @@ -146,25 +301,71 @@ export const LabCard: React.FC = ({ lab, progress = 0, isLocked = WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', minHeight: '2.8em', + lineHeight: 1.4, }} > {lab.description} - {/* Metadata */} - - - - + {/* Metadata with Icons */} + + + + {lab.estimatedTime}min - - - + + + {lab.points} pts + {progress > 0 && progress < 100 && ( + + + + {progress}% done + + + )} {/* Tags */} @@ -174,28 +375,41 @@ export const LabCard: React.FC = ({ lab, progress = 0, isLocked = key={tag} label={tag} size="small" - variant="outlined" - sx={{ fontSize: '0.65rem', height: 20 }} + sx={{ + fontSize: '0.65rem', + height: 22, + background: alpha(theme.palette.primary.main, 0.08), + color: theme.palette.primary.main, + fontWeight: 600, + '&:hover': { + background: alpha(theme.palette.primary.main, 0.15), + }, + }} /> ))} {lab.tags.length > 3 && ( )} {/* Progress Bar */} - {progress > 0 && ( + {progress > 0 && progress < 100 && ( - + Progress - + {progress}% @@ -203,12 +417,12 @@ export const LabCard: React.FC = ({ lab, progress = 0, isLocked = variant="determinate" value={progress} sx={{ - height: 6, - borderRadius: 3, - backgroundColor: 'grey.200', + height: 8, + borderRadius: 4, + background: alpha(theme.palette.primary.main, 0.1), '& .MuiLinearProgress-bar': { - borderRadius: 3, - backgroundColor: progress === 100 ? '#4caf50' : '#2196f3', + borderRadius: 4, + background: `linear-gradient(90deg, ${theme.palette.primary.main}, ${theme.palette.secondary.main})`, }, }} /> @@ -220,21 +434,34 @@ export const LabCard: React.FC = ({ lab, progress = 0, isLocked = diff --git a/frontend/src/components/labs/Terminal.tsx b/frontend/src/components/labs/Terminal.tsx new file mode 100644 index 0000000..d43a2cf --- /dev/null +++ b/frontend/src/components/labs/Terminal.tsx @@ -0,0 +1,366 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { Box, IconButton, Typography, Chip, alpha, useTheme } from '@mui/material'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import FullscreenIcon from '@mui/icons-material/Fullscreen'; +import CloseIcon from '@mui/icons-material/Close'; + +/** + * Professional Terminal Component + * Features: Command history, syntax highlighting, copy functionality + * Note: In production, integrate with xterm.js for full terminal emulation + */ + +interface TerminalProps { + labId?: string; + instanceId?: string; + onClose?: () => void; +} + +const Terminal: React.FC = ({ labId, instanceId, onClose }) => { + const theme = useTheme(); + const terminalRef = useRef(null); + const inputRef = useRef(null); + + const [output, setOutput] = useState>([ + { type: 'output', text: '╔════════════════════════════════════════════════════════════════╗' }, + { type: 'output', text: '║ AURON SECURITY LAB - TERMINAL v2.0.0 ║' }, + { type: 'output', text: '╚════════════════════════════════════════════════════════════════╝' }, + { type: 'output', text: '' }, + { type: 'output', text: '🔒 Secure connection established...' }, + { type: 'output', text: '📡 Connected to lab environment' }, + { type: 'output', text: `🆔 Lab ID: ${labId || 'sandbox-001'}` }, + { type: 'output', text: '' }, + { type: 'output', text: 'Type "help" for available commands or "exit" to disconnect.' }, + { type: 'output', text: '' }, + ]); + + const [currentInput, setCurrentInput] = useState(''); + const [commandHistory, setCommandHistory] = useState([]); + const [historyIndex, setHistoryIndex] = useState(-1); + const [isConnected, setIsConnected] = useState(true); + + // Available commands (demo - replace with real shell commands in production) + const commands: Record string[]> = { + help: () => [ + 'Available commands:', + ' help - Show this help message', + ' ls - List files and directories', + ' pwd - Print working directory', + ' whoami - Display current user', + ' cat - Display file contents', + ' clear - Clear terminal', + ' date - Show current date and time', + ' uname - Show system information', + ' exit - Disconnect from terminal', + '', + 'Note: This is a demo terminal. In production, this connects to a real shell.', + ], + ls: () => [ + 'drwxr-xr-x 5 auron auron 160 Nov 18 12:34 .', + 'drwxr-xr-x 8 auron auron 256 Nov 17 09:15 ..', + '-rw-r--r-- 1 auron auron 1234 Nov 18 11:20 exploit.py', + '-rw-r--r-- 1 auron auron 456 Nov 18 10:15 notes.txt', + 'drwxr-xr-x 3 auron auron 96 Nov 17 14:22 payloads', + '-rwxr-xr-x 1 auron auron 2048 Nov 18 12:34 scan.sh', + ], + pwd: () => ['/home/auron/lab'], + whoami: () => ['auron'], + date: () => [new Date().toString()], + uname: () => ['Linux auron-lab 5.15.0-1044-aws #49-Ubuntu SMP Thu Oct 6 02:08:18 UTC 2023 x86_64 GNU/Linux'], + clear: () => [], + cat: (args) => { + if (args.length === 0) return ['cat: missing file operand']; + if (args[0] === 'notes.txt') { + return [ + '=== Security Lab Notes ===', + '', + '1. Check for SQL injection vulnerabilities', + '2. Test XSS on input fields', + '3. Enumerate open ports: nmap -sV target', + '4. Try default credentials', + '', + '⚠️ Remember to log all findings!', + ]; + } + return [`cat: ${args[0]}: No such file or directory`]; + }, + }; + + const executeCommand = (cmd: string) => { + const trimmed = cmd.trim(); + if (!trimmed) return; + + // Add to history + setCommandHistory((prev) => [...prev, trimmed]); + setHistoryIndex(-1); + + // Add command to output + setOutput((prev) => [...prev, { type: 'input', text: `$ ${trimmed}` }]); + + // Handle exit + if (trimmed === 'exit') { + setOutput((prev) => [ + ...prev, + { type: 'output', text: 'Connection closed. Goodbye!' }, + ]); + setIsConnected(false); + setTimeout(() => onClose?.(), 1500); + return; + } + + // Handle clear + if (trimmed === 'clear') { + setOutput([]); + return; + } + + // Parse command and arguments + const parts = trimmed.split(' '); + const command = parts[0]; + const args = parts.slice(1); + + // Execute command + if (commands[command]) { + const result = commands[command](args); + setOutput((prev) => [ + ...prev, + ...result.map((line) => ({ type: 'output' as const, text: line })), + ]); + } else { + setOutput((prev) => [ + ...prev, + { type: 'error', text: `Command not found: ${command}` }, + { type: 'output', text: 'Type "help" for available commands.' }, + ]); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + executeCommand(currentInput); + setCurrentInput(''); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + if (commandHistory.length > 0) { + const newIndex = historyIndex < commandHistory.length - 1 ? historyIndex + 1 : historyIndex; + setHistoryIndex(newIndex); + setCurrentInput(commandHistory[commandHistory.length - 1 - newIndex]); + } + } else if (e.key === 'ArrowDown') { + e.preventDefault(); + if (historyIndex > 0) { + const newIndex = historyIndex - 1; + setHistoryIndex(newIndex); + setCurrentInput(commandHistory[commandHistory.length - 1 - newIndex]); + } else if (historyIndex === 0) { + setHistoryIndex(-1); + setCurrentInput(''); + } + } + }; + + const copyToClipboard = () => { + const text = output.map((line) => line.text).join('\n'); + navigator.clipboard.writeText(text); + }; + + const handleRefresh = () => { + setOutput([ + { type: 'output', text: 'Reconnecting to lab environment...' }, + { type: 'output', text: '✓ Connection re-established' }, + { type: 'output', text: '' }, + ]); + setIsConnected(true); + }; + + // Auto-scroll to bottom + useEffect(() => { + if (terminalRef.current) { + terminalRef.current.scrollTop = terminalRef.current.scrollHeight; + } + }, [output]); + + // Focus input on mount + useEffect(() => { + inputRef.current?.focus(); + }, []); + + return ( + + {/* Terminal Header */} + + + + + + + auron@lab:~ + + + + + + + + + + + + + + + {onClose && ( + + + + )} + + + + {/* Terminal Output */} + + {output.map((line, index) => ( + + {line.text} + + ))} + + {/* Input Line */} + {isConnected && ( + + $ + setCurrentInput(e.target.value)} + onKeyDown={handleKeyDown} + style={{ + flex: 1, + background: 'transparent', + border: 'none', + outline: 'none', + color: '#ffffff', + fontFamily: 'inherit', + fontSize: 'inherit', + caretColor: '#00ff41', + }} + autoFocus + /> + + + )} + + + ); +}; + +export default Terminal; diff --git a/frontend/src/components/layout/AppBar.tsx b/frontend/src/components/layout/AppBar.tsx index 43b2bc7..1a09a75 100644 --- a/frontend/src/components/layout/AppBar.tsx +++ b/frontend/src/components/layout/AppBar.tsx @@ -1,8 +1,24 @@ -import { AppBar as MuiAppBar, Toolbar, IconButton, Typography, Box, Avatar, Menu, MenuItem } from '@mui/material'; +import { + AppBar as MuiAppBar, + Toolbar, + IconButton, + Typography, + Box, + Avatar, + Menu, + MenuItem, + alpha, + useTheme, + Fade, + Slide, + Badge, +} from '@mui/material'; import MenuIcon from '@mui/icons-material/Menu'; import AccountCircleIcon from '@mui/icons-material/AccountCircle'; import LogoutIcon from '@mui/icons-material/Logout'; -import { useState } from 'react'; +import NotificationsIcon from '@mui/icons-material/Notifications'; +import SettingsIcon from '@mui/icons-material/Settings'; +import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAppDispatch, useAppSelector } from '@hooks/redux'; import { logout } from '@features/auth/authSlice'; @@ -12,12 +28,27 @@ interface AppBarProps { handleDrawerToggle: () => void; } +/** + * Modern AppBar Component + * Features: Glassmorphism, scroll elevation, smooth animations + */ export default function AppBar({ drawerWidth, handleDrawerToggle }: AppBarProps): JSX.Element { const [anchorEl, setAnchorEl] = useState(null); + const [scrolled, setScrolled] = useState(false); const dispatch = useAppDispatch(); const navigate = useNavigate(); + const theme = useTheme(); const { user } = useAppSelector((state) => state.auth); + // Handle scroll effect + useEffect(() => { + const handleScroll = () => { + setScrolled(window.scrollY > 20); + }; + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + const handleMenu = (event: React.MouseEvent): void => { setAnchorEl(event.currentTarget); }; @@ -37,46 +68,253 @@ export default function AppBar({ drawerWidth, handleDrawerToggle }: AppBarProps) handleClose(); }; + const handleSettings = (): void => { + navigate('/settings'); + handleClose(); + }; + return ( - - - - - - - Auron Security Platform - - - - {user?.avatar ? ( - - ) : ( - - )} + + + + + - - - - Profile - - - - Logout - - - - - + + {/* Logo and Title */} + navigate('/dashboard')} + > + + A + + + Auron Security + + + + {/* Action Icons */} + + {/* Notifications */} + + + + + + + {/* User Menu */} + + {user?.avatar ? ( + + ) : ( + + )} + + + + + + + + Profile + + + View your profile + + + + + + + + Settings + + + Manage preferences + + + + + + + + Logout + + + Sign out of your account + + + + + + + + ); } diff --git a/frontend/src/components/progress/ProgressDashboard.tsx b/frontend/src/components/progress/ProgressDashboard.tsx index 8ffb36a..67bfce8 100644 --- a/frontend/src/components/progress/ProgressDashboard.tsx +++ b/frontend/src/components/progress/ProgressDashboard.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Grid, @@ -9,6 +9,11 @@ import { Card, CardContent, Avatar, + Button, + alpha, + useTheme, + Fade, + Grow, } from '@mui/material'; import { EmojiEvents, @@ -17,9 +22,19 @@ import { AccessTime, Lightbulb, Star, + PlayArrow, + Assessment, + Speed, + FireIcon, + LocalFireDepartment, + Explore, + Settings, + BookmarkBorder, } from '@mui/icons-material'; import { useDispatch, useSelector } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; import { fetchUserProgress } from '@features/progress/progressSlice'; +import { StatsCardSkeleton } from '@components/common/LoadingSkeleton'; import type { RootState, AppDispatch } from '../../store'; interface StatCardProps { @@ -27,44 +42,214 @@ interface StatCardProps { value: string | number; icon: React.ReactElement; color?: string; + gradient?: string; subtitle?: string; + trend?: number; + delay?: number; } -const StatCard: React.FC = ({ title, value, icon, color = 'primary.main', subtitle }) => { +/** + * Modern Stat Card with Glassmorphism and Animations + */ +const StatCard: React.FC = ({ + title, + value, + icon, + color = 'primary.main', + gradient, + subtitle, + trend, + delay = 0, +}) => { + const theme = useTheme(); + return ( - - - - - - {title} - - - {value} - - {subtitle && ( - - {subtitle} + + + + + + + {title} + + + {value} - )} + {subtitle && ( + + {subtitle} + + )} + {trend && ( + + 0 ? 'success.main' : 'error.main', + transform: trend < 0 ? 'rotate(180deg)' : 'none', + }} + /> + 0 ? 'success.main' : 'error.main', + }} + > + {trend > 0 ? '+' : ''} + {trend}% this week + + + )} + + + {icon} + - - {icon} - - - - + + + + ); +}; + +/** + * Quick Action Card Component + */ +interface QuickActionProps { + title: string; + description: string; + icon: React.ReactElement; + color: string; + onClick: () => void; + delay?: number; +} + +const QuickActionCard: React.FC = ({ + title, + description, + icon, + color, + onClick, + delay = 0, +}) => { + const theme = useTheme(); + + return ( + + + + + {icon} + + + {title} + + + {description} + + + + + + ); }; /** - * ProgressDashboard Component - * Displays user progress statistics and achievements + * Modern ProgressDashboard Component + * Enhanced with glassmorphism, animations, and modern widgets */ export const ProgressDashboard: React.FC = () => { const dispatch = useDispatch(); + const navigate = useNavigate(); + const theme = useTheme(); const { user } = useSelector((state: RootState) => state.auth); const { stats, isLoading } = useSelector((state: RootState) => state.progress); + const [showWelcome, setShowWelcome] = useState(true); useEffect(() => { if (user?.id) { @@ -72,8 +257,17 @@ export const ProgressDashboard: React.FC = () => { } }, [dispatch, user]); - // Mock data for demonstration (stats is unknown type, so we provide defaults) - const statsData = stats as { completedLabs?: number; totalPoints?: number; rank?: number; timeSpent?: number; hintsUsed?: number; streak?: number } | null; + // Mock data for demonstration + const statsData = stats as + | { + completedLabs?: number; + totalPoints?: number; + rank?: number; + timeSpent?: number; + hintsUsed?: number; + streak?: number; + } + | null; const completedLabs = statsData?.completedLabs || 8; const totalLabs = 25; const totalPoints = statsData?.totalPoints || 1250; @@ -84,26 +278,96 @@ export const ProgressDashboard: React.FC = () => { const completionPercentage = (completedLabs / totalLabs) * 100; const hoursSpent = Math.floor(timeSpent / 60); + const currentHour = new Date().getHours(); + const greeting = currentHour < 12 ? 'Good Morning' : currentHour < 18 ? 'Good Afternoon' : 'Good Evening'; if (isLoading) { return ( - - + + + {[1, 2, 3, 4].map((i) => ( + + + + ))} + ); } return ( - {/* Header */} - - - Your Progress - - - Track your learning journey and achievements - - + {/* Welcome Banner */} + {showWelcome && ( + + + + + + + {greeting}, {user?.username || 'Hacker'}! 👋 + + + Ready to level up your cybersecurity skills? + + + } + label={`${streak} Day Streak`} + sx={{ + backgroundColor: alpha('#ffffff', 0.2), + color: 'white', + fontWeight: 700, + backdropFilter: 'blur(10px)', + }} + /> + } + label={`Rank #${rank}`} + sx={{ + backgroundColor: alpha('#ffffff', 0.2), + color: 'white', + fontWeight: 700, + backdropFilter: 'blur(10px)', + }} + /> + } + label={`${totalPoints.toLocaleString()} Points`} + sx={{ + backgroundColor: alpha('#ffffff', 0.2), + color: 'white', + fontWeight: 700, + backdropFilter: 'blur(10px)', + }} + /> + + + + + + + )} {/* Stats Grid */} @@ -111,8 +375,11 @@ export const ProgressDashboard: React.FC = () => { } - color="#f57c00" + icon={} + color="#ffb020" + gradient="linear-gradient(135deg, #ffb020 0%, #ff8c00 100%)" + trend={12} + delay={0} /> @@ -120,190 +387,400 @@ export const ProgressDashboard: React.FC = () => { title="Labs Completed" value={completedLabs} subtitle={`of ${totalLabs} total`} - icon={} - color="#4caf50" + icon={} + color="#00ff88" + gradient="linear-gradient(135deg, #00ff88 0%, #00cc6d 100%)" + trend={8} + delay={100} /> } - color="#2196f3" + icon={} + color="#42a5f5" + gradient="linear-gradient(135deg, #42a5f5 0%, #1976d2 100%)" + trend={-2} + delay={200} /> } - color="#ff9800" + value={`${streak}`} + subtitle="days in a row" + icon={} + color="#ff3864" + gradient="linear-gradient(135deg, #ff3864 0%, #cc2d50 100%)" + trend={5} + delay={300} /> + {/* Quick Actions */} + + + + Quick Actions + + + + } + color={theme.palette.primary.main} + onClick={() => navigate('/labs')} + delay={0} + /> + + + } + color="#00ff88" + onClick={() => navigate('/labs')} + delay={100} + /> + + + } + color="#ffb020" + onClick={() => navigate('/reports')} + delay={200} + /> + + + } + color="#9c27b0" + onClick={() => navigate('/settings')} + delay={300} + /> + + + + + {/* Progress Overview */} - - - Overall Progress - - - - - Lab Completion - - - {completedLabs}/{totalLabs} ({completionPercentage.toFixed(0)}%) - + + + + Overall Progress + + + + + Lab Completion + + + {completedLabs}/{totalLabs} ({completionPercentage.toFixed(0)}%) + + + - - - - - - - - - Time Spent - - - {hoursSpent}h {timeSpent % 60}m - + + + + + + + + + Time Spent + + + {hoursSpent}h {timeSpent % 60}m + + - - - - - - - - Hints Used - - - {hintsUsed} - + + + + + + + + + Hints Used + + + {hintsUsed} + + - - - - - - - - Avg Points/Lab - - - {completedLabs > 0 ? Math.round(totalPoints / completedLabs) : 0} - + + + + + + + + + Avg Points/Lab + + + {completedLabs > 0 ? Math.round(totalPoints / completedLabs) : 0} + + - + - - + + - {/* Recent Activity */} - - - Recent Activity - - - {/* Mock recent activities */} - {[ - { lab: 'DVWA - SQL Injection', action: 'Completed', points: 150, time: '2 hours ago' }, - { lab: 'Juice Shop - XSS Challenge', action: 'Started', points: 0, time: '1 day ago' }, - { lab: 'Metasploitable - Port Scanning', action: 'Completed', points: 200, time: '3 days ago' }, - ].map((activity, index) => ( - + {/* Recent Activity */} + + + - - - {activity.lab} - - - {activity.time} - - - - - {activity.points > 0 && ( - } - label={`+${activity.points}`} - size="small" - color="warning" - /> - )} + + Recent Activity + + + {[ + { + lab: 'DVWA - SQL Injection', + action: 'Completed', + points: 150, + time: '2 hours ago', + color: '#00ff88', + }, + { + lab: 'Juice Shop - XSS Challenge', + action: 'Started', + points: 0, + time: '1 day ago', + color: '#42a5f5', + }, + { + lab: 'Metasploitable - Port Scanning', + action: 'Completed', + points: 200, + time: '3 days ago', + color: '#00ff88', + }, + ].map((activity, index) => ( + + + + + {activity.lab} + + + {activity.time} + + + + + {activity.points > 0 && ( + } + label={`+${activity.points}`} + size="small" + sx={{ + bgcolor: '#ffb020', + color: '#000', + fontWeight: 700, + }} + /> + )} + + + + ))} - - ))} - - + + + - {/* Skills Breakdown */} - - - Skills Breakdown - - - {[ - { skill: 'Web Security', level: 75, color: '#2196f3' }, - { skill: 'Network Security', level: 60, color: '#4caf50' }, - { skill: 'Cryptography', level: 45, color: '#ff9800' }, - { skill: 'Exploitation', level: 80, color: '#f44336' }, - ].map((skill) => ( - - - - {skill.skill} - - {skill.level}% - - - + {/* Skills Breakdown */} + + + + + Skills Breakdown + + + {[ + { skill: 'Web Security', level: 75, color: '#42a5f5' }, + { skill: 'Network Security', level: 60, color: '#00ff88' }, + { skill: 'Cryptography', level: 45, color: '#ffb020' }, + { skill: 'Exploitation', level: 80, color: '#ff3864' }, + ].map((skill, index) => ( + + + + + {skill.skill} + + + {skill.level}% + + + + + + ))} - - ))} + + - + ); }; diff --git a/frontend/src/pages/NotFoundPage.tsx b/frontend/src/pages/NotFoundPage.tsx index 9360f48..7ab67b8 100644 --- a/frontend/src/pages/NotFoundPage.tsx +++ b/frontend/src/pages/NotFoundPage.tsx @@ -1,14 +1,287 @@ -import { Typography, Container } from '@mui/material'; +import React from 'react'; +import { Box, Typography, Button, Container, useTheme, alpha } from '@mui/material'; +import { useNavigate } from 'react-router-dom'; +import HomeIcon from '@mui/icons-material/Home'; +import SearchIcon from '@mui/icons-material/Search'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import SecurityIcon from '@mui/icons-material/Security'; + +/** + * Modern 404 Not Found Page + * Features: Cybersecurity-themed, animated, professional design + */ +const NotFoundPage: React.FC = () => { + const navigate = useNavigate(); + const theme = useTheme(); -export default function NotFoundPage(): JSX.Element { return ( - - - NotFoundPage - - - This page is under construction. Feature coming soon! - - + + + + {/* Animated 404 */} + + + 404 + + + {/* Lock Icon Overlay */} + + + + {/* Error Message */} + + Access Denied + + + + The page you're looking for has been classified or doesn't exist in our database. + Our security systems have logged this attempt. + + + {/* Terminal-style code block */} + + + {`$ security-scan --status 404 +>> ERROR: Resource not found +>> Possible causes: + - Incorrect URL path + - Resource moved or deleted + - Access permissions required +>> Recommendation: Return to dashboard`} + + + + {/* Action Buttons */} + + + + + + + + + {/* Additional Help Text */} + + Need help? Check our{' '} + navigate('/help')} + > + documentation + + {' '}or contact support. + + + + ); -} +}; + +export default NotFoundPage; diff --git a/frontend/src/pages/auth/LoginPage.tsx b/frontend/src/pages/auth/LoginPage.tsx index 841c873..26bc7fc 100644 --- a/frontend/src/pages/auth/LoginPage.tsx +++ b/frontend/src/pages/auth/LoginPage.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useNavigate, Link as RouterLink } from 'react-router-dom'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -12,7 +12,22 @@ import { Typography, Link, Alert, + alpha, + useTheme, + Fade, + Zoom, + InputAdornment, + IconButton, + CircularProgress, } from '@mui/material'; +import { + Email, + Lock, + Visibility, + VisibilityOff, + Security, + ArrowForward, +} from '@mui/icons-material'; import { useAppDispatch, useAppSelector } from '@hooks/redux'; import { login } from '@features/auth/authSlice'; import type { LoginCredentials } from '../../types'; @@ -24,10 +39,16 @@ const loginSchema = z.object({ type LoginFormData = z.infer; +/** + * Modern Login Page + * Features: Glassmorphism, animated background, smooth transitions + */ export default function LoginPage(): JSX.Element { const navigate = useNavigate(); const dispatch = useAppDispatch(); + const theme = useTheme(); const { isAuthenticated, isLoading, error } = useAppSelector((state) => state.auth); + const [showPassword, setShowPassword] = useState(false); const { register, @@ -48,64 +69,306 @@ export default function LoginPage(): JSX.Element { }; return ( - - - - - Sign in to Auron - - {error && ( - - {error} - - )} - - - - - - - {"Don't have an account? Sign Up"} - - + + + + + {/* Logo and Branding */} + + + + + + + Auron Security + + + Cybersecurity Training Platform + + + + + {/* Login Form Card */} + + + + Welcome Back + + + Sign in to continue your security training + + + {error && ( + + + {error} + + + )} + + + + + + ), + }} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 2, + transition: 'all 0.3s ease', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 12px ${alpha(theme.palette.primary.main, 0.1)}`, + }, + '&.Mui-focused': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 16px ${alpha(theme.palette.primary.main, 0.2)}`, + }, + }, + }} + /> + + + + + ), + endAdornment: ( + + setShowPassword(!showPassword)} + edge="end" + sx={{ + transition: 'all 0.3s ease', + '&:hover': { + transform: 'scale(1.1)', + }, + }} + > + {showPassword ? : } + + + ), + }} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 2, + transition: 'all 0.3s ease', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 12px ${alpha(theme.palette.primary.main, 0.1)}`, + }, + '&.Mui-focused': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 16px ${alpha(theme.palette.primary.main, 0.2)}`, + }, + }, + }} + /> + + + + + + Don't have an account?{' '} + + + Sign Up + + + + + + + {/* Footer */} + + + + © 2025 Auron Security Platform. All rights reserved. + + + - - - + + + ); } diff --git a/frontend/src/pages/auth/RegisterPage.tsx b/frontend/src/pages/auth/RegisterPage.tsx index 1025520..c7773e1 100644 --- a/frontend/src/pages/auth/RegisterPage.tsx +++ b/frontend/src/pages/auth/RegisterPage.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useNavigate, Link as RouterLink } from 'react-router-dom'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -12,42 +12,84 @@ import { Typography, Link, Alert, + alpha, + useTheme, + Fade, + Zoom, + InputAdornment, + IconButton, + CircularProgress, + LinearProgress, } from '@mui/material'; +import { + Email, + Lock, + Visibility, + VisibilityOff, + Security, + Person, + ArrowForward, + CheckCircle, +} from '@mui/icons-material'; import { useAppDispatch, useAppSelector } from '@hooks/redux'; import { register as registerUser } from '@features/auth/authSlice'; import { VALIDATION } from '@config/constants'; -const registerSchema = z.object({ - email: z.string().email('Invalid email address'), - username: z - .string() - .min(VALIDATION.USERNAME_MIN_LENGTH, `Username must be at least ${VALIDATION.USERNAME_MIN_LENGTH} characters`) - .max(VALIDATION.USERNAME_MAX_LENGTH, `Username must be at most ${VALIDATION.USERNAME_MAX_LENGTH} characters`) - .regex(VALIDATION.USERNAME_REGEX, 'Username can only contain letters, numbers, _ and -'), - password: z - .string() - .min(VALIDATION.PASSWORD_MIN_LENGTH, `Password must be at least ${VALIDATION.PASSWORD_MIN_LENGTH} characters`), - confirmPassword: z.string(), -}).refine((data) => data.password === data.confirmPassword, { - message: "Passwords don't match", - path: ['confirmPassword'], -}); +const registerSchema = z + .object({ + email: z.string().email('Invalid email address'), + username: z + .string() + .min(VALIDATION.USERNAME_MIN_LENGTH, `Username must be at least ${VALIDATION.USERNAME_MIN_LENGTH} characters`) + .max(VALIDATION.USERNAME_MAX_LENGTH, `Username must be at most ${VALIDATION.USERNAME_MAX_LENGTH} characters`) + .regex(VALIDATION.USERNAME_REGEX, 'Username can only contain letters, numbers, _ and -'), + password: z + .string() + .min(VALIDATION.PASSWORD_MIN_LENGTH, `Password must be at least ${VALIDATION.PASSWORD_MIN_LENGTH} characters`), + confirmPassword: z.string(), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "Passwords don't match", + path: ['confirmPassword'], + }); type RegisterFormData = z.infer; +/** + * Modern Register Page + * Features: Glassmorphism, animated background, smooth transitions, password strength indicator + */ export default function RegisterPage(): JSX.Element { const navigate = useNavigate(); const dispatch = useAppDispatch(); + const theme = useTheme(); const { isAuthenticated, isLoading, error } = useAppSelector((state) => state.auth); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [passwordStrength, setPasswordStrength] = useState(0); const { register, handleSubmit, + watch, formState: { errors }, } = useForm({ resolver: zodResolver(registerSchema), }); + const password = watch('password', ''); + + // Calculate password strength + useEffect(() => { + let strength = 0; + if (password.length >= 8) strength += 25; + if (password.length >= 12) strength += 25; + if (/[a-z]/.test(password) && /[A-Z]/.test(password)) strength += 25; + if (/[0-9]/.test(password)) strength += 12.5; + if (/[^a-zA-Z0-9]/.test(password)) strength += 12.5; + setPasswordStrength(Math.min(strength, 100)); + }, [password]); + useEffect(() => { if (isAuthenticated) { navigate('/dashboard'); @@ -59,70 +101,425 @@ export default function RegisterPage(): JSX.Element { await dispatch(registerUser(registerData)); }; + const getPasswordStrengthColor = () => { + if (passwordStrength < 40) return theme.palette.error.main; + if (passwordStrength < 70) return theme.palette.warning.main; + return theme.palette.success.main; + }; + + const getPasswordStrengthLabel = () => { + if (passwordStrength < 40) return 'Weak'; + if (passwordStrength < 70) return 'Medium'; + return 'Strong'; + }; + return ( - - - - - Create Account - - {error && {error}} - - - - - - - - - Already have an account? Sign In - - + + + + + {/* Logo and Branding */} + + + + + + + Auron Security + + + Join Our Cybersecurity Community + + + + + {/* Register Form Card */} + + + + Create Account + + + Start your journey to becoming a security expert + + + {error && ( + + + {error} + + + )} + + + + + + ), + }} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 2, + transition: 'all 0.3s ease', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 12px ${alpha(theme.palette.secondary.main, 0.1)}`, + }, + '&.Mui-focused': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 16px ${alpha(theme.palette.secondary.main, 0.2)}`, + }, + }, + }} + /> + + + + + ), + }} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 2, + transition: 'all 0.3s ease', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 12px ${alpha(theme.palette.secondary.main, 0.1)}`, + }, + '&.Mui-focused': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 16px ${alpha(theme.palette.secondary.main, 0.2)}`, + }, + }, + }} + /> + + + + + ), + endAdornment: ( + + setShowPassword(!showPassword)} + edge="end" + sx={{ + transition: 'all 0.3s ease', + '&:hover': { + transform: 'scale(1.1)', + }, + }} + > + {showPassword ? : } + + + ), + }} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 2, + transition: 'all 0.3s ease', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 12px ${alpha(theme.palette.secondary.main, 0.1)}`, + }, + '&.Mui-focused': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 16px ${alpha(theme.palette.secondary.main, 0.2)}`, + }, + }, + }} + /> + + {/* Password Strength Indicator */} + {password && ( + + + + + Password Strength + + + {getPasswordStrengthLabel()} + + + + + + )} + + + + + ), + endAdornment: ( + + setShowConfirmPassword(!showConfirmPassword)} + edge="end" + sx={{ + transition: 'all 0.3s ease', + '&:hover': { + transform: 'scale(1.1)', + }, + }} + > + {showConfirmPassword ? : } + + + ), + }} + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 2, + transition: 'all 0.3s ease', + '&:hover': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 12px ${alpha(theme.palette.secondary.main, 0.1)}`, + }, + '&.Mui-focused': { + transform: 'translateY(-2px)', + boxShadow: `0 4px 16px ${alpha(theme.palette.secondary.main, 0.2)}`, + }, + }, + }} + /> + + + + + + Already have an account?{' '} + + + Sign In + + + + + + + {/* Footer */} + + + + © 2025 Auron Security Platform. All rights reserved. + + + - - - + + + ); } diff --git a/frontend/src/styles/modernTheme.ts b/frontend/src/styles/modernTheme.ts new file mode 100644 index 0000000..2d1ef54 --- /dev/null +++ b/frontend/src/styles/modernTheme.ts @@ -0,0 +1,457 @@ +import { createTheme, ThemeOptions } from '@mui/material/styles'; + +/** + * Modern Cybersecurity-Themed Design System for Auron + * Features: Terminal aesthetics, neon accents, glassmorphism, professional polish + */ + +// Custom color palette - Cybersecurity themed +const colors = { + // Primary - Electric Blue (cybersecurity brand color) + electric: { + 50: '#e3f2fd', + 100: '#bbdefb', + 200: '#90caf9', + 300: '#64b5f6', + 400: '#42a5f5', + 500: '#2196f3', // Main + 600: '#1e88e5', + 700: '#1976d2', + 800: '#1565c0', + 900: '#0d47a1', + }, + + // Terminal Green (hacker aesthetic) + terminal: { + 50: '#e8f5e9', + 100: '#c8e6c9', + 200: '#a5d6a7', + 300: '#81c784', + 400: '#66bb6a', + 500: '#00ff41', // Bright terminal green + 600: '#00e676', + 700: '#00c853', + 800: '#00b248', + 900: '#00962d', + }, + + // Neon Purple (accent) + neon: { + 50: '#f3e5f5', + 100: '#e1bee7', + 200: '#ce93d8', + 300: '#ba68c8', + 400: '#ab47bc', + 500: '#9c27b0', + 600: '#8e24aa', + 700: '#7b1fa2', + 800: '#6a1b9a', + 900: '#4a148c', + }, + + // Cyber Dark (backgrounds) + cyber: { + 900: '#0a0e27', // Darkest + 800: '#0f172a', + 700: '#1e293b', + 600: '#334155', + 500: '#475569', + 400: '#64748b', + 300: '#94a3b8', + 200: '#cbd5e1', + 100: '#e2e8f0', + 50: '#f8fafc', + }, + + // Status colors + danger: '#ff3864', + warning: '#ffb020', + success: '#00ff88', + info: '#00d4ff', +}; + +// Light Theme Options +const lightThemeOptions: ThemeOptions = { + palette: { + mode: 'light', + primary: { + main: colors.electric[600], + light: colors.electric[400], + dark: colors.electric[800], + contrastText: '#ffffff', + }, + secondary: { + main: colors.terminal[500], + light: colors.terminal[400], + dark: colors.terminal[700], + contrastText: '#000000', + }, + error: { + main: colors.danger, + light: '#ff6b8a', + dark: '#cc2d50', + }, + warning: { + main: colors.warning, + light: '#ffc14d', + dark: '#cc8d1a', + }, + info: { + main: colors.info, + light: '#33ddff', + dark: '#00aacc', + }, + success: { + main: colors.success, + light: '#33ffa0', + dark: '#00cc6d', + }, + background: { + default: '#f8fafc', + paper: '#ffffff', + }, + text: { + primary: colors.cyber[900], + secondary: colors.cyber[600], + disabled: colors.cyber[400], + }, + }, + typography: { + fontFamily: [ + 'Inter', + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + 'Arial', + 'sans-serif', + ].join(','), + fontFamilyMonospace: [ + '"Fira Code"', + '"JetBrains Mono"', + 'Monaco', + 'Consolas', + '"Courier New"', + 'monospace', + ].join(','), + h1: { + fontSize: '3rem', + fontWeight: 800, + lineHeight: 1.1, + letterSpacing: '-0.02em', + }, + h2: { + fontSize: '2.25rem', + fontWeight: 700, + lineHeight: 1.2, + letterSpacing: '-0.01em', + }, + h3: { + fontSize: '1.875rem', + fontWeight: 600, + lineHeight: 1.3, + }, + h4: { + fontSize: '1.5rem', + fontWeight: 600, + lineHeight: 1.4, + }, + h5: { + fontSize: '1.25rem', + fontWeight: 600, + lineHeight: 1.5, + }, + h6: { + fontSize: '1.125rem', + fontWeight: 600, + lineHeight: 1.5, + }, + subtitle1: { + fontSize: '1.125rem', + fontWeight: 500, + lineHeight: 1.75, + }, + subtitle2: { + fontSize: '1rem', + fontWeight: 500, + lineHeight: 1.75, + }, + body1: { + fontSize: '1rem', + lineHeight: 1.75, + }, + body2: { + fontSize: '0.875rem', + lineHeight: 1.6, + }, + button: { + textTransform: 'none', + fontWeight: 600, + letterSpacing: '0.02em', + }, + caption: { + fontSize: '0.75rem', + lineHeight: 1.5, + letterSpacing: '0.01em', + }, + overline: { + fontSize: '0.75rem', + fontWeight: 700, + lineHeight: 2, + letterSpacing: '0.1em', + textTransform: 'uppercase', + }, + }, + shape: { + borderRadius: 12, + }, + shadows: [ + 'none', + '0 1px 2px 0 rgba(0, 0, 0, 0.05)', + '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)', + '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', + '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', + '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + ], + components: { + MuiButton: { + styleOverrides: { + root: { + borderRadius: 10, + padding: '10px 24px', + fontWeight: 600, + fontSize: '0.95rem', + transition: 'all 0.2s ease-in-out', + '&:hover': { + transform: 'translateY(-2px)', + }, + }, + contained: { + boxShadow: '0 4px 12px rgba(33, 150, 243, 0.25)', + '&:hover': { + boxShadow: '0 8px 24px rgba(33, 150, 243, 0.35)', + }, + }, + containedPrimary: { + background: `linear-gradient(135deg, ${colors.electric[600]} 0%, ${colors.electric[700]} 100%)`, + '&:hover': { + background: `linear-gradient(135deg, ${colors.electric[700]} 0%, ${colors.electric[800]} 100%)`, + }, + }, + outlined: { + borderWidth: 2, + '&:hover': { + borderWidth: 2, + }, + }, + }, + }, + MuiCard: { + styleOverrides: { + root: { + borderRadius: 16, + boxShadow: '0 4px 20px rgba(0, 0, 0, 0.08)', + transition: 'all 0.3s ease', + overflow: 'hidden', + '&:hover': { + boxShadow: '0 8px 30px rgba(0, 0, 0, 0.12)', + transform: 'translateY(-4px)', + }, + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + borderRadius: 12, + }, + elevation1: { + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', + }, + elevation2: { + boxShadow: '0 4px 16px rgba(0, 0, 0, 0.1)', + }, + elevation3: { + boxShadow: '0 8px 24px rgba(0, 0, 0, 0.12)', + }, + }, + }, + MuiAppBar: { + styleOverrides: { + root: { + boxShadow: '0 1px 3px rgba(0, 0, 0, 0.08)', + backdropFilter: 'blur(10px)', + backgroundColor: 'rgba(255, 255, 255, 0.8)', + }, + }, + }, + MuiChip: { + styleOverrides: { + root: { + borderRadius: 8, + fontWeight: 500, + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-root': { + borderRadius: 10, + transition: 'all 0.2s ease', + '&:hover': { + transform: 'translateY(-1px)', + }, + '&.Mui-focused': { + transform: 'translateY(-1px)', + boxShadow: '0 4px 12px rgba(33, 150, 243, 0.15)', + }, + }, + }, + }, + }, + MuiLinearProgress: { + styleOverrides: { + root: { + borderRadius: 4, + height: 8, + }, + bar: { + borderRadius: 4, + }, + }, + }, + }, +}; + +// Dark Theme Options +const darkThemeOptions: ThemeOptions = { + ...lightThemeOptions, + palette: { + mode: 'dark', + primary: { + main: colors.electric[400], + light: colors.electric[300], + dark: colors.electric[600], + contrastText: '#ffffff', + }, + secondary: { + main: colors.terminal[500], + light: colors.terminal[400], + dark: colors.terminal[600], + contrastText: '#000000', + }, + error: { + main: colors.danger, + light: '#ff6b8a', + dark: '#cc2d50', + }, + warning: { + main: colors.warning, + light: '#ffc14d', + dark: '#cc8d1a', + }, + info: { + main: colors.info, + light: '#33ddff', + dark: '#00aacc', + }, + success: { + main: colors.success, + light: '#33ffa0', + dark: '#00cc6d', + }, + background: { + default: colors.cyber[900], + paper: colors.cyber[800], + }, + text: { + primary: '#ffffff', + secondary: colors.cyber[300], + disabled: colors.cyber[500], + }, + }, + components: { + ...lightThemeOptions.components, + MuiAppBar: { + styleOverrides: { + root: { + boxShadow: '0 1px 3px rgba(0, 0, 0, 0.3)', + backdropFilter: 'blur(10px)', + backgroundColor: 'rgba(10, 14, 39, 0.8)', + }, + }, + }, + MuiCard: { + styleOverrides: { + root: { + borderRadius: 16, + boxShadow: '0 4px 20px rgba(0, 0, 0, 0.3)', + transition: 'all 0.3s ease', + background: `linear-gradient(135deg, ${colors.cyber[800]} 0%, ${colors.cyber[700]} 100%)`, + border: `1px solid ${colors.cyber[600]}`, + '&:hover': { + boxShadow: '0 8px 30px rgba(0, 0, 0, 0.4)', + transform: 'translateY(-4px)', + border: `1px solid ${colors.electric[700]}`, + }, + }, + }, + }, + }, +}; + +// Export themes +export const lightTheme = createTheme(lightThemeOptions); +export const darkTheme = createTheme(darkThemeOptions); + +// Export color palette for direct use +export { colors }; + +// Utility: Glassmorphism effect +export const glassmorphism = (opacity: number = 0.8) => ({ + background: `rgba(255, 255, 255, ${opacity})`, + backdropFilter: 'blur(10px)', + WebkitBackdropFilter: 'blur(10px)', + border: '1px solid rgba(255, 255, 255, 0.18)', +}); + +// Utility: Glassmorphism dark +export const glassmorphismDark = (opacity: number = 0.8) => ({ + background: `rgba(10, 14, 39, ${opacity})`, + backdropFilter: 'blur(10px)', + WebkitBackdropFilter: 'blur(10px)', + border: '1px solid rgba(255, 255, 255, 0.1)', +}); + +// Utility: Gradient text +export const gradientText = (color1: string = colors.electric[400], color2: string = colors.terminal[500]) => ({ + background: `linear-gradient(135deg, ${color1}, ${color2})`, + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + backgroundClip: 'text', +}); + +// Utility: Neon glow +export const neonGlow = (color: string = colors.terminal[500]) => ({ + boxShadow: `0 0 10px ${color}, 0 0 20px ${color}, 0 0 30px ${color}`, +});