111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
import React, { useEffect } from "react";
|
|
import { Routes, Route, Navigate, useNavigate } from "react-router-dom";
|
|
import { ToastContainer } from "react-toastify";
|
|
import "react-toastify/dist/ReactToastify.css";
|
|
import { AuthContext, useAuthProvider } from "./hooks/useAuth";
|
|
import { ThemeProvider, useTheme } from "./hooks/useTheme";
|
|
import { Layout } from "./components/Layout";
|
|
import { Login } from "./pages/Login";
|
|
import { Overview } from "./pages/Overview";
|
|
import { Dashboard } from "./pages/Dashboard";
|
|
import { PreviewDetail } from "./pages/PreviewDetail";
|
|
import { Settings } from "./pages/Settings";
|
|
import { Repos } from "./pages/Repos";
|
|
import { RepoConfig } from "./pages/RepoConfig";
|
|
import { Admin } from "./pages/Admin";
|
|
import { SetupWizard } from "./pages/SetupWizard";
|
|
import { Privacy } from "./pages/Privacy";
|
|
import { Loader2 } from "lucide-react";
|
|
|
|
function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
|
const { user, loading } = React.useContext(AuthContext);
|
|
const navigate = useNavigate();
|
|
|
|
useEffect(() => {
|
|
if (!loading && !user) navigate("/login");
|
|
}, [user, loading, navigate]);
|
|
|
|
if (loading) return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<Loader2 size={32} className="animate-spin text-blue-600 dark:text-blue-400" />
|
|
</div>
|
|
);
|
|
|
|
if (!user) return null;
|
|
return <>{children}</>;
|
|
}
|
|
|
|
function SetupCheck({ children }: { children: React.ReactNode }) {
|
|
const { user } = React.useContext(AuthContext);
|
|
const navigate = useNavigate();
|
|
|
|
useEffect(() => {
|
|
if (user && !user.setupComplete) {
|
|
const path = window.location.pathname;
|
|
if (path !== "/setup" && path !== "/settings" && path !== "/privacy") {
|
|
navigate("/setup");
|
|
}
|
|
}
|
|
}, [user, navigate]);
|
|
|
|
return <>{children}</>;
|
|
}
|
|
|
|
function AppContent({ auth }: { auth: ReturnType<typeof useAuthProvider> }) {
|
|
const { resolvedTheme } = useTheme();
|
|
|
|
return (
|
|
<AuthContext.Provider value={auth}>
|
|
<ToastContainer
|
|
position="top-right"
|
|
autoClose={4000}
|
|
hideProgressBar={false}
|
|
newestOnTop
|
|
closeOnClick
|
|
theme={resolvedTheme}
|
|
/>
|
|
<Routes>
|
|
<Route path="/login" element={
|
|
auth.user ? <Navigate to="/" replace /> : <Login />
|
|
} />
|
|
<Route path="/privacy" element={<Layout><Privacy /></Layout>} />
|
|
|
|
<Route path="/setup" element={
|
|
<ProtectedRoute>
|
|
<SetupWizard />
|
|
</ProtectedRoute>
|
|
} />
|
|
|
|
<Route path="/*" element={
|
|
<ProtectedRoute>
|
|
<SetupCheck>
|
|
<Layout>
|
|
<Routes>
|
|
<Route path="/" element={<Overview />} />
|
|
<Route path="/previews" element={<Dashboard />} />
|
|
<Route path="/previews/:id" element={<PreviewDetail />} />
|
|
<Route path="/repos" element={<Repos />} />
|
|
<Route path="/repos/:owner/:repo" element={<RepoConfig />} />
|
|
<Route path="/settings" element={<Settings />} />
|
|
<Route path="/admin" element={<Admin />} />
|
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
</Routes>
|
|
</Layout>
|
|
</SetupCheck>
|
|
</ProtectedRoute>
|
|
} />
|
|
</Routes>
|
|
</AuthContext.Provider>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
const auth = useAuthProvider();
|
|
|
|
return (
|
|
<ThemeProvider>
|
|
<AppContent auth={auth} />
|
|
</ThemeProvider>
|
|
);
|
|
}
|