Merge branch 'feature/vbv-90-new-main-navigation' into develop

This commit is contained in:
Christian Cueni 2022-08-10 07:10:39 +02:00
commit 737ceb6fd4
8 changed files with 434 additions and 156 deletions

View File

@ -1,26 +1,29 @@
<script setup>
<script setup lang="ts">
import * as log from 'loglevel';
import { onMounted, reactive } from 'vue';
import { onMounted, reactive} from 'vue';
import { useUserStore } from '@/stores/user';
import { useRoute } from 'vue-router';
import { useLearningPathStore } from '@/stores/learningPath';
import { useRoute, useRouter } from 'vue-router';
import { useAppStore } from '@/stores/app';
import IconLogout from "@/components/icons/IconLogout.vue";
import IconSettings from "@/components/icons/IconSettings.vue";
import ItDropdown from "@/components/ui/ItDropdown.vue";
import MobileMenu from "@/components/MobileMenu.vue"
log.debug('MainNavigationBar created');
const route = useRoute()
const router = useRouter()
const userStore = useUserStore();
const appStore = useAppStore();
const learningPathStore = useLearningPathStore();
const state = reactive({showMenu: false});
function toggleNav() {
state.showMenu = !state.showMenu;
}
function calcNavigationMobileOpenClasses() {
return state.showMenu ? ['fixed', 'w-full', 'h-screen'] : [];
}
function menuActive(checkPath) {
return route.path.startsWith(checkPath);
}
@ -29,6 +32,18 @@ function inLearningPath() {
return route.path.startsWith('/learningpath/') || route.path.startsWith('/circle/');
}
function getLearningPathStringProp (prop: 'title' | 'slug'): string {
return inLearningPath() && learningPathStore.learningPath ? learningPathStore.learningPath[prop] : '';
}
function learningPathName (): string {
return getLearningPathStringProp('title')
}
function learninPathSlug (): string {
return getLearningPathStringProp('slug')
}
function backButtonUrl() {
if (route.path.startsWith('/circle/')) {
return '/learningpath/versicherungsvermittlerin';
@ -36,130 +51,188 @@ function backButtonUrl() {
return '/';
}
function handleDropdownSelect(data) {
log.debug('Selected action:', data.action)
switch (data.action) {
case 'settings':
router.push('/profile')
break
case 'logout':
router.push('/logout')
break
default:
console.log('no action')
}
}
onMounted(() => {
log.debug('MainNavigationBar mounted');
})
const profileDropdownData = [
[
{
title: 'Kontoeinstellungen',
icon: IconSettings,
data: {
action: 'settings'
}
}
],
[
{
title: 'Abmelden',
icon: IconLogout,
data: {
action: 'logout'
}
},
]
]
</script>
<template>
<Transition name="nav">
<div v-if="appStore.showMainNavigationBar" class="navigation bg-blue-900" :class="calcNavigationMobileOpenClasses()">
<nav
class="
px-8
py-4
mx-auto
lg:flex lg:justify-start lg:items-center
"
>
<div class="flex items-center justify-between">
<router-link
to="/"
class="flex">
<it-icon-vbv class="h-8 w-16 -mt-3 -ml-3"/>
<div class="
text-white
text-2xl
pr-10
pl-3
ml-1
border-l border-white
"
>
myVBV
</div>
</router-link>
<!-- Mobile menu button -->
<div @click="toggleNav" class="flex lg:hidden">
<button
type="button"
class="
w-8
text-white
hover:text-sky-500
focus:outline-none focus:text-sky-500
"
>
<it-icon-menu class="h-8 w-8"/>
</button>
</div>
</div>
<!-- Mobile Menu open: "block", Menu closed: "hidden" -->
<div
v-if="appStore.userLoaded && appStore.routingFinished && userStore.loggedIn "
:class="state.showMenu ? 'flex' : 'hidden'"
<div>
<Teleport to="body">
<MobileMenu
:user="userStore"
:show="state.showMenu"
:learning-path-name="learningPathName()"
@closemodal="state.showMenu = false"
/>
</Teleport>
<Transition name="nav">
<div v-if="appStore.showMainNavigationBar" class="navigation bg-blue-900">
<nav
class="
flex-auto
flex-col
mt-8
space-y-8
lg:flex lg:space-y-0 lg:flex-row lg:items-center lg:space-x-10 lg:mt-0
px-8
py-2
mx-auto
lg:flex lg:justify-start lg:items-center lg:py-4
"
>
<router-link
v-if="!inLearningPath()"
to="/"
class="nav-item"
:class="{'nav-item--active': route.path === '/'}"
>
Cockpit
</router-link>
<router-link
v-if="!inLearningPath()"
to="/shop"
class="nav-item"
:class="{'nav-item--active': menuActive('/shop')}"
>
Shop
</router-link>
<div class="flex items-center justify-between">
<div class="flex items-center">
<a
href="https://www.vbv.ch"
class="flex">
<it-icon-vbv class="h-8 w-16 mr-3 -mt-6 -ml-3"/>
</a>
<router-link
to="/"
class="flex">
<div class="
text-white
text-2xl
pr-10
pl-3
ml-1
border-l border-white
"
>
myVBV
</div>
</router-link>
</div>
<router-link
v-if="inLearningPath()"
to="/learningpath/versicherungsvermittlerin"
class="nav-item"
:class="{'nav-item--active': menuActive('/learningpath/')}"
>
Lernpfad
</router-link>
<hr class="text-white lg:hidden">
<div class="hidden lg:block flex-auto"></div>
<router-link
to="/mediacenter"
class="nav-item"
:class="{'nav-item--active': menuActive('/mediacenter')}"
>
Mediathek
</router-link>
<router-link
to="/messages"
class="nav-item flex flex-row items-center"
>
<it-icon-message class="w-8 h-8 mr-2"/>
</router-link>
<router-link
to="/profile"
class="nav-item flex items-center"
>
<div v-if="userStore.loggedIn">
<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 class="flex items-center lg:hidden">
<router-link
to="/messages"
class="nav-item flex flex-row items-center"
>
<it-icon-message class="w-8 h-8 mr-6"/>
</router-link>
<!-- Mobile menu button -->
<div @click="toggleNav" class="flex">
<button
type="button"
class="
w-8
h-8
text-white
hover:text-sky-500
focus:outline-none focus:text-sky-500
"
>
<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="
flex-auto
mt-8
lg:flex lg:space-y-0 lg:flex-row lg:items-center lg:space-x-10 lg:mt-0
"
>
<router-link
v-if="inLearningPath()"
to="/learningpath/versicherungsvermittlerin"
class="nav-item"
:class="{'nav-item--active': menuActive('/learningpath/')}"
>
Lernpfad
</router-link>
<router-link
v-if="inLearningPath()"
to="/competences/"
class="nav-item"
:class="{'nav-item--active': menuActive('/competences/')}"
>
Kompetenzprofil
</router-link>
<div class="hidden lg:block flex-auto"></div>
<router-link
to="/shop"
class="nav-item"
:class="{'nav-item--active': menuActive('/shop')}"
>
Shop
</router-link>
<router-link
to="/mediacenter"
class="nav-item"
:class="{'nav-item--active': menuActive('/mediacenter')}"
>
Mediathek
</router-link>
<router-link
to="/messages"
class="nav-item flex flex-row items-center"
>
<it-icon-message class="w-8 h-8 mr-6"/>
</router-link>
<div class="nav-item flex items-center" v-if="userStore.loggedIn">
<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>
</router-link>
</div>
</nav>
</div>
</Transition>
</div>
</nav>
</div>
</Transition>
</div>
</template>
<style lang="postcss" scoped>

View File

@ -0,0 +1,70 @@
<script setup lang="ts">
import ItFullScreenModal from '@/components/ui/ItFullScreenModal.vue'
import IconLogout from '@/components/icons/IconLogout.vue'
import IconSettings from '@/components/icons/IconSettings.vue'
import {useRouter} from "vue-router";
const router = useRouter()
const props = defineProps<{
show: boolean,
user: object,
learningPathName: string,
learningPathSlug: string
}>()
const emits = defineEmits(['closemodal'])
const clickLink = (to: string) => {
router.push(to)
emits('closemodal')
}
</script>
<template>
<ItFullScreenModal
:show="show"
@closemodal="$emit('closemodal')"
>
<div>
<div>
<div class="flex border-b border-gray-500 -mx-8 px-8 pb-4">
<div v-if="user.avatar_url">
<img class="inline-block h-16 w-16 rounded-full"
:src="user.avatar_url"
alt=""/>
</div>
<div class="ml-6">
<h3>{{user.first_name}} {{user.last_name}}</h3>
<button
@click="clickLink('/profile')"
class="mt-2 inline-block items-center">
<IconSettings class="inline-block" /><span class="ml-3">Kontoeinstellungen</span>
</button>
</div>
</div>
<div>
<div
class="mt-6 pb-6 border-b border-gray-500"
v-if="learningPathName">
<h4 class="text-gray-900 text-sm">Kurs: {{learningPathName}}</h4>
<ul class="mt-6">
<li><button @click="clickLink('/learningpath')">Lernpfad</button></li>
<li class="mt-6">Kompetenzprofil</li>
</ul>
</div>
<div class="mt-6 pb-6 border-b border-gray-500">
<ul>
<li>Shop</li>
<li class="mt-6">Mediathek</li>
</ul>
</div>
<div class="mt-6 items-center">
<IconLogout class="inline-block" /><span class="ml-1">Abmelden</span>
</div>
</div>
</div>
</div>
</ItFullScreenModal>
</template>

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
// inspiration https://vuejs.org/examples/#modal
import {Circle} from '@/services/circle';
import ItFullScreenModal from '@/components/ui/ItFullScreenModal.vue'
const props = defineProps<{
circle: Circle,
@ -12,50 +12,39 @@ const emits = defineEmits(['closemodal'])
</script>
<template>
<Transition mode="in-out">
<div
v-if="show"
class="circle-overview px-4 py-16 lg:px-16 lg:py-24 fixed top-0 overflow-y-scroll bg-white h-full w-full">
<button
type="button"
class="w-8 h-8 absolute right-4 top-4 cursor-pointer"
@click="$emit('closemodal')"
>
<it-icon-close></it-icon-close>
</button>
<ItFullScreenModal
:show="show"
@closemodal="$emit('closemodal')"
>
<h1 class="">Überblick: Circle "{{ circle.title }}"</h1>
<p class="mt-8 text-xl">Hier zeigen wir dir, was du in diesem Circle lernen wirst.</p>
<h1 class="">Überblick: Circle "{{ circle.title }}"</h1>
<div class="mt-8 p-4 border border-gray-500">
<h3>Du wirst in der Lage sein, ... </h3>
<p class="mt-8 text-xl">Hier zeigen wir dir, was du in diesem Circle lernen wirst.</p>
<div class="mt-8 p-4 border border-gray-500">
<h3>Du wirst in der Lage sein, ... </h3>
<ul class="mt-4">
<li class="text-xl flex items-center" v-for="goal in circle.goals" :key="goal.id">
<it-icon-check class="mt-4 hidden lg:block w-12 h-12 text-sky-500 flex-none"></it-icon-check>
<div class="mt-4">{{ goal.value }}</div>
</li>
</ul>
</div>
<h3 class="mt-16">
Du wirst dein neu erworbenes Wissen auf folgenden berufstypischen Situation anwenden können:
</h3>
<ul class="grid grid-cols-1 lg:grid-cols-3 auto-rows-fr gap-6 mt-8">
<li
v-for="jobSituation in circle.job_situations"
:key="jobSituation.id"
class="job-situation border border-gray-500 p-4 text-xl flex items-center"
>
{{jobSituation.value}}
<ul class="mt-4">
<li class="text-xl flex items-center" v-for="goal in circle.goals" :key="goal.id">
<it-icon-check class="mt-4 hidden lg:block w-12 h-12 text-sky-500 flex-none"></it-icon-check>
<div class="mt-4">{{ goal.value }}</div>
</li>
</ul>
</div>
</Transition>
<h3 class="mt-16">
Du wirst dein neu erworbenes Wissen auf folgenden berufstypischen Situation anwenden können:
</h3>
<ul class="grid grid-cols-1 lg:grid-cols-3 auto-rows-fr gap-6 mt-8">
<li
v-for="jobSituation in circle.job_situations"
:key="jobSituation.id"
class="job-situation border border-gray-500 p-4 text-xl flex items-center"
>
{{jobSituation.value}}
</li>
</ul>
</ItFullScreenModal>
</template>
<style scoped>

View File

@ -0,0 +1,6 @@
<template>
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26.3292 15.7779C26.3292 15.7625 26.3474 15.7474 26.3536 15.7321C26.3619 15.71 26.3691 15.6877 26.3749 15.6649V15.6222C26.39 15.5448 26.39 15.465 26.3749 15.3876V15.3449C26.3691 15.3221 26.3619 15.2998 26.3536 15.2777C26.3536 15.2623 26.3383 15.2472 26.3292 15.2319C26.32 15.2132 26.3098 15.1949 26.2985 15.1771L26.262 15.1313L26.2315 15.0918L20.9236 9.44226C20.8147 9.31721 20.6597 9.24193 20.494 9.23364C20.3283 9.22556 20.1665 9.2851 20.0457 9.39888C19.9249 9.51244 19.8558 9.67046 19.8541 9.83634C19.8522 10.002 19.9181 10.1615 20.0363 10.2776L24.3779 14.8906H7.47836C7.1417 14.8906 6.86865 15.1634 6.86865 15.5003C6.86865 15.8371 7.14172 16.11 7.47836 16.11H24.3779L20.0363 20.7229C19.8213 20.9698 19.8398 21.3426 20.0782 21.5672C20.3169 21.7916 20.6898 21.7877 20.9236 21.5583L26.2315 15.918L26.2619 15.8784L26.2985 15.8327C26.3098 15.8149 26.32 15.7966 26.3291 15.7779L26.3292 15.7779Z" fill="#0A0A0A"/>
<path d="M6.65855 27H11.5884C12.5588 27 13.4894 26.6146 14.1755 25.9286C14.8616 25.2423 15.247 24.3119 15.247 23.3414V21.2926C15.247 20.9559 14.9741 20.6829 14.6372 20.6829C14.3006 20.6829 14.0275 20.956 14.0275 21.2926V23.3506C14.0275 23.9975 13.7706 24.6179 13.3132 25.0753C12.8558 25.5328 12.2354 25.7897 11.5885 25.7897H6.65861C6.01166 25.7897 5.39134 25.5328 4.93386 25.0753C4.47642 24.6179 4.21952 23.9975 4.21952 23.3506V7.65855C4.21952 7.01161 4.47642 6.39129 4.93386 5.93381C5.39131 5.47636 6.01166 5.21946 6.65861 5.21946H11.5885C12.2354 5.21946 12.8557 5.47637 13.3132 5.93381C13.7706 6.39125 14.0275 7.01161 14.0275 7.65855V9.7074C14.0275 10.0441 14.3006 10.3171 14.6372 10.3171C14.9741 10.3171 15.247 10.044 15.247 9.7074V7.65855C15.247 6.68817 14.8616 5.75774 14.1755 5.07143C13.4894 4.38535 12.5588 4 11.5884 4H6.65855C5.68817 4 4.75774 4.38535 4.07143 5.07143C3.38535 5.75768 3 6.68811 3 7.65855V23.3413C3 24.3117 3.38535 25.2422 4.07143 25.9285C4.75768 26.6145 5.68811 26.9999 6.65855 26.9999V27Z" fill="#0A0A0A"/>
</svg>
</template>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,63 @@
<script setup lang="ts">
import {reactive} from 'vue'
import {Menu, MenuButton, MenuItems, MenuItem} from '@headlessui/vue'
const props = defineProps<{
buttonClasses: [string],
listItems: [
[object]
],
align: 'left' | 'right'
}>()
const emit = defineEmits<{
(e: 'select', data: object): void
}>()
</script>
<template>
<Menu as="div" class="relative inline-block text-left">
<div>
<MenuButton
:class="buttonClasses"
>
<slot></slot>
</MenuButton>
</div>
<transition
enter-active-class="transition duration-100 ease-out"
enter-from-class="transform scale-95 opacity-0"
enter-to-class="transform scale-100 opacity-100"
leave-active-class="transition duration-75 ease-in"
leave-from-class="transform scale-100 opacity-100"
leave-to-class="transform scale-95 opacity-0"
>
<MenuItems
class="absolute mt-2 px-6 w-56 origin-top-right divide-y divide-gray-500 bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
:class="[align === 'left' ? 'left-0' : 'right-0']"
>
<div class="" v-for="section in listItems" :key="section">
<div class="px-1 py-1" v-for="item in section" :key="item">
<MenuItem>
<button
@click="$emit('select', item.data)"
class="text-black group flex w-full items-center px-0 py-2 text-sm"
>
<span class="inline-block pr-2">
<component
v-if="item.icon"
:is="item.icon"
></component>
</span>
{{item.title}}
</button>
</MenuItem>
</div>
</div>
</MenuItems>
</transition>
</Menu>
</template>

View File

@ -0,0 +1,27 @@
<script setup lang="ts">
// inspiration https://vuejs.org/examples/#modal
const props = defineProps<{
show: boolean
}>()
const emits = defineEmits(['closemodal'])
</script>
<template>
<Transition mode="in-out">
<div
v-if="show"
class="circle-overview px-4 py-16 lg:px-16 lg:py-24 fixed top-0 overflow-y-scroll bg-white h-full w-full">
<button
type="button"
class="w-8 h-8 absolute right-4 top-4 cursor-pointer"
@click="$emit('closemodal')"
>
<it-icon-close></it-icon-close>
</button>
<slot></slot>
</div>
</Transition>
</template>

View File

@ -3,6 +3,9 @@
import {reactive} from 'vue'
import {Listbox, ListboxButton, ListboxOption, ListboxOptions} from '@headlessui/vue'
import ItCheckbox from '@/components/ui/ItCheckbox.vue';
import ItDropdown from "@/components/ui/ItDropdown.vue";
import IconLogout from "@/components/icons/IconLogout.vue"
import IconSettings from "@/components/icons/IconSettings.vue"
const state = reactive({
@ -22,6 +25,32 @@ const state = reactive({
dropdownSelected: {id: 8},
})
const dropdownData = [
[
{
title: 'Option 1',
icon: IconLogout,
data: {}
},
{
title: 'Option 2',
icon: null,
data: {
test: 12
}
}
],
[
{
title: 'Option 3',
icon: IconSettings,
data: {
amount: 34
}
},
]
]
// TODO: die CSS-Klasse für die Farben wird hier in der StyleGuideView.vue generiert.
// deshalb muss man diese CSS-Klassen in tailwind.config.js "safelist"en, wenn diese sonst
// noch nirgendwo verwendet werden.
@ -32,6 +61,10 @@ function colorBgClass(color: string, value: number) {
return `bg-${color}-${value}`;
}
function log(data: any) {
console.log(data);
}
</script>
<template>
@ -300,6 +333,17 @@ function colorBgClass(color: string, value: number) {
<ItCheckbox disabled class="mt-4">Disabled</ItCheckbox>
<h2 class="mt-8 mb-8">Dropdown</h2>
<div class="h-60">
<ItDropdown
:button-classes="['btn-primary']"
:list-items="dropdownData"
:align="'left'"
@select="log"
>Click Me</ItDropdown>
</div>
</main>
</template>