Merged in feature/new-navigation-layout (pull request #414)
Feature/new navigation layout Approved-by: Elia Bieri
This commit is contained in:
commit
d361dabd16
|
|
@ -1,29 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<AccountMenuContent
|
<AccountMenuContent :user="userStore" @logout="logout" @close="emit('close')" />
|
||||||
:course-sessions="courseSessionsStore.allCurrentCourseSessions"
|
|
||||||
:selected-course-session="courseSessionsStore.currentCourseSession?.id"
|
|
||||||
:user="userStore"
|
|
||||||
@logout="logout"
|
|
||||||
@select-course-session="selectCourseSession"
|
|
||||||
@close="emit('close')"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AccountMenuContent from "@/components/header/AccountMenuContent.vue";
|
import AccountMenuContent from "@/components/header/AccountMenuContent.vue";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import type { CourseSession } from "@/types";
|
|
||||||
|
|
||||||
const emit = defineEmits(["close"]);
|
const emit = defineEmits(["close"]);
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
userStore.handleLogout();
|
userStore.handleLogout();
|
||||||
};
|
};
|
||||||
const selectCourseSession = (courseSession: CourseSession) => {
|
|
||||||
courseSessionsStore.switchCourseSessionById(courseSession.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,28 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import CourseSessionsMenu from "@/components/header/CourseSessionsMenu.vue";
|
import { SETTINGS_ROUTE } from "@/router/names";
|
||||||
import type { User } from "@/stores/user";
|
import type { User } from "@/stores/user";
|
||||||
import type { CourseSession } from "@/types";
|
|
||||||
import { useRouteLookups } from "@/utils/route";
|
|
||||||
import { computed } from "vue";
|
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
courseSessions: CourseSession[];
|
|
||||||
user: User;
|
user: User;
|
||||||
selectedCourseSession?: string;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits(["selectCourseSession", "logout", "close"]);
|
const emit = defineEmits(["logout", "close"]);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { inCourse } = useRouteLookups();
|
|
||||||
|
|
||||||
const showCourseSessionMenu = computed(() => inCourse() && props.courseSessions.length);
|
|
||||||
|
|
||||||
async function navigate(routeName: string) {
|
async function navigate(routeName: string) {
|
||||||
await router.push({ name: routeName });
|
await router.push({ name: routeName });
|
||||||
emit("close");
|
emit("close");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const settingsRoute = {
|
||||||
|
name: SETTINGS_ROUTE,
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="text-black">
|
<div class="flex flex-col gap-4 text-black">
|
||||||
<div class="border-b py-4">
|
<div class="border-b py-4">
|
||||||
<div class="flex justify-start">
|
<div class="flex justify-start">
|
||||||
<div v-if="user.avatar_url">
|
<div v-if="user.avatar_url">
|
||||||
|
|
@ -46,22 +42,19 @@ async function navigate(routeName: string) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="showCourseSessionMenu" class="border-b py-4">
|
<router-link class="flex items-center gap-2" :to="settingsRoute">
|
||||||
<CourseSessionsMenu
|
<it-icon-settings />
|
||||||
:items="courseSessions"
|
{{ $t("a.Einstellungen") }}
|
||||||
:selected="selectedCourseSession"
|
</router-link>
|
||||||
@select="emit('selectCourseSession', $event)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="mt-6 flex items-center"
|
class="flex items-center gap-2"
|
||||||
data-cy="logout-button"
|
data-cy="logout-button"
|
||||||
@click="emit('logout')"
|
@click="emit('logout')"
|
||||||
>
|
>
|
||||||
<it-icon-logout class="inline-block" />
|
<it-icon-logout class="inline-block" />
|
||||||
<span class="ml-1">{{ $t("mainNavigation.logout") }}</span>
|
<span>{{ $t("mainNavigation.logout") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,12 @@ import {
|
||||||
} from "@/utils/utils";
|
} from "@/utils/utils";
|
||||||
import { useTranslation } from "i18next-vue";
|
import { useTranslation } from "i18next-vue";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import SelectedCourseSession from "./SelectedCourseSession.vue";
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
|
|
||||||
const { inCockpit, inCompetenceProfile, inLearningMentor, inLearningPath } =
|
const { isInCockpit, inCompetenceProfile, inLearningMentor, inLearningPath } =
|
||||||
useRouteLookups();
|
useRouteLookups();
|
||||||
const {
|
const {
|
||||||
hasCompetenceNaviMenu,
|
hasCompetenceNaviMenu,
|
||||||
|
|
@ -30,13 +31,18 @@ const mentorTabTitle = computed(() =>
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div v-if="courseSessionsStore.currentCourseSession" class="hidden space-x-8 lg:flex">
|
<div
|
||||||
|
v-if="courseSessionsStore.currentCourseSession"
|
||||||
|
class="flex space-x-8 px-2 lg:px-10"
|
||||||
|
>
|
||||||
|
<SelectedCourseSession />
|
||||||
|
|
||||||
<router-link
|
<router-link
|
||||||
v-if="hasCockpitMenu"
|
v-if="hasCockpitMenu"
|
||||||
data-cy="navigation-cockpit-link"
|
data-cy="navigation-cockpit-link"
|
||||||
:to="getCockpitUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
:to="getCockpitUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
||||||
class="nav-item"
|
class="nav-item-no-mobile"
|
||||||
:class="{ 'nav-item--active': inCockpit() }"
|
:class="{ 'nav-item--active': isInCockpit }"
|
||||||
>
|
>
|
||||||
{{ t("cockpit.title") }}
|
{{ t("cockpit.title") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
@ -46,7 +52,7 @@ const mentorTabTitle = computed(() =>
|
||||||
data-cy="navigation-preview-link"
|
data-cy="navigation-preview-link"
|
||||||
:to="getLearningPathUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
:to="getLearningPathUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="nav-item"
|
class="nav-item-no-mobile"
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span>{{ t("a.Vorschau Teilnehmer") }}</span>
|
<span>{{ t("a.Vorschau Teilnehmer") }}</span>
|
||||||
|
|
@ -57,7 +63,7 @@ const mentorTabTitle = computed(() =>
|
||||||
v-if="hasLearningPathMenu"
|
v-if="hasLearningPathMenu"
|
||||||
data-cy="navigation-learning-path-link"
|
data-cy="navigation-learning-path-link"
|
||||||
:to="getLearningPathUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
:to="getLearningPathUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
||||||
class="nav-item"
|
class="nav-item-no-mobile"
|
||||||
:class="{ 'nav-item--active': inLearningPath() }"
|
:class="{ 'nav-item--active': inLearningPath() }"
|
||||||
>
|
>
|
||||||
{{ t("general.learningPath") }}
|
{{ t("general.learningPath") }}
|
||||||
|
|
@ -67,7 +73,7 @@ const mentorTabTitle = computed(() =>
|
||||||
v-if="hasCompetenceNaviMenu"
|
v-if="hasCompetenceNaviMenu"
|
||||||
data-cy="navigation-competence-profile-link"
|
data-cy="navigation-competence-profile-link"
|
||||||
:to="getCompetenceNaviUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
:to="getCompetenceNaviUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
||||||
class="nav-item"
|
class="nav-item-no-mobile"
|
||||||
:class="{ 'nav-item--active': inCompetenceProfile() }"
|
:class="{ 'nav-item--active': inCompetenceProfile() }"
|
||||||
>
|
>
|
||||||
{{ t("competences.title") }}
|
{{ t("competences.title") }}
|
||||||
|
|
@ -77,7 +83,7 @@ const mentorTabTitle = computed(() =>
|
||||||
v-if="hasLearningMentor"
|
v-if="hasLearningMentor"
|
||||||
data-cy="navigation-learning-mentor-link"
|
data-cy="navigation-learning-mentor-link"
|
||||||
:to="getLearningMentorUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
:to="getLearningMentorUrl(courseSessionsStore.currentCourseSession.course.slug)"
|
||||||
class="nav-item"
|
class="nav-item-no-mobile"
|
||||||
:class="{ 'nav-item--active': inLearningMentor() }"
|
:class="{ 'nav-item--active': inLearningMentor() }"
|
||||||
>
|
>
|
||||||
{{ t(mentorTabTitle) }}
|
{{ t(mentorTabTitle) }}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,24 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useRouteLookups } from "@/utils/route";
|
||||||
import { useTranslation } from "i18next-vue";
|
import { useTranslation } from "i18next-vue";
|
||||||
|
const { isInCourse } = useRouteLookups();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="hidden flex-shrink-0 items-center lg:flex">
|
<div class="flex flex-shrink-0 items-center">
|
||||||
<div class="flex items-center">
|
<template v-if="isInCourse">
|
||||||
|
<div class="flex h-full items-center border-r border-slate-500">
|
||||||
|
<router-link to="/" class="flex items-center pr-3">
|
||||||
|
<it-icon-arrow-left />
|
||||||
|
<span class="hidden text-slate-500 lg:inline">
|
||||||
|
{{ t("a.Dashboard") }}
|
||||||
|
</span>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<router-link to="/" class="flex">
|
<router-link to="/" class="flex">
|
||||||
<it-icon-vbv class="-ml-3 -mt-6 mr-3 h-8 w-16" />
|
<it-icon-vbv class="-ml-3 -mt-6 mr-3 h-8 w-16" />
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
@ -15,6 +27,6 @@ const { t } = useTranslation();
|
||||||
{{ t("general.title") }}
|
{{ t("general.title") }}
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,7 @@ log.debug("MainNavigationBar created");
|
||||||
|
|
||||||
const courseSessionsStore = useCourseSessionsStore();
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
const { inMediaLibrary, inAppointments } = useRouteLookups();
|
const { inMediaLibrary, inAppointments } = useRouteLookups();
|
||||||
const { hasMediaLibraryMenu, hasAppointmentsMenu, hasSessionTitle } =
|
const { hasMediaLibraryMenu, hasAppointmentsMenu } = useNavigationAttributes();
|
||||||
useNavigationAttributes();
|
|
||||||
|
|
||||||
const selectedCourseSessionTitle = computed(() => {
|
|
||||||
return courseSessionsStore.currentCourseSession?.title;
|
|
||||||
});
|
|
||||||
|
|
||||||
const appointmentsUrl = computed(() => {
|
const appointmentsUrl = computed(() => {
|
||||||
const currentCourseSession = courseSessionsStore.currentCourseSession;
|
const currentCourseSession = courseSessionsStore.currentCourseSession;
|
||||||
|
|
@ -40,7 +35,6 @@ onMounted(() => {
|
||||||
<nav class="bg-blue-900 text-white">
|
<nav class="bg-blue-900 text-white">
|
||||||
<div class="mx-auto px-4 lg:px-8">
|
<div class="mx-auto px-4 lg:px-8">
|
||||||
<div class="relative flex h-16 justify-between">
|
<div class="relative flex h-16 justify-between">
|
||||||
<MobileMenuButton />
|
|
||||||
<div class="flex flex-1 items-stretch justify-start">
|
<div class="flex flex-1 items-stretch justify-start">
|
||||||
<HomeNavigation />
|
<HomeNavigation />
|
||||||
<CourseSessionNavigation />
|
<CourseSessionNavigation />
|
||||||
|
|
@ -62,7 +56,7 @@ onMounted(() => {
|
||||||
v-if="hasAppointmentsMenu"
|
v-if="hasAppointmentsMenu"
|
||||||
:to="appointmentsUrl"
|
:to="appointmentsUrl"
|
||||||
data-cy="all-duedates-link"
|
data-cy="all-duedates-link"
|
||||||
class="nav-item"
|
class="nav-item-no-mobile"
|
||||||
:class="{ 'nav-item--active': inAppointments() }"
|
:class="{ 'nav-item--active': inAppointments() }"
|
||||||
>
|
>
|
||||||
<it-icon-calendar-light class="h-8 w-8" />
|
<it-icon-calendar-light class="h-8 w-8" />
|
||||||
|
|
@ -71,22 +65,12 @@ onMounted(() => {
|
||||||
<!-- Notification Bell & Menu -->
|
<!-- Notification Bell & Menu -->
|
||||||
<NotificationButton />
|
<NotificationButton />
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="hasSessionTitle"
|
|
||||||
class="nav-item hidden items-center lg:inline-flex"
|
|
||||||
>
|
|
||||||
<div class="" data-cy="current-course-session-title">
|
|
||||||
{{ selectedCourseSessionTitle }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="nav-item">
|
<div class="nav-item">
|
||||||
<ProfileMenuButton />
|
<ProfileMenuButton />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<MobileMenuButton />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="postcss"></style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
|
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
|
||||||
import { useVVByLink } from "@/composables";
|
import { useVVByLink } from "@/composables";
|
||||||
|
import { SETTINGS_ROUTE } from "@/router/names";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
import type { User } from "@/stores/user";
|
import type { User } from "@/stores/user";
|
||||||
import type { CourseSession } from "@/types";
|
import type { CourseSession } from "@/types";
|
||||||
|
|
@ -49,6 +50,10 @@ const mentorTabTitle = computed(() =>
|
||||||
? "a.Praxisbildner"
|
? "a.Praxisbildner"
|
||||||
: "a.Lernbegleitung"
|
: "a.Lernbegleitung"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const settingsRoute = {
|
||||||
|
name: SETTINGS_ROUTE,
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -71,57 +76,64 @@ const mentorTabTitle = computed(() =>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="courseSession" class="mt-6 border-b">
|
<div v-if="courseSession" class="mt-6 border-b">
|
||||||
<h4 class="text-sm text-gray-900">{{ courseSession.course.title }}</h4>
|
<h4 class="px-4 text-sm text-gray-900">{{ courseSession.course.title }}</h4>
|
||||||
<ul class="mt-6">
|
<ul class="mt-6 flex flex-col">
|
||||||
<li v-if="hasCockpitMenu" class="mb-6">
|
<li v-if="hasCockpitMenu" class="mb-6">
|
||||||
<button
|
<router-link
|
||||||
|
class="w-full px-4 py-2"
|
||||||
|
active-class="bg-gray-200 text-blue-900 font-bold"
|
||||||
data-cy="navigation-mobile-cockpit-link"
|
data-cy="navigation-mobile-cockpit-link"
|
||||||
@click="clickLink(getCockpitUrl(courseSession.course.slug))"
|
:to="getCockpitUrl(courseSession.course.slug)"
|
||||||
|
@click="$emit('closemodal')"
|
||||||
>
|
>
|
||||||
{{ $t("cockpit.title") }}
|
{{ $t("cockpit.title") }}
|
||||||
</button>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="hasPreviewMenu" class="mb-6">
|
<li v-if="hasPreviewMenu" class="mb-2 flex">
|
||||||
<button
|
<router-link
|
||||||
|
class="w-full px-4 py-2"
|
||||||
|
active-class="bg-gray-200 text-blue-900 font-bold"
|
||||||
data-cy="navigation-mobile-preview-link"
|
data-cy="navigation-mobile-preview-link"
|
||||||
@click="clickLink(getLearningPathUrl(courseSession.course.slug))"
|
:to="getLearningPathUrl(courseSession.course.slug)"
|
||||||
|
@click="$emit('closemodal')"
|
||||||
>
|
>
|
||||||
{{ $t("a.Vorschau Teilnehmer") }}
|
{{ $t("a.Vorschau Teilnehmer") }}
|
||||||
</button>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="hasLearningPathMenu" class="mb-6">
|
<li v-if="hasLearningPathMenu" class="mb-2 flex">
|
||||||
<button
|
<router-link
|
||||||
|
class="w-full px-4 py-2"
|
||||||
|
active-class="bg-gray-200 text-blue-900 font-bold"
|
||||||
data-cy="navigation-mobile-learning-path-link"
|
data-cy="navigation-mobile-learning-path-link"
|
||||||
@click="clickLink(getLearningPathUrl(courseSession.course.slug))"
|
:to="getLearningPathUrl(courseSession.course.slug)"
|
||||||
|
@click="$emit('closemodal')"
|
||||||
>
|
>
|
||||||
{{ $t("general.learningPath") }}
|
{{ $t("general.learningPath") }}
|
||||||
</button>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="hasCompetenceNaviMenu" class="mb-6">
|
<li v-if="hasCompetenceNaviMenu" class="mb-2 flex">
|
||||||
<button
|
<router-link
|
||||||
|
class="w-full px-4 py-2"
|
||||||
|
active-class="bg-gray-200 text-blue-900 font-bold"
|
||||||
data-cy="navigation-mobile-competence-profile-link"
|
data-cy="navigation-mobile-competence-profile-link"
|
||||||
@click="clickLink(getCompetenceNaviUrl(courseSession.course.slug))"
|
:to="getCompetenceNaviUrl(courseSession.course.slug)"
|
||||||
|
@click="$emit('closemodal')"
|
||||||
>
|
>
|
||||||
{{ $t("competences.title") }}
|
{{ $t("competences.title") }}
|
||||||
</button>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="hasLearningMentor" class="mb-6">
|
<li v-if="hasLearningMentor" class="mb-2 flex">
|
||||||
<button
|
<router-link
|
||||||
|
class="w-full px-4 py-2"
|
||||||
|
active-class="bg-gray-200 text-blue-900 font-bold"
|
||||||
data-cy="navigation-mobile-mentor-link"
|
data-cy="navigation-mobile-mentor-link"
|
||||||
@click="clickLink(getLearningMentorUrl(courseSession.course.slug))"
|
:to="getLearningMentorUrl(courseSession.course.slug)"
|
||||||
|
@click="$emit('closemodal')"
|
||||||
>
|
>
|
||||||
{{ $t(mentorTabTitle) }}
|
{{ $t(mentorTabTitle) }}
|
||||||
</button>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li v-if="hasMediaLibraryMenu" class="mb-6">
|
|
||||||
<button
|
|
||||||
data-cy="medialibrary-link"
|
|
||||||
@click="clickLink(getMediaCenterUrl(courseSession.course.slug))"
|
|
||||||
>
|
|
||||||
{{ $t("a.Mediathek") }}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li
|
<li
|
||||||
v-if="isVVLearningMentor(courseSessionsStore.currentCourseSession)"
|
v-if="isVVLearningMentor(courseSessionsStore.currentCourseSession)"
|
||||||
class="mb-6"
|
class="mb-6"
|
||||||
|
|
@ -137,15 +149,47 @@ const mentorTabTitle = computed(() =>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-6 border-b">
|
<div class="mt-6 border-b">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="mb-6">
|
<li v-if="courseSession && hasMediaLibraryMenu" class="mb-6 flex">
|
||||||
<button data-cy="dashboard-link" @click="clickLink('/')">myVBV</button>
|
<router-link
|
||||||
|
data-cy="medialibrary-link"
|
||||||
|
class="flex w-full items-center gap-2 px-4 py-2"
|
||||||
|
active-class="bg-gray-200 text-blue-900 font-bold"
|
||||||
|
:to="getMediaCenterUrl(courseSession.course.slug)"
|
||||||
|
@click="$emit('closemodal')"
|
||||||
|
>
|
||||||
|
<it-icon-media-library />
|
||||||
|
{{ $t("a.Mediathek") }}
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li v-if="courseSession && hasMediaLibraryMenu" class="mb-6 flex">
|
||||||
|
<router-link
|
||||||
|
data-cy="calendar-link"
|
||||||
|
class="flex w-full items-center gap-2 px-4 py-2"
|
||||||
|
active-class="bg-gray-200 text-blue-900 font-bold"
|
||||||
|
:to="'/'"
|
||||||
|
@click="$emit('closemodal')"
|
||||||
|
>
|
||||||
|
<!-- todo: correct route -->
|
||||||
|
<it-icon-calendar-light />
|
||||||
|
{{ $t("a.Termine") }}
|
||||||
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<router-link
|
||||||
|
:to="settingsRoute"
|
||||||
|
class="mt-6 flex w-full items-center gap-2 px-4 py-2"
|
||||||
|
active-class="bg-gray-200 text-blue-900 font-bold"
|
||||||
|
v-if="user?.loggedIn"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<it-icon-settings />
|
||||||
|
{{ $t("a.Einstellungen") }}
|
||||||
|
</router-link>
|
||||||
<button
|
<button
|
||||||
v-if="user?.loggedIn"
|
v-if="user?.loggedIn"
|
||||||
type="button"
|
type="button"
|
||||||
class="mt-6 flex items-center"
|
class="mt-6 flex items-center px-4 py-2"
|
||||||
@click="$emit('logout')"
|
@click="$emit('logout')"
|
||||||
>
|
>
|
||||||
<it-icon-logout class="inline-block" />
|
<it-icon-logout class="inline-block" />
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ const showMenu = ref(false);
|
||||||
@logout="userStore.handleLogout()"
|
@logout="userStore.handleLogout()"
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<div class="absolute inset-y-0 left-0 flex items-center lg:hidden">
|
<div class="inset-y-0 flex items-center lg:hidden">
|
||||||
<!-- Mobile menu button -->
|
<!-- Mobile menu button -->
|
||||||
<div data-cy="navigation-mobile-menu-button" class="flex" @click="showMenu = true">
|
<div data-cy="navigation-mobile-menu-button" class="flex" @click="showMenu = true">
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,11 @@ function popoverClick(event: Event) {
|
||||||
<AccountMenu @close="showMenu = false" />
|
<AccountMenu @close="showMenu = false" />
|
||||||
</ItFullScreenModal>
|
</ItFullScreenModal>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<div v-if="userStore.loggedIn" class="flex items-center" data-cy="header-profile">
|
<div
|
||||||
|
v-if="userStore.loggedIn"
|
||||||
|
class="hidden items-center lg:flex"
|
||||||
|
data-cy="header-profile"
|
||||||
|
>
|
||||||
<Popover class="relative">
|
<Popover class="relative">
|
||||||
<PopoverButton @click="popoverClick($event)">
|
<PopoverButton @click="popoverClick($event)">
|
||||||
<div v-if="userStore.avatar_url">
|
<div v-if="userStore.avatar_url">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
|
import type { CourseSession } from "@/types";
|
||||||
|
import { useNavigationAttributes } from "@/utils/navigation";
|
||||||
|
import { useRouteLookups } from "@/utils/route";
|
||||||
|
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import CourseSessionsMenu from "./CourseSessionsMenu.vue";
|
||||||
|
|
||||||
|
const { isInCourse } = useRouteLookups();
|
||||||
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
|
const { hasSessionTitle } = useNavigationAttributes();
|
||||||
|
|
||||||
|
const selectedCourseSessionTitle = computed(() => {
|
||||||
|
return courseSessionsStore.currentCourseSession?.title;
|
||||||
|
});
|
||||||
|
const selectedCourseSession = computed(() => {
|
||||||
|
return courseSessionsStore.currentCourseSession;
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectCourseSession = (courseSession: CourseSession) => {
|
||||||
|
courseSessionsStore.switchCourseSessionById(courseSession.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const courseSessions = computed(() => {
|
||||||
|
return courseSessionsStore.allCourseSessions;
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCourseSessionMenu = computed(
|
||||||
|
() => isInCourse.value && courseSessions.value.length
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="hasSessionTitle"
|
||||||
|
class="nav-item-base inline-flex items-center lg:inline-flex"
|
||||||
|
>
|
||||||
|
<div data-cy="current-course-session-title" class="text-bold">
|
||||||
|
{{ selectedCourseSessionTitle }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Popover v-if="showCourseSessionMenu" class="relative">
|
||||||
|
<PopoverButton
|
||||||
|
class="group flex items-center rounded-md bg-transparent px-3 text-base focus:outline-none"
|
||||||
|
>
|
||||||
|
<it-icon-arrow-down class="h-6 w-6" />
|
||||||
|
</PopoverButton>
|
||||||
|
<PopoverPanel class="absolute left-0 z-10 mt-3 w-64 px-1 sm:px-0 lg:max-w-3xl">
|
||||||
|
<div
|
||||||
|
class="flex flex-col rounded-lg bg-white p-4 shadow-lg ring-1 ring-black/5"
|
||||||
|
>
|
||||||
|
<h3 class="fond-bold mb-2 text-base text-black">Durchführung</h3>
|
||||||
|
<CourseSessionsMenu
|
||||||
|
:items="courseSessions"
|
||||||
|
:selected="selectedCourseSession?.id"
|
||||||
|
@select="selectCourseSession"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</PopoverPanel>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { RouterLink } from "vue-router";
|
||||||
|
// https://router.vuejs.org/guide/advanced/extending-router-link
|
||||||
|
import { isExternalLink as isExternalLinkFn } from "@/utils/navigation";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// @ts-expect-error the example above mentions needing @ts-ignore
|
||||||
|
...RouterLink.props,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isExternalLink = computed(() => {
|
||||||
|
return isExternalLinkFn(props.to);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a
|
||||||
|
v-if="isExternalLink"
|
||||||
|
class="flex items-center gap-2"
|
||||||
|
v-bind="$attrs"
|
||||||
|
:href="to"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<it-icon-external-link class="w-6" />
|
||||||
|
</a>
|
||||||
|
<!-- make `:to` explicit -->
|
||||||
|
<router-link
|
||||||
|
v-else
|
||||||
|
v-slot="{ isActive, href, navigate }"
|
||||||
|
v-bind="$props"
|
||||||
|
:to="$props.to"
|
||||||
|
custom
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
v-bind="$attrs"
|
||||||
|
:class="isActive ? activeClass : ''"
|
||||||
|
:href="href"
|
||||||
|
@click="navigate"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</a>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import SubNavItem from "@/components/header/SubNavItem.vue";
|
||||||
|
import { isExternalLink } from "@/utils/navigation";
|
||||||
|
import { Listbox, ListboxOption, ListboxOptions } from "@headlessui/vue";
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
export interface EntryRoute {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EntryOrExternalRoute = EntryRoute | string;
|
||||||
|
|
||||||
|
export interface SubNavEntry {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
route: EntryOrExternalRoute;
|
||||||
|
dataCy?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
items: SubNavEntry[];
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const isCurrentRoute = (route: { name: string } | string) => {
|
||||||
|
return typeof route !== "string" && route?.name === router.currentRoute.value.name;
|
||||||
|
};
|
||||||
|
const currentRouteName = computed(() => {
|
||||||
|
return props.items.find((item) => isCurrentRoute(item.route))?.name || "";
|
||||||
|
});
|
||||||
|
const open = ref<boolean>(false);
|
||||||
|
const currentRoute = ref(props.items.find((item) => isCurrentRoute(item.route)));
|
||||||
|
const selectRoute = (current: SubNavEntry) => {
|
||||||
|
// we use this to mimic VueRouter's active flag
|
||||||
|
open.value = false;
|
||||||
|
currentRoute.value = current;
|
||||||
|
};
|
||||||
|
|
||||||
|
const internalLinks = computed(() => {
|
||||||
|
return props.items.filter((i) => !isExternalLink(i.route));
|
||||||
|
});
|
||||||
|
const externalLinks = computed(() => {
|
||||||
|
return props.items.filter((i) => isExternalLink(i.route));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<nav class="border-b bg-white px-4 lg:px-8">
|
||||||
|
<Listbox as="div" :model-value="currentRoute" by="id">
|
||||||
|
<div class="relative w-full py-2 lg:hidden">
|
||||||
|
<button
|
||||||
|
class="relative flex w-full cursor-default flex-row items-center border bg-white py-3 pl-5 pr-10 text-left"
|
||||||
|
@click="open = !open"
|
||||||
|
>
|
||||||
|
{{ currentRouteName }}
|
||||||
|
<span
|
||||||
|
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
|
||||||
|
>
|
||||||
|
<it-icon-arrow-down class="h-5 w-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<ListboxOptions
|
||||||
|
v-if="open"
|
||||||
|
class="absolute top-14 z-50 flex w-full cursor-default flex-col rounded-xl border-0 bg-white text-left shadow-lg"
|
||||||
|
static
|
||||||
|
>
|
||||||
|
<ListboxOption
|
||||||
|
v-for="item in items"
|
||||||
|
:key="item.id"
|
||||||
|
v-slot="{ selected }"
|
||||||
|
:value="item"
|
||||||
|
class="relative w-full border-b py-3 pl-10 pr-10 last:border-b-0"
|
||||||
|
>
|
||||||
|
<SubNavItem
|
||||||
|
:to="item.route"
|
||||||
|
class="flex items-center gap-2"
|
||||||
|
@click="selectRoute(item)"
|
||||||
|
>
|
||||||
|
<it-icon-check
|
||||||
|
v-if="selected"
|
||||||
|
class="absolute left-2 top-1/2 w-8 -translate-y-1/2"
|
||||||
|
/>
|
||||||
|
{{ item.name }}
|
||||||
|
</SubNavItem>
|
||||||
|
</ListboxOption>
|
||||||
|
</ListboxOptions>
|
||||||
|
</div>
|
||||||
|
</Listbox>
|
||||||
|
<div class="center hidden items-end justify-between lg:flex">
|
||||||
|
<ul class="flex flex-row gap-10">
|
||||||
|
<li
|
||||||
|
v-for="item in internalLinks"
|
||||||
|
:key="item.id"
|
||||||
|
class="border-t-2 border-t-transparent"
|
||||||
|
:class="{ 'border-b-2 border-b-blue-900': isCurrentRoute(item.route) }"
|
||||||
|
>
|
||||||
|
<SubNavItem :data-cy="item.dataCy" :to="item.route" class="block py-3">
|
||||||
|
{{ item.name }}
|
||||||
|
</SubNavItem>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="flex flex-row gap-10">
|
||||||
|
<li
|
||||||
|
v-for="item in externalLinks"
|
||||||
|
:key="item.id"
|
||||||
|
class="border-b-2 border-t-2 border-b-transparent border-t-transparent"
|
||||||
|
>
|
||||||
|
<SubNavItem :data-cy="item.dataCy" :to="item.route" class="block py-3">
|
||||||
|
{{ item.name }}
|
||||||
|
</SubNavItem>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
"a.Damit du myVBV nutzen kannst, brauchst du ein Konto.": "Damit du myVBV nutzen kannst, brauchst du ein Konto.",
|
"a.Damit du myVBV nutzen kannst, brauchst du ein Konto.": "Damit du myVBV nutzen kannst, brauchst du ein Konto.",
|
||||||
"a.Das muss ich nochmals anschauen": "Das muss ich nochmals anschauen",
|
"a.Das muss ich nochmals anschauen": "Das muss ich nochmals anschauen",
|
||||||
"a.Das wurde mit dir geteilt": "Das wurde mit dir geteilt",
|
"a.Das wurde mit dir geteilt": "Das wurde mit dir geteilt",
|
||||||
|
"a.Dashboard": "Dashboard",
|
||||||
"a.Datei auswählen": "Datei auswählen",
|
"a.Datei auswählen": "Datei auswählen",
|
||||||
"a.Datei hochladen": "Datei hochladen",
|
"a.Datei hochladen": "Datei hochladen",
|
||||||
"a.Datei kann nicht gespeichert werden.": "Datei kann nicht gespeichert werden.",
|
"a.Datei kann nicht gespeichert werden.": "Datei kann nicht gespeichert werden.",
|
||||||
|
|
@ -104,6 +105,7 @@
|
||||||
"a.E-Mail Adresse": "E-Mail Adresse",
|
"a.E-Mail Adresse": "E-Mail Adresse",
|
||||||
"a.Einladung": "Einladung",
|
"a.Einladung": "Einladung",
|
||||||
"a.Einladung abschicken": "Einladung abschicken",
|
"a.Einladung abschicken": "Einladung abschicken",
|
||||||
|
"a.Einstellungen": "Einstellungen",
|
||||||
"a.Elemente zu erledigen": "Elemente zu erledigen",
|
"a.Elemente zu erledigen": "Elemente zu erledigen",
|
||||||
"a.Email": "Email",
|
"a.Email": "Email",
|
||||||
"a.Entfernen": "Entfernen",
|
"a.Entfernen": "Entfernen",
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
"a.Damit du myVBV nutzen kannst, brauchst du ein Konto.": "Pour utiliser myVBV, vous devez créer un compte.",
|
"a.Damit du myVBV nutzen kannst, brauchst du ein Konto.": "Pour utiliser myVBV, vous devez créer un compte.",
|
||||||
"a.Das muss ich nochmals anschauen": "Il faut que je regarde cela encore une fois de plus près",
|
"a.Das muss ich nochmals anschauen": "Il faut que je regarde cela encore une fois de plus près",
|
||||||
"a.Das wurde mit dir geteilt": "Cela a été partagé avec toi",
|
"a.Das wurde mit dir geteilt": "Cela a été partagé avec toi",
|
||||||
|
"a.Dashboard": "Dashboard",
|
||||||
"a.Datei auswählen": "Sélectionner le fichier",
|
"a.Datei auswählen": "Sélectionner le fichier",
|
||||||
"a.Datei hochladen": "Télécharger le fichier",
|
"a.Datei hochladen": "Télécharger le fichier",
|
||||||
"a.Datei kann nicht gespeichert werden.": "Impossible d'enregistrer le fichier.",
|
"a.Datei kann nicht gespeichert werden.": "Impossible d'enregistrer le fichier.",
|
||||||
|
|
@ -104,6 +105,7 @@
|
||||||
"a.E-Mail Adresse": "Adresse e-mail",
|
"a.E-Mail Adresse": "Adresse e-mail",
|
||||||
"a.Einladung": "Invitation",
|
"a.Einladung": "Invitation",
|
||||||
"a.Einladung abschicken": "Envoyer l'invitation",
|
"a.Einladung abschicken": "Envoyer l'invitation",
|
||||||
|
"a.Einstellungen": "Paramètres",
|
||||||
"a.Elemente zu erledigen": "Eléments à faire",
|
"a.Elemente zu erledigen": "Eléments à faire",
|
||||||
"a.Email": "Email",
|
"a.Email": "Email",
|
||||||
"a.Entfernen": "Supprimer",
|
"a.Entfernen": "Supprimer",
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@
|
||||||
"a.Damit du myVBV nutzen kannst, brauchst du ein Konto.": "Per utilizzare myVBV, hai bisogno di un account.",
|
"a.Damit du myVBV nutzen kannst, brauchst du ein Konto.": "Per utilizzare myVBV, hai bisogno di un account.",
|
||||||
"a.Das muss ich nochmals anschauen": "Devo riguardarlo ancora una volta",
|
"a.Das muss ich nochmals anschauen": "Devo riguardarlo ancora una volta",
|
||||||
"a.Das wurde mit dir geteilt": "Questo è stato condiviso con te",
|
"a.Das wurde mit dir geteilt": "Questo è stato condiviso con te",
|
||||||
|
"a.Dashboard": "Dashboard",
|
||||||
"a.Datei auswählen": "Selezionare il file",
|
"a.Datei auswählen": "Selezionare il file",
|
||||||
"a.Datei hochladen": "Carica il file",
|
"a.Datei hochladen": "Carica il file",
|
||||||
"a.Datei kann nicht gespeichert werden.": "Impossibile salvare il file.",
|
"a.Datei kann nicht gespeichert werden.": "Impossibile salvare il file.",
|
||||||
|
|
@ -104,6 +105,7 @@
|
||||||
"a.E-Mail Adresse": "Indirizzo e-mail",
|
"a.E-Mail Adresse": "Indirizzo e-mail",
|
||||||
"a.Einladung": "Invito",
|
"a.Einladung": "Invito",
|
||||||
"a.Einladung abschicken": "Inviare l'invito",
|
"a.Einladung abschicken": "Inviare l'invito",
|
||||||
|
"a.Einstellungen": "Impostazioni",
|
||||||
"a.Elemente zu erledigen": "Elementi da completare",
|
"a.Elemente zu erledigen": "Elementi da completare",
|
||||||
"a.Email": "E-mail",
|
"a.Email": "E-mail",
|
||||||
"a.Entfernen": "Rimuovere",
|
"a.Entfernen": "Rimuovere",
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,78 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import SubNavigation, { type SubNavEntry } from "@/components/header/SubNavigation.vue";
|
||||||
import { useCurrentCourseSession, useEvaluationWithFeedback } from "@/composables";
|
import { useCurrentCourseSession, useEvaluationWithFeedback } from "@/composables";
|
||||||
|
import {
|
||||||
|
CERTIFICATES_ROUTE,
|
||||||
|
COMPETENCE_ROUTE,
|
||||||
|
COMPETENCES_ROUTE,
|
||||||
|
SELF_EVALUATION_ROUTE,
|
||||||
|
} from "@/router/names";
|
||||||
|
import { useTranslation } from "i18next-vue";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
|
|
||||||
log.debug("CompetenceParentPage created");
|
log.debug("CompetenceParentPage created");
|
||||||
|
|
||||||
const props = defineProps<{
|
const { t } = useTranslation();
|
||||||
courseSlug: string;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
function routeInOverview() {
|
|
||||||
return route.path.endsWith("/competence");
|
|
||||||
}
|
|
||||||
|
|
||||||
function routeInCompetenceCertificate() {
|
|
||||||
return route.path.includes("/certificate");
|
|
||||||
}
|
|
||||||
|
|
||||||
function routeInActionCompetences() {
|
|
||||||
return route.path.endsWith("/competences");
|
|
||||||
}
|
|
||||||
|
|
||||||
function routeInSelfEvaluationAndFeedback() {
|
|
||||||
return route.path.endsWith("/self-evaluation-and-feedback");
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentCourseSession = useCurrentCourseSession();
|
const currentCourseSession = useCurrentCourseSession();
|
||||||
const hasEvaluationFeedback = useEvaluationWithFeedback().hasFeedback;
|
const hasEvaluationFeedback = useEvaluationWithFeedback().hasFeedback;
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug("CompetenceParentPage mounted", props.courseSlug);
|
log.debug("CompetenceParentPage mounted");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const competenceRoute = {
|
||||||
|
name: COMPETENCE_ROUTE,
|
||||||
|
};
|
||||||
|
const certificatesRoute = {
|
||||||
|
name: CERTIFICATES_ROUTE,
|
||||||
|
};
|
||||||
|
const selfEvaluationRoute = {
|
||||||
|
name: SELF_EVALUATION_ROUTE,
|
||||||
|
};
|
||||||
|
const competencesRoute = {
|
||||||
|
name: COMPETENCES_ROUTE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// todo: replace this menu with a real one before going live
|
||||||
|
const items: SubNavEntry[] = [
|
||||||
|
{ id: 0, name: t("a.Übersicht"), route: competenceRoute },
|
||||||
|
...(currentCourseSession.value.course.configuration.enable_competence_certificates
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: t("a.Kompetenznachweise"),
|
||||||
|
route: certificatesRoute,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: hasEvaluationFeedback.value
|
||||||
|
? t("a.Selbst- und Fremdeinschätzungen")
|
||||||
|
: t("a.Selbsteinschätzungen"),
|
||||||
|
dataCy: "self-evaluation-and-feedback-navigation-link",
|
||||||
|
route: selfEvaluationRoute,
|
||||||
|
},
|
||||||
|
{ id: 3, name: t("a.Handlungskompetenzen"), route: competencesRoute },
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "MS Teams",
|
||||||
|
route: "https://iterativ.ch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "Vorschau Teilnehmer",
|
||||||
|
route: "https://iterativ.ch",
|
||||||
|
},
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-gray-200">
|
<div class="bg-gray-200">
|
||||||
<nav class="border-b bg-white px-4 lg:px-8">
|
<SubNavigation :items="items" />
|
||||||
<ul class="flex flex-col lg:flex-row">
|
|
||||||
<li
|
|
||||||
class="border-t-2 border-t-transparent"
|
|
||||||
:class="{ 'border-b-2 border-b-blue-900': routeInOverview() }"
|
|
||||||
>
|
|
||||||
<router-link :to="`/course/${courseSlug}/competence`" class="block py-3">
|
|
||||||
{{ $t("a.Übersicht") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-if="
|
|
||||||
currentCourseSession.course.configuration.enable_competence_certificates
|
|
||||||
"
|
|
||||||
class="border-t-2 border-t-transparent lg:ml-12"
|
|
||||||
:class="{ 'border-b-2 border-b-blue-900': routeInCompetenceCertificate() }"
|
|
||||||
>
|
|
||||||
<router-link
|
|
||||||
:to="`/course/${courseSlug}/competence/certificates`"
|
|
||||||
class="block py-3"
|
|
||||||
>
|
|
||||||
{{ $t("a.Kompetenznachweise") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
class="border-t-2 border-t-transparent lg:ml-12"
|
|
||||||
:class="{
|
|
||||||
'border-b-2 border-b-blue-900': routeInSelfEvaluationAndFeedback(),
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<router-link
|
|
||||||
:to="`/course/${courseSlug}/competence/self-evaluation-and-feedback`"
|
|
||||||
class="block py-3"
|
|
||||||
data-cy="self-evaluation-and-feedback-navigation-link"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
hasEvaluationFeedback
|
|
||||||
? $t("a.Selbst- und Fremdeinschätzungen")
|
|
||||||
: $t("a.Selbsteinschätzungen")
|
|
||||||
}}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
class="border-t-2 border-t-transparent lg:ml-12"
|
|
||||||
:class="{ 'border-b-2 border-b-blue-900': routeInActionCompetences() }"
|
|
||||||
>
|
|
||||||
<router-link
|
|
||||||
:to="`/course/${courseSlug}/competence/competences`"
|
|
||||||
class="block py-3"
|
|
||||||
>
|
|
||||||
{{ $t("a.Handlungskompetenzen") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<!-- Add similar logic for other `li` items as you expand the list -->
|
|
||||||
<li class="ml-6 inline-block lg:ml-12"></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<main>
|
<main>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,13 @@ import {
|
||||||
import { addToHistory, setLastNavigationWasPush } from "@/router/history";
|
import { addToHistory, setLastNavigationWasPush } from "@/router/history";
|
||||||
import { onboardingRedirect } from "@/router/onboarding";
|
import { onboardingRedirect } from "@/router/onboarding";
|
||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
import {
|
||||||
|
CERTIFICATES_ROUTE,
|
||||||
|
COMPETENCE_ROUTE,
|
||||||
|
COMPETENCES_ROUTE,
|
||||||
|
SELF_EVALUATION_ROUTE,
|
||||||
|
SETTINGS_ROUTE,
|
||||||
|
} from "./names";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
|
@ -107,10 +114,12 @@ const router = createRouter({
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
props: true,
|
props: true,
|
||||||
|
name: COMPETENCE_ROUTE,
|
||||||
component: () => import("@/pages/competence/CompetenceIndexPage.vue"),
|
component: () => import("@/pages/competence/CompetenceIndexPage.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "certificates",
|
path: "certificates",
|
||||||
|
name: CERTIFICATES_ROUTE,
|
||||||
props: true,
|
props: true,
|
||||||
component: () =>
|
component: () =>
|
||||||
import("@/pages/competence/CompetenceCertificateListPage.vue"),
|
import("@/pages/competence/CompetenceCertificateListPage.vue"),
|
||||||
|
|
@ -122,7 +131,7 @@ const router = createRouter({
|
||||||
import("@/pages/competence/CompetenceCertificateDetailPage.vue"),
|
import("@/pages/competence/CompetenceCertificateDetailPage.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "selfEvaluationAndFeedback",
|
name: SELF_EVALUATION_ROUTE,
|
||||||
path: "self-evaluation-and-feedback",
|
path: "self-evaluation-and-feedback",
|
||||||
props: true,
|
props: true,
|
||||||
component: () =>
|
component: () =>
|
||||||
|
|
@ -130,6 +139,7 @@ const router = createRouter({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "competences",
|
path: "competences",
|
||||||
|
name: COMPETENCES_ROUTE,
|
||||||
props: true,
|
props: true,
|
||||||
component: () => import("@/pages/competence/ActionCompetenceListPage.vue"),
|
component: () => import("@/pages/competence/ActionCompetenceListPage.vue"),
|
||||||
},
|
},
|
||||||
|
|
@ -393,6 +403,7 @@ const router = createRouter({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/settings",
|
path: "/settings",
|
||||||
|
name: SETTINGS_ROUTE,
|
||||||
component: () => import("@/pages/SettingsPage.vue"),
|
component: () => import("@/pages/SettingsPage.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export const COMPETENCE_ROUTE = "competence";
|
||||||
|
export const CERTIFICATES_ROUTE = "certificates";
|
||||||
|
export const SELF_EVALUATION_ROUTE = "selfEvaluationAndFeedback";
|
||||||
|
export const COMPETENCES_ROUTE = "competences";
|
||||||
|
export const SETTINGS_ROUTE = "settings";
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import type { RouteLocationRaw } from "vue-router";
|
||||||
import { useRouteLookups } from "./route";
|
import { useRouteLookups } from "./route";
|
||||||
|
|
||||||
export function useNavigationAttributes() {
|
export function useNavigationAttributes() {
|
||||||
|
|
@ -77,3 +78,7 @@ export function useNavigationAttributes() {
|
||||||
hasSessionTitle,
|
hasSessionTitle,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isExternalLink = (route: string | RouteLocationRaw) => {
|
||||||
|
return typeof route === "string" && route.startsWith("https");
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { computed } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
export function useRouteLookups() {
|
export function useRouteLookups() {
|
||||||
|
|
@ -7,11 +8,15 @@ export function useRouteLookups() {
|
||||||
return route.path.startsWith("/course/");
|
return route.path.startsWith("/course/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isInCourse = computed(() => inCourse());
|
||||||
|
|
||||||
function inCockpit() {
|
function inCockpit() {
|
||||||
const regex = new RegExp("/course/[^/]+/cockpit($|/)");
|
const regex = new RegExp("/course/[^/]+/cockpit($|/)");
|
||||||
return regex.test(route.path);
|
return regex.test(route.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isInCockpit = computed(() => inCockpit());
|
||||||
|
|
||||||
function inLearningPath() {
|
function inLearningPath() {
|
||||||
const regex = new RegExp("/course/[^/]+/learn($|/)");
|
const regex = new RegExp("/course/[^/]+/learn($|/)");
|
||||||
return regex.test(route.path);
|
return regex.test(route.path);
|
||||||
|
|
@ -39,7 +44,9 @@ export function useRouteLookups() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
inMediaLibrary,
|
inMediaLibrary,
|
||||||
|
isInCourse,
|
||||||
inCockpit,
|
inCockpit,
|
||||||
|
isInCockpit,
|
||||||
inLearningPath,
|
inLearningPath,
|
||||||
inCompetenceProfile,
|
inCompetenceProfile,
|
||||||
inLearningMentor,
|
inLearningMentor,
|
||||||
|
|
|
||||||
|
|
@ -176,8 +176,12 @@ textarea {
|
||||||
@apply rounded-full bg-blue-900 px-4 py-2 font-semibold text-white;
|
@apply rounded-full bg-blue-900 px-4 py-2 font-semibold text-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-item-base {
|
||||||
|
@apply inline-flex items-center border-b-4 border-transparent px-1 pt-1 text-white;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
@apply inline-flex items-center border-b-4 border-transparent px-1 pt-1 text-white hover:text-sky-500;
|
@apply nav-item-base hover:text-sky-500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item-no-mobile {
|
.nav-item-no-mobile {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue