initial design
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
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>
|
||||
);
|
||||
Reference in New Issue
Block a user