2026-01-13 10:46:40 +07:00
|
|
|
<script setup lang="ts">
|
|
|
|
|
/**
|
|
|
|
|
* @file UserMenu.vue
|
2026-01-26 09:27:31 +07:00
|
|
|
* @description User profile dropdown menu component using Quasar.
|
2026-01-13 10:46:40 +07:00
|
|
|
*/
|
|
|
|
|
|
2026-01-26 09:27:31 +07:00
|
|
|
import { ref, computed, onMounted, watch } from 'vue'
|
2026-01-13 10:46:40 +07:00
|
|
|
import { useAuth } from '~/composables/useAuth'
|
|
|
|
|
|
|
|
|
|
const { currentUser, logout } = useAuth()
|
2026-01-19 16:43:05 +07:00
|
|
|
const { t } = useI18n()
|
2026-01-13 10:46:40 +07:00
|
|
|
const isDarkMode = ref(false)
|
|
|
|
|
|
2026-01-26 09:27:31 +07:00
|
|
|
// Sync Dark Mode state
|
2026-01-13 10:46:40 +07:00
|
|
|
onMounted(() => {
|
|
|
|
|
if (document.documentElement.classList.contains('dark')) {
|
|
|
|
|
isDarkMode.value = true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-01-26 09:27:31 +07:00
|
|
|
// Watch for dark mode changes
|
|
|
|
|
watch(isDarkMode, (val) => {
|
|
|
|
|
if (val) {
|
|
|
|
|
document.documentElement.classList.add('dark')
|
|
|
|
|
localStorage.setItem('theme', 'dark')
|
|
|
|
|
} else {
|
|
|
|
|
document.documentElement.classList.remove('dark')
|
|
|
|
|
localStorage.setItem('theme', 'light')
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// User Initials
|
2026-01-13 10:46:40 +07:00
|
|
|
const userInitials = computed(() => {
|
2026-01-15 11:26:37 +07:00
|
|
|
if (!currentUser.value) return ''
|
2026-01-26 09:27:31 +07:00
|
|
|
const f = currentUser.value.firstName?.charAt(0).toUpperCase() || 'U'
|
2026-01-13 10:46:40 +07:00
|
|
|
const l = currentUser.value.lastName?.charAt(0).toUpperCase() || ''
|
|
|
|
|
return f + l
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const userName = computed(() => {
|
2026-01-26 09:27:31 +07:00
|
|
|
if (!currentUser.value) return 'User'
|
2026-01-13 10:46:40 +07:00
|
|
|
return `${currentUser.value.firstName} ${currentUser.value.lastName}`
|
|
|
|
|
})
|
|
|
|
|
|
2026-01-19 16:43:05 +07:00
|
|
|
const menuItems = computed(() => [
|
|
|
|
|
{ label: t('userMenu.home'), to: '/dashboard' },
|
|
|
|
|
{ label: t('userMenu.courseList'), to: '/browse/discovery' },
|
|
|
|
|
{ label: t('userMenu.myCourses'), to: '/dashboard/my-courses' },
|
|
|
|
|
{ label: t('userMenu.settings'), to: '/dashboard/profile' }
|
|
|
|
|
])
|
2026-01-13 10:46:40 +07:00
|
|
|
|
|
|
|
|
const handleLogout = async () => {
|
|
|
|
|
await logout()
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-01-26 09:27:31 +07:00
|
|
|
<div class="q-pa-md">
|
|
|
|
|
<q-btn round flat class="text-slate-700 dark:text-white">
|
|
|
|
|
<q-avatar color="primary" text-color="white" size="40px" font-size="14px" class="font-bold shadow-md">
|
|
|
|
|
{{ userInitials }}
|
|
|
|
|
</q-avatar>
|
|
|
|
|
|
|
|
|
|
<q-menu
|
|
|
|
|
anchor="bottom end"
|
|
|
|
|
self="top end"
|
|
|
|
|
class="rounded-2xl shadow-xl overflow-hidden border border-slate-100 dark:border-slate-700"
|
|
|
|
|
:offset="[0, 10]"
|
|
|
|
|
style="min-width: 240px; background-color: var(--bg-surface);"
|
2026-01-13 10:46:40 +07:00
|
|
|
>
|
2026-01-26 09:27:31 +07:00
|
|
|
<div class="bg-[#1e293b] text-white">
|
|
|
|
|
<!-- Optional header if needed, but per design keeping it simple list -->
|
|
|
|
|
</div>
|
2026-01-13 10:46:40 +07:00
|
|
|
|
2026-01-26 09:27:31 +07:00
|
|
|
<q-list class="bg-[#1e293b] text-white py-2">
|
|
|
|
|
<q-item
|
|
|
|
|
v-for="item in menuItems"
|
|
|
|
|
:key="item.label"
|
|
|
|
|
clickable
|
|
|
|
|
v-close-popup
|
|
|
|
|
@click="navigateTo(item.to)"
|
|
|
|
|
class="hover:bg-white/10 transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<q-item-section>
|
|
|
|
|
<q-item-label class="font-bold text-sm">{{ item.label }}</q-item-label>
|
|
|
|
|
</q-item-section>
|
|
|
|
|
</q-item>
|
|
|
|
|
|
|
|
|
|
<q-item class="hover:bg-white/10 transition-colors">
|
|
|
|
|
<q-item-section>
|
|
|
|
|
<q-item-label class="font-bold text-sm">{{ $t('userMenu.darkMode') }}</q-item-label>
|
|
|
|
|
</q-item-section>
|
|
|
|
|
<q-item-section side>
|
|
|
|
|
<q-toggle
|
|
|
|
|
v-model="isDarkMode"
|
|
|
|
|
color="blue"
|
|
|
|
|
keep-color
|
|
|
|
|
/>
|
|
|
|
|
</q-item-section>
|
|
|
|
|
</q-item>
|
|
|
|
|
|
|
|
|
|
<q-separator class="bg-white/10 my-1" />
|
|
|
|
|
|
|
|
|
|
<div class="p-4">
|
|
|
|
|
<q-btn
|
|
|
|
|
unelevated
|
|
|
|
|
class="w-full bg-red-500/10 text-red-400 hover:bg-red-500/20 font-bold rounded-lg"
|
|
|
|
|
no-caps
|
2026-01-13 10:46:40 +07:00
|
|
|
@click="handleLogout"
|
|
|
|
|
>
|
2026-01-19 16:43:05 +07:00
|
|
|
{{ $t('userMenu.logout') }}
|
2026-01-26 09:27:31 +07:00
|
|
|
</q-btn>
|
2026-01-13 10:46:40 +07:00
|
|
|
</div>
|
2026-01-26 09:27:31 +07:00
|
|
|
</q-list>
|
|
|
|
|
</q-menu>
|
|
|
|
|
</q-btn>
|
2026-01-13 10:46:40 +07:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2026-01-26 09:27:31 +07:00
|
|
|
/* Override Quasar generic styles if necessary */
|
2026-01-13 10:46:40 +07:00
|
|
|
</style>
|