Files
nlcc-itinerary/components/Menu.vue
Joshua Ryder 160a4e58b3 feat: Redirect to current page after logout for better UX
- Updated logout handler to return user to current page instead of login
- Consistent with login behavior which redirects back to original page
- Allows users to continue browsing after logout without forced redirect
- Users who accidentally click logout can easily log back in and continue

This improves UX by keeping users on the page they were viewing,
rather than forcing them to the login page when they may have just
wanted to log out temporarily.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-06 10:26:22 -05:00

175 lines
5.1 KiB
Vue

<template>
<div class="relative" ref="menuContainer">
<!-- Hamburger Button -->
<button
@click="toggleMenu"
:disabled="isToggling"
class="p-2 text-gray-700 hover:bg-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50"
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 isToggling = ref(false)
const menuContainer = ref<HTMLElement | null>(null)
const route = useRoute()
const loginRedirectUrl = computed(() => {
const redirectPath = props.currentPath || route.fullPath
return `/login?redirect=${encodeURIComponent(redirectPath)}`
})
// Toggle menu with debounce to prevent accidental double-taps
const toggleMenu = () => {
if (isToggling.value) return
isToggling.value = true
isOpen.value = !isOpen.value
// Re-enable after 500ms
setTimeout(() => {
isToggling.value = false
}, 500)
}
const handleLogout = async () => {
isOpen.value = false
const currentPath = props.currentPath || route.fullPath
await $fetch('/api/auth/logout', { method: 'POST' })
// Redirect back to the current page so user can continue browsing
await navigateTo(currentPath)
}
// Close menu when clicking outside
const handleClickOutside = (event: MouseEvent) => {
if (!isOpen.value || !menuContainer.value) return
const target = event.target as HTMLElement
// Check if click is outside the menu container
if (!menuContainer.value.contains(target)) {
isOpen.value = false
}
}
onMounted(() => {
// Use capture phase to ensure we catch the event before other handlers
document.addEventListener('click', handleClickOutside, true)
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside, true)
})
</script>