Files
tablejerseys-web/app/components/AppNavbar.vue
Frank John Begornia 3ba0b250ed
Some checks failed
Deploy Production / deploy (push) Has been cancelled
first commit
2026-01-12 22:16:36 +08:00

158 lines
5.5 KiB
Vue

<script setup lang="ts">
const { user, backendUser, signOut, initAuth, isLoading } = useAuth()
const loginModal = useLoginModal()
const route = useRoute()
const dropdownRef = ref<HTMLElement | null>(null)
const showMenu = ref(false)
const displayName = computed(() => backendUser.value?.name || user.value?.displayName || backendUser.value?.email || user.value?.email || "Account")
const avatarInitials = computed(() => {
const source = displayName.value
if (!source) {
return "S"
}
const parts = source.trim().split(/\s+/)
if (parts.length === 1) {
return parts[0].charAt(0).toUpperCase()
}
return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase()
})
const openLoginModal = () => {
loginModal.value = true
}
const handleSignOut = async () => {
try {
await signOut()
showMenu.value = false
} catch (error) {
console.error('Sign out failed:', error)
}
}
const toggleMenu = () => {
showMenu.value = !showMenu.value
}
const closeMenu = () => {
if (showMenu.value) {
showMenu.value = false
}
}
const handleClickOutside = (event: MouseEvent) => {
if (!dropdownRef.value) {
return
}
if (!dropdownRef.value.contains(event.target as Node)) {
closeMenu()
}
}
onMounted(() => {
initAuth()
document.addEventListener('click', handleClickOutside)
})
onBeforeUnmount(() => {
document.removeEventListener('click', handleClickOutside)
})
watch(
() => route.fullPath,
() => {
closeMenu()
}
)
</script>
<template>
<nav class="sticky top-0 z-30 border-b border-slate-200 bg-white/90 backdrop-blur shadow-sm">
<div class="mx-auto max-w-6xl px-4 py-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between gap-6">
<NuxtLink to="/" class="text-lg font-semibold text-slate-900 transition hover:text-slate-600 sm:text-xl">
TableJerseys
</NuxtLink>
<div class="flex items-center gap-3">
<!-- Show user info and logout when authenticated -->
<div v-if="user && !isLoading" ref="dropdownRef" class="relative flex items-center gap-3">
<span class="hidden text-sm text-slate-700 sm:inline">{{ backendUser?.email || user.email }}</span>
<button
type="button"
class="flex h-10 w-10 items-center justify-center rounded-full bg-slate-900 text-sm font-semibold uppercase text-white transition hover:bg-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
@click.stop="toggleMenu"
aria-haspopup="menu"
:aria-expanded="showMenu"
>
{{ avatarInitials }}
</button>
<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="showMenu"
class="absolute right-0 top-12 w-56 rounded-2xl border border-slate-200 bg-white p-3 shadow-xl backdrop-blur"
role="menu"
>
<p class="px-3 text-xs uppercase tracking-[0.25em] text-slate-500">Signed in as</p>
<p class="px-3 text-sm font-medium text-slate-900">{{ displayName }}</p>
<p class="px-3 text-xs text-slate-600">{{ backendUser?.email || user.email }}</p>
<div class="my-3 h-px bg-slate-200"></div>
<NuxtLink
to="/profile"
class="flex items-center gap-2 rounded-xl px-3 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-100"
role="menuitem"
>
Profile
</NuxtLink>
<NuxtLink
to="/orders"
class="flex items-center gap-2 rounded-xl px-3 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-100"
role="menuitem"
>
Orders
</NuxtLink>
<button
type="button"
class="mt-2 flex w-full items-center gap-2 rounded-xl px-3 py-2 text-sm font-semibold text-rose-600 transition hover:bg-rose-50"
@click="handleSignOut"
role="menuitem"
>
Logout
</button>
</div>
</transition>
</div>
<!-- Show login button when not authenticated -->
<div v-else-if="!isLoading" class="flex items-center gap-3">
<button
type="button"
@click="openLoginModal"
class="rounded-full border-2 border-slate-900 bg-slate-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-white hover:text-slate-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-900 focus-visible:ring-offset-2 focus-visible:ring-offset-white"
>
Login
</button>
</div>
<!-- Loading state -->
<div v-else class="flex items-center gap-3">
<div class="h-8 w-16 animate-pulse rounded bg-slate-200"></div>
</div>
</div>
</div>
</div>
<LoginModal />
</nav>
</template>