feat: Implement core application layout, global styling, dark mode, and essential UI components.
This commit is contained in:
parent
84e4d478c7
commit
a2ce1d79a2
7 changed files with 87 additions and 67 deletions
|
|
@ -4,29 +4,44 @@
|
|||
* @description User profile dropdown menu component using Quasar.
|
||||
*/
|
||||
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useAuth } from '~/composables/useAuth'
|
||||
import { useQuasar } from 'quasar'
|
||||
|
||||
const { currentUser, logout } = useAuth()
|
||||
const { t } = useI18n()
|
||||
const $q = useQuasar()
|
||||
|
||||
const isDarkMode = ref(false)
|
||||
const isHydrated = ref(false)
|
||||
|
||||
// Sync Dark Mode state
|
||||
const applyTheme = (value: boolean) => {
|
||||
// Tailwind dark mode
|
||||
document.documentElement.classList.toggle('dark', value)
|
||||
|
||||
// Quasar dark mode
|
||||
$q.dark.set(value)
|
||||
|
||||
// persist
|
||||
localStorage.setItem('theme', value ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
const toggleDarkMode = (val: boolean) => {
|
||||
isDarkMode.value = val
|
||||
applyTheme(val)
|
||||
}
|
||||
|
||||
// Sync Dark Mode state on mount
|
||||
onMounted(() => {
|
||||
if (document.documentElement.classList.contains('dark')) {
|
||||
isDarkMode.value = true
|
||||
}
|
||||
})
|
||||
isHydrated.value = true
|
||||
const savedTheme = localStorage.getItem('theme')
|
||||
const preferredDark = window.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false
|
||||
|
||||
// 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')
|
||||
}
|
||||
isDarkMode.value = savedTheme
|
||||
? savedTheme === 'dark'
|
||||
: preferredDark
|
||||
|
||||
applyTheme(isDarkMode.value)
|
||||
})
|
||||
|
||||
// User Initials
|
||||
|
|
@ -37,11 +52,6 @@ const userInitials = computed(() => {
|
|||
return f + l
|
||||
})
|
||||
|
||||
const userName = computed(() => {
|
||||
if (!currentUser.value) return 'User'
|
||||
return `${currentUser.value.firstName} ${currentUser.value.lastName}`
|
||||
})
|
||||
|
||||
const menuItems = computed(() => [
|
||||
{ label: t('userMenu.home'), to: '/dashboard' },
|
||||
{ label: t('userMenu.courseList'), to: '/browse/discovery' },
|
||||
|
|
@ -64,42 +74,40 @@ const handleLogout = async () => {
|
|||
<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);"
|
||||
content-class="bg-white dark:bg-slate-800 text-slate-900 dark:text-white rounded-2xl shadow-xl border border-slate-200/70 dark:border-white/10"
|
||||
style="min-width: 240px;"
|
||||
>
|
||||
<div class="bg-[#1e293b] text-white">
|
||||
<!-- Optional header if needed, but per design keeping it simple list -->
|
||||
</div>
|
||||
|
||||
<q-list class="bg-[#1e293b] text-white py-2">
|
||||
<q-list class="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"
|
||||
class="hover:bg-slate-100 dark: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 class="hover:bg-slate-100 dark: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"
|
||||
v-if="isHydrated"
|
||||
:model-value="isDarkMode"
|
||||
@update:model-value="toggleDarkMode"
|
||||
color="blue"
|
||||
keep-color
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
||||
<q-separator class="bg-white/10 my-1" />
|
||||
<q-separator class="bg-slate-100 dark:bg-white/10 my-1" />
|
||||
|
||||
<div class="p-4">
|
||||
<q-btn
|
||||
|
|
@ -116,7 +124,3 @@ const handleLogout = async () => {
|
|||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Override Quasar generic styles if necessary */
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue