THE BIGGER PICTURE - Root Cause Analysis:
Problem Chain:
1. FABContext value recreated every render
2. All FAB consumers re-render
3. Dashboard re-renders
4. useFABConfig runs
5. Creates new icon/callbacks
6. Triggers FABContext update
7. INFINITE LOOP!
The Bug (in BOTH contexts):
<Context.Provider value={{ config, setFAB, clearFAB }}>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
NEW object every render!
Every time Provider re-renders:
- Creates NEW value object
- All consumers see "new" value
- All consumers re-render
- Causes more Provider re-renders
- INFINITE LOOP!
The Fix:
const setFAB = useCallback(..., []); // Stable function
const clearFAB = useCallback(..., []); // Stable function
const value = useMemo(() => ({ config, setFAB, clearFAB }), [config, setFAB, clearFAB]);
^^^^^^^
Only creates new object when dependencies actually change!
<Context.Provider value={value}>
^^^^^^^
Stable reference!
Why This is Critical:
Context is at the TOP of the component tree:
App
└─ FABProvider ← Bug here affects EVERYTHING below
└─ PageHeaderProvider ← Bug here too
└─ DashboardProvider
└─ Shell
└─ Dashboard ← Infinite re-renders
└─ Charts ← Break from constant re-renders
React Context Performance Rules:
1. ALWAYS memoize context value object
2. ALWAYS use useCallback for context functions
3. NEVER create inline objects in Provider value
4. Context updates trigger ALL consumers
Fixed Contexts:
1. FABContext - Memoized value, callbacks
2. PageHeaderContext - Memoized value, callbacks
Before:
Every render → new value object → all consumers re-render → LOOP
After:
Only config changes → new value object → consumers re-render once → done
Result:
✅ No infinite loops
✅ No unnecessary re-renders
✅ Clean console
✅ Smooth performance
✅ All features working
Files Modified:
- FABContext.tsx: Added useMemo and useCallback
- PageHeaderContext.tsx: Added useMemo and useCallback
- useFABConfig.tsx: Memoized icon and callbacks (previous fix)
- App.tsx: Fixed scroll detection with useRef (previous fix)
All infinite loop sources now eliminated!
Implemented mobile-optimized navigation structure:
1. Bottom Navigation (Mobile Only)
- 5 items: Dashboard, Orders, Products, Customers, More
- Fixed at bottom, always visible
- Thumb-friendly positioning
- Active state indication
- Hidden on desktop (md:hidden)
2. More Menu Page
- Overflow menu for Coupons and Settings
- Clean list layout with icons
- Descriptions for each item
- Chevron indicators
3. FAB (Floating Action Button)
- Context-aware system via FABContext
- Fixed bottom-right (72px from bottom)
- Hidden on desktop (md:hidden)
- Ready for contextual actions per page
4. FAB Context System
- Global state for FAB configuration
- setFAB() / clearFAB() methods
- Supports icon, label, onClick, visibility
- Allows pages to control FAB behavior
5. Layout Updates
- Added pb-14 to main for bottom nav spacing
- BottomNav and FAB in mobile fullscreen layout
- Wrapped app with FABProvider
Structure (Mobile):
┌─────────────────────────────────┐
│ App Bar (will hide on scroll) │
├─────────────────────────────────┤
│ Page Header (sticky, contextual)│
├─────────────────────────────────┤
│ Submenu (sticky) │
├─────────────────────────────────┤
│ Content (scrollable) │
│ [+] FAB │
├─────────────────────────────────┤
│ Bottom Nav (fixed) │
└─────────────────────────────────┘
Next Steps:
- Implement scroll-hide for app bar
- Add contextual FAB per page
- Test on real devices
Files Created:
- BottomNav.tsx: Bottom navigation component
- More/index.tsx: More menu page
- FABContext.tsx: FAB state management
- FAB.tsx: Floating action button component
- useScrollDirection.ts: Scroll detection hook
Files Modified:
- App.tsx: Added bottom nav, FAB, More route, providers
Created responsive dialog pattern for better mobile UX:
Components Added:
1. drawer.tsx - Vaul-based drawer component (bottom sheet)
2. responsive-dialog.tsx - Smart wrapper that switches based on screen size
3. use-media-query.ts - Hook to detect screen size
Pattern:
- Desktop (≥768px): Use Dialog (modal overlay)
- Mobile (<768px): Use Drawer (bottom sheet)
- Provides consistent API for both
Usage Example:
<ResponsiveDialog
open={isOpen}
onOpenChange={setIsOpen}
title="Settings"
description="Configure your options"
footer={<Button>Save</Button>}
>
<FormContent />
</ResponsiveDialog>
Benefits:
- Better mobile UX with native-feeling bottom sheet
- Easier to reach buttons on mobile
- Consistent desktop experience
- Single component API
Dependencies:
- npm install vaul (drawer library)
- @radix-ui/react-dialog (already installed)
Next Steps:
- Convert payment gateway modal to use ResponsiveDialog
- Use AlertDialog for confirmations
- Apply pattern to other modals in project
Note: Payment gateway modal needs custom implementation
due to complex layout (scrollable body + sticky footer)