Files
thrilltrack-explorer/src/App.tsx
gpt-engineer-app[bot] ee09e3652c feat: Add error boundaries
2025-11-03 14:51:39 +00:00

336 lines
14 KiB
TypeScript

import * as React from "react";
import { lazy, Suspense } from "react";
import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { AuthProvider } from "@/hooks/useAuth";
import { AuthModalProvider } from "@/contexts/AuthModalContext";
import { LocationAutoDetectProvider } from "@/components/providers/LocationAutoDetectProvider";
import { AnalyticsWrapper } from "@/components/analytics/AnalyticsWrapper";
import { Footer } from "@/components/layout/Footer";
import { PageLoader } from "@/components/loading/PageSkeletons";
import { RouteErrorBoundary } from "@/components/error/RouteErrorBoundary";
import { AdminErrorBoundary } from "@/components/error/AdminErrorBoundary";
import { EntityErrorBoundary } from "@/components/error/EntityErrorBoundary";
// Core routes (eager-loaded for best UX)
import Index from "./pages/Index";
import Parks from "./pages/Parks";
import Rides from "./pages/Rides";
import Search from "./pages/Search";
import Auth from "./pages/Auth";
// Detail routes (lazy-loaded)
const ParkDetail = lazy(() => import("./pages/ParkDetail"));
const RideDetail = lazy(() => import("./pages/RideDetail"));
const ParkRides = lazy(() => import("./pages/ParkRides"));
const Manufacturers = lazy(() => import("./pages/Manufacturers"));
const ManufacturerDetail = lazy(() => import("./pages/ManufacturerDetail"));
const ManufacturerRides = lazy(() => import("./pages/ManufacturerRides"));
const ManufacturerModels = lazy(() => import("./pages/ManufacturerModels"));
const RideModelDetail = lazy(() => import("./pages/RideModelDetail"));
const RideModelRides = lazy(() => import("./pages/RideModelRides"));
const Designers = lazy(() => import("./pages/Designers"));
const DesignerDetail = lazy(() => import("./pages/DesignerDetail"));
const DesignerRides = lazy(() => import("./pages/DesignerRides"));
const ParkOwners = lazy(() => import("./pages/ParkOwners"));
const PropertyOwnerDetail = lazy(() => import("./pages/PropertyOwnerDetail"));
const OwnerParks = lazy(() => import("./pages/OwnerParks"));
const Operators = lazy(() => import("./pages/Operators"));
const OperatorDetail = lazy(() => import("./pages/OperatorDetail"));
const OperatorParks = lazy(() => import("./pages/OperatorParks"));
const BlogIndex = lazy(() => import("./pages/BlogIndex"));
const BlogPost = lazy(() => import("./pages/BlogPost"));
const Terms = lazy(() => import("./pages/Terms"));
const Privacy = lazy(() => import("./pages/Privacy"));
const SubmissionGuidelines = lazy(() => import("./pages/SubmissionGuidelines"));
const Contact = lazy(() => import("./pages/Contact"));
// Admin routes (lazy-loaded - heavy bundle)
const AdminDashboard = lazy(() => import("./pages/AdminDashboard"));
const AdminModeration = lazy(() => import("./pages/AdminModeration"));
const AdminReports = lazy(() => import("./pages/AdminReports"));
const AdminSystemLog = lazy(() => import("./pages/AdminSystemLog"));
const AdminUsers = lazy(() => import("./pages/AdminUsers"));
const AdminBlog = lazy(() => import("./pages/AdminBlog"));
const AdminSettings = lazy(() => import("./pages/AdminSettings"));
const AdminContact = lazy(() => import("./pages/admin/AdminContact"));
const AdminEmailSettings = lazy(() => import("./pages/admin/AdminEmailSettings"));
// User routes (lazy-loaded)
const Profile = lazy(() => import("./pages/Profile"));
const UserSettings = lazy(() => import("./pages/UserSettings"));
const AuthCallback = lazy(() => import("./pages/AuthCallback"));
// Utility routes (lazy-loaded)
const NotFound = lazy(() => import("./pages/NotFound"));
const ForceLogout = lazy(() => import("./pages/ForceLogout"));
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false, // Disable automatic refetch on tab focus
refetchOnMount: true, // Keep refetch on component mount
refetchOnReconnect: true, // Keep refetch on network reconnect
retry: 1, // Keep retry attempts
staleTime: 30000, // 30 seconds - queries stay fresh for 30s
gcTime: 5 * 60 * 1000, // 5 minutes - keep in cache for 5 mins
},
},
});
function AppContent(): React.JSX.Element {
return (
<TooltipProvider>
<BrowserRouter>
<LocationAutoDetectProvider />
<Toaster />
<Sonner />
<div className="min-h-screen flex flex-col">
<div className="flex-1">
<Suspense fallback={<PageLoader />}>
<RouteErrorBoundary>
<Routes>
{/* Core routes - eager loaded */}
<Route path="/" element={<Index />} />
<Route path="/parks" element={<Parks />} />
<Route path="/rides" element={<Rides />} />
<Route path="/search" element={<Search />} />
<Route path="/auth" element={<Auth />} />
{/* Detail routes with entity error boundaries */}
<Route
path="/parks/:slug"
element={
<EntityErrorBoundary entityType="park">
<ParkDetail />
</EntityErrorBoundary>
}
/>
<Route
path="/parks/:parkSlug/rides"
element={
<EntityErrorBoundary entityType="park">
<ParkRides />
</EntityErrorBoundary>
}
/>
<Route
path="/parks/:parkSlug/rides/:rideSlug"
element={
<EntityErrorBoundary entityType="ride">
<RideDetail />
</EntityErrorBoundary>
}
/>
<Route path="/manufacturers" element={<Manufacturers />} />
<Route
path="/manufacturers/:slug"
element={
<EntityErrorBoundary entityType="manufacturer">
<ManufacturerDetail />
</EntityErrorBoundary>
}
/>
<Route
path="/manufacturers/:manufacturerSlug/rides"
element={
<EntityErrorBoundary entityType="manufacturer">
<ManufacturerRides />
</EntityErrorBoundary>
}
/>
<Route
path="/manufacturers/:manufacturerSlug/models"
element={
<EntityErrorBoundary entityType="manufacturer">
<ManufacturerModels />
</EntityErrorBoundary>
}
/>
<Route
path="/manufacturers/:manufacturerSlug/models/:modelSlug"
element={
<EntityErrorBoundary entityType="manufacturer">
<RideModelDetail />
</EntityErrorBoundary>
}
/>
<Route
path="/manufacturers/:manufacturerSlug/models/:modelSlug/rides"
element={
<EntityErrorBoundary entityType="manufacturer">
<RideModelRides />
</EntityErrorBoundary>
}
/>
<Route path="/designers" element={<Designers />} />
<Route
path="/designers/:slug"
element={
<EntityErrorBoundary entityType="designer">
<DesignerDetail />
</EntityErrorBoundary>
}
/>
<Route
path="/designers/:designerSlug/rides"
element={
<EntityErrorBoundary entityType="designer">
<DesignerRides />
</EntityErrorBoundary>
}
/>
<Route path="/owners" element={<ParkOwners />} />
<Route
path="/owners/:slug"
element={
<EntityErrorBoundary entityType="owner">
<PropertyOwnerDetail />
</EntityErrorBoundary>
}
/>
<Route
path="/owners/:ownerSlug/parks"
element={
<EntityErrorBoundary entityType="owner">
<OwnerParks />
</EntityErrorBoundary>
}
/>
<Route path="/operators" element={<Operators />} />
<Route
path="/operators/:slug"
element={
<EntityErrorBoundary entityType="operator">
<OperatorDetail />
</EntityErrorBoundary>
}
/>
<Route
path="/operators/:operatorSlug/parks"
element={
<EntityErrorBoundary entityType="operator">
<OperatorParks />
</EntityErrorBoundary>
}
/>
<Route path="/blog" element={<BlogIndex />} />
<Route path="/blog/:slug" element={<BlogPost />} />
<Route path="/terms" element={<Terms />} />
<Route path="/privacy" element={<Privacy />} />
<Route path="/submission-guidelines" element={<SubmissionGuidelines />} />
<Route path="/contact" element={<Contact />} />
{/* User routes - lazy loaded */}
<Route path="/auth/callback" element={<AuthCallback />} />
<Route path="/profile" element={<Profile />} />
<Route path="/profile/:username" element={<Profile />} />
<Route path="/settings" element={<UserSettings />} />
{/* Admin routes with admin error boundaries */}
<Route
path="/admin"
element={
<AdminErrorBoundary section="Dashboard">
<AdminDashboard />
</AdminErrorBoundary>
}
/>
<Route
path="/admin/moderation"
element={
<AdminErrorBoundary section="Moderation Queue">
<AdminModeration />
</AdminErrorBoundary>
}
/>
<Route
path="/admin/reports"
element={
<AdminErrorBoundary section="Reports">
<AdminReports />
</AdminErrorBoundary>
}
/>
<Route
path="/admin/system-log"
element={
<AdminErrorBoundary section="System Log">
<AdminSystemLog />
</AdminErrorBoundary>
}
/>
<Route
path="/admin/users"
element={
<AdminErrorBoundary section="User Management">
<AdminUsers />
</AdminErrorBoundary>
}
/>
<Route
path="/admin/blog"
element={
<AdminErrorBoundary section="Blog Management">
<AdminBlog />
</AdminErrorBoundary>
}
/>
<Route
path="/admin/settings"
element={
<AdminErrorBoundary section="Settings">
<AdminSettings />
</AdminErrorBoundary>
}
/>
<Route
path="/admin/contact"
element={
<AdminErrorBoundary section="Contact Management">
<AdminContact />
</AdminErrorBoundary>
}
/>
<Route
path="/admin/email-settings"
element={
<AdminErrorBoundary section="Email Settings">
<AdminEmailSettings />
</AdminErrorBoundary>
}
/>
{/* Utility routes - lazy loaded */}
<Route path="/force-logout" element={<ForceLogout />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</RouteErrorBoundary>
</Suspense>
</div>
<Footer />
</div>
</BrowserRouter>
</TooltipProvider>
);
}
const App = (): React.JSX.Element => (
<QueryClientProvider client={queryClient}>
<AuthProvider>
<AuthModalProvider>
<AppContent />
</AuthModalProvider>
</AuthProvider>
{import.meta.env.DEV && <ReactQueryDevtools initialIsOpen={false} position="bottom" />}
<AnalyticsWrapper />
</QueryClientProvider>
);
export default App;