84 lines
2.8 KiB
TypeScript
84 lines
2.8 KiB
TypeScript
import React from 'react';
|
|
import ReactDOM from 'react-dom/client';
|
|
import { BrowserRouter, Routes, Route, Navigate, useLocation } from 'react-router-dom';
|
|
import './index.css';
|
|
import { SettingsContext, useSettingsProvider } from './hooks/useSettings';
|
|
import { AuthContext, useAuthProvider } from './hooks/useAuth';
|
|
import { useAuth } from './hooks/useAuth';
|
|
import Layout from './components/layout/Layout';
|
|
import Login from './pages/Login';
|
|
import Dashboard from './pages/Dashboard';
|
|
import Projects from './pages/Projects';
|
|
import ProjectDetail from './pages/ProjectDetail';
|
|
import Tools from './pages/Tools';
|
|
import SettingsPage from './pages/Settings';
|
|
import AdminUsers from './pages/AdminUsers';
|
|
|
|
// Guard: must be logged in
|
|
function RequireAuth({ children }: { children: React.ReactNode }) {
|
|
const { user, loading } = useAuth();
|
|
const location = useLocation();
|
|
if (loading) return (
|
|
<div className="min-h-screen bg-base flex items-center justify-center">
|
|
<div className="w-6 h-6 border-2 border-[var(--accent)] border-t-transparent rounded-full animate-spin" />
|
|
</div>
|
|
);
|
|
if (!user) return <Navigate to="/login" state={{ from: location }} replace />;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
// Guard: must be admin
|
|
function RequireAdmin({ children }: { children: React.ReactNode }) {
|
|
const { user, loading } = useAuth();
|
|
if (loading) return null;
|
|
if (!user || user.role !== 'admin') return <Navigate to="/" replace />;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
function App() {
|
|
const authCtx = useAuthProvider();
|
|
const settingsCtx = useSettingsProvider();
|
|
|
|
return (
|
|
<AuthContext.Provider value={authCtx}>
|
|
<SettingsContext.Provider value={settingsCtx}>
|
|
<BrowserRouter>
|
|
<Routes>
|
|
{/* Public */}
|
|
<Route path="/login" element={<Login />} />
|
|
|
|
{/* Protected */}
|
|
<Route path="/" element={
|
|
<RequireAuth>
|
|
<Layout />
|
|
</RequireAuth>
|
|
}>
|
|
<Route index element={<Dashboard />} />
|
|
<Route path="projects" element={<Projects />} />
|
|
<Route path="projects/:id" element={<ProjectDetail />} />
|
|
<Route path="tools" element={<Tools />} />
|
|
|
|
{/* Admin-only routes */}
|
|
<Route path="settings" element={
|
|
<RequireAdmin><SettingsPage /></RequireAdmin>
|
|
} />
|
|
<Route path="admin/users" element={
|
|
<RequireAdmin><AdminUsers /></RequireAdmin>
|
|
} />
|
|
</Route>
|
|
|
|
{/* Catch-all */}
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
</BrowserRouter>
|
|
</SettingsContext.Provider>
|
|
</AuthContext.Provider>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
<React.StrictMode>
|
|
<App />
|
|
</React.StrictMode>
|
|
);
|