diff --git a/LAZY_LOADING.md b/LAZY_LOADING.md
new file mode 100644
index 0000000..6a41ddc
--- /dev/null
+++ b/LAZY_LOADING.md
@@ -0,0 +1,131 @@
+# Lazy Loading Implementation
+
+## Overview
+
+The application now implements lazy loading (code splitting) for all view components to significantly improve initial load time. Each view is loaded on-demand only when the user navigates to it.
+
+## How It Works
+
+### React.lazy()
+All view components are wrapped with `React.lazy()` which enables dynamic imports:
+
+```typescript
+const DashboardView = lazy(() => import('@/components/views').then(m => ({ default: m.DashboardView })))
+```
+
+### Suspense Boundary
+The `ViewRouter` component wraps all lazy-loaded views in a `Suspense` boundary with a loading fallback:
+
+```typescript
+}>
+ {renderView()}
+
+```
+
+### Smart Preloading
+The application implements intelligent preloading strategies to make navigation feel instant:
+
+1. **Idle Preloading**: After 2 seconds of initial load, the most commonly used views are preloaded in the background
+2. **Hover Preloading**: When hovering over navigation items, the corresponding view starts loading immediately
+3. **Deduplication**: Views are only preloaded once and tracked to avoid redundant downloads
+
+## Preloading Strategy
+
+### Automatic Preloading
+Common views are automatically preloaded after initial page load:
+- Timesheets
+- Billing
+- Reports
+- Missing Timesheets Report
+
+### Hover-Based Preloading
+All navigation items trigger view preloading on hover, making navigation feel instant for users who hover before clicking.
+
+## Benefits
+
+1. **Faster Initial Load**: Only the dashboard view loads initially, not all 26+ views
+2. **Reduced Bundle Size**: Main bundle is smaller, improving Time to Interactive (TTI)
+3. **Progressive Loading**: Views load as needed, spreading the load over time
+4. **Better Performance**: Reduces memory usage and parsing time on initial load
+5. **Automatic Code Splitting**: Vite automatically creates separate chunks for each lazy-loaded component
+6. **Instant Navigation**: Preloading ensures commonly used views are ready immediately
+7. **Smooth UX**: Loading states provide feedback when views aren't yet loaded
+
+## Lazy-Loaded Views
+
+All views are now lazy-loaded:
+
+### Core Views
+- Dashboard
+- Timesheets
+- Billing
+- Payroll
+- Compliance
+- Expenses
+- Reports
+
+### Feature Views
+- Currency Management
+- Email Template Manager
+- Invoice Template Manager
+- QR Timesheet Scanner
+- Missing Timesheets Report
+- Purchase Order Manager
+- Onboarding Workflow Manager
+- Audit Trail Viewer
+- Notification Rules Manager
+- Batch Import Manager
+- Rate Template Manager
+- Custom Report Builder
+- Holiday Pay Manager
+- Contract Validator
+- Shift Pattern Manager
+- Query Language Guide
+- Roadmap View
+- Component Showcase
+- Business Logic Demo
+
+## Loading States
+
+When navigating to a new view:
+1. User clicks navigation item
+2. Loading spinner appears (centered, large size)
+3. View chunk downloads (if not cached)
+4. View renders and replaces loading spinner
+
+The loading fallback uses a minimum height of 400px to prevent layout shift.
+
+## Performance Metrics
+
+### Before Lazy Loading
+- Initial bundle includes all 26+ views
+- Larger initial download size
+- Longer Time to Interactive
+
+### After Lazy Loading
+- Initial bundle includes only essential code + dashboard
+- Views load in separate chunks (typically 5-50KB each)
+- Faster initial page load
+- Views cached after first load
+- Hover preloading makes navigation feel instant
+
+## Browser Caching
+
+Once a view chunk is loaded, it's cached by the browser and won't need to be re-downloaded on subsequent navigations.
+
+## Implementation Files
+
+- `/src/components/ViewRouter.tsx` - Lazy loading and suspense boundary
+- `/src/lib/view-preloader.ts` - Preloading logic and view map
+- `/src/hooks/use-view-preload.ts` - Idle preloading hook
+- `/src/components/nav/NavItem.tsx` - Hover-based preloading integration
+
+## Future Improvements
+
+Potential optimizations:
+- **Usage Analytics**: Track which views are most commonly accessed and adjust preloading strategy
+- **Network-Aware Loading**: Adjust preloading behavior based on connection speed
+- **Progressive Hydration**: Load critical views first, defer others
+- **Bundle size monitoring**: Track chunk sizes to keep them optimized
+- **Predictive Preloading**: Learn user navigation patterns and preload accordingly
+
diff --git a/src/App.tsx b/src/App.tsx
index a646754..7bb8c5a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -3,6 +3,7 @@ import { useSampleData } from '@/hooks/use-sample-data'
import { useNotifications } from '@/hooks/use-notifications'
import { useAppData } from '@/hooks/use-app-data'
import { useAppActions } from '@/hooks/use-app-actions'
+import { useViewPreload } from '@/hooks/use-view-preload'
import { Sidebar } from '@/components/navigation'
import { NotificationCenter } from '@/components/NotificationCenter'
import { ViewRouter } from '@/components/ViewRouter'
@@ -11,6 +12,7 @@ export type View = 'dashboard' | 'timesheets' | 'billing' | 'payroll' | 'complia
function App() {
useSampleData()
+ useViewPreload()
const [currentView, setCurrentView] = useState('dashboard')
const [currentEntity, setCurrentEntity] = useState('Main Agency')
diff --git a/src/components/ViewRouter.tsx b/src/components/ViewRouter.tsx
index 1e052b8..a5f50d8 100644
--- a/src/components/ViewRouter.tsx
+++ b/src/components/ViewRouter.tsx
@@ -1,3 +1,4 @@
+import { lazy, Suspense } from 'react'
import type { View } from '@/App'
import type {
Timesheet,
@@ -9,36 +10,36 @@ import type {
RateCard,
DashboardMetrics
} from '@/lib/types'
-import {
- DashboardView,
- TimesheetsView,
- BillingView,
- PayrollView,
- ComplianceView,
- ExpensesView
-} from '@/components/views'
-import { ReportsView } from '@/components/ReportsView'
-import { CurrencyManagement } from '@/components/CurrencyManagement'
-import { EmailTemplateManager } from '@/components/EmailTemplateManager'
-import { InvoiceTemplateManager } from '@/components/InvoiceTemplateManager'
-import { QRTimesheetScanner } from '@/components/QRTimesheetScanner'
-import { MissingTimesheetsReport } from '@/components/MissingTimesheetsReport'
-import { PurchaseOrderManager } from '@/components/PurchaseOrderManager'
-import { OnboardingWorkflowManager } from '@/components/OnboardingWorkflowManager'
-import { AuditTrailViewer } from '@/components/AuditTrailViewer'
-import { NotificationRulesManager } from '@/components/NotificationRulesManager'
-import { BatchImportManager } from '@/components/BatchImportManager'
-import { RateTemplateManager } from '@/components/RateTemplateManager'
-import { CustomReportBuilder } from '@/components/CustomReportBuilder'
-import { HolidayPayManager } from '@/components/HolidayPayManager'
-import { ContractValidator } from '@/components/ContractValidator'
-import { ShiftPatternManager } from '@/components/ShiftPatternManager'
-import { QueryLanguageGuide } from '@/components/QueryLanguageGuide'
-import { RoadmapView } from '@/components/roadmap-view'
-import { ComponentShowcase } from '@/components/ComponentShowcase'
-import { BusinessLogicDemo } from '@/components/BusinessLogicDemo'
+import { LoadingSpinner } from '@/components/ui/loading-spinner'
import { toast } from 'sonner'
+const DashboardView = lazy(() => import('@/components/views').then(m => ({ default: m.DashboardView })))
+const TimesheetsView = lazy(() => import('@/components/views').then(m => ({ default: m.TimesheetsView })))
+const BillingView = lazy(() => import('@/components/views').then(m => ({ default: m.BillingView })))
+const PayrollView = lazy(() => import('@/components/views').then(m => ({ default: m.PayrollView })))
+const ComplianceView = lazy(() => import('@/components/views').then(m => ({ default: m.ComplianceView })))
+const ExpensesView = lazy(() => import('@/components/views').then(m => ({ default: m.ExpensesView })))
+const ReportsView = lazy(() => import('@/components/ReportsView').then(m => ({ default: m.ReportsView })))
+const CurrencyManagement = lazy(() => import('@/components/CurrencyManagement').then(m => ({ default: m.CurrencyManagement })))
+const EmailTemplateManager = lazy(() => import('@/components/EmailTemplateManager').then(m => ({ default: m.EmailTemplateManager })))
+const InvoiceTemplateManager = lazy(() => import('@/components/InvoiceTemplateManager').then(m => ({ default: m.InvoiceTemplateManager })))
+const QRTimesheetScanner = lazy(() => import('@/components/QRTimesheetScanner').then(m => ({ default: m.QRTimesheetScanner })))
+const MissingTimesheetsReport = lazy(() => import('@/components/MissingTimesheetsReport').then(m => ({ default: m.MissingTimesheetsReport })))
+const PurchaseOrderManager = lazy(() => import('@/components/PurchaseOrderManager').then(m => ({ default: m.PurchaseOrderManager })))
+const OnboardingWorkflowManager = lazy(() => import('@/components/OnboardingWorkflowManager').then(m => ({ default: m.OnboardingWorkflowManager })))
+const AuditTrailViewer = lazy(() => import('@/components/AuditTrailViewer').then(m => ({ default: m.AuditTrailViewer })))
+const NotificationRulesManager = lazy(() => import('@/components/NotificationRulesManager').then(m => ({ default: m.NotificationRulesManager })))
+const BatchImportManager = lazy(() => import('@/components/BatchImportManager').then(m => ({ default: m.BatchImportManager })))
+const RateTemplateManager = lazy(() => import('@/components/RateTemplateManager').then(m => ({ default: m.RateTemplateManager })))
+const CustomReportBuilder = lazy(() => import('@/components/CustomReportBuilder').then(m => ({ default: m.CustomReportBuilder })))
+const HolidayPayManager = lazy(() => import('@/components/HolidayPayManager').then(m => ({ default: m.HolidayPayManager })))
+const ContractValidator = lazy(() => import('@/components/ContractValidator').then(m => ({ default: m.ContractValidator })))
+const ShiftPatternManager = lazy(() => import('@/components/ShiftPatternManager').then(m => ({ default: m.ShiftPatternManager })))
+const QueryLanguageGuide = lazy(() => import('@/components/QueryLanguageGuide').then(m => ({ default: m.QueryLanguageGuide })))
+const RoadmapView = lazy(() => import('@/components/roadmap-view').then(m => ({ default: m.RoadmapView })))
+const ComponentShowcase = lazy(() => import('@/components/ComponentShowcase').then(m => ({ default: m.ComponentShowcase })))
+const BusinessLogicDemo = lazy(() => import('@/components/BusinessLogicDemo').then(m => ({ default: m.BusinessLogicDemo })))
+
interface ViewRouterProps {
currentView: View
searchQuery: string
@@ -56,6 +57,14 @@ interface ViewRouterProps {
actions: any
}
+function LoadingFallback() {
+ return (
+
+
+
+ )
+}
+
export function ViewRouter({
currentView,
searchQuery,
@@ -72,9 +81,10 @@ export function ViewRouter({
setPayrollRuns,
actions
}: ViewRouterProps) {
- switch (currentView) {
- case 'dashboard':
- return
+ const renderView = () => {
+ switch (currentView) {
+ case 'dashboard':
+ return
case 'timesheets':
return (
@@ -238,5 +248,12 @@ export function ViewRouter({
default:
return
+ }
}
+
+ return (
+ }>
+ {renderView()}
+
+ )
}
diff --git a/src/components/nav/NavItem.tsx b/src/components/nav/NavItem.tsx
index 83c6d7c..fabb87c 100644
--- a/src/components/nav/NavItem.tsx
+++ b/src/components/nav/NavItem.tsx
@@ -1,5 +1,7 @@
import { Badge } from '@/components/ui/badge'
import { cn } from '@/lib/utils'
+import type { View } from '@/App'
+import { preloadView } from '@/lib/view-preloader'
interface NavItemProps {
icon: React.ReactNode
@@ -7,12 +9,20 @@ interface NavItemProps {
active?: boolean
onClick?: () => void
badge?: number
+ view?: View
}
-export function NavItem({ icon, label, active, onClick, badge }: NavItemProps) {
+export function NavItem({ icon, label, active, onClick, badge, view }: NavItemProps) {
+ const handleMouseEnter = () => {
+ if (view) {
+ preloadView(view)
+ }
+ }
+
return (