Add convenience features

This commit is contained in:
Daniel Egger 2022-07-06 09:19:34 +02:00
parent 8edea0b92f
commit 724b8a8cb1
9 changed files with 177 additions and 158 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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()
}
}

View File

@ -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

View File

@ -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: {
},

View File

@ -54,8 +54,6 @@ export const useLearningPathStore = defineStore({
})
this.learningPath.topics.push(topic);
console.log('#######################');
console.log(this.learningPath);
}
return this.learningPath;
} catch (error) {

View File

@ -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;
})
}
}

View File

@ -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>