- Updated UserMenu.vue to enhance button styling and spacing for a more modern look. - Simplified CartFAB.vue to always show the cart button when items are present, regardless of the route. - Adjusted AppHeader.vue for better alignment of elements. - Enhanced AreaTabs.vue to enable the educator tab and improve badge styling. - Refined BottomNav.vue to handle cart visibility and navigation more effectively. These changes aim to enhance user navigation and overall experience within the application.
140 lines
4.0 KiB
Vue
140 lines
4.0 KiB
Vue
<script setup lang="ts">
|
|
import { Home, Wrench, ShoppingCart, User } from 'lucide-vue-next'
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
const route = useRoute()
|
|
const { loggedIn } = useAuth()
|
|
const { itemCount } = useCart()
|
|
const { open: openCart, isOpen: isCartOpen } = useCartUI()
|
|
|
|
interface NavItem {
|
|
id: string
|
|
label: string
|
|
icon: any
|
|
route: string
|
|
requireAuth?: boolean
|
|
}
|
|
|
|
const navItems: NavItem[] = [
|
|
{
|
|
id: 'home',
|
|
label: 'Start',
|
|
icon: Home,
|
|
route: '/',
|
|
},
|
|
{
|
|
id: 'products',
|
|
label: 'Makerspace',
|
|
icon: Wrench,
|
|
route: '/products',
|
|
},
|
|
{
|
|
id: 'cart',
|
|
label: 'Warenkorb',
|
|
icon: ShoppingCart,
|
|
route: '/cart', // Not used for navigation, but kept for consistency
|
|
requireAuth: false, // Cart should be accessible without auth
|
|
},
|
|
{
|
|
id: 'profile',
|
|
label: 'Profil',
|
|
icon: User,
|
|
route: '/auth',
|
|
},
|
|
]
|
|
|
|
const isActive = (item: NavItem) => {
|
|
// Special handling for cart: check if cart is open
|
|
if (item.id === 'cart') {
|
|
return isCartOpen.value
|
|
}
|
|
|
|
// For other items, check route
|
|
if (item.route === '/') {
|
|
return route.path === '/'
|
|
}
|
|
return route.path.startsWith(item.route)
|
|
}
|
|
|
|
function handleNavClick(item: NavItem) {
|
|
// Special handling for cart: open cart instead of navigating
|
|
if (item.id === 'cart') {
|
|
openCart()
|
|
return
|
|
}
|
|
|
|
if (item.requireAuth && !loggedIn.value) {
|
|
// Redirect to auth page
|
|
navigateTo('/auth')
|
|
return
|
|
}
|
|
|
|
navigateTo(item.route)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<!-- Mobile Bottom Navigation - only visible on small screens -->
|
|
<nav
|
|
class="fixed bottom-0 left-0 right-0 z-50 md:hidden border-t bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 safe-area-inset-bottom"
|
|
role="navigation" aria-label="Mobile Navigation">
|
|
<div class="flex items-center justify-around h-16 px-2">
|
|
<button v-for="item in navItems" :key="item.id" :class="[
|
|
'flex flex-col items-center justify-center flex-1 gap-1 py-2 px-1 rounded-lg transition-all relative',
|
|
isActive(item)
|
|
? 'text-experimenta-accent bg-experimenta-accent/10'
|
|
: 'text-muted-foreground hover:text-foreground hover:bg-muted',
|
|
item.requireAuth && !loggedIn && 'opacity-75',
|
|
]" @click="handleNavClick(item)" :aria-label="item.label" :aria-current="isActive(item) ? 'page' : undefined">
|
|
<!-- Icon with badge -->
|
|
<div class="relative">
|
|
<component :is="item.icon" :class="[
|
|
'h-5 w-5 transition-transform',
|
|
isActive(item) && 'scale-110',
|
|
]" />
|
|
|
|
<!-- Badge for cart -->
|
|
<Badge v-if="item.id === 'cart' && itemCount > 0"
|
|
class="absolute -top-2 -right-2 h-4 min-w-[16px] px-1 flex items-center justify-center text-[10px] bg-experimenta-accent">
|
|
{{ itemCount > 99 ? '99+' : itemCount }}
|
|
</Badge>
|
|
|
|
<!-- Login indicator dot for profile when not logged in -->
|
|
<span v-if="item.id === 'profile' && !loggedIn"
|
|
class="absolute -top-1 -right-1 h-2 w-2 rounded-full bg-yellow-500 animate-pulse"
|
|
aria-label="Nicht angemeldet" />
|
|
</div>
|
|
|
|
<!-- Label -->
|
|
<span :class="[
|
|
'text-[10px] font-medium transition-all',
|
|
isActive(item) && 'font-bold',
|
|
]">
|
|
{{ item.label }}
|
|
</span>
|
|
|
|
<!-- Active indicator bar -->
|
|
<span v-if="isActive(item)"
|
|
class="absolute bottom-0 left-1/2 -translate-x-1/2 w-12 h-0.5 bg-experimenta-accent rounded-full"
|
|
aria-hidden="true" />
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Safe area spacer for devices with notches/home indicators -->
|
|
<div class="h-[env(safe-area-inset-bottom)] bg-background" />
|
|
</nav>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* Support for safe area insets (iPhone X+) */
|
|
.safe-area-inset-bottom {
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
}
|
|
|
|
/* Ensure bottom nav doesn't overlay content */
|
|
@media (max-width: 768px) {
|
|
/* Add bottom padding to body/main to account for fixed bottom nav */
|
|
/* This will be handled in the layout component */
|
|
}
|
|
</style>
|