Lazy loading for smaller build chunks/faster initial load
This commit is contained in:
@@ -1,27 +1,41 @@
|
|||||||
import { Routes, Route, useNavigate, Navigate, useLocation } from 'react-router-dom';
|
import { Routes, Route, useNavigate, Navigate, useLocation } from 'react-router-dom';
|
||||||
import { MainLayout } from './components/layout/MainLayout';
|
import { MainLayout } from './components/layout/MainLayout';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import { Products } from './pages/Products';
|
|
||||||
import { Overview } from './pages/Overview';
|
|
||||||
import { Settings } from './pages/Settings';
|
|
||||||
import { Analytics } from './pages/Analytics';
|
|
||||||
import { Toaster } from '@/components/ui/sonner';
|
import { Toaster } from '@/components/ui/sonner';
|
||||||
import PurchaseOrders from './pages/PurchaseOrders';
|
import { useEffect, Suspense, lazy } from 'react';
|
||||||
import { Login } from './pages/Login';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import config from './config';
|
import config from './config';
|
||||||
import { RequireAuth } from './components/auth/RequireAuth';
|
import { RequireAuth } from './components/auth/RequireAuth';
|
||||||
import Forecasting from "@/pages/Forecasting";
|
|
||||||
import { Vendors } from '@/pages/Vendors';
|
|
||||||
import { Categories } from '@/pages/Categories';
|
|
||||||
import { Import } from '@/pages/Import';
|
|
||||||
import { AuthProvider } from './contexts/AuthContext';
|
import { AuthProvider } from './contexts/AuthContext';
|
||||||
import { Protected } from './components/auth/Protected';
|
import { Protected } from './components/auth/Protected';
|
||||||
import { FirstAccessiblePage } from './components/auth/FirstAccessiblePage';
|
import { FirstAccessiblePage } from './components/auth/FirstAccessiblePage';
|
||||||
import { Brands } from '@/pages/Brands';
|
import { PageLoading } from '@/components/ui/page-loading';
|
||||||
import { Chat } from '@/pages/Chat';
|
|
||||||
import { Dashboard } from '@/pages/Dashboard';
|
// Always loaded components (Login and Settings stay in main bundle)
|
||||||
import { SmallDashboard } from '@/pages/SmallDashboard';
|
import { Settings } from './pages/Settings';
|
||||||
|
import { Login } from './pages/Login';
|
||||||
|
|
||||||
|
// Lazy load the 4 main chunks you wanted:
|
||||||
|
|
||||||
|
// 1. Core inventory app - loaded as one chunk when any inventory page is accessed
|
||||||
|
const Overview = lazy(() => import('./pages/Overview'));
|
||||||
|
const Products = lazy(() => import('./pages/Products').then(module => ({ default: module.Products })));
|
||||||
|
const Analytics = lazy(() => import('./pages/Analytics').then(module => ({ default: module.Analytics })));
|
||||||
|
const Forecasting = lazy(() => import('./pages/Forecasting'));
|
||||||
|
const Vendors = lazy(() => import('./pages/Vendors'));
|
||||||
|
const Categories = lazy(() => import('./pages/Categories'));
|
||||||
|
const Brands = lazy(() => import('./pages/Brands'));
|
||||||
|
const PurchaseOrders = lazy(() => import('./pages/PurchaseOrders'));
|
||||||
|
|
||||||
|
// 2. Dashboard app - separate chunk
|
||||||
|
const Dashboard = lazy(() => import('./pages/Dashboard'));
|
||||||
|
const SmallDashboard = lazy(() => import('./pages/SmallDashboard'));
|
||||||
|
|
||||||
|
// 3. Product import - separate chunk
|
||||||
|
const Import = lazy(() => import('./pages/Import').then(module => ({ default: module.Import })));
|
||||||
|
|
||||||
|
// 4. Chat archive - separate chunk
|
||||||
|
const Chat = lazy(() => import('./pages/Chat').then(module => ({ default: module.Chat })));
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@@ -75,78 +89,117 @@ function App() {
|
|||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<Toaster richColors position="top-center" />
|
<Toaster richColors position="top-center" />
|
||||||
<Routes>
|
<Routes>
|
||||||
|
{/* Always loaded routes */}
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/small" element={<SmallDashboard />} />
|
<Route path="/small" element={
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
|
<SmallDashboard />
|
||||||
|
</Suspense>
|
||||||
|
} />
|
||||||
<Route element={
|
<Route element={
|
||||||
<RequireAuth>
|
<RequireAuth>
|
||||||
<MainLayout />
|
<MainLayout />
|
||||||
</RequireAuth>
|
</RequireAuth>
|
||||||
}>
|
}>
|
||||||
|
{/* Core inventory app routes - will be lazy loaded */}
|
||||||
<Route index element={
|
<Route index element={
|
||||||
<Protected page="dashboard" fallback={<FirstAccessiblePage />}>
|
<Protected page="dashboard" fallback={<FirstAccessiblePage />}>
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Overview />
|
<Overview />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
<Route path="/" element={
|
<Route path="/" element={
|
||||||
<Protected page="dashboard">
|
<Protected page="dashboard">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Overview />
|
<Overview />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
<Route path="/products" element={
|
<Route path="/products" element={
|
||||||
<Protected page="products">
|
<Protected page="products">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Products />
|
<Products />
|
||||||
</Protected>
|
</Suspense>
|
||||||
} />
|
|
||||||
<Route path="/import" element={
|
|
||||||
<Protected page="import">
|
|
||||||
<Import />
|
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
<Route path="/categories" element={
|
<Route path="/categories" element={
|
||||||
<Protected page="categories">
|
<Protected page="categories">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Categories />
|
<Categories />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
<Route path="/vendors" element={
|
<Route path="/vendors" element={
|
||||||
<Protected page="vendors">
|
<Protected page="vendors">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Vendors />
|
<Vendors />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
<Route path="/brands" element={
|
<Route path="/brands" element={
|
||||||
<Protected page="brands">
|
<Protected page="brands">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Brands />
|
<Brands />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
<Route path="/purchase-orders" element={
|
<Route path="/purchase-orders" element={
|
||||||
<Protected page="purchase_orders">
|
<Protected page="purchase_orders">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<PurchaseOrders />
|
<PurchaseOrders />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
<Route path="/analytics" element={
|
<Route path="/analytics" element={
|
||||||
<Protected page="analytics">
|
<Protected page="analytics">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Analytics />
|
<Analytics />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
|
<Route path="/forecasting" element={
|
||||||
|
<Protected page="forecasting">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
|
<Forecasting />
|
||||||
|
</Suspense>
|
||||||
|
</Protected>
|
||||||
|
} />
|
||||||
|
|
||||||
|
{/* Always loaded settings */}
|
||||||
<Route path="/settings" element={
|
<Route path="/settings" element={
|
||||||
<Protected page="settings">
|
<Protected page="settings">
|
||||||
<Settings />
|
<Settings />
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
<Route path="/forecasting" element={
|
|
||||||
<Protected page="forecasting">
|
{/* Product import - separate chunk */}
|
||||||
<Forecasting />
|
<Route path="/import" element={
|
||||||
|
<Protected page="import">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
|
<Import />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
|
{/* Chat archive - separate chunk */}
|
||||||
<Route path="/chat" element={
|
<Route path="/chat" element={
|
||||||
<Protected page="chat">
|
<Protected page="chat">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Chat />
|
<Chat />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
|
{/* Dashboard app - separate chunk */}
|
||||||
<Route path="/dashboard" element={
|
<Route path="/dashboard" element={
|
||||||
<Protected page="dashboard">
|
<Protected page="dashboard">
|
||||||
|
<Suspense fallback={<PageLoading />}>
|
||||||
<Dashboard />
|
<Dashboard />
|
||||||
|
</Suspense>
|
||||||
</Protected>
|
</Protected>
|
||||||
} />
|
} />
|
||||||
|
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
31
inventory/src/components/ui/page-loading.tsx
Normal file
31
inventory/src/components/ui/page-loading.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
|
interface PageLoadingProps {
|
||||||
|
message?: string;
|
||||||
|
size?: 'sm' | 'md' | 'lg';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PageLoading = ({ message = 'Loading...', size = 'md' }: PageLoadingProps) => {
|
||||||
|
const sizeClasses = {
|
||||||
|
sm: 'h-6 w-6',
|
||||||
|
md: 'h-8 w-8',
|
||||||
|
lg: 'h-12 w-12'
|
||||||
|
};
|
||||||
|
|
||||||
|
const containerClasses = {
|
||||||
|
sm: 'min-h-[200px]',
|
||||||
|
md: 'min-h-[400px]',
|
||||||
|
lg: 'min-h-[600px]'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex items-center justify-center ${containerClasses[size]}`}>
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<Loader2 className={`animate-spin text-primary ${sizeClasses[size]}`} />
|
||||||
|
<p className="text-sm text-muted-foreground animate-pulse">{message}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageLoading;
|
||||||
File diff suppressed because one or more lines are too long
@@ -221,10 +221,14 @@ export default defineConfig(({ mode }) => {
|
|||||||
build: {
|
build: {
|
||||||
outDir: "build",
|
outDir: "build",
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
|
chunkSizeWarningLimit: 1000,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
manualChunks: {
|
manualChunks: {
|
||||||
vendor: ["react", "react-dom", "react-router-dom"],
|
// Simple static chunking approach - safer than function-based chunking
|
||||||
|
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
|
||||||
|
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu', 'lucide-react'],
|
||||||
|
'query-vendor': ['@tanstack/react-query'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user