import React, { useState, useEffect, useRef, useCallback, Suspense } from "react"; import { Link, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom"; import Home from "./pages/Home"; import SearchResults from "./pages/SearchResults"; import TechniquePage from "./pages/TechniquePage"; import CreatorsBrowse from "./pages/CreatorsBrowse"; import CreatorDetail from "./pages/CreatorDetail"; import TopicsBrowse from "./pages/TopicsBrowse"; import SubTopicPage from "./pages/SubTopicPage"; import Login from "./pages/Login"; import Register from "./pages/Register"; // Lazy-loaded pages — admin, creator, and info routes split into separate chunks const AdminReports = React.lazy(() => import("./pages/AdminReports")); const AdminPipeline = React.lazy(() => import("./pages/AdminPipeline")); const AdminTechniquePages = React.lazy(() => import("./pages/AdminTechniquePages")); const About = React.lazy(() => import("./pages/About")); const CreatorDashboard = React.lazy(() => import("./pages/CreatorDashboard")); const CreatorSettings = React.lazy(() => import("./pages/CreatorSettings")); const ConsentDashboard = React.lazy(() => import("./pages/ConsentDashboard")); const WatchPage = React.lazy(() => import("./pages/WatchPage")); const AdminUsers = React.lazy(() => import("./pages/AdminUsers")); const AdminAuditLog = React.lazy(() => import("./pages/AdminAuditLog")); const ChatPage = React.lazy(() => import("./pages/ChatPage")); const ChapterReview = React.lazy(() => import("./pages/ChapterReview")); import AdminDropdown from "./components/AdminDropdown"; import ImpersonationBanner from "./components/ImpersonationBanner"; import AppFooter from "./components/AppFooter"; import SearchAutocomplete from "./components/SearchAutocomplete"; import ProtectedRoute from "./components/ProtectedRoute"; import { AuthProvider, useAuth } from "./context/AuthContext"; function LoadingFallback() { return (

Loading…

); } function AuthNav() { const { isAuthenticated, user, logout } = useAuth(); const navigate = useNavigate(); if (isAuthenticated) { return ( <> {user?.display_name ?? "Dashboard"} ); } return Login; } function AppShell() { const location = useLocation(); const navigate = useNavigate(); const showNavSearch = location.pathname !== "/"; const [menuOpen, setMenuOpen] = useState(false); const headerRef = useRef(null); // Close menu on route change useEffect(() => { setMenuOpen(false); }, [location.pathname]); // Close menu on Escape useEffect(() => { if (!menuOpen) return; const handleKey = (e: KeyboardEvent) => { if (e.key === "Escape") setMenuOpen(false); }; document.addEventListener("keydown", handleKey); return () => document.removeEventListener("keydown", handleKey); }, [menuOpen]); // Close menu on outside click const handleOutsideClick = useCallback( (e: MouseEvent) => { if ( menuOpen && headerRef.current && !headerRef.current.contains(e.target as Node) ) { setMenuOpen(false); } }, [menuOpen], ); useEffect(() => { if (!menuOpen) return; document.addEventListener("mousedown", handleOutsideClick); return () => document.removeEventListener("mousedown", handleOutsideClick); }, [menuOpen, handleOutsideClick]); return (
Skip to content
Chrysopedia {showNavSearch && ( navigate(`/search?q=${encodeURIComponent(q)}`)} /> )}
{/* Public routes */} } /> } /> } /> }>} /> }>} /> {/* Browse routes */} } /> } /> } /> } /> {/* Admin routes */} }>} /> }>} /> }>} /> }>} /> }>} /> {/* Info routes */} }>} /> {/* Auth routes */} } /> } /> {/* Creator routes (protected) */} }>} /> }>} /> }>} /> }>} /> }>} /> {/* Fallback */} } />
); } export default function App() { return ( ); }