Add convenience features
This commit is contained in:
parent
8edea0b92f
commit
724b8a8cb1
|
|
@ -11,6 +11,12 @@ import * as log from 'loglevel';
|
|||
|
||||
import MainNavigationBar from '@/components/MainNavigationBar.vue';
|
||||
import Footer from '@/components/Footer.vue';
|
||||
import {onMounted} from 'vue';
|
||||
|
||||
log.debug('App created');
|
||||
|
||||
onMounted(() => {
|
||||
log.debug('App mounted');
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script setup>
|
||||
import * as log from 'loglevel';
|
||||
|
||||
import { reactive } from 'vue';
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useAppStore } from '@/stores/app';
|
||||
|
||||
log.debug('MainNavigationBar.vue created');
|
||||
log.debug('MainNavigationBar created');
|
||||
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore();
|
||||
|
|
@ -14,7 +14,6 @@ const appStore = useAppStore();
|
|||
const state = reactive({showMenu: false});
|
||||
|
||||
function toggleNav() {
|
||||
console.log(state.showMenu);
|
||||
state.showMenu = !state.showMenu;
|
||||
}
|
||||
|
||||
|
|
@ -34,127 +33,133 @@ function backButtonUrl() {
|
|||
if (route.path.startsWith('/circle/')) {
|
||||
return '/learningpath/versicherungsvermittlerin';
|
||||
}
|
||||
|
||||
return '/';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
log.debug('MainNavigationBar mounted');
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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
|
||||
:class="state.showMenu ? 'flex' : 'hidden'"
|
||||
<Transition>
|
||||
<div v-if="appStore.showMainNavigationBar" class="navigation bg-blue-900" :class="calcNavigationMobileOpenClasses()">
|
||||
<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-4
|
||||
mx-auto
|
||||
lg:flex lg:justify-start lg:items-center
|
||||
"
|
||||
>
|
||||
<router-link
|
||||
v-if="userStore.loggedIn && !inLearningPath()"
|
||||
to="/"
|
||||
class="nav-item"
|
||||
:class="{'nav-item--active': route.path === '/'}"
|
||||
>
|
||||
Cockpit
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="userStore.loggedIn && !inLearningPath()"
|
||||
to="/shop"
|
||||
class="nav-item"
|
||||
:class="{'nav-item--active': menuActive('/shop')}"
|
||||
>
|
||||
Shop
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
v-if="userStore.loggedIn && 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:list-item flex-auto"></div>
|
||||
<router-link
|
||||
v-if="userStore.loggedIn"
|
||||
to="/mediacenter"
|
||||
class="nav-item"
|
||||
:class="{'nav-item--active': menuActive('/mediacenter')}"
|
||||
>
|
||||
Mediathek
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="userStore.loggedIn"
|
||||
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 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 v-else><a class="" href="/login">Login</a></div>
|
||||
</router-link>
|
||||
</div>
|
||||
</nav>
|
||||
</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
|
||||
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
|
||||
"
|
||||
>
|
||||
<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>
|
||||
|
||||
<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:list-item 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>
|
||||
</div>
|
||||
<div v-else><a class="" href="/login">Login</a></div>
|
||||
</router-link>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
|
|
@ -165,4 +170,16 @@ function backButtonUrl() {
|
|||
.nav-item--active {
|
||||
@apply underline underline-offset-[21px] decoration-sky-500 decoration-4
|
||||
}
|
||||
|
||||
.v-enter-active,
|
||||
.v-leave-active {
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.v-enter-from,
|
||||
.v-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-80px);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,20 +5,6 @@ import * as _ from 'underscore'
|
|||
import {useCircleStore} from '@/stores/circle';
|
||||
import * as log from 'loglevel';
|
||||
|
||||
const props = defineProps<{
|
||||
width: {
|
||||
default: 500,
|
||||
type: number,
|
||||
required: false
|
||||
|
||||
},
|
||||
height: {
|
||||
default: 500,
|
||||
type: number,
|
||||
required: false
|
||||
},
|
||||
}>()
|
||||
|
||||
const circleStore = useCircleStore();
|
||||
|
||||
function someFinished(learningSequence) {
|
||||
|
|
@ -70,12 +56,12 @@ const blue900 = '#00224D',
|
|||
sky400 = '#72CAFF',
|
||||
sky500 = '#41B5FA'
|
||||
|
||||
function render() {
|
||||
const width = 450, //props.width,
|
||||
height = 450, //props.height,
|
||||
radius: number = Math.min(width, height) / 2.4,
|
||||
arrowStrokeWidth = 2
|
||||
const width = 450
|
||||
const height = 450
|
||||
const radius = Math.min(width, height) / 2.4
|
||||
|
||||
function render() {
|
||||
const arrowStrokeWidth = 2
|
||||
|
||||
const svg = d3.select('.circle-visualization')
|
||||
.attr('viewBox', `0 0 ${width} ${height}`)
|
||||
|
|
@ -224,6 +210,8 @@ function render() {
|
|||
<pre hidden>{{ pieData }}</pre>
|
||||
<pre hidden>{{render()}}</pre>
|
||||
<svg class="circle-visualization h-full">
|
||||
<circle v-if="!circleStore.circle" :cx="width / 2" :cy="height / 2" :r="radius" :color="gray300"/>
|
||||
<circle v-if="!circleStore.circle" :cx="width / 2" :cy="height / 2" :r="radius / 2.5" color="white"/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import type {NavigationGuardWithThis, RouteLocationNormalized} from 'vue-router';
|
||||
import {useUserStore} from '@/stores/user';
|
||||
|
||||
import type { NavigationGuardWithThis, RouteLocationNormalized } from 'vue-router'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
export const updateLoggedIn: NavigationGuardWithThis<undefined> = (_to) => {
|
||||
const loggedIn = getCookieValue('loginStatus') === 'true'
|
||||
const userStore = useUserStore()
|
||||
|
||||
userStore.$patch({loggedIn});
|
||||
userStore.$patch({ loggedIn })
|
||||
if (loggedIn && !userStore.email) {
|
||||
userStore.fetchUser();
|
||||
userStore.fetchUser()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import {createRouter, createWebHistory} from 'vue-router'
|
||||
import CockpitView from '@/views/CockpitView.vue';
|
||||
import LoginView from '@/views/LoginView.vue';
|
||||
import {redirectToLoginIfRequired, updateLoggedIn} from '@/router/guards';
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import CockpitView from '@/views/CockpitView.vue'
|
||||
import LoginView from '@/views/LoginView.vue'
|
||||
import { redirectToLoginIfRequired, updateLoggedIn } from '@/router/guards'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
|
|
@ -11,8 +12,8 @@ const router = createRouter({
|
|||
component: LoginView,
|
||||
meta: {
|
||||
// no login required -> so `public === true`
|
||||
public: true
|
||||
}
|
||||
public: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
|
|
@ -38,28 +39,33 @@ const router = createRouter({
|
|||
{
|
||||
path: '/learningpath/:learningPathSlug',
|
||||
component: () => import('../views/LearningPathView.vue'),
|
||||
props: true
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/circle/:circleSlug',
|
||||
component: () => import('../views/CircleView.vue'),
|
||||
props: true
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/styleguide',
|
||||
component: () => import('../views/StyelGuideView.vue'),
|
||||
meta: {
|
||||
public: true
|
||||
}
|
||||
public: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: () => import('../views/404View.vue'),
|
||||
},
|
||||
]
|
||||
],
|
||||
})
|
||||
|
||||
router.beforeEach(updateLoggedIn)
|
||||
router.beforeEach(redirectToLoginIfRequired)
|
||||
|
||||
router.afterEach((to, from) => {
|
||||
const appStore = useAppStore();
|
||||
appStore.routingFinished = true;
|
||||
});
|
||||
|
||||
export default router
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
import {defineStore} from 'pinia'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export type AppState = {
|
||||
showMainNavigationBar: boolean;
|
||||
userLoaded: boolean
|
||||
routingFinished: boolean
|
||||
showMainNavigationBar: boolean
|
||||
}
|
||||
|
||||
export const useAppStore = defineStore({
|
||||
id: 'app',
|
||||
state: () => ({
|
||||
showMainNavigationBar: true,
|
||||
userLoaded: false,
|
||||
routingFinished: false,
|
||||
} as AppState),
|
||||
getters: {
|
||||
},
|
||||
|
|
|
|||
|
|
@ -54,8 +54,6 @@ export const useLearningPathStore = defineStore({
|
|||
|
||||
})
|
||||
this.learningPath.topics.push(topic);
|
||||
console.log('#######################');
|
||||
console.log(this.learningPath);
|
||||
}
|
||||
return this.learningPath;
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import * as log from 'loglevel';
|
|||
|
||||
import {defineStore} from 'pinia'
|
||||
import {itGet, itPost} from '@/fetchHelpers';
|
||||
import {useAppStore} from '@/stores/app';
|
||||
// typed state https://stackoverflow.com/questions/71012513/when-using-pinia-and-typescript-how-do-you-use-an-action-to-set-the-state
|
||||
|
||||
export type UserState = {
|
||||
|
|
@ -46,11 +47,14 @@ export const useUserStore = defineStore({
|
|||
}
|
||||
},
|
||||
fetchUser() {
|
||||
const appStore = useAppStore();
|
||||
itGet('/api/core/me/').then((data) => {
|
||||
this.$state = data;
|
||||
this.loggedIn = true;
|
||||
appStore.userLoaded = true;
|
||||
}).catch(() => {
|
||||
this.loggedIn = false;
|
||||
appStore.userLoaded = true;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
import * as log from 'loglevel';
|
||||
import LearningSequence from '@/components/circle/LearningSequence.vue';
|
||||
import CircleOverview from '@/components/circle/CircleOverview.vue';
|
||||
import CircleDiagram from '@/components/circle/CircleDiagram.vue';
|
||||
import LearningContent from '@/components/circle/LearningContent.vue';
|
||||
|
||||
import {onMounted} from 'vue'
|
||||
import {useCircleStore} from '@/stores/circle';
|
||||
import SelfEvaluation from '@/components/circle/SelfEvaluation.vue';
|
||||
import CircleDiagram from '@/components/circle/CircleDiagram.vue';
|
||||
|
||||
log.debug('CircleView.vue created');
|
||||
|
||||
|
|
@ -16,16 +16,16 @@ const props = defineProps<{
|
|||
}>()
|
||||
|
||||
const circleStore = useCircleStore();
|
||||
circleStore.loadCircle(props.circleSlug);
|
||||
|
||||
onMounted(async () => {
|
||||
log.info('CircleView.vue mounted');
|
||||
await circleStore.loadCircle(props.circleSlug);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition>
|
||||
<Transition mode="out-in">
|
||||
<div v-if="circleStore.page === 'OVERVIEW'">
|
||||
<CircleOverview :circle="circleStore.circle" @close="circleStore.page = 'INDEX'"/>
|
||||
</div>
|
||||
|
|
@ -111,7 +111,4 @@ onMounted(async () => {
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
.v-enter-active {
|
||||
transition-delay: 0.3s;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue