trung.le
feat: introduce initial frontend application with user dashboard, admin, and login pages.
1fe7da5 | import { useState, useEffect } from 'react'; | |
| import { BrowserRouter as Router, Routes, Route, NavLink, Navigate } from 'react-router-dom'; | |
| import { ThemeProvider } from './context/ThemeContext'; | |
| import Login from './pages/Login'; | |
| import AdminDashboard from './pages/AdminDashboard'; | |
| import UserDashboard from './pages/UserDashboard'; | |
| import ThemeSettings from './components/ThemeSettings'; | |
| import api from './api'; | |
| import { LogOut, Home, Shield, BookOpen } from 'lucide-react'; | |
| function AppContent() { | |
| const [user, setUser] = useState(null); | |
| const [loading, setLoading] = useState(true); | |
| useEffect(() => { | |
| const fetchUser = async () => { | |
| const token = localStorage.getItem('token'); | |
| if (token) { | |
| try { | |
| const res = await api.get('/auth/me'); | |
| setUser(res.data); | |
| } catch (err) { | |
| localStorage.removeItem('token'); | |
| } | |
| } | |
| setLoading(false); | |
| }; | |
| fetchUser(); | |
| }, []); | |
| const logout = () => { | |
| localStorage.removeItem('token'); | |
| setUser(null); | |
| }; | |
| if (loading) { | |
| return ( | |
| <div className="loading-container"> | |
| <div className="loading-spinner"></div> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <Router> | |
| <header className="app-header glass-container"> | |
| {/* Brand */} | |
| <div className="header-brand"> | |
| <div className="header-logo"> | |
| <BookOpen size={22} /> | |
| </div> | |
| <span className="header-title">MCQ Portal</span> | |
| </div> | |
| {/* Navigation */} | |
| {user && ( | |
| <nav className="header-nav"> | |
| <NavLink | |
| to="/" | |
| className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`} | |
| > | |
| <Home size={18} /> | |
| <span>Dashboard</span> | |
| </NavLink> | |
| {user?.is_admin && ( | |
| <NavLink | |
| to="/admin" | |
| className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`} | |
| > | |
| <Shield size={18} /> | |
| <span>Admin</span> | |
| </NavLink> | |
| )} | |
| </nav> | |
| )} | |
| {/* Actions */} | |
| <div className="header-actions"> | |
| <ThemeSettings /> | |
| {user ? ( | |
| <div className="user-info"> | |
| <div className="user-avatar"> | |
| {user.username.charAt(0).toUpperCase()} | |
| </div> | |
| <span className="user-name">{user.username}</span> | |
| <button onClick={logout} className="danger" title="Logout"> | |
| <LogOut size={18} /> | |
| </button> | |
| </div> | |
| ) : ( | |
| <NavLink to="/login" className="nav-link"> | |
| Login | |
| </NavLink> | |
| )} | |
| </div> | |
| </header> | |
| <main className="main-content"> | |
| <Routes> | |
| <Route path="/" element={user ? <UserDashboard /> : <Navigate to="/login" />} /> | |
| <Route path="/login" element={!user ? <Login setUser={setUser} /> : <Navigate to="/" />} /> | |
| <Route path="/admin" element={user?.is_admin ? <AdminDashboard /> : <Navigate to="/" />} /> | |
| </Routes> | |
| </main> | |
| </Router> | |
| ); | |
| } | |
| export default function App() { | |
| return ( | |
| <ThemeProvider> | |
| <AppContent /> | |
| </ThemeProvider> | |
| ); | |
| } | |