1- import { useState } from 'react' ;
1+ import { useState , lazy , Suspense } from 'react' ;
22import { AuthProvider , useAuth } from './contexts/AuthContext' ;
33import { NavigationProvider , useNavigation } from './contexts/NavigationContext' ;
44import { GamificationProvider } from './contexts/GamificationContext' ;
55import { ErrorBoundary } from './components/ErrorBoundary' ;
6+ import { SkipLink } from './components/SkipLink' ;
7+ import { OfflineIndicator } from './components/OfflineIndicator' ;
68import { useTodos } from './hooks/useFirestore' ;
79import { formatDateKey , formatDisplayDate , generateId } from './utils/dateUtils' ;
810
9- // Navigation components
11+ // Navigation components (always needed)
1012import { Sidebar } from './components/Navigation/Sidebar' ;
1113import { MobileNav } from './components/Navigation/MobileNav' ;
1214
13- // Dashboard
14- import { Dashboard } from './components/Dashboard/Dashboard' ;
15+ // Auth components (needed early)
16+ import { SignIn } from './components/Auth/SignIn' ;
17+ import { UserMenu } from './components/Auth/UserMenu' ;
1518
16- // Feature components
19+ // Core task components (frequently used)
1720import { Calendar } from './components/Calendar' ;
1821import { TodoList } from './components/TodoList' ;
1922import { AddTodo } from './components/AddTodo' ;
2023import { EditTodoModal } from './components/EditTodoModal' ;
21- import { AttendanceTracker } from './components/AttendanceTracker' ;
22- import { SignIn } from './components/Auth/SignIn' ;
23- import { UserMenu } from './components/Auth/UserMenu' ;
2424import { DailyQuote } from './components/DailyQuote' ;
25- import { QuickLinks } from './components/QuickLinks' ;
26- import { UpcomingMeetings } from './components/UpcomingMeetings' ;
27- import { TripPlanner } from './components/TripPlanner' ;
28- import { PomodoroTimer } from './components/PomodoroTimer' ;
29- import { TaxCalculator } from './components/TaxCalculator' ;
30- import { LeaveTracker } from './components/LeaveTracker' ;
31- import { ExpenseTracker } from './components/ExpenseTracker' ;
32- import { SalaryCalculator } from './components/SalaryCalculator' ;
33- import { Notes } from './components/Notes' ;
34-
35- // Gamification
36- import { LevelProgress } from './components/Gamification/LevelProgress' ;
37-
38- // ChatBot
39- import { ChatBot } from './components/ChatBot/ChatBot' ;
25+
26+ // Lazy loaded components (loaded on demand)
27+ const Dashboard = lazy ( ( ) => import ( './components/Dashboard/Dashboard' ) . then ( m => ( { default : m . Dashboard } ) ) ) ;
28+ const AttendanceTracker = lazy ( ( ) => import ( './components/AttendanceTracker' ) . then ( m => ( { default : m . AttendanceTracker } ) ) ) ;
29+ const QuickLinks = lazy ( ( ) => import ( './components/QuickLinks' ) . then ( m => ( { default : m . QuickLinks } ) ) ) ;
30+ const UpcomingMeetings = lazy ( ( ) => import ( './components/UpcomingMeetings' ) . then ( m => ( { default : m . UpcomingMeetings } ) ) ) ;
31+ const TripPlanner = lazy ( ( ) => import ( './components/TripPlanner' ) . then ( m => ( { default : m . TripPlanner } ) ) ) ;
32+ const PomodoroTimer = lazy ( ( ) => import ( './components/PomodoroTimer' ) . then ( m => ( { default : m . PomodoroTimer } ) ) ) ;
33+ const TaxCalculator = lazy ( ( ) => import ( './components/TaxCalculator' ) . then ( m => ( { default : m . TaxCalculator } ) ) ) ;
34+ const LeaveTracker = lazy ( ( ) => import ( './components/LeaveTracker' ) . then ( m => ( { default : m . LeaveTracker } ) ) ) ;
35+ const ExpenseTracker = lazy ( ( ) => import ( './components/ExpenseTracker' ) . then ( m => ( { default : m . ExpenseTracker } ) ) ) ;
36+ const SalaryCalculator = lazy ( ( ) => import ( './components/SalaryCalculator' ) . then ( m => ( { default : m . SalaryCalculator } ) ) ) ;
37+ const Notes = lazy ( ( ) => import ( './components/Notes' ) . then ( m => ( { default : m . Notes } ) ) ) ;
38+ const ChatBot = lazy ( ( ) => import ( './components/ChatBot/ChatBot' ) . then ( m => ( { default : m . ChatBot } ) ) ) ;
39+
40+ // Loading spinner component for Suspense fallback
41+ function LoadingSpinner ( ) {
42+ return (
43+ < div className = "flex items-center justify-center py-12" >
44+ < div className = "text-center" >
45+ < div className = "w-10 h-10 mx-auto mb-3 rounded-xl bg-white/20 flex items-center justify-center animate-pulse" >
46+ < svg className = "w-5 h-5 text-white animate-spin" fill = "none" viewBox = "0 0 24 24" >
47+ < circle className = "opacity-25" cx = "12" cy = "12" r = "10" stroke = "currentColor" strokeWidth = "4" />
48+ < path className = "opacity-75" fill = "currentColor" d = "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
49+ </ svg >
50+ </ div >
51+ < p className = "text-white/60 text-sm" > Loading...</ p >
52+ </ div >
53+ </ div >
54+ ) ;
55+ }
4056
4157function MainContent ( ) {
4258 const { activeView, navigate, sidebarCollapsed } = useNavigation ( ) ;
@@ -238,10 +254,10 @@ function MainContent() {
238254 return (
239255 < >
240256 { /* Main content area */ }
241- < main className = "app-content" >
257+ < main id = "main-content" className = "app-content" role = "main" tabIndex = { - 1 } >
242258 < div className = "min-h-screen py-6 px-4 lg:py-8 lg:px-8" >
243259 { /* Decorative background elements */ }
244- < div className = "fixed inset-0 overflow-hidden pointer-events-none" >
260+ < div className = "fixed inset-0 overflow-hidden pointer-events-none" aria-hidden = "true" >
245261 < div className = "absolute -top-40 -right-40 w-80 h-80 bg-white/10 rounded-full blur-3xl" />
246262 < div className = "absolute -bottom-40 -left-40 w-80 h-80 bg-white/10 rounded-full blur-3xl" />
247263 </ div >
@@ -276,8 +292,10 @@ function MainContent() {
276292 </ header >
277293 ) }
278294
279- { /* Page Content */ }
280- < div className = "animate-fade-in" > { renderContent ( ) } </ div >
295+ { /* Page Content with Suspense */ }
296+ < Suspense fallback = { < LoadingSpinner /> } >
297+ < div className = "animate-fade-in" > { renderContent ( ) } </ div >
298+ </ Suspense >
281299 </ div >
282300 </ div >
283301 </ main >
@@ -301,24 +319,12 @@ function AppContent() {
301319 // Show loading state
302320 if ( authLoading ) {
303321 return (
304- < div className = "min-h-screen flex items-center justify-center" >
322+ < div className = "min-h-screen flex items-center justify-center" role = "status" aria-label = "Loading application" >
305323 < div className = "text-center" >
306- < div className = "w-16 h-16 mx-auto mb-4 rounded-2xl bg-white/20 backdrop-blur-sm flex items-center justify-center animate-pulse" >
307- < svg
308- className = "w-8 h-8 text-white"
309- fill = "none"
310- stroke = "currentColor"
311- viewBox = "0 0 24 24"
312- >
313- < path
314- strokeLinecap = "round"
315- strokeLinejoin = "round"
316- strokeWidth = { 1.5 }
317- d = "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
318- />
319- </ svg >
324+ < div className = "w-16 h-16 mx-auto mb-4 rounded-2xl bg-gradient-to-br from-violet-500 to-purple-600 flex items-center justify-center animate-pulse shadow-lg" >
325+ < span className = "text-white font-bold text-2xl" > F</ span >
320326 </ div >
321- < p className = "text-white/60" > Loading...</ p >
327+ < p className = "text-white/60" > Loading Flowly ...</ p >
322328 </ div >
323329 </ div >
324330 ) ;
@@ -339,8 +345,10 @@ function AppContent() {
339345 { /* Main Content */ }
340346 < MainContent />
341347
342- { /* ChatBot */ }
343- < ChatBot />
348+ { /* ChatBot - Lazy loaded */ }
349+ < Suspense fallback = { null } >
350+ < ChatBot />
351+ </ Suspense >
344352 </ div >
345353 </ GamificationProvider >
346354 </ NavigationProvider >
@@ -350,6 +358,8 @@ function AppContent() {
350358function App ( ) {
351359 return (
352360 < ErrorBoundary >
361+ < SkipLink />
362+ < OfflineIndicator />
353363 < AuthProvider >
354364 < AppContent />
355365 </ AuthProvider >
0 commit comments