315 lines
10 KiB
Vue
315 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import log from "loglevel";
|
|
|
|
import IconLogout from "@/components/icons/IconLogout.vue";
|
|
import IconSettings from "@/components/icons/IconSettings.vue";
|
|
import MobileMenu from "@/components/MobileMenu.vue";
|
|
import NotificationPopover from "@/components/notifications/NotificationPopover.vue";
|
|
import NotificationPopoverContent from "@/components/notifications/NotificationPopoverContent.vue";
|
|
import ItDropdown from "@/components/ui/ItDropdown.vue";
|
|
import { useAppStore } from "@/stores/app";
|
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
import { useNotificationsStore } from "@/stores/notifications";
|
|
import { useUserStore } from "@/stores/user";
|
|
import type { DropdownListItem } from "@/types";
|
|
import type { Component } from "vue";
|
|
import { onMounted, reactive } from "vue";
|
|
import { useI18n } from "vue-i18n";
|
|
import { useRoute, useRouter } from "vue-router";
|
|
|
|
type DropdownActions = "logout" | "settings" | "profile";
|
|
|
|
interface DropdownData {
|
|
action: DropdownActions;
|
|
}
|
|
|
|
log.debug("MainNavigationBar created");
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const userStore = useUserStore();
|
|
const appStore = useAppStore();
|
|
const courseSessionsStore = useCourseSessionsStore();
|
|
const notificationsStore = useNotificationsStore();
|
|
|
|
const { t } = useI18n();
|
|
const state = reactive({ showMenu: false });
|
|
|
|
function toggleNav() {
|
|
state.showMenu = !state.showMenu;
|
|
}
|
|
|
|
function inCourse() {
|
|
return route.path.startsWith("/course/");
|
|
}
|
|
|
|
function inCockpit() {
|
|
const regex = new RegExp("/course/[^/]+/cockpit");
|
|
return regex.test(route.path);
|
|
}
|
|
|
|
function inLearningPath() {
|
|
const regex = new RegExp("/course/[^/]+/learn");
|
|
return regex.test(route.path);
|
|
}
|
|
|
|
function inCompetenceProfile() {
|
|
const regex = new RegExp("/course/[^/]+/competence");
|
|
return regex.test(route.path);
|
|
}
|
|
|
|
function inMediaLibrary() {
|
|
return route.path.startsWith("/media/");
|
|
}
|
|
|
|
function handleDropdownSelect(data: DropdownData) {
|
|
switch (data.action) {
|
|
case "profile":
|
|
router.push("/profile");
|
|
break;
|
|
case "settings":
|
|
router.push("/settings");
|
|
break;
|
|
case "logout":
|
|
userStore.handleLogout();
|
|
break;
|
|
default:
|
|
console.log("No action");
|
|
}
|
|
}
|
|
|
|
function logout() {
|
|
userStore.handleLogout();
|
|
}
|
|
|
|
onMounted(() => {
|
|
log.debug("MainNavigationBar mounted");
|
|
if (userStore.loggedIn) {
|
|
// fixme: only when i'm logged in? should this be handled in the store?
|
|
// courseSessionsStore.loadCourseSessionsData();
|
|
}
|
|
});
|
|
|
|
const profileDropdownData: DropdownListItem[] = [
|
|
{
|
|
title: t("mainNavigation.profile"),
|
|
icon: IconSettings as Component,
|
|
data: {
|
|
action: "profile",
|
|
},
|
|
},
|
|
{
|
|
title: t("general.settings"),
|
|
icon: IconSettings as Component,
|
|
data: {
|
|
action: "settings",
|
|
},
|
|
},
|
|
{
|
|
title: t("mainNavigation.logout"),
|
|
icon: IconLogout as Component,
|
|
data: {
|
|
action: "logout",
|
|
},
|
|
},
|
|
];
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<Teleport to="body">
|
|
<MobileMenu
|
|
v-if="userStore.loggedIn"
|
|
:show="state.showMenu"
|
|
:course-session="courseSessionsStore.courseSessionForRoute"
|
|
@closemodal="state.showMenu = false"
|
|
/>
|
|
</Teleport>
|
|
<Transition name="nav">
|
|
<div v-if="appStore.showMainNavigationBar" class="navigation bg-blue-900">
|
|
<nav class="mx-auto px-8 py-2 lg:flex lg:items-center lg:justify-start lg:py-4">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<router-link to="/" class="flex">
|
|
<it-icon-vbv class="mr-3 -mt-6 -ml-3 h-8 w-16" />
|
|
</router-link>
|
|
<router-link to="/" class="flex">
|
|
<div class="ml-1 border-l border-white pr-10 pl-3 text-2xl text-white">
|
|
myVBV
|
|
</div>
|
|
</router-link>
|
|
</div>
|
|
|
|
<div class="flex items-center lg:hidden">
|
|
<div v-if="userStore.loggedIn" class="mr-6 flex flex-row items-center">
|
|
<NotificationPopover>
|
|
<template #toggleButtonContent>
|
|
<div class="nav-item flex">
|
|
<it-icon-notification class="h-6 w-6" />
|
|
<div
|
|
v-if="notificationsStore.hasUnread"
|
|
aria-label="unread notifications"
|
|
class="mt-1 h-1.5 w-1.5 rounded-full bg-sky-500"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<template #popoverContent>
|
|
<NotificationPopoverContent />
|
|
</template>
|
|
</NotificationPopover>
|
|
</div>
|
|
<router-link
|
|
v-if="userStore.loggedIn"
|
|
to="/messages"
|
|
class="nav-item flex flex-row items-center"
|
|
data-cy="messages-link"
|
|
>
|
|
<it-icon-persons class="mr-6 h-6 w-6" />
|
|
</router-link>
|
|
<!-- Mobile menu button -->
|
|
<div class="flex" @click="toggleNav">
|
|
<button
|
|
type="button"
|
|
class="h-8 w-8 text-white hover:text-sky-500 focus:text-sky-500 focus:outline-none"
|
|
>
|
|
<it-icon-menu class="h-8 w-8" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mobile Menu open: "block", Menu closed: "hidden" -->
|
|
<div
|
|
v-if="appStore.userLoaded && appStore.routingFinished && userStore.loggedIn"
|
|
:class="state.showMenu ? 'flex' : 'hidden'"
|
|
class="mt-8 flex-auto lg:mt-0 lg:flex lg:flex-row lg:items-center lg:space-y-0 lg:space-x-10"
|
|
>
|
|
<!-- <router-link-->
|
|
<!-- v-if="inCourse() && courseSessionsStore.courseSessionForRoute"-->
|
|
<!-- :to="`${courseSessionsStore.courseSessionForRoute.course_url}/cockpit`"-->
|
|
<!-- class="nav-item"-->
|
|
<!-- :class="{ 'nav-item--active': inCockpit() }"-->
|
|
<!-- >-->
|
|
<!-- Cockpit-->
|
|
<!-- </router-link>-->
|
|
|
|
<router-link
|
|
v-if="
|
|
inCourse() &&
|
|
courseSessionsStore.courseSessionForRoute &&
|
|
courseSessionsStore.hasCockpit
|
|
"
|
|
:to="`${courseSessionsStore.courseSessionForRoute.course_url}/cockpit`"
|
|
class="nav-item"
|
|
:class="{ 'nav-item--active': inCockpit() }"
|
|
>
|
|
{{ $t("cockpit.title") }}
|
|
</router-link>
|
|
|
|
<router-link
|
|
v-if="inCourse() && courseSessionsStore.courseSessionForRoute"
|
|
:to="courseSessionsStore.courseSessionForRoute.learning_path_url"
|
|
class="nav-item"
|
|
:class="{ 'nav-item--active': inLearningPath() }"
|
|
>
|
|
{{ $t("general.learningPath") }}
|
|
</router-link>
|
|
|
|
<router-link
|
|
v-if="inCourse() && courseSessionsStore.courseSessionForRoute"
|
|
:to="courseSessionsStore.courseSessionForRoute.competence_url"
|
|
class="nav-item"
|
|
:class="{ 'nav-item--active': inCompetenceProfile() }"
|
|
>
|
|
{{ $t("competences.title") }}
|
|
</router-link>
|
|
|
|
<div class="hidden flex-auto lg:block"></div>
|
|
<a
|
|
class="nav-item"
|
|
target="_blank"
|
|
href="https://bildung.vbv.ch/ilp/pages/catalogsearch.jsf"
|
|
>
|
|
{{ $t("general.shop") }}
|
|
</a>
|
|
<router-link
|
|
v-if="courseSessionsStore.courseSessionForRoute"
|
|
:to="courseSessionsStore.courseSessionForRoute.media_library_url"
|
|
class="nav-item"
|
|
:class="{ 'nav-item--active': inMediaLibrary() }"
|
|
data-cy="medialibrary-link"
|
|
>
|
|
{{ $t("mediaLibrary.title") }}
|
|
</router-link>
|
|
<div v-if="userStore.loggedIn" class="mr-6 flex items-center">
|
|
<NotificationPopover>
|
|
<template #toggleButtonContent>
|
|
<div class="nav-item flex">
|
|
<it-icon-notification class="h-6 w-6" />
|
|
<div
|
|
v-if="notificationsStore.hasUnread"
|
|
aria-label="unread notifications"
|
|
class="mt-1 h-1.5 w-1.5 rounded-full bg-sky-500"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<template #popoverContent>
|
|
<NotificationPopoverContent />
|
|
</template>
|
|
</NotificationPopover>
|
|
</div>
|
|
<router-link
|
|
to="/messages"
|
|
class="nav-item flex flex-row items-center"
|
|
data-cy="messages-link"
|
|
>
|
|
<it-icon-persons class="mr-6 h-6 w-6" />
|
|
</router-link>
|
|
<div v-if="userStore.loggedIn" class="nav-item flex items-center">
|
|
<ItDropdown
|
|
:button-classes="[]"
|
|
:list-items="profileDropdownData"
|
|
:align="'right'"
|
|
@select="handleDropdownSelect"
|
|
>
|
|
<div v-if="userStore.avatar_url">
|
|
<img
|
|
class="inline-block h-8 w-8 rounded-full"
|
|
:src="userStore.avatar_url"
|
|
alt=""
|
|
/>
|
|
</div>
|
|
<div v-else>
|
|
{{ userStore.getFullName }}
|
|
</div>
|
|
</ItDropdown>
|
|
</div>
|
|
<div v-else><a class="" href="/login">Login</a></div>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="postcss" scoped>
|
|
.nav-item {
|
|
@apply text-2xl font-bold text-white hover:text-sky-500 lg:text-base lg:font-normal;
|
|
}
|
|
|
|
.nav-item--active {
|
|
@apply underline decoration-sky-500 decoration-4 underline-offset-[21px];
|
|
}
|
|
|
|
.nav-enter-active,
|
|
.nav-leave-active {
|
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
}
|
|
|
|
.nav-enter-from,
|
|
.nav-leave-to {
|
|
opacity: 0;
|
|
transform: translateY(-80px);
|
|
}
|
|
</style>
|