diff --git a/src/App.jsx b/src/App.jsx index b52371d..301773a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -50,10 +50,34 @@ const LayoutWrapper = ({ children, currentPageName }) => Layout ? {children} : <>{children}; +// Routes that render for anyone, signed in or not. LandingPage is pure marketing +// markup — it reads no entities and never touches useAuth — so the only thing the +// auth gate ever did for it was bounce visitors to a Google login before they +// could read what the product is. Its CTAs point at /Dashboard, which is still +// gated, so signing in stays one click away. +const PUBLIC_ROUTES = new Set(['/LandingPage']); + +const isPublicRoute = (pathname) => { + // Tolerate a trailing slash so /LandingPage/ isn't quietly sent to login. + const normalized = pathname.length > 1 ? pathname.replace(/\/+$/, '') : pathname; + return PUBLIC_ROUTES.has(normalized); +}; + const AuthenticatedApp = () => { const { isLoadingAuth, isLoadingPublicSettings, authError, isAuthenticated, navigateToLogin } = useAuth(); const location = useLocation(); + // Ahead of both gates below. The spinner would stall this page on a network + // round-trip it has no use for, and the auth gate would redirect away from it — + // either one defeats the point of a public page. + if (isPublicRoute(location.pathname)) { + return ( + + } /> + + ); + } + // Show loading spinner while checking app public settings or auth if (isLoadingPublicSettings || isLoadingAuth) { return ( @@ -92,14 +116,6 @@ const AuthenticatedApp = () => { // Render the main app - if (location.pathname === '/LandingPage') { - return ( - - } /> - - ); - } - return (