All checks were successful
Build Images and Deploy / Update-PROD-Stack (push) Successful in 34s
128 lines
4.2 KiB
JavaScript
128 lines
4.2 KiB
JavaScript
import React, { useState } from 'react';
|
|
import ReactDOM from 'react-dom/client';
|
|
import { BrowserRouter, Routes, Route, Navigate, Link } from 'react-router-dom';
|
|
import { Toaster } from 'react-hot-toast';
|
|
import { AuthProvider, useAuth } from './AuthContext';
|
|
import { SocketProvider } from './SocketContext';
|
|
import Login from './pages/Login';
|
|
import Register from './pages/Register';
|
|
import ChallengeList from './pages/ChallengeList';
|
|
import ChallengeDetail from './pages/ChallengeDetail';
|
|
import Profile from './pages/Profile';
|
|
import Friends from './pages/Friends';
|
|
import Leaderboard from './pages/Leaderboard';
|
|
import ErrorBoundary from './components/ErrorBoundary';
|
|
import './App.css';
|
|
|
|
function ProtectedRoute({ children }) {
|
|
const { user, loading } = useAuth();
|
|
|
|
if (loading) {
|
|
return <div className="loading">Loading...</div>;
|
|
}
|
|
|
|
return user ? children : <Navigate to="/login" />;
|
|
}
|
|
|
|
function Header() {
|
|
const { user, logout } = useAuth();
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
|
|
if (!user) return null;
|
|
|
|
const handleLogout = () => {
|
|
setMobileMenuOpen(false);
|
|
logout();
|
|
};
|
|
|
|
const closeMobileMenu = () => setMobileMenuOpen(false);
|
|
|
|
return (
|
|
<header className="header">
|
|
<div className="container">
|
|
<nav className="nav">
|
|
<div className="nav-brand">
|
|
<h2>WTP</h2>
|
|
<button
|
|
className={`hamburger ${mobileMenuOpen ? 'active' : ''}`}
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
aria-label="Toggle menu"
|
|
>
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</button>
|
|
</div>
|
|
{mobileMenuOpen && (
|
|
<div
|
|
className="nav-backdrop"
|
|
onClick={closeMobileMenu}
|
|
aria-hidden="true"
|
|
/>
|
|
)}
|
|
<div className={`nav-menu ${mobileMenuOpen ? 'active' : ''}`}>
|
|
<ul className="nav-links">
|
|
<li><Link to="/challenges" onClick={closeMobileMenu}>Challenges</Link></li>
|
|
<li><Link to="/leaderboard" onClick={closeMobileMenu}>Leaderboard</Link></li>
|
|
<li><Link to="/friends" onClick={closeMobileMenu}>Friends</Link></li>
|
|
<li><Link to="/profile" onClick={closeMobileMenu}>Profile</Link></li>
|
|
</ul>
|
|
<button onClick={handleLogout} className="btn btn-secondary btn-sm logout-btn">
|
|
Logout
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|
|
|
|
function App() {
|
|
return (
|
|
<ErrorBoundary>
|
|
<BrowserRouter>
|
|
<AuthProvider>
|
|
<SocketProvider>
|
|
<Toaster
|
|
position="top-right"
|
|
toastOptions={{
|
|
duration: 3000,
|
|
style: {
|
|
background: '#1e293b',
|
|
color: '#f1f5f9',
|
|
border: '1px solid #334155',
|
|
},
|
|
success: {
|
|
iconTheme: {
|
|
primary: '#10b981',
|
|
secondary: '#f1f5f9',
|
|
},
|
|
},
|
|
error: {
|
|
iconTheme: {
|
|
primary: '#ef4444',
|
|
secondary: '#f1f5f9',
|
|
},
|
|
},
|
|
}}
|
|
/>
|
|
<Header />
|
|
<Routes>
|
|
<Route path="/login" element={<Login />} />
|
|
<Route path="/register" element={<Register />} />
|
|
<Route path="/challenges" element={<ProtectedRoute><ChallengeList /></ProtectedRoute>} />
|
|
<Route path="/challenges/:id" element={<ProtectedRoute><ChallengeDetail /></ProtectedRoute>} />
|
|
<Route path="/profile" element={<ProtectedRoute><Profile /></ProtectedRoute>} />
|
|
<Route path="/friends" element={<ProtectedRoute><Friends /></ProtectedRoute>} />
|
|
<Route path="/leaderboard" element={<ProtectedRoute><Leaderboard /></ProtectedRoute>} />
|
|
<Route path="/" element={<Navigate to="/challenges" />} />
|
|
</Routes>
|
|
</SocketProvider>
|
|
</AuthProvider>
|
|
</BrowserRouter>
|
|
</ErrorBoundary>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|