Merged in feature/new-navigation-structure (pull request #407)

Feature/new navigation structure

Approved-by: Elia Bieri
This commit is contained in:
Ramon Wenger 2024-10-29 15:35:06 +00:00
commit 345e935655
11 changed files with 431 additions and 366 deletions

View File

@ -1,6 +1,6 @@
<template>
<div class="flex min-h-full flex-col">
<MainNavigationBar v-if="!route.meta.hideChrome" class="flex-none" />
<HeaderBar v-if="!route.meta.hideChrome" class="flex-none" />
<CloseButton v-if="route.meta.showCloseButton" class="flex-none" />
<RouterView v-slot="{ Component }" class="flex-auto">
<Transition mode="out-in" name="app">
@ -15,13 +15,14 @@
import log from "loglevel";
import AppFooter from "@/components/AppFooter.vue";
import MainNavigationBar from "@/components/header/MainNavigationBar.vue";
// import MainNavigationBar from "@/components/header/MainNavigationBar.vue";
import { graphqlClient } from "@/graphql/client";
import eventBus from "@/utils/eventBus";
import { provideClient } from "@urql/vue";
import { onMounted, ref } from "vue";
import { useRoute } from "vue-router";
import CloseButton from "./components/header/CloseButton.vue";
import HeaderBar from "./components/header/HeaderBar.vue";
const route = useRoute();

View File

@ -0,0 +1,86 @@
<script setup lang="ts">
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useNavigationAttributes } from "@/utils/navigation";
import { useRouteLookups } from "@/utils/route";
import {
getCockpitUrl,
getCompetenceNaviUrl,
getLearningMentorUrl,
getLearningPathUrl,
} from "@/utils/utils";
import { useTranslation } from "i18next-vue";
import { computed } from "vue";
const { t } = useTranslation();
const courseSessionsStore = useCourseSessionsStore();
const { inCockpit, inCompetenceProfile, inLearningMentor, inLearningPath } =
useRouteLookups();
const {
hasCompetenceNaviMenu,
hasLearningPathMenu,
hasCockpitMenu,
hasPreviewMenu,
hasLearningMentor,
} = useNavigationAttributes();
const mentorTabTitle = computed(() =>
courseSessionsStore.currentCourseSession?.course.configuration.is_uk
? "a.Praxisbildner"
: "a.Lernbegleitung"
);
</script>
<template>
<div v-if="courseSessionsStore.currentCourseSession" class="hidden space-x-8 lg:flex">
<router-link
v-if="hasCockpitMenu"
data-cy="navigation-cockpit-link"
:to="getCockpitUrl(courseSessionsStore.currentCourseSession.course.slug)"
class="nav-item"
:class="{ 'nav-item--active': inCockpit() }"
>
{{ t("cockpit.title") }}
</router-link>
<router-link
v-if="hasPreviewMenu"
data-cy="navigation-preview-link"
:to="getLearningPathUrl(courseSessionsStore.currentCourseSession.course.slug)"
target="_blank"
class="nav-item"
>
<div class="flex items-center">
<span>{{ t("a.Vorschau Teilnehmer") }}</span>
<it-icon-external-link class="ml-2" />
</div>
</router-link>
<router-link
v-if="hasLearningPathMenu"
data-cy="navigation-learning-path-link"
:to="getLearningPathUrl(courseSessionsStore.currentCourseSession.course.slug)"
class="nav-item"
:class="{ 'nav-item--active': inLearningPath() }"
>
{{ t("general.learningPath") }}
</router-link>
<router-link
v-if="hasCompetenceNaviMenu"
data-cy="navigation-competence-profile-link"
:to="getCompetenceNaviUrl(courseSessionsStore.currentCourseSession.course.slug)"
class="nav-item"
:class="{ 'nav-item--active': inCompetenceProfile() }"
>
{{ t("competences.title") }}
</router-link>
<router-link
v-if="hasLearningMentor"
data-cy="navigation-learning-mentor-link"
:to="getLearningMentorUrl(courseSessionsStore.currentCourseSession.course.slug)"
class="nav-item"
:class="{ 'nav-item--active': inLearningMentor() }"
>
{{ t(mentorTabTitle) }}
</router-link>
</div>
</template>

View File

@ -0,0 +1,13 @@
<script setup lang="ts">
import { useCourseSessionsStore } from "@/stores/courseSessions";
import MainNavigationBar from "./MainNavigationBar.vue";
const courseSessionsStore = useCourseSessionsStore();
</script>
<template>
<CoursePreviewBar v-if="courseSessionsStore.isCourseSessionPreviewActive" />
<template v-else>
<MainNavigationBar />
</template>
</template>

View File

@ -0,0 +1,20 @@
<script setup lang="ts">
import { useTranslation } from "i18next-vue";
const { t } = useTranslation();
</script>
<template>
<div class="hidden flex-shrink-0 items-center lg:flex">
<div class="flex items-center">
<router-link to="/" class="flex">
<it-icon-vbv class="-ml-3 -mt-6 mr-3 h-8 w-16" />
</router-link>
<router-link to="/" class="flex">
<div class="ml-1 border-l border-white pl-3 pr-10 text-2xl text-white">
{{ t("general.title") }}
</div>
</router-link>
</div>
</div>
</template>

View File

@ -0,0 +1,12 @@
- nav
- default
- backnav
- cockpitnav
- course selector
- subnav
- mediathek
- calendar
- notifications
- profile
- links

View File

@ -1,56 +1,22 @@
<script setup lang="ts">
import AccountMenu from "@/components/header/AccountMenu.vue";
import CoursePreviewBar from "@/components/header/CoursePreviewBar.vue";
import MobileMenu from "@/components/header/MobileMenu.vue";
import NotificationPopover from "@/components/notifications/NotificationPopover.vue";
import NotificationPopoverContent from "@/components/notifications/NotificationPopoverContent.vue";
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
import { getLoginURL } from "@/router/utils";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useNotificationsStore } from "@/stores/notifications";
import { useUserStore } from "@/stores/user";
import { useNavigationAttributes } from "@/utils/navigation";
import { useRouteLookups } from "@/utils/route";
import {
getCockpitUrl,
getCompetenceNaviUrl,
getLearningMentorUrl,
getLearningPathUrl,
getMediaCenterUrl,
} from "@/utils/utils";
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
import { useTranslation } from "i18next-vue";
import { getMediaCenterUrl } from "@/utils/utils";
import log from "loglevel";
import { computed, onMounted, reactive } from "vue";
import { computed, onMounted } from "vue";
import CourseSessionNavigation from "./CourseSessionNavigation.vue";
import HomeNavigation from "./HomeNavigation.vue";
import MobileMenuButton from "./MobileMenuButton.vue";
import NotificationButton from "./NotificationButton.vue";
import ProfileMenuButton from "./ProfileMenuButton.vue";
log.debug("MainNavigationBar created");
const breakpoints = useBreakpoints(breakpointsTailwind);
const userStore = useUserStore();
const courseSessionsStore = useCourseSessionsStore();
const notificationsStore = useNotificationsStore();
const {
inCockpit,
inCompetenceProfile,
inLearningMentor,
inCourse,
inLearningPath,
inMediaLibrary,
inAppointments,
} = useRouteLookups();
const { t } = useTranslation();
const state = reactive({
showMobileNavigationMenu: false,
showMobileProfileMenu: false,
});
function popoverClick(event: Event) {
if (breakpoints.smaller("lg").value) {
event.preventDefault();
state.showMobileProfileMenu = true;
}
}
const { inMediaLibrary, inAppointments } = useRouteLookups();
const { hasMediaLibraryMenu, hasAppointmentsMenu, hasSessionTitle } =
useNavigationAttributes();
const selectedCourseSessionTitle = computed(() => {
return courseSessionsStore.currentCourseSession?.title;
@ -68,333 +34,59 @@ const appointmentsUrl = computed(() => {
onMounted(() => {
log.debug("MainNavigationBar mounted");
});
const hasLearningPathMenu = computed(() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("learning-path") &&
inCourse()
)
);
const hasCompetenceNaviMenu = computed(() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("competence-navi") &&
inCourse()
)
);
const hasMediaLibraryMenu = computed(() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("media-library") &&
inCourse()
)
);
const hasCockpitMenu = computed(
() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("expert-cockpit")
) && inCourse()
);
const hasPreviewMenu = computed(
() =>
Boolean(courseSessionsStore.currentCourseSession?.actions.includes("preview")) &&
inCourse()
);
const hasAppointmentsMenu = computed(() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("appointments") &&
userStore.loggedIn &&
inCourse()
)
);
const hasNotificationsMenu = computed(() => {
return userStore.loggedIn;
});
const hasLearningMentor = computed(() => {
if (!inCourse()) {
return false;
}
if (!courseSessionsStore.currentCourseSession) {
return false;
}
const courseSession = courseSessionsStore.currentCourseSession;
return courseSession.actions.includes("learning-mentor");
});
const mentorTabTitle = computed(() =>
courseSessionsStore.currentCourseSession?.course.configuration.is_uk
? "a.Praxisbildner"
: "a.Lernbegleitung"
);
const hasSessionTitle = computed(() => {
return courseSessionsStore.currentCourseSession?.title && inCourse();
});
</script>
<template>
<CoursePreviewBar v-if="courseSessionsStore.isCourseSessionPreviewActive" />
<div v-else>
<Teleport to="body">
<MobileMenu
v-if="userStore.loggedIn"
:show="state.showMobileNavigationMenu"
:course-session="courseSessionsStore.currentCourseSession"
:has-media-library-menu="hasMediaLibraryMenu"
:has-cockpit-menu="hasCockpitMenu"
:has-preview-menu="hasPreviewMenu"
:has-learning-path-menu="hasLearningPathMenu"
:has-competence-navi-menu="hasCompetenceNaviMenu"
:has-learning-mentor="hasLearningMentor"
:has-notifications-menu="hasNotificationsMenu"
:has-appointments-menu="hasAppointmentsMenu"
:media-url="
getMediaCenterUrl(courseSessionsStore.currentCourseSession?.course?.slug)
"
:user="userStore"
@closemodal="state.showMobileNavigationMenu = false"
@logout="userStore.handleLogout()"
/>
</Teleport>
<Teleport to="body">
<ItFullScreenModal
v-if="userStore.loggedIn"
:show="state.showMobileProfileMenu"
@closemodal="state.showMobileProfileMenu = false"
>
<AccountMenu @close="state.showMobileProfileMenu = false" />
</ItFullScreenModal>
</Teleport>
<Transition name="nav">
<nav class="bg-blue-900 text-white">
<div class="mx-auto px-4 lg:px-8">
<div class="relative flex h-16 justify-between">
<div class="absolute inset-y-0 left-0 flex items-center lg:hidden">
<!-- Mobile menu button -->
<div
data-cy="navigation-mobile-menu-button"
class="flex"
@click="state.showMobileNavigationMenu = true"
>
<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>
<nav class="bg-blue-900 text-white">
<div class="mx-auto px-4 lg:px-8">
<div class="relative flex h-16 justify-between">
<MobileMenuButton />
<div class="flex flex-1 items-stretch justify-start">
<HomeNavigation />
<CourseSessionNavigation />
</div>
<div class="flex flex-1 items-stretch justify-start">
<div class="hidden flex-shrink-0 items-center lg:flex">
<div class="flex items-center">
<router-link to="/" class="flex">
<it-icon-vbv class="-ml-3 -mt-6 mr-3 h-8 w-16" />
</router-link>
<router-link to="/" class="flex">
<div
class="ml-1 border-l border-white pl-3 pr-10 text-2xl text-white"
>
{{ t("general.title") }}
</div>
</router-link>
</div>
</div>
<div class="flex items-stretch justify-start space-x-8">
<router-link
v-if="hasMediaLibraryMenu"
:to="
getMediaCenterUrl(courseSessionsStore.currentCourseSession?.course.slug)
"
data-cy="medialibrary-link"
class="nav-item-no-mobile"
:class="{ 'nav-item--active': inMediaLibrary() }"
>
<it-icon-media-library class="h-8 w-8" />
</router-link>
<router-link
v-if="hasAppointmentsMenu"
:to="appointmentsUrl"
data-cy="all-duedates-link"
class="nav-item"
:class="{ 'nav-item--active': inAppointments() }"
>
<it-icon-calendar-light class="h-8 w-8" />
</router-link>
<!-- Satisfy the type checker; these menu items are
only relevant if there is a current course session -->
<template v-if="courseSessionsStore.currentCourseSession">
<div class="hidden space-x-8 lg:flex">
<router-link
v-if="hasCockpitMenu"
data-cy="navigation-cockpit-link"
:to="
getCockpitUrl(
courseSessionsStore.currentCourseSession.course.slug
)
"
class="nav-item"
:class="{ 'nav-item--active': inCockpit() }"
>
{{ t("cockpit.title") }}
</router-link>
<!-- Notification Bell & Menu -->
<NotificationButton />
<router-link
v-if="hasPreviewMenu"
data-cy="navigation-preview-link"
:to="
getLearningPathUrl(
courseSessionsStore.currentCourseSession.course.slug
)
"
target="_blank"
class="nav-item"
>
<div class="flex items-center">
<span>{{ t("a.Vorschau Teilnehmer") }}</span>
<it-icon-external-link class="ml-2" />
</div>
</router-link>
<router-link
v-if="hasLearningPathMenu"
data-cy="navigation-learning-path-link"
:to="
getLearningPathUrl(
courseSessionsStore.currentCourseSession.course.slug
)
"
class="nav-item"
:class="{ 'nav-item--active': inLearningPath() }"
>
{{ t("general.learningPath") }}
</router-link>
<router-link
v-if="hasCompetenceNaviMenu"
data-cy="navigation-competence-profile-link"
:to="
getCompetenceNaviUrl(
courseSessionsStore.currentCourseSession.course.slug
)
"
class="nav-item"
:class="{ 'nav-item--active': inCompetenceProfile() }"
>
{{ t("competences.title") }}
</router-link>
<router-link
v-if="hasLearningMentor"
data-cy="navigation-learning-mentor-link"
:to="
getLearningMentorUrl(
courseSessionsStore.currentCourseSession.course.slug
)
"
class="nav-item"
:class="{ 'nav-item--active': inLearningMentor() }"
>
{{ t(mentorTabTitle) }}
</router-link>
</div>
</template>
</div>
<div class="flex items-stretch justify-start space-x-8">
<router-link
v-if="hasMediaLibraryMenu"
:to="
getMediaCenterUrl(
courseSessionsStore.currentCourseSession?.course.slug
)
"
data-cy="medialibrary-link"
class="nav-item-no-mobile"
:class="{ 'nav-item--active': inMediaLibrary() }"
>
<it-icon-media-library class="h-8 w-8" />
</router-link>
<router-link
v-if="hasAppointmentsMenu"
:to="appointmentsUrl"
data-cy="all-duedates-link"
class="nav-item"
:class="{ 'nav-item--active': inAppointments() }"
>
<it-icon-calendar-light class="h-8 w-8" />
</router-link>
<!-- Notification Bell & Menu -->
<div v-if="hasNotificationsMenu" class="nav-item leading-none">
<NotificationPopover>
<template #toggleButtonContent>
<div class="relative h-8 w-8">
<div>
<it-icon-notification class="h-8 w-8" />
<div
v-if="notificationsStore.hasUnread"
aria-label="unread notifications"
class="absolute inset-y-0 right-0 h-1.5 w-1.5 rounded-full bg-sky-500"
/>
</div>
</div>
</template>
<template #popoverContent>
<NotificationPopoverContent />
</template>
</NotificationPopover>
</div>
<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
v-if="userStore.loggedIn"
class="flex items-center"
data-cy="header-profile"
>
<Popover class="relative">
<PopoverButton @click="popoverClick($event)">
<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>
</PopoverButton>
<PopoverPanel
v-slot="{ close }"
class="absolute -right-2 top-8 z-50 w-[500px] bg-white shadow-lg"
>
<div class="p-4">
<AccountMenu @close="close" />
</div>
</PopoverPanel>
</Popover>
</div>
<div v-else>
<a class="" :href="getLoginURL({ lang: userStore.language })">
Login
</a>
</div>
</div>
<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">
<ProfileMenuButton />
</div>
</div>
</nav>
</Transition>
</div>
</div>
</div>
</nav>
</template>
<style lang="postcss" scoped>
.nav-item {
@apply inline-flex items-center border-b-4 border-transparent px-1 pt-1 text-white hover:text-sky-500;
}
.nav-item-no-mobile {
@apply hidden items-center border-b-4 border-transparent px-1 pt-1 text-white hover:text-sky-500 lg:inline-flex;
}
.nav-item--active {
@apply border-sky-500;
}
</style>
<style lang="postcss"></style>

View File

@ -0,0 +1,59 @@
<script setup lang="ts">
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useUserStore } from "@/stores/user";
import { useNavigationAttributes } from "@/utils/navigation";
import { getMediaCenterUrl } from "@/utils/utils";
import { ref } from "vue";
import MobileMenu from "./MobileMenu.vue";
const userStore = useUserStore();
const courseSessionsStore = useCourseSessionsStore();
const {
hasCompetenceNaviMenu,
hasLearningPathMenu,
hasMediaLibraryMenu,
hasCockpitMenu,
hasPreviewMenu,
hasAppointmentsMenu,
hasNotificationsMenu,
hasLearningMentor,
} = useNavigationAttributes();
const showMenu = ref(false);
</script>
<template>
<Teleport to="body">
<MobileMenu
v-if="userStore.loggedIn"
:show="showMenu"
:course-session="courseSessionsStore.currentCourseSession"
:has-media-library-menu="hasMediaLibraryMenu"
:has-cockpit-menu="hasCockpitMenu"
:has-preview-menu="hasPreviewMenu"
:has-learning-path-menu="hasLearningPathMenu"
:has-competence-navi-menu="hasCompetenceNaviMenu"
:has-learning-mentor="hasLearningMentor"
:has-notifications-menu="hasNotificationsMenu"
:has-appointments-menu="hasAppointmentsMenu"
:media-url="
getMediaCenterUrl(courseSessionsStore.currentCourseSession?.course?.slug)
"
:user="userStore"
@closemodal="showMenu = false"
@logout="userStore.handleLogout()"
/>
</Teleport>
<div class="absolute inset-y-0 left-0 flex items-center lg:hidden">
<!-- Mobile menu button -->
<div data-cy="navigation-mobile-menu-button" class="flex" @click="showMenu = true">
<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>
</template>

View File

@ -0,0 +1,31 @@
<script setup lang="ts">
import NotificationPopover from "@/components/notifications/NotificationPopover.vue";
import NotificationPopoverContent from "@/components/notifications/NotificationPopoverContent.vue";
import { useNotificationsStore } from "@/stores/notifications";
import { useNavigationAttributes } from "@/utils/navigation";
const notificationsStore = useNotificationsStore();
const { hasNotificationsMenu } = useNavigationAttributes();
</script>
<template>
<div v-if="hasNotificationsMenu" class="nav-item leading-none">
<NotificationPopover>
<template #toggleButtonContent>
<div class="relative h-8 w-8">
<div>
<it-icon-notification class="h-8 w-8" />
<div
v-if="notificationsStore.hasUnread"
aria-label="unread notifications"
class="absolute inset-y-0 right-0 h-1.5 w-1.5 rounded-full bg-sky-500"
/>
</div>
</div>
</template>
<template #popoverContent>
<NotificationPopoverContent />
</template>
</NotificationPopover>
</div>
</template>

View File

@ -0,0 +1,60 @@
<script setup lang="ts">
import AccountMenu from "@/components/header/AccountMenu.vue";
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
import { getLoginURL } from "@/router/utils";
import { useUserStore } from "@/stores/user";
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/vue";
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
import { ref } from "vue";
const userStore = useUserStore();
const breakpoints = useBreakpoints(breakpointsTailwind);
const showMenu = ref(false);
function popoverClick(event: Event) {
if (breakpoints.smaller("lg").value) {
event.preventDefault();
showMenu.value = true;
}
}
</script>
<template>
<Teleport to="body">
<ItFullScreenModal
v-if="userStore.loggedIn"
:show="showMenu"
@closemodal="showMenu = false"
>
<AccountMenu @close="showMenu = false" />
</ItFullScreenModal>
</Teleport>
<div v-if="userStore.loggedIn" class="flex items-center" data-cy="header-profile">
<Popover class="relative">
<PopoverButton @click="popoverClick($event)">
<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>
</PopoverButton>
<PopoverPanel
v-slot="{ close }"
class="absolute -right-2 top-8 z-50 w-[500px] bg-white shadow-lg"
>
<div class="p-4">
<AccountMenu @close="close" />
</div>
</PopoverPanel>
</Popover>
</div>
<div v-else>
<a class="" :href="getLoginURL({ lang: userStore.language })">Login</a>
</div>
</template>

View File

@ -0,0 +1,79 @@
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useUserStore } from "@/stores/user";
import { computed } from "vue";
import { useRouteLookups } from "./route";
export function useNavigationAttributes() {
const courseSessionsStore = useCourseSessionsStore();
const userStore = useUserStore();
const { inCourse } = useRouteLookups();
const hasCompetenceNaviMenu = computed(() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("competence-navi") &&
inCourse()
)
);
const hasLearningPathMenu = computed(() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("learning-path") &&
inCourse()
)
);
const hasMediaLibraryMenu = computed(() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("media-library") &&
inCourse()
)
);
const hasCockpitMenu = computed(
() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("expert-cockpit")
) && inCourse()
);
const hasPreviewMenu = computed(
() =>
Boolean(courseSessionsStore.currentCourseSession?.actions.includes("preview")) &&
inCourse()
);
const hasAppointmentsMenu = computed(() =>
Boolean(
courseSessionsStore.currentCourseSession?.actions.includes("appointments") &&
userStore.loggedIn &&
inCourse()
)
);
const hasNotificationsMenu = computed(() => {
return userStore.loggedIn;
});
const hasLearningMentor = computed(() => {
if (!inCourse()) {
return false;
}
if (!courseSessionsStore.currentCourseSession) {
return false;
}
const courseSession = courseSessionsStore.currentCourseSession;
return courseSession.actions.includes("learning-mentor");
});
const hasSessionTitle = computed(() => {
return courseSessionsStore.currentCourseSession?.title && inCourse();
});
return {
hasCompetenceNaviMenu,
hasLearningPathMenu,
hasMediaLibraryMenu,
hasCockpitMenu,
hasPreviewMenu,
hasAppointmentsMenu,
hasNotificationsMenu,
hasLearningMentor,
hasSessionTitle,
};
}

View File

@ -175,6 +175,18 @@ textarea {
.tag-active {
@apply rounded-full bg-blue-900 px-4 py-2 font-semibold text-white;
}
.nav-item {
@apply inline-flex items-center border-b-4 border-transparent px-1 pt-1 text-white hover:text-sky-500;
}
.nav-item-no-mobile {
@apply hidden items-center border-b-4 border-transparent px-1 pt-1 text-white hover:text-sky-500 lg:inline-flex;
}
.nav-item--active {
@apply border-sky-500;
}
}
@layer utilities {