Update bottom navigation with raised scanner button

- Change Dashboard to Home in navigation
- Add raised purple scanner button in center
- Implement new nav layout: Home | Cards | Scanner | Collections | More
- Scanner button features elevated design with purple gradient
- Add proper active/inactive states for all nav items
- Remove unused navigationItems array to fix linting warning
- Perfect spacing with justify-around layout
This commit is contained in:
Randall Stillwell 2025-07-22 19:08:04 -05:00
parent 0a03f0ed14
commit 708c68fb07

View file

@ -3,13 +3,6 @@ import { Link, useLocation, useNavigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import { useTheme } from '../contexts/ThemeContext';
interface NavigationItem {
name: string;
href: string;
icon: React.ReactNode;
activeIcon?: React.ReactNode;
}
const MobileNavbar: React.FC = () => {
const { user, logout, isAdmin } = useAuth();
const { toggleTheme, effectiveTheme } = useTheme();
@ -17,70 +10,6 @@ const MobileNavbar: React.FC = () => {
const navigate = useNavigate();
const [showUserMenu, setShowUserMenu] = useState(false);
const navigationItems: NavigationItem[] = [
{
name: 'Dashboard',
href: '/dashboard',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
),
activeIcon: (
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
),
},
{
name: 'Cards',
href: '/cards',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
),
activeIcon: (
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
),
},
{
name: 'Scanner',
href: '/scanner',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
),
activeIcon: (
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path fillRule="evenodd" d="M1.5 6a2.25 2.25 0 012.25-2.25h1.386c.17 0 .318.114.362.278l.558 2.047c.25.895 1.03 1.425 1.944 1.425H19.5a2.25 2.25 0 012.25 2.25v6.75a2.25 2.25 0 01-2.25 2.25H4.5A2.25 2.25 0 012.25 18V9.75a2.25 2.25 0 012.25-2.25H1.5V6zM12 15a3 3 0 100-6 3 3 0 000 6z" clipRule="evenodd" />
</svg>
),
},
{
name: 'Collections',
href: '/collections',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
),
},
{
name: 'More',
href: '/menu',
icon: (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
),
},
];
const handleLogout = () => {
logout();
setShowUserMenu(false);
@ -143,27 +72,106 @@ const MobileNavbar: React.FC = () => {
{/* Bottom Navigation */}
<nav className="fixed bottom-0 left-0 right-0 z-50 bg-white dark:bg-surface-900 border-t border-surface-200 dark:border-surface-700 safe-area-inset-bottom">
<div className="grid grid-cols-5 px-2 py-2">
{navigationItems.map((item) => (
<Link
key={item.name}
to={item.href === '/menu' ? '#' : item.href}
onClick={item.href === '/menu' ? (e) => {
e.preventDefault();
setShowUserMenu(true);
} : undefined}
className={`flex flex-col items-center justify-center p-2 rounded-xl transition-all duration-200 ${
isActive(item.href) && item.href !== '/menu'
? 'text-primary-600 dark:text-primary-400 bg-primary-50 dark:bg-primary-900/30'
: 'text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white hover:bg-surface-100 dark:hover:bg-surface-800'
}`}
>
<div className="mb-1">
{isActive(item.href) && item.activeIcon ? item.activeIcon : item.icon}
</div>
<span className="text-xs font-medium">{item.name}</span>
</Link>
))}
<div className="relative flex items-end justify-around px-4 py-2">
{/* Home */}
<Link
to="/dashboard"
className={`flex flex-col items-center justify-center p-2 rounded-xl transition-all duration-200 ${
isActive('/dashboard')
? 'text-primary-600 dark:text-primary-400'
: 'text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white'
}`}
>
<div className="mb-1">
{isActive('/dashboard') ? (
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
</svg>
) : (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
)}
</div>
<span className="text-xs font-medium">Home</span>
</Link>
{/* Cards */}
<Link
to="/cards"
className={`flex flex-col items-center justify-center p-2 rounded-xl transition-all duration-200 ${
isActive('/cards')
? 'text-primary-600 dark:text-primary-400'
: 'text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white'
}`}
>
<div className="mb-1">
{isActive('/cards') ? (
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
) : (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
)}
</div>
<span className="text-xs font-medium">Cards</span>
</Link>
{/* Scanner - Special Raised Button */}
<Link
to="/scanner"
className="relative flex flex-col items-center justify-center -mt-6 mb-2"
>
<div className={`w-14 h-14 rounded-full shadow-lg transition-all duration-200 flex items-center justify-center ${
isActive('/scanner')
? 'bg-gradient-to-r from-primary-600 to-accent-600 shadow-primary-500/25 shadow-xl'
: 'bg-gradient-to-r from-primary-500 to-accent-500 hover:from-primary-600 hover:to-accent-600 shadow-primary-500/20 hover:shadow-xl'
}`}>
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<span className="text-xs font-medium text-primary-600 dark:text-primary-400 mt-1">Scanner</span>
</Link>
{/* Collections */}
<Link
to="/collections"
className={`flex flex-col items-center justify-center p-2 rounded-xl transition-all duration-200 ${
isActive('/collections')
? 'text-primary-600 dark:text-primary-400'
: 'text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white'
}`}
>
<div className="mb-1">
{isActive('/collections') ? (
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M7 4V2a1 1 0 011-1h8a1 1 0 011 1v2h3a1 1 0 110 2h-1v10a2 2 0 01-2 2H7a2 2 0 01-2-2V6H4a1 1 0 010-2h3zM9 4h6V3H9v1zm8 2H7v10h10V6z"/>
</svg>
) : (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
)}
</div>
<span className="text-xs font-medium">Collections</span>
</Link>
{/* More */}
<button
onClick={() => setShowUserMenu(true)}
className="flex flex-col items-center justify-center p-2 rounded-xl transition-all duration-200 text-surface-600 dark:text-surface-400 hover:text-surface-900 dark:hover:text-white"
>
<div className="mb-1">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</div>
<span className="text-xs font-medium">More</span>
</button>
</div>
</nav>