Files
nlcc-itinerary/components/MobileMenu.vue
Joshua Ryder c7dc88cdf6 feat: Add mobile hamburger menu and Register button
Implemented comprehensive navigation improvements to make registration
more discoverable and provide a cleaner mobile experience.

## Mobile Improvements
- Created reusable MobileMenu component with hamburger icon
- Replaces cluttered button layout with clean dropdown menu
- Includes Home, Profile, Admin links, and authentication options
- Auto-closes when clicking outside or navigating
- Smooth transition animations

## Register Button Added
- Sermon page: Added Register button next to Login in notes section
- Sermon page: Added Register button in header for non-authenticated users
- Home page: Added Register button next to Login for easy discovery
- All Register links redirect to login page in register mode

## Navigation Consistency
- Home button now visible on sermon pages (desktop and mobile)
- Consistent navigation experience across all pages
- Mobile menu available on both home and sermon pages
- Better mobile UX with less header crowding

Benefits:
- Users can easily find how to register
- Cleaner mobile interface
- Consistent navigation patterns
- Better discoverability of account features

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 09:02:41 -05:00

152 lines
4.4 KiB
Vue

<template>
<div class="relative">
<!-- Hamburger Button -->
<button
@click="isOpen = !isOpen"
class="p-2 text-gray-700 hover:bg-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
aria-label="Menu"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
v-if="!isOpen"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
/>
<path
v-else
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
<!-- Dropdown Menu -->
<Transition
enter-active-class="transition ease-out duration-100"
enter-from-class="transform opacity-0 scale-95"
enter-to-class="transform opacity-100 scale-100"
leave-active-class="transition ease-in duration-75"
leave-from-class="transform opacity-100 scale-100"
leave-to-class="transform opacity-0 scale-95"
>
<div
v-if="isOpen"
class="absolute right-0 mt-2 w-56 bg-white rounded-md shadow-lg ring-1 ring-black ring-opacity-5 z-50"
>
<div class="py-1">
<!-- Home Link -->
<NuxtLink
v-if="showHome"
to="/"
@click="isOpen = false"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
>
🏠 Home
</NuxtLink>
<!-- Authentication Links -->
<ClientOnly>
<template v-if="isAuthenticated">
<!-- Profile Link -->
<NuxtLink
to="/profile"
@click="isOpen = false"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
>
👤 Edit Profile
</NuxtLink>
<!-- Admin Links -->
<template v-if="isAdmin">
<NuxtLink
to="/admin"
@click="isOpen = false"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
>
📝 Manage Sermons
</NuxtLink>
<NuxtLink
to="/users"
@click="isOpen = false"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
>
👥 Manage Users
</NuxtLink>
</template>
<!-- Logout -->
<button
@click="handleLogout"
class="block w-full text-left px-4 py-2 text-sm text-red-700 hover:bg-red-50"
>
🚪 Log Out
</button>
</template>
<!-- Not authenticated -->
<template v-else>
<NuxtLink
:to="loginRedirectUrl"
@click="isOpen = false"
class="block px-4 py-2 text-sm text-green-700 hover:bg-green-50 font-medium"
>
🔑 Log In
</NuxtLink>
<NuxtLink
to="/register"
@click="isOpen = false"
class="block px-4 py-2 text-sm text-blue-700 hover:bg-blue-50 font-medium"
>
Register
</NuxtLink>
</template>
</ClientOnly>
</div>
</div>
</Transition>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
isAuthenticated?: boolean
isAdmin?: boolean
showHome?: boolean
currentPath?: string
}>()
const isOpen = ref(false)
const route = useRoute()
const loginRedirectUrl = computed(() => {
const redirectPath = props.currentPath || route.fullPath
return `/login?redirect=${encodeURIComponent(redirectPath)}`
})
const handleLogout = async () => {
isOpen.value = false
await $fetch('/api/auth/logout', { method: 'POST' })
await navigateTo('/login')
}
// Close menu when clicking outside
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as HTMLElement
if (isOpen.value && !target.closest('.relative')) {
isOpen.value = false
}
}
onMounted(() => {
document.addEventListener('click', handleClickOutside)
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
})
</script>