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>
152 lines
4.4 KiB
Vue
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>
|