All files / src/components/layout/MobileBottomNav index.tsx

0% Statements 0/119
100% Branches 0/0
0% Functions 0/1
0% Lines 0/119

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                                                                                                                                                                                                                               
'use client';

/**
 * MobileBottomNav Component
 *
 * A bottom navigation bar optimized for mobile devices.
 * Features:
 * - 44px minimum tap targets
 * - Cart badge with item count
 * - Active state indication
 * - Safe area inset support for notched devices
 * - Hidden on desktop (md:hidden)
 *
 * @example
 * ```tsx
 * <MobileBottomNav />
 * ```
 */

import { usePathname } from 'next/navigation';
import Link from 'next/link';
import { useSelector } from 'react-redux';
import { Icon, IconName } from '@/components/ui/icons';
import { selectTotalItemCount } from '@/redux/features/cartSlice';
import { useAppSelector } from '@/redux/store';
import { cn } from '@/lib/core';

interface NavItem {
  href: string;
  icon: IconName;
  label: string;
  showCartBadge?: boolean;
  showWishlistBadge?: boolean;
}

const navItems: NavItem[] = [
  { href: '/', icon: 'home', label: 'Home' },
  { href: '/shop', icon: 'grid', label: 'Shop' },
  { href: '/cart', icon: 'cart', label: 'Cart', showCartBadge: true },
  { href: '/wishlist', icon: 'heart', label: 'Wishlist', showWishlistBadge: true },
  { href: '/my-account', icon: 'user', label: 'Account' },
];

export function MobileBottomNav() {
  const pathname = usePathname();
  const cartItemCount = useSelector(selectTotalItemCount);
  const wishlistItemCount = useAppSelector((state) => state.wishlistReducer.items?.length ?? 0);

  // Don't show on certain pages (checkout, admin, etc.)
  const hiddenPaths = ['/checkout', '/admin', '/signin', '/signup'];
  if (hiddenPaths.some((path) => pathname.startsWith(path))) {
    return null;
  }

  return (
    <nav
      role="navigation"
      aria-label="Mobile navigation"
      className={cn(
        'fixed bottom-0 left-0 right-0 z-50',
        'bg-white dark:bg-gray-900',
        'border-t border-gray-200 dark:border-gray-800',
        'pb-[env(safe-area-inset-bottom)]',
        'md:hidden' // Hide on desktop
      )}
    >
      <div className="flex items-center justify-around h-16">
        {navItems.map((item) => {
          const isActive = pathname === item.href ||
            (item.href !== '/' && pathname.startsWith(item.href));

          const badgeCount = item.showCartBadge
            ? cartItemCount
            : item.showWishlistBadge
            ? wishlistItemCount
            : 0;

          return (
            <Link
              key={item.href}
              href={item.href}
              className={cn(
                'flex flex-col items-center justify-center',
                'w-full h-full',
                'min-h-[44px] min-w-[44px]', // Minimum tap target
                'transition-colors duration-200',
                'touch-manipulation', // Optimize touch response
                isActive
                  ? 'text-primary-600 dark:text-primary-400'
                  : 'text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
              )}
              aria-current={isActive ? 'page' : undefined}
            >
              <div className="relative">
                <Icon name={item.icon} size={24} />
                {badgeCount > 0 && (
                  <span
                    className={cn(
                      'absolute -top-1.5 -right-1.5',
                      'min-w-[18px] h-[18px] px-1',
                      'flex items-center justify-center',
                      'bg-red-500 text-white text-[10px] font-bold rounded-full'
                    )}
                    aria-label={`${badgeCount} items`}
                  >
                    {badgeCount > 99 ? '99+' : badgeCount}
                  </span>
                )}
              </div>
              <span className="text-xs mt-1 font-medium">{item.label}</span>
            </Link>
          );
        })}
      </div>
    </nav>
  );
}

export default MobileBottomNav;