LandingPage is pure marketing markup — no entity reads, no useAuth — but it sat below both gates in AuthenticatedApp, so anonymous visitors were held on the spinner for a bootstrap round-trip and then redirected to Google before they could read anything about the product. Check the path against a PUBLIC_ROUTES set first and return early. Its CTAs still point at /Dashboard, which stays gated, so signing in is one click away. Drops the old /LandingPage block that sat below the auth gate; the early return makes it unreachable, as the previous ordering already did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
178 lines
7.6 KiB
JavaScript
178 lines
7.6 KiB
JavaScript
import './App.css'
|
|
import { Toaster } from "@/components/ui/toaster"
|
|
import { QueryClientProvider } from '@tanstack/react-query'
|
|
import { queryClientInstance } from '@/lib/query-client'
|
|
import VisualEditAgent from '@/lib/VisualEditAgent'
|
|
import NavigationTracker from '@/lib/NavigationTracker'
|
|
import { pagesConfig } from './pages.config'
|
|
import { BrowserRouter as Router, Route, Routes, useLocation } from 'react-router-dom';
|
|
import { setupIframeMessaging } from './lib/iframe-messaging';
|
|
import PageNotFound from './lib/PageNotFound';
|
|
import { AuthProvider, useAuth } from '@/lib/AuthContext';
|
|
import ErrorBoundary from '@/lib/ErrorBoundary';
|
|
import { appParams } from '@/lib/app-params';
|
|
import UserNotRegisteredError from '@/components/UserNotRegisteredError';
|
|
import FeatureReport from './pages/FeatureReport';
|
|
import LandingPage from './pages/LandingPage';
|
|
import KrowX from './pages/KrowX';
|
|
import AboutUs from './pages/AboutUs';
|
|
import ThePlatform from './pages/ThePlatform';
|
|
import ForWorkers from './pages/ForWorkers';
|
|
import PredictiveIntelligence from './pages/PredictiveIntelligence';
|
|
import KrowEdgePage from './pages/KrowEdge';
|
|
import Recruiting from './pages/Recruiting';
|
|
import Communications from './pages/Communications';
|
|
import Announcements from './pages/Announcements';
|
|
import StaffProfileDemo from './pages/StaffProfileDemo';
|
|
import CapabilityMap from './pages/CapabilityMap';
|
|
import PlatformOnboarding from './pages/PlatformOnboarding';
|
|
import DailyPL from './pages/DailyPL';
|
|
import Sales from './pages/Sales';
|
|
import PayrollDetail from './pages/PayrollDetail';
|
|
import EmployeeTimecard from './pages/EmployeeTimecard';
|
|
import ClientLaborReport from './pages/ClientLaborReport';
|
|
import WorkforceNetwork from './pages/WorkforceNetwork';
|
|
import KrowAI from './pages/KrowAI';
|
|
import StaffingMarketplace from './pages/StaffingMarketplace';
|
|
import KrowConsole from './pages/KrowConsole';
|
|
import KrowCommand from './pages/KrowCommand';
|
|
import DemandSupply from './pages/DemandSupply';
|
|
import DocumentApproval from './pages/DocumentApproval';
|
|
import VendorReports from './pages/VendorReports';
|
|
|
|
const { Pages, Layout, mainPage } = pagesConfig;
|
|
const mainPageKey = mainPage ?? Object.keys(Pages)[0];
|
|
const MainPage = mainPageKey ? Pages[mainPageKey] : <></>;
|
|
|
|
setupIframeMessaging();
|
|
|
|
const LayoutWrapper = ({ children, currentPageName }) => Layout ?
|
|
<Layout currentPageName={currentPageName}>{children}</Layout>
|
|
: <>{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 (
|
|
<Routes>
|
|
<Route path="/LandingPage" element={<LandingPage />} />
|
|
</Routes>
|
|
);
|
|
}
|
|
|
|
// Show loading spinner while checking app public settings or auth
|
|
if (isLoadingPublicSettings || isLoadingAuth) {
|
|
return (
|
|
<div className="fixed inset-0 flex items-center justify-center">
|
|
<div className="w-8 h-8 border-4 border-slate-200 border-t-slate-800 rounded-full animate-spin"></div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Handle authentication errors
|
|
if (authError) {
|
|
if (authError.type === 'user_not_registered') {
|
|
return <UserNotRegisteredError />;
|
|
} else if (authError.type === 'auth_required') {
|
|
// Redirect to login automatically
|
|
navigateToLogin();
|
|
return null;
|
|
}
|
|
|
|
// Anything else (bad/missing app id, unreachable backend, network failure)
|
|
// used to fall straight through to the app, which then crashed on the empty
|
|
// data and left a blank page. Say what happened instead.
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-slate-50 p-6">
|
|
<div className="max-w-lg w-full bg-white border border-slate-200 rounded-lg shadow-sm p-6">
|
|
<h1 className="text-lg font-semibold text-slate-900">Unable to load the app</h1>
|
|
<p className="mt-2 text-sm text-slate-600">{authError.message || 'The Base44 backend could not be reached.'}</p>
|
|
<p className="mt-4 text-xs text-slate-500">
|
|
App ID: <code>{String(appParams.appId)}</code><br />
|
|
Backend: <code>{String(appParams.serverUrl)}</code>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Render the main app
|
|
|
|
return (
|
|
<LayoutWrapper currentPageName={mainPageKey}>
|
|
<Routes>
|
|
<Route path="/" element={<MainPage />} />
|
|
{Object.entries(Pages).map(([path, Page]) => (
|
|
<Route key={path} path={`/${path}`} element={<Page />} />
|
|
))}
|
|
<Route path="/FeatureReport" element={<FeatureReport />} />
|
|
<Route path="/LandingPage" element={<LandingPage />} />
|
|
<Route path="/KrowX" element={<KrowX />} />
|
|
<Route path="/AboutUs" element={<AboutUs />} />
|
|
<Route path="/ThePlatform" element={<ThePlatform />} />
|
|
<Route path="/ForWorkers" element={<ForWorkers />} />
|
|
<Route path="/PredictiveIntelligence" element={<PredictiveIntelligence />} />
|
|
<Route path="/KrowEdge" element={<KrowEdgePage />} />
|
|
<Route path="/Recruiting" element={<Recruiting />} />
|
|
<Route path="/Communications" element={<Communications />} />
|
|
<Route path="/Announcements" element={<Announcements />} />
|
|
<Route path="/StaffProfileDemo" element={<StaffProfileDemo />} />
|
|
<Route path="/CapabilityMap" element={<CapabilityMap />} />
|
|
<Route path="/PlatformOnboarding" element={<PlatformOnboarding />} />
|
|
<Route path="/DailyPL" element={<DailyPL />} />
|
|
<Route path="/Sales" element={<Sales />} />
|
|
<Route path="/PayrollDetail" element={<PayrollDetail />} />
|
|
<Route path="/EmployeeTimecard" element={<EmployeeTimecard />} />
|
|
<Route path="/ClientLaborReport" element={<ClientLaborReport />} />
|
|
<Route path="/WorkforceNetwork" element={<WorkforceNetwork />} />
|
|
<Route path="/KrowAI" element={<KrowAI />} />
|
|
<Route path="/StaffingMarketplace" element={<StaffingMarketplace />} />
|
|
<Route path="/KrowConsole" element={<KrowConsole />} />
|
|
<Route path="/KrowCommand" element={<KrowCommand />} />
|
|
<Route path="/DemandSupply" element={<DemandSupply />} />
|
|
<Route path="/DocumentApproval" element={<DocumentApproval />} />
|
|
<Route path="/VendorReports" element={<VendorReports />} />
|
|
<Route path="*" element={<PageNotFound />} />
|
|
</Routes>
|
|
</LayoutWrapper>
|
|
);
|
|
};
|
|
|
|
|
|
function App() {
|
|
|
|
return (
|
|
<ErrorBoundary>
|
|
<AuthProvider>
|
|
<QueryClientProvider client={queryClientInstance}>
|
|
<Router>
|
|
<NavigationTracker />
|
|
<AuthenticatedApp />
|
|
</Router>
|
|
<Toaster />
|
|
<VisualEditAgent />
|
|
</QueryClientProvider>
|
|
</AuthProvider>
|
|
</ErrorBoundary>
|
|
)
|
|
}
|
|
|
|
export default App |