All checks were successful
Build and Deploy Frontend Learner / Build Frontend Learner Docker Image (push) Successful in 38s
Build and Deploy Frontend Learner / Deploy E-learning Frontend Learner to Dev Server (push) Successful in 3s
Build and Deploy Frontend Learner / Notify Deployment Status (push) Successful in 1s
168 lines
4.8 KiB
Vue
168 lines
4.8 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* @file AppHeader.vue
|
|
* @description The main header for the authenticated application dashboard.
|
|
* Uses Quasar QToolbar.
|
|
*/
|
|
|
|
import { ref, computed } from "vue";
|
|
|
|
const props = defineProps<{
|
|
/** Controls visibility of the sidebar toggle button */
|
|
showSidebarToggle?: boolean;
|
|
/** Type of navigation links to display */
|
|
navType?: "public" | "learner";
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
/** Emitted when the hamburger menu is clicked */
|
|
toggleSidebar: [];
|
|
/** Emitted when the mobile menu toggle is clicked */
|
|
toggleRightDrawer: [];
|
|
}>();
|
|
|
|
const { t } = useI18n();
|
|
const route = useRoute();
|
|
|
|
// Automatically determine navType based on route if not explicitly passed
|
|
const navTypeComputed = computed(() => {
|
|
if (props.navType) return props.navType;
|
|
// Show learner nav for dashboard, browse, classroom, and course details
|
|
const learnerRoutes = ["/dashboard", "/browse", "/classroom", "/course"];
|
|
return learnerRoutes.some((r) => route.path.startsWith(r))
|
|
? "learner"
|
|
: "public";
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<q-toolbar class="bg-white dark:!bg-[#0f172a] text-slate-900 dark:!text-white h-16 border-none p-0 overflow-visible">
|
|
<div class="w-full px-4 md:px-12 flex items-center h-full no-wrap relative">
|
|
|
|
<!-- Mobile Sidebar Toggle (For non-learner routes) -->
|
|
<q-btn
|
|
v-if="showSidebarToggle !== false && navTypeComputed !== 'learner' && $q.screen.lt.md"
|
|
flat
|
|
round
|
|
dense
|
|
icon="menu"
|
|
class="mr-2 text-gray-500"
|
|
@click="$emit('toggleSidebar')"
|
|
/>
|
|
|
|
<!-- Branding: Logo + Name -->
|
|
<div
|
|
class="flex items-center gap-3 cursor-pointer group flex-shrink-0"
|
|
@click="navigateTo('/dashboard')"
|
|
>
|
|
<div class="w-10 h-10 rounded-xl bg-blue-600 flex items-center justify-center text-white font-black shadow-lg shadow-blue-600/30 group-hover:scale-110 transition-transform flex-shrink-0">
|
|
E
|
|
</div>
|
|
<div class="flex flex-col text-left">
|
|
<span class="font-black text-[15px] md:text-lg leading-none tracking-tight text-slate-900 dark:text-white group-hover:text-blue-600 transition-colors">E-Learning</span>
|
|
<span class="text-[9px] md:text-[10px] font-bold uppercase tracking-[0.2em] leading-none mt-1 text-slate-500">Platform</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Desktop Navigation -->
|
|
<nav class="header-desktop items-center gap-6 lg:gap-8 text-[14px] font-bold ml-12 flex-shrink-0 h-full">
|
|
<NuxtLink to="/dashboard" class="nav-link" exact-active-class="active">{{ $t("sidebar.overview") }}</NuxtLink>
|
|
<NuxtLink to="/browse/discovery" class="nav-link" active-class="active">{{ $t("landing.allCourses") }}</NuxtLink>
|
|
<NuxtLink to="/dashboard/my-courses" class="nav-link" exact-active-class="active">{{ $t("sidebar.myCourses") || 'คอร์สเรียนของฉัน' }}</NuxtLink>
|
|
</nav>
|
|
|
|
<q-space />
|
|
|
|
<!-- Right Section: Tools -->
|
|
<div class="flex items-center gap-2 flex-shrink-0 no-wrap">
|
|
|
|
<!-- Desktop Only Tools -->
|
|
<div class="header-desktop items-center gap-4 flex-shrink-0">
|
|
<LanguageSwitcher />
|
|
<UserMenu />
|
|
</div>
|
|
|
|
<!-- Mobile/Tablet Tools Hidden (Moved to Drawer) -->
|
|
<!-- Just keep space between logo and hamburger -->
|
|
|
|
<!-- Mobile/Tablet Hamburger -->
|
|
<q-btn
|
|
flat
|
|
round
|
|
dense
|
|
icon="menu"
|
|
class="header-mobile text-slate-700 dark:text-white bg-slate-100 dark:bg-slate-800 flex-shrink-0"
|
|
style="width: 40px; height: 40px; min-width: 40px;"
|
|
@click="$emit('toggleRightDrawer')"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</q-toolbar>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* High Priority Visibility Logic */
|
|
@media (max-width: 1023px) {
|
|
.header-desktop {
|
|
display: none !important;
|
|
}
|
|
.header-mobile {
|
|
display: flex !important;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 1024px) {
|
|
.header-mobile {
|
|
display: none !important;
|
|
}
|
|
.header-desktop {
|
|
display: flex !important;
|
|
}
|
|
}
|
|
|
|
.nav-link {
|
|
color: #64748b; /* slate-500 */
|
|
text-decoration: none;
|
|
transition: all 0.3s ease;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
white-space: nowrap;
|
|
position: relative;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 4px;
|
|
}
|
|
|
|
.nav-link:hover {
|
|
color: #2563eb; /* blue-600 */
|
|
}
|
|
|
|
.nav-link.active {
|
|
color: #2563eb; /* blue-600 */
|
|
}
|
|
|
|
.nav-link::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
width: 0;
|
|
height: 3px;
|
|
background-color: #2563eb;
|
|
border-top-left-radius: 4px;
|
|
border-top-right-radius: 4px;
|
|
transition: all 0.3s ease;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
.nav-link.active::after {
|
|
width: 100%;
|
|
}
|
|
|
|
.router-link-active {
|
|
color: #2563eb !important;
|
|
}
|
|
</style>
|