2025-07-21 14:53:06 -04:00
|
|
|
import React from 'react';
|
2025-07-21 22:06:38 -04:00
|
|
|
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
2025-07-21 14:53:06 -04:00
|
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
2025-07-21 15:34:34 -04:00
|
|
|
import { Analytics } from '@vercel/analytics/react';
|
|
|
|
|
import { SpeedInsights } from '@vercel/speed-insights/react';
|
2025-07-21 22:06:38 -04:00
|
|
|
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
2025-07-22 19:52:39 -04:00
|
|
|
import { ThemeProvider } from './contexts/ThemeContext';
|
2025-07-22 21:24:02 -04:00
|
|
|
import ResponsiveLayout from './components/ResponsiveLayout';
|
2025-07-21 14:53:06 -04:00
|
|
|
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';
|
2025-07-22 11:53:44 -04:00
|
|
|
import Settings from './pages/Settings';
|
2025-07-21 22:06:38 -04:00
|
|
|
import LoginForm from './components/auth/LoginForm';
|
|
|
|
|
import RegisterForm from './components/auth/RegisterForm';
|
|
|
|
|
import AdminPanel from './components/admin/AdminPanel';
|
2025-07-21 14:53:06 -04:00
|
|
|
|
|
|
|
|
const queryClient = new QueryClient({
|
|
|
|
|
defaultOptions: {
|
|
|
|
|
queries: {
|
2025-07-21 22:06:38 -04:00
|
|
|
retry: 1,
|
2025-07-21 14:53:06 -04:00
|
|
|
refetchOnWindowFocus: false,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-21 22:06:38 -04:00
|
|
|
// Protected Route Component
|
|
|
|
|
const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
|
|
|
const { user, isLoading } = useAuth();
|
|
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
return (
|
2025-07-22 19:52:39 -04:00
|
|
|
<div className="min-h-screen-safe flex items-center justify-center bg-white dark:bg-surface-900">
|
|
|
|
|
<div className="flex flex-col items-center space-y-4">
|
|
|
|
|
<div className="w-12 h-12 bg-gradient-to-r from-primary-500 to-accent-500 rounded-xl flex items-center justify-center animate-bounce-subtle">
|
|
|
|
|
<svg className="w-6 h-6 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
|
|
|
<path d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-surface-600 dark:text-surface-400 text-sm">Loading TCG Vault...</p>
|
|
|
|
|
</div>
|
2025-07-21 22:06:38 -04:00
|
|
|
</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 (
|
2025-07-22 19:52:39 -04:00
|
|
|
<div className="min-h-screen-safe flex items-center justify-center bg-white dark:bg-surface-900">
|
|
|
|
|
<div className="flex flex-col items-center space-y-4">
|
|
|
|
|
<div className="w-12 h-12 bg-gradient-to-r from-primary-500 to-accent-500 rounded-xl flex items-center justify-center animate-bounce-subtle">
|
|
|
|
|
<svg className="w-6 h-6 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
|
|
|
<path d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-surface-600 dark:text-surface-400 text-sm">Loading TCG Vault...</p>
|
|
|
|
|
</div>
|
2025-07-21 22:06:38 -04:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (user) {
|
|
|
|
|
return <Navigate to="/dashboard" replace />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <>{children}</>;
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-22 21:24:02 -04:00
|
|
|
// Responsive Layout Component
|
|
|
|
|
const AppLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
2025-07-21 22:06:38 -04:00
|
|
|
return (
|
2025-07-22 21:24:02 -04:00
|
|
|
<ResponsiveLayout>
|
|
|
|
|
{children}
|
|
|
|
|
</ResponsiveLayout>
|
2025-07-21 22:06:38 -04:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-22 19:52:39 -04:00
|
|
|
// Auth Layout Component for login/register
|
|
|
|
|
const AuthLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
|
|
|
return (
|
|
|
|
|
<div className="min-h-screen-safe bg-gradient-to-br from-primary-50 via-white to-accent-50 dark:from-surface-900 dark:via-surface-800 dark:to-surface-900 flex items-center justify-center px-4 transition-colors">
|
|
|
|
|
<div className="w-full max-w-sm animate-slide-up">
|
|
|
|
|
{children}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-21 14:53:06 -04:00
|
|
|
function App() {
|
|
|
|
|
return (
|
|
|
|
|
<QueryClientProvider client={queryClient}>
|
2025-07-22 19:52:39 -04:00
|
|
|
<ThemeProvider>
|
|
|
|
|
<AuthProvider>
|
|
|
|
|
<Router>
|
|
|
|
|
<Routes>
|
|
|
|
|
{/* Public Routes */}
|
|
|
|
|
<Route path="/login" element={
|
|
|
|
|
<PublicRoute>
|
|
|
|
|
<AuthLayout>
|
|
|
|
|
<LoginForm />
|
|
|
|
|
</AuthLayout>
|
|
|
|
|
</PublicRoute>
|
|
|
|
|
} />
|
|
|
|
|
<Route path="/register" element={
|
|
|
|
|
<PublicRoute>
|
|
|
|
|
<AuthLayout>
|
|
|
|
|
<RegisterForm />
|
|
|
|
|
</AuthLayout>
|
|
|
|
|
</PublicRoute>
|
|
|
|
|
} />
|
2025-07-21 22:06:38 -04:00
|
|
|
|
2025-07-22 19:52:39 -04:00
|
|
|
{/* Protected Routes */}
|
|
|
|
|
<Route path="/dashboard" element={
|
|
|
|
|
<ProtectedRoute>
|
2025-07-22 21:24:02 -04:00
|
|
|
<AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
<Dashboard />
|
2025-07-22 21:24:02 -04:00
|
|
|
</AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
</ProtectedRoute>
|
|
|
|
|
} />
|
|
|
|
|
|
|
|
|
|
<Route path="/collections" element={
|
|
|
|
|
<ProtectedRoute>
|
2025-07-22 21:24:02 -04:00
|
|
|
<AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
<Collections />
|
2025-07-22 21:24:02 -04:00
|
|
|
</AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
</ProtectedRoute>
|
|
|
|
|
} />
|
|
|
|
|
|
|
|
|
|
<Route path="/decks" element={
|
|
|
|
|
<ProtectedRoute>
|
2025-07-22 21:24:02 -04:00
|
|
|
<AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
<Decks />
|
2025-07-22 21:24:02 -04:00
|
|
|
</AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
</ProtectedRoute>
|
|
|
|
|
} />
|
|
|
|
|
|
|
|
|
|
<Route path="/cards" element={
|
|
|
|
|
<ProtectedRoute>
|
2025-07-22 21:24:02 -04:00
|
|
|
<AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
<Cards />
|
2025-07-22 21:24:02 -04:00
|
|
|
</AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
</ProtectedRoute>
|
|
|
|
|
} />
|
|
|
|
|
|
|
|
|
|
<Route path="/scanner" element={
|
|
|
|
|
<ProtectedRoute>
|
2025-07-22 21:24:02 -04:00
|
|
|
<AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
<Scanner />
|
2025-07-22 21:24:02 -04:00
|
|
|
</AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
</ProtectedRoute>
|
|
|
|
|
} />
|
2025-07-21 22:06:38 -04:00
|
|
|
|
2025-07-22 19:52:39 -04:00
|
|
|
<Route path="/settings" element={
|
|
|
|
|
<ProtectedRoute>
|
2025-07-22 21:24:02 -04:00
|
|
|
<AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
<Settings />
|
2025-07-22 21:24:02 -04:00
|
|
|
</AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
</ProtectedRoute>
|
|
|
|
|
} />
|
2025-07-22 11:53:44 -04:00
|
|
|
|
2025-07-22 19:52:39 -04:00
|
|
|
{/* Admin Routes */}
|
|
|
|
|
<Route path="/admin" element={
|
|
|
|
|
<ProtectedRoute>
|
2025-07-22 21:24:02 -04:00
|
|
|
<AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
<AdminPanel />
|
2025-07-22 21:24:02 -04:00
|
|
|
</AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
</ProtectedRoute>
|
|
|
|
|
} />
|
2025-07-21 22:06:38 -04:00
|
|
|
|
2025-07-22 19:52:39 -04:00
|
|
|
{/* Default redirect */}
|
|
|
|
|
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
|
|
|
|
|
|
|
|
|
{/* 404 fallback */}
|
|
|
|
|
<Route path="*" element={
|
2025-07-22 21:24:02 -04:00
|
|
|
<AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
<div className="text-center py-12">
|
|
|
|
|
<div className="w-16 h-16 bg-surface-200 dark:bg-surface-700 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
|
|
|
|
<svg className="w-8 h-8 text-surface-500 dark:text-surface-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<h1 className="text-2xl font-bold text-surface-900 dark:text-white mb-2">Page Not Found</h1>
|
|
|
|
|
<p className="text-surface-600 dark:text-surface-400 mb-6">The page you're looking for doesn't exist.</p>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => window.history.back()}
|
|
|
|
|
className="bg-primary-600 hover:bg-primary-700 text-white px-6 py-3 rounded-xl font-medium transition-colors"
|
|
|
|
|
>
|
|
|
|
|
Go Back
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-07-22 21:24:02 -04:00
|
|
|
</AppLayout>
|
2025-07-22 19:52:39 -04:00
|
|
|
} />
|
|
|
|
|
</Routes>
|
|
|
|
|
<Analytics />
|
|
|
|
|
<SpeedInsights />
|
|
|
|
|
</Router>
|
|
|
|
|
</AuthProvider>
|
|
|
|
|
</ThemeProvider>
|
2025-07-21 14:53:06 -04:00
|
|
|
</QueryClientProvider>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default App;
|