✨ Features Added: - User registration and login with JWT authentication - Role-based access control (user/admin) - Comprehensive admin panel with user management - Password hashing with bcrypt - Session management and token storage - Protected routes and public routes - Modern login/register forms with validation 🗄️ Database Schema: - Users table with profile information - Roles and permissions system - User-role junction tables - Session management tables - Database triggers and indexes 🎨 UI/UX Improvements: - Updated navigation with user menu - Admin badge and access controls - Responsive authentication forms - Loading states and error handling - Role-based UI elements 🔧 API Endpoints: - /api/auth/login - User authentication - /api/auth/register - User registration - /api/admin/users - User management (admin only) - /api/setup-auth - Database schema setup 🚀 Admin Panel Features: - User listing with search and pagination - Role assignment (user/admin) - User activation/deactivation - System dashboard with stats - Card management placeholder - Real-time user management
166 lines
4.7 KiB
TypeScript
166 lines
4.7 KiB
TypeScript
import React from 'react';
|
|
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { Analytics } from '@vercel/analytics/react';
|
|
import { SpeedInsights } from '@vercel/speed-insights/react';
|
|
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
|
import Navbar from './components/Navbar';
|
|
import Dashboard from './pages/Dashboard';
|
|
import Collections from './pages/Collections';
|
|
import Decks from './pages/Decks';
|
|
import Cards from './pages/Cards';
|
|
import Scanner from './pages/Scanner';
|
|
import LoginForm from './components/auth/LoginForm';
|
|
import RegisterForm from './components/auth/RegisterForm';
|
|
import AdminPanel from './components/admin/AdminPanel';
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: 1,
|
|
refetchOnWindowFocus: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
// Protected Route Component
|
|
const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const { user, isLoading } = useAuth();
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-indigo-600"></div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!user) {
|
|
return <Navigate to="/login" replace />;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
};
|
|
|
|
// Public Route Component (redirect if authenticated)
|
|
const PublicRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const { user, isLoading } = useAuth();
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-indigo-600"></div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (user) {
|
|
return <Navigate to="/dashboard" replace />;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
};
|
|
|
|
// Layout Component
|
|
const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
return (
|
|
<div className="min-h-screen bg-gray-50">
|
|
<Navbar />
|
|
<main className="container mx-auto px-4 py-8">
|
|
{children}
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
function App() {
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
<AuthProvider>
|
|
<Router>
|
|
<Routes>
|
|
{/* Public Routes */}
|
|
<Route path="/login" element={
|
|
<PublicRoute>
|
|
<LoginForm />
|
|
</PublicRoute>
|
|
} />
|
|
<Route path="/register" element={
|
|
<PublicRoute>
|
|
<RegisterForm />
|
|
</PublicRoute>
|
|
} />
|
|
|
|
{/* Protected Routes */}
|
|
<Route path="/dashboard" element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Dashboard />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
} />
|
|
|
|
<Route path="/collections" element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Collections />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
} />
|
|
|
|
<Route path="/decks" element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Decks />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
} />
|
|
|
|
<Route path="/cards" element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Cards />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
} />
|
|
|
|
<Route path="/scanner" element={
|
|
<ProtectedRoute>
|
|
<Layout>
|
|
<Scanner />
|
|
</Layout>
|
|
</ProtectedRoute>
|
|
} />
|
|
|
|
{/* Admin Routes */}
|
|
<Route path="/admin" element={
|
|
<ProtectedRoute>
|
|
<AdminPanel />
|
|
</ProtectedRoute>
|
|
} />
|
|
|
|
{/* Default redirect */}
|
|
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
|
|
|
{/* 404 fallback */}
|
|
<Route path="*" element={
|
|
<Layout>
|
|
<div className="text-center py-12">
|
|
<h1 className="text-4xl font-bold text-gray-900 mb-4">404 - Page Not Found</h1>
|
|
<p className="text-gray-600 mb-8">The page you're looking for doesn't exist.</p>
|
|
<a href="/dashboard" className="bg-indigo-600 text-white px-6 py-3 rounded-lg hover:bg-indigo-700 transition-colors">
|
|
Go to Dashboard
|
|
</a>
|
|
</div>
|
|
</Layout>
|
|
} />
|
|
</Routes>
|
|
<Analytics />
|
|
<SpeedInsights />
|
|
</Router>
|
|
</AuthProvider>
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|