Refactor to new url structure for learningPath and circle
This commit is contained in:
parent
0acdab60cd
commit
18acf10c9a
|
|
@ -1,27 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import * as log from 'loglevel';
|
||||
import * as log from 'loglevel'
|
||||
|
||||
import {onMounted, reactive} from 'vue';
|
||||
import {useUserStore} from '@/stores/user';
|
||||
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"
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
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');
|
||||
log.debug('MainNavigationBar created')
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const learningPathStore = useLearningPathStore();
|
||||
const state = reactive({showMenu: false});
|
||||
const userStore = useUserStore()
|
||||
const appStore = useAppStore()
|
||||
const learningPathStore = useLearningPathStore()
|
||||
const state = reactive({ showMenu: false })
|
||||
|
||||
function toggleNav() {
|
||||
state.showMenu = !state.showMenu;
|
||||
state.showMenu = !state.showMenu
|
||||
}
|
||||
|
||||
function isInRoutePath(checkPaths: string[]) {
|
||||
|
|
@ -29,28 +29,21 @@ function isInRoutePath(checkPaths: string[]) {
|
|||
}
|
||||
|
||||
function inLearningPath() {
|
||||
return isInRoutePath(['/learningpath/', '/circle/']);
|
||||
return isInRoutePath(['/learn/'])
|
||||
}
|
||||
|
||||
function getLearningPathStringProp (prop: 'title' | 'slug'): string {
|
||||
return inLearningPath() && learningPathStore.learningPath ? learningPathStore.learningPath[prop] : '';
|
||||
function getLearningPathStringProp(prop: 'title' | 'slug'): string {
|
||||
return inLearningPath() && learningPathStore.learningPath ? learningPathStore.learningPath[prop] : ''
|
||||
}
|
||||
|
||||
function learningPathName (): string {
|
||||
function learningPathName(): string {
|
||||
return getLearningPathStringProp('title')
|
||||
}
|
||||
|
||||
function learninPathSlug (): string {
|
||||
function learninPathSlug(): string {
|
||||
return getLearningPathStringProp('slug')
|
||||
}
|
||||
|
||||
function backButtonUrl() {
|
||||
if (route.path.startsWith('/circle/')) {
|
||||
return '/learningpath/versicherungsvermittlerin';
|
||||
}
|
||||
return '/';
|
||||
}
|
||||
|
||||
function handleDropdownSelect(data) {
|
||||
log.debug('Selected action:', data.action)
|
||||
switch (data.action) {
|
||||
|
|
@ -58,42 +51,41 @@ function handleDropdownSelect(data) {
|
|||
router.push('/profile')
|
||||
break
|
||||
case 'logout':
|
||||
userStore.handleLogout();
|
||||
userStore.handleLogout()
|
||||
break
|
||||
default:
|
||||
console.log('no action')
|
||||
}
|
||||
}
|
||||
|
||||
function logout () {
|
||||
userStore.handleLogout();
|
||||
function logout() {
|
||||
userStore.handleLogout()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
log.debug('MainNavigationBar mounted');
|
||||
log.debug('MainNavigationBar mounted')
|
||||
})
|
||||
|
||||
const profileDropdownData = [
|
||||
[
|
||||
{
|
||||
title: 'Kontoeinstellungen',
|
||||
icon: IconSettings,
|
||||
data: {
|
||||
action: 'settings'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
title: 'Abmelden',
|
||||
icon: IconLogout,
|
||||
data: {
|
||||
action: 'logout'
|
||||
}
|
||||
[
|
||||
{
|
||||
title: 'Kontoeinstellungen',
|
||||
icon: IconSettings,
|
||||
data: {
|
||||
action: 'settings',
|
||||
},
|
||||
]
|
||||
]
|
||||
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
title: 'Abmelden',
|
||||
icon: IconLogout,
|
||||
data: {
|
||||
action: 'logout',
|
||||
},
|
||||
},
|
||||
],
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -109,35 +101,14 @@ const profileDropdownData = [
|
|||
</Teleport>
|
||||
<Transition name="nav">
|
||||
<div v-if="appStore.showMainNavigationBar" class="navigation bg-blue-900">
|
||||
<nav
|
||||
class="
|
||||
px-8
|
||||
py-2
|
||||
mx-auto
|
||||
lg:flex lg:justify-start lg:items-center lg:py-4
|
||||
"
|
||||
>
|
||||
<nav class="px-8 py-2 mx-auto lg:flex lg:justify-start lg:items-center lg:py-4">
|
||||
<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 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 to="/" class="flex">
|
||||
<div class="text-white text-2xl pr-10 pl-3 ml-1 border-l border-white">myVBV</div>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
|
|
@ -148,21 +119,15 @@ const profileDropdownData = [
|
|||
class="nav-item flex flex-row items-center"
|
||||
data-cy="messages-link"
|
||||
>
|
||||
<it-icon-message class="w-8 h-8 mr-6"/>
|
||||
<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
|
||||
"
|
||||
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"/>
|
||||
<it-icon-menu class="h-8 w-8" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -172,17 +137,13 @@ const profileDropdownData = [
|
|||
<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
|
||||
"
|
||||
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': inLearningPath()}"
|
||||
:class="{ 'nav-item--active': inLearningPath() }"
|
||||
>
|
||||
Lernpfad
|
||||
</router-link>
|
||||
|
|
@ -191,32 +152,24 @@ const profileDropdownData = [
|
|||
v-if="inLearningPath()"
|
||||
to="/competences/"
|
||||
class="nav-item"
|
||||
:class="{'nav-item--active': isInRoutePath(['/competences/'])}"
|
||||
:class="{ 'nav-item--active': isInRoutePath(['/competences/']) }"
|
||||
>
|
||||
Kompetenzprofil
|
||||
</router-link>
|
||||
|
||||
<div class="hidden lg:block flex-auto"></div>
|
||||
<router-link
|
||||
to="/shop"
|
||||
class="nav-item"
|
||||
:class="{'nav-item--active': isInRoutePath(['/shop'])}"
|
||||
>
|
||||
<router-link to="/shop" class="nav-item" :class="{ 'nav-item--active': isInRoutePath(['/shop']) }">
|
||||
Shop
|
||||
</router-link>
|
||||
<router-link
|
||||
to="/mediacenter"
|
||||
class="nav-item"
|
||||
:class="{'nav-item--active': isInRoutePath(['/mediacenter'])}"
|
||||
:class="{ 'nav-item--active': isInRoutePath(['/mediacenter']) }"
|
||||
>
|
||||
Mediathek
|
||||
</router-link>
|
||||
<router-link
|
||||
to="/messages"
|
||||
class="nav-item flex flex-row items-center"
|
||||
data-cy="messages-link"
|
||||
>
|
||||
<it-icon-message class="w-8 h-8 mr-6"/>
|
||||
<router-link to="/messages" class="nav-item flex flex-row items-center" data-cy="messages-link">
|
||||
<it-icon-message class="w-8 h-8 mr-6" />
|
||||
</router-link>
|
||||
<div class="nav-item flex items-center" v-if="userStore.loggedIn">
|
||||
<ItDropdown
|
||||
|
|
@ -226,9 +179,7 @@ const profileDropdownData = [
|
|||
@select="handleDropdownSelect"
|
||||
>
|
||||
<div v-if="userStore.avatar_url">
|
||||
<img class="inline-block h-8 w-8 rounded-full"
|
||||
:src="userStore.avatar_url"
|
||||
alt=""/>
|
||||
<img class="inline-block h-8 w-8 rounded-full" :src="userStore.avatar_url" alt="" />
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ userStore.getFullName }}
|
||||
|
|
@ -249,7 +200,7 @@ const profileDropdownData = [
|
|||
}
|
||||
|
||||
.nav-item--active {
|
||||
@apply underline underline-offset-[21px] decoration-sky-500 decoration-4
|
||||
@apply underline underline-offset-[21px] decoration-sky-500 decoration-4;
|
||||
}
|
||||
|
||||
.nav-enter-active,
|
||||
|
|
@ -262,5 +213,4 @@ const profileDropdownData = [
|
|||
opacity: 0;
|
||||
transform: translateY(-80px);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import * as d3 from 'd3';
|
||||
import { useLearningPathStore } from '../../stores/learningPath';
|
||||
import colors from '@/colors.json';
|
||||
import * as d3 from 'd3'
|
||||
import { useLearningPathStore } from '../../stores/learningPath'
|
||||
import colors from '@/colors.json'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -15,37 +15,35 @@ export default {
|
|||
},
|
||||
vertical: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
identifier: {
|
||||
required: true,
|
||||
type: String
|
||||
}
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const learningPathStore = useLearningPathStore()
|
||||
return {learningPathStore}
|
||||
return { learningPathStore }
|
||||
},
|
||||
|
||||
|
||||
computed: {
|
||||
viewBox() {
|
||||
return `0 0 ${this.width} ${this.height * 1.5}`
|
||||
},
|
||||
circles() {
|
||||
|
||||
function someFinished(circle, learningSequence) {
|
||||
if (circle) {
|
||||
return circle.someFinishedInLearningSequence(learningSequence.translation_key);
|
||||
return circle.someFinishedInLearningSequence(learningSequence.translation_key)
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
function allFinished(circle, learningSequence) {
|
||||
if (circle) {
|
||||
return circle.allFinishedInLearningSequence(learningSequence.translation_key);
|
||||
return circle.allFinishedInLearningSequence(learningSequence.translation_key)
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
if (this.learningPathStore.learningPath) {
|
||||
|
|
@ -58,29 +56,28 @@ export default {
|
|||
const thisLearningSequence = circle.learningSequences[parseInt(pie.index)]
|
||||
pie.startAngle = pie.startAngle + Math.PI
|
||||
pie.endAngle = pie.endAngle + Math.PI
|
||||
pie.done = circle.someFinishedInLearningSequence(thisLearningSequence.translation_key);
|
||||
pie.done = circle.someFinishedInLearningSequence(thisLearningSequence.translation_key)
|
||||
pie.someFinished = someFinished(circle, thisLearningSequence)
|
||||
pie.allFinished = allFinished(circle, thisLearningSequence)
|
||||
});
|
||||
})
|
||||
const newCircle = {}
|
||||
newCircle.pieData = pieData.reverse()
|
||||
newCircle.title = circle.title
|
||||
newCircle.slug = circle.slug
|
||||
newCircle.id = circle.id
|
||||
internalCircles.push(newCircle)
|
||||
});
|
||||
})
|
||||
return internalCircles
|
||||
}
|
||||
return [];
|
||||
return []
|
||||
},
|
||||
svg() {
|
||||
return d3.select("#" + this.identifier)
|
||||
return d3.select('#' + this.identifier)
|
||||
},
|
||||
|
||||
learningPath() {
|
||||
return Object.assign({}, this.learningPathStore.learningPath)
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
|
@ -111,7 +108,6 @@ export default {
|
|||
|
||||
const vueRouter = this.$router
|
||||
|
||||
|
||||
// Create append pie charts to the main svg
|
||||
const circle_groups = this.svg
|
||||
.selectAll('.circle')
|
||||
|
|
@ -121,7 +117,7 @@ export default {
|
|||
.attr('class', 'circle')
|
||||
.attr('data-cy', (d) => {
|
||||
if (this.vertical) {
|
||||
return `circle-${d.slug}-vertical`;
|
||||
return `circle-${d.slug}-vertical`
|
||||
} else {
|
||||
return `circle-${d.slug}`
|
||||
}
|
||||
|
|
@ -144,8 +140,8 @@ export default {
|
|||
return getColor(d)
|
||||
})
|
||||
})
|
||||
.on('click', function (d, i) {
|
||||
vueRouter.push('/circle/' + i.slug)
|
||||
.on('click', (d, i) => {
|
||||
vueRouter.push(`/learn/${this.learningPathStore.learningPath.slug}/${i.slug}`)
|
||||
})
|
||||
|
||||
.attr('role', 'button')
|
||||
|
|
@ -178,7 +174,6 @@ export default {
|
|||
//Draw arc paths
|
||||
arcs.append('path').attr('d', arcGenerator)
|
||||
|
||||
|
||||
const circlesText = circle_groups
|
||||
.append('text')
|
||||
.attr('fill', colors.blue[900])
|
||||
|
|
@ -223,21 +218,13 @@ export default {
|
|||
return y
|
||||
}
|
||||
y += circleHeigth
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const topicGroups = this.svg
|
||||
.selectAll('.topic')
|
||||
.data(this.learningPath.topics)
|
||||
.enter()
|
||||
.append('g')
|
||||
const topicGroups = this.svg.selectAll('.topic').data(this.learningPath.topics).enter().append('g')
|
||||
|
||||
const topicLines = topicGroups
|
||||
.append('line')
|
||||
.attr('class', 'stroke-gray-500')
|
||||
.attr('stroke-width', 1)
|
||||
const topicLines = topicGroups.append('line').attr('class', 'stroke-gray-500').attr('stroke-width', 1)
|
||||
|
||||
const topicTitles = topicGroups
|
||||
.append('text')
|
||||
|
|
@ -245,47 +232,40 @@ export default {
|
|||
.style('font-size', 16)
|
||||
.text((d) => d.title)
|
||||
|
||||
|
||||
// Calculate positions of objects
|
||||
|
||||
if (this.vertical) {
|
||||
const Circles_X = 60
|
||||
const Topics_X = Circles_X - radius
|
||||
|
||||
|
||||
circle_groups
|
||||
.attr('transform', (d, i) => {
|
||||
return 'translate(' + Circles_X + ',' + getCircleVerticalPostion(i, d, this.learningPath.topics) + ')'
|
||||
})
|
||||
circle_groups.attr('transform', (d, i) => {
|
||||
return 'translate(' + Circles_X + ',' + getCircleVerticalPostion(i, d, this.learningPath.topics) + ')'
|
||||
})
|
||||
|
||||
circlesText
|
||||
.attr('y', 7)
|
||||
.attr('x', radius + 40)
|
||||
.attr('class', 'circlesText text-xl font-bold block')
|
||||
|
||||
|
||||
topicGroups
|
||||
.attr('transform', (d, i) => {
|
||||
return "translate(" + Topics_X + ", " + getTopicVerticalPosition(i, d, this.learningPath.topics) + ")"
|
||||
return 'translate(' + Topics_X + ', ' + getTopicVerticalPosition(i, d, this.learningPath.topics) + ')'
|
||||
})
|
||||
.attr('class', (d) => {
|
||||
return 'topic '.concat(d.is_visible ? "block" : "hidden")
|
||||
return 'topic '.concat(d.is_visible ? 'block' : 'hidden')
|
||||
})
|
||||
|
||||
|
||||
topicLines
|
||||
.transition().duration('1000').attr('x2', this.width * 0.8)
|
||||
|
||||
topicTitles
|
||||
.attr('y', 30)
|
||||
|
||||
.transition()
|
||||
.duration('1000')
|
||||
.attr('x2', this.width * 0.8)
|
||||
|
||||
topicTitles.attr('y', 30)
|
||||
} else {
|
||||
circle_groups
|
||||
.attr('transform', (d, i) => {
|
||||
const x_coord = (i + 1) * circleWidth - radius
|
||||
return 'translate(' + x_coord + ', 200)'
|
||||
})
|
||||
circle_groups.attr('transform', (d, i) => {
|
||||
const x_coord = (i + 1) * circleWidth - radius
|
||||
return 'translate(' + x_coord + ', 200)'
|
||||
})
|
||||
|
||||
circlesText
|
||||
.attr('y', radius + 30)
|
||||
|
|
@ -293,30 +273,28 @@ export default {
|
|||
.call(wrap, circleWidth - 20)
|
||||
.attr('class', 'circlesText text-xl font-bold hidden lg:block')
|
||||
|
||||
|
||||
topicGroups
|
||||
.attr('transform', (d, i) => {
|
||||
return "translate(" + getTopicHorizontalPosition(i, d, this.learningPathStore.learningPath.topics) + ",0)"
|
||||
return 'translate(' + getTopicHorizontalPosition(i, d, this.learningPathStore.learningPath.topics) + ',0)'
|
||||
})
|
||||
.attr('class', (d) => {
|
||||
return 'topic '.concat(d.is_visible ? "hidden lg:block" : "hidden")
|
||||
return 'topic '.concat(d.is_visible ? 'hidden lg:block' : 'hidden')
|
||||
})
|
||||
|
||||
|
||||
topicLines
|
||||
.attr('x1', -10)
|
||||
.attr('y1', 0)
|
||||
.attr('x2', -10)
|
||||
.attr('y2', 0)
|
||||
.transition().duration('1000').attr('y2', 350)
|
||||
.transition()
|
||||
.duration('1000')
|
||||
.attr('y2', 350)
|
||||
|
||||
topicTitles
|
||||
.attr('y', 20)
|
||||
.style('font-size', 19)
|
||||
.call(wrap, circleWidth * 0.8)
|
||||
.attr('class', 'topicTitles font-bold')
|
||||
|
||||
|
||||
}
|
||||
|
||||
function wrap(text, width) {
|
||||
|
|
@ -357,12 +335,8 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div class="svg-container h-full content-start">
|
||||
<svg class="learning-path-visualization h-full" :viewBox="viewBox" :id=identifier>
|
||||
</svg>
|
||||
<svg class="learning-path-visualization h-full" :viewBox="viewBox" :id="identifier"></svg>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ const router = createRouter({
|
|||
component: () => import('@/views/ProfileView.vue'),
|
||||
},
|
||||
{
|
||||
path: '/learningpath/:learningPathSlug',
|
||||
path: '/learn/:learningPathSlug',
|
||||
component: () => import('../views/LearningPathView.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/circle/:circleSlug',
|
||||
path: '/learn/:learningPathSlug/:circleSlug',
|
||||
component: () => import('../views/CircleView.vue'),
|
||||
props: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { defineStore } from 'pinia'
|
|||
|
||||
import type { LearningContent, LearningUnit, LearningUnitQuestion } from '@/types'
|
||||
import type { Circle } from '@/services/circle'
|
||||
import { itGet, itPost } from '@/fetchHelpers'
|
||||
import { itPost } from '@/fetchHelpers'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useLearningPathStore } from '@/stores/learningPath'
|
||||
|
||||
|
|
@ -28,26 +28,19 @@ export const useCircleStore = defineStore({
|
|||
getters: {
|
||||
},
|
||||
actions: {
|
||||
async loadCircle(slug: string) {
|
||||
async loadCircle(learningPathSlug: string, circleSlug: string) {
|
||||
this.circle = undefined;
|
||||
try {
|
||||
// const circleData = await itGet(`/learnpath/api/circle/${slug}/`);
|
||||
// this.circle = Circle.fromJson(circleData);
|
||||
// this.circle.parseCompletionData(completionData);
|
||||
const learningPathStore = useLearningPathStore();
|
||||
await learningPathStore.loadLearningPath('versicherungsvermittlerin');
|
||||
if (learningPathStore.learningPath) {
|
||||
this.circle = learningPathStore.learningPath.circles.find(circle => circle.slug === slug);
|
||||
if (this.circle) {
|
||||
const completionData = await itGet(`/api/completion/circle/${this.circle.translation_key}/`);
|
||||
this.circle.parseCompletionData(completionData);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(this.circle)
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
return error
|
||||
const learningPathStore = useLearningPathStore();
|
||||
await learningPathStore.loadLearningPath(learningPathSlug);
|
||||
if (learningPathStore.learningPath) {
|
||||
this.circle = learningPathStore.learningPath.circles.find(circle => circle.slug === circleSlug);
|
||||
}
|
||||
|
||||
if (!this.circle) {
|
||||
throw `No circle found with slug: ${circleSlug}`;
|
||||
}
|
||||
|
||||
return this.circle
|
||||
},
|
||||
async markCompletion(page: LearningContent | LearningUnitQuestion, flag = true) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import * as log from 'loglevel'
|
||||
|
||||
import { defineStore } from 'pinia'
|
||||
import { itGet } from '@/fetchHelpers'
|
||||
import { LearningPath } from '@/services/learningPath'
|
||||
|
|
@ -21,18 +19,15 @@ export const useLearningPathStore = defineStore({
|
|||
if (this.learningPath && !reload) {
|
||||
return this.learningPath;
|
||||
}
|
||||
try {
|
||||
const learningPathData = await itGet(`/learnpath/api/page/${slug}/`);
|
||||
const completionData = await itGet(`/api/completion/learning_path/${learningPathData.translation_key}/`);
|
||||
const learningPathData = await itGet(`/learnpath/api/page/${slug}/`);
|
||||
const completionData = await itGet(`/api/completion/learning_path/${learningPathData.translation_key}/`);
|
||||
|
||||
if (learningPathData) {
|
||||
this.learningPath = LearningPath.fromJson(learningPathData, completionData);
|
||||
}
|
||||
return this.learningPath;
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
return error
|
||||
if (!learningPathData) {
|
||||
throw `No learning path found with: ${slug}`;
|
||||
}
|
||||
|
||||
this.learningPath = LearningPath.fromJson(learningPathData, completionData);
|
||||
return this.learningPath;
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,27 +1,32 @@
|
|||
<script setup lang="ts">
|
||||
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 * 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 { onMounted } from 'vue'
|
||||
import { useCircleStore } from '@/stores/circle'
|
||||
import SelfEvaluation from '@/components/circle/SelfEvaluation.vue'
|
||||
|
||||
log.debug('CircleView.vue created');
|
||||
log.debug('CircleView.vue created')
|
||||
|
||||
const props = defineProps<{
|
||||
learningPathSlug: string
|
||||
circleSlug: string
|
||||
}>()
|
||||
|
||||
const circleStore = useCircleStore();
|
||||
circleStore.loadCircle(props.circleSlug);
|
||||
const circleStore = useCircleStore()
|
||||
|
||||
onMounted(async () => {
|
||||
log.info('CircleView.vue mounted');
|
||||
});
|
||||
log.debug('CircleView.vue mounted', props.learningPathSlug, props.circleSlug)
|
||||
|
||||
try {
|
||||
await circleStore.loadCircle(props.learningPathSlug, props.circleSlug)
|
||||
} catch (error) {
|
||||
log.error(error)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -35,10 +40,10 @@ onMounted(async () => {
|
|||
</Teleport>
|
||||
<Transition mode="out-in">
|
||||
<div v-if="circleStore.page === 'LEARNING_CONTENT'">
|
||||
<LearningContent :key="circleStore.currentLearningContent.translation_key"/>
|
||||
<LearningContent :key="circleStore.currentLearningContent.translation_key" />
|
||||
</div>
|
||||
<div v-else-if="circleStore.page === 'SELF_EVALUATION'">
|
||||
<SelfEvaluation :key="circleStore.currentSelfEvaluation.translation_key"/>
|
||||
<SelfEvaluation :key="circleStore.currentSelfEvaluation.translation_key" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="circle-container">
|
||||
|
|
@ -46,7 +51,7 @@ onMounted(async () => {
|
|||
<div class="flex flex-col lg:flex-row">
|
||||
<div class="flex-initial lg:w-128 px-4 py-4 lg:px-8 lg:pt-4 bg-white">
|
||||
<router-link
|
||||
to="/learningpath/versicherungsvermittlerin"
|
||||
:to="`/learn/${props.learningPathSlug}`"
|
||||
class="btn-text inline-flex items-center px-3 py-4 font-normal"
|
||||
>
|
||||
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
||||
|
|
@ -62,15 +67,12 @@ onMounted(async () => {
|
|||
</div>
|
||||
|
||||
<div class="border-t-2 border-gray-500 mt-4 lg:hidden">
|
||||
<div
|
||||
class="mt-4 inline-flex items-center"
|
||||
@click="circleStore.page = 'OVERVIEW'"
|
||||
>
|
||||
<it-icon-info class="mr-2"/>
|
||||
<div class="mt-4 inline-flex items-center" @click="circleStore.page = 'OVERVIEW'">
|
||||
<it-icon-info class="mr-2" />
|
||||
Das lernst du in diesem Circle
|
||||
</div>
|
||||
<div class="inline-flex items-center">
|
||||
<it-icon-message class="mr-2"/>
|
||||
<it-icon-message class="mr-2" />
|
||||
Fachexpertin kontaktieren
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -82,16 +84,15 @@ onMounted(async () => {
|
|||
{{ circleStore.circle?.description }}
|
||||
</div>
|
||||
|
||||
<button class="btn-primary mt-4 text-xl" @click="circleStore.page = 'OVERVIEW'">Erfahre mehr dazu
|
||||
<button class="btn-primary mt-4 text-xl" @click="circleStore.page = 'OVERVIEW'">
|
||||
Erfahre mehr dazu
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="expert border border-gray-500 mt-8 p-6">
|
||||
<h3 class="text-blue-dark">Hast du Fragen?</h3>
|
||||
<div class="prose mt-4">Tausche dich mit der Fachexpertin aus für den Circle Analyse aus.</div>
|
||||
<button class="btn-secondary mt-4 text-xl">
|
||||
Fachexpertin kontaktieren
|
||||
</button>
|
||||
<button class="btn-secondary mt-4 text-xl">Fachexpertin kontaktieren</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -101,14 +102,10 @@ onMounted(async () => {
|
|||
v-for="learningSequence in circleStore.circle?.learningSequences || []"
|
||||
:key="learningSequence.translation_key"
|
||||
>
|
||||
<LearningSequence
|
||||
:learning-sequence="learningSequence"
|
||||
></LearningSequence>
|
||||
<LearningSequence :learning-sequence="learningSequence"></LearningSequence>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -117,15 +114,8 @@ onMounted(async () => {
|
|||
</template>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
|
||||
.circle-container {
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
white 0%,
|
||||
white 50%,
|
||||
theme(colors.gray.200) 50%,
|
||||
theme(colors.gray.200) 100%
|
||||
);
|
||||
background: linear-gradient(to right, white 0%, white 50%, theme(colors.gray.200) 50%, theme(colors.gray.200) 100%);
|
||||
}
|
||||
|
||||
.circle {
|
||||
|
|
@ -142,5 +132,4 @@ onMounted(async () => {
|
|||
.v-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,31 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import * as log from 'loglevel';
|
||||
import {useUserStore} from '@/stores/user';
|
||||
import * as log from 'loglevel'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
log.debug('CockpitView created');
|
||||
|
||||
const userStore = useUserStore();
|
||||
log.debug('CockpitView created')
|
||||
|
||||
const userStore = useUserStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="px-8 py-8 lg:px-12 lg:py-12 bg-gray-200">
|
||||
<h1 data-cy="welcome-message">Willkommen, {{userStore.first_name}}</h1>
|
||||
<h1 data-cy="welcome-message">Willkommen, {{ userStore.first_name }}</h1>
|
||||
|
||||
<h2 class="mt-12">Deine Kurse</h2>
|
||||
|
||||
<div class="mt-8 p-8 break-words bg-white max-w-xl">
|
||||
<h3>Versicherungsvermittler/in</h3>
|
||||
<div class="mt-4">
|
||||
<router-link class="btn-blue" to="/learningpath/versicherungsvermittlerin">
|
||||
Weiter gehts
|
||||
</router-link>
|
||||
<router-link class="btn-blue" to="/learn/versicherungsvermittlerin"> Weiter gehts </router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<main class="px-8 py-8">
|
||||
<h1>myVBV Start Page</h1>
|
||||
|
||||
<div class="mt-8 flex flex-col lg:flex-row justify-start gap-4">
|
||||
<router-link class="link text-xl" to="/styleguide">Styelguide</router-link>
|
||||
<a class="link text-xl" href="/login">Login</a>
|
||||
|
||||
<router-link class="link text-xl" to="/learningpath/versicherungsvermittlerin">Lernpfad "Versicherungsvermittlerin"</router-link>
|
||||
<router-link class="link text-xl" to="/circle/analyse">Circle "Analyse"</router-link>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
|
@ -1,31 +1,31 @@
|
|||
<script setup lang="ts">
|
||||
import * as log from 'loglevel'
|
||||
|
||||
import * as log from 'loglevel';
|
||||
import { onMounted } from 'vue'
|
||||
import { useLearningPathStore } from '@/stores/learningPath'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
import {onMounted} from 'vue'
|
||||
import {useLearningPathStore} from '@/stores/learningPath';
|
||||
import {useUserStore} from '@/stores/user';
|
||||
import LearningPathDiagram from '@/components/circle/LearningPathDiagram.vue'
|
||||
import LearningPathViewVertical from '@/views/LearningPathViewVertical.vue'
|
||||
|
||||
import LearningPathDiagram from '@/components/circle/LearningPathDiagram.vue';
|
||||
import LearningPathViewVertical from "@/views/LearningPathViewVertical.vue";
|
||||
|
||||
|
||||
log.debug('LearningPathView created');
|
||||
log.debug('LearningPathView created')
|
||||
|
||||
const props = defineProps<{
|
||||
learningPathSlug: string
|
||||
}>()
|
||||
|
||||
const learningPathStore = useLearningPathStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const learningPathStore = useLearningPathStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
onMounted(async () => {
|
||||
log.info('LearningPathView mounted');
|
||||
await learningPathStore.loadLearningPath(props.learningPathSlug);
|
||||
console.log(learningPathStore)
|
||||
});
|
||||
log.debug('LearningPathView mounted')
|
||||
|
||||
try {
|
||||
await learningPathStore.loadLearningPath(props.learningPathSlug)
|
||||
} catch (error) {
|
||||
log.error(error)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -42,12 +42,8 @@ onMounted(async () => {
|
|||
<div class="flex flex-col h-max">
|
||||
<div class="bg-white py-8 flex flex-col">
|
||||
<div class="flex justify-end p-3">
|
||||
<button
|
||||
class="flex items-center"
|
||||
@click="learningPathStore.page = 'OVERVIEW'"
|
||||
data-cy="show-list-view"
|
||||
>
|
||||
<it-icon-list/>
|
||||
<button class="flex items-center" @click="learningPathStore.page = 'OVERVIEW'" data-cy="show-list-view">
|
||||
<it-icon-list />
|
||||
Listen Ansicht anzeigen
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -61,19 +57,21 @@ onMounted(async () => {
|
|||
<h1 data-cy="learning-path-title" class="m-12">{{ learningPathStore.learningPath.title }}</h1>
|
||||
|
||||
<div
|
||||
class="bg-white m-12 p-8 flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-gray-500 justify-start">
|
||||
class="bg-white m-12 p-8 flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-gray-500 justify-start"
|
||||
>
|
||||
<div class="p-8 flex-auto">
|
||||
<h2 translate>Willkommmen zurück, {{ userStore.first_name }}</h2>
|
||||
<p class="mt-4 text-xl">
|
||||
|
||||
</p>
|
||||
<p class="mt-4 text-xl"></p>
|
||||
</div>
|
||||
<div class="p-8 flex-2" v-if="learningPathStore.learningPath.nextLearningContent" translate>
|
||||
Nächster Schirtt
|
||||
<h3>{{ learningPathStore.learningPath.nextLearningContent.parentCircle.title }}: {{ learningPathStore.learningPath.nextLearningContent.parentLearningSequence.title }}</h3>
|
||||
<h3>
|
||||
{{ learningPathStore.learningPath.nextLearningContent.parentCircle.title }}:
|
||||
{{ learningPathStore.learningPath.nextLearningContent.parentLearningSequence.title }}
|
||||
</h3>
|
||||
<router-link
|
||||
class="mt-4 btn-blue"
|
||||
:to="`/circle/${learningPathStore.learningPath.nextLearningContent.parentCircle.slug}/`"
|
||||
:to="`/learn/${learningPathStore.learningPath.slug}/${learningPathStore.learningPath.nextLearningContent.parentCircle.slug}`"
|
||||
translate
|
||||
>
|
||||
Los geht's
|
||||
|
|
@ -86,5 +84,4 @@ onMounted(async () => {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class LearningPath(Page):
|
|||
verbose_name = "Learning Path"
|
||||
|
||||
def full_clean(self, *args, **kwargs):
|
||||
self.slug = find_available_slug(Page, slugify(self.title, allow_unicode=True))
|
||||
self.slug = find_available_slug(slugify(self.title, allow_unicode=True))
|
||||
super(LearningPath, self).full_clean(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
|
|
@ -54,8 +54,7 @@ class Topic(Page):
|
|||
# subpage_types = ['learnpath.Circle']
|
||||
|
||||
def full_clean(self, *args, **kwargs):
|
||||
self.slug = find_available_slug(Topic, slugify(self.title, allow_unicode=True))
|
||||
print(self.slug)
|
||||
self.slug = find_available_slug(slugify(f'topic-{self.title}', allow_unicode=True))
|
||||
super(Topic, self).full_clean(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -115,8 +114,7 @@ class Circle(Page):
|
|||
)
|
||||
|
||||
def full_clean(self, *args, **kwargs):
|
||||
# TODO: why own slug function?
|
||||
self.slug = find_available_slug(Page, slugify(self.title, allow_unicode=True))
|
||||
self.slug = find_available_slug(slugify(self.title, allow_unicode=True))
|
||||
super(Circle, self).full_clean(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
|
|
@ -157,6 +155,7 @@ class LearningSequence(Page):
|
|||
</span>'''
|
||||
|
||||
def full_clean(self, *args, **kwargs):
|
||||
self.slug = find_available_slug(slugify(f'ls-{self.title}', allow_unicode=True))
|
||||
super(LearningSequence, self).full_clean(*args, **kwargs)
|
||||
|
||||
|
||||
|
|
@ -170,6 +169,10 @@ class LearningUnit(Page):
|
|||
def __str__(self):
|
||||
return f"{self.title}"
|
||||
|
||||
def full_clean(self, *args, **kwargs):
|
||||
self.slug = find_available_slug(slugify(f'lu-{self.title}', allow_unicode=True))
|
||||
super(LearningUnit, self).full_clean(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_serializer_class(cls):
|
||||
return get_it_serializer_class(cls, field_names=['id', 'title', 'slug', 'type', 'translation_key', 'children'])
|
||||
|
|
@ -188,6 +191,10 @@ class LearningUnitQuestion(Page):
|
|||
def __str__(self):
|
||||
return f"{self.title}"
|
||||
|
||||
def full_clean(self, *args, **kwargs):
|
||||
self.slug = find_available_slug(slugify(f'luq-{self.title}', allow_unicode=True))
|
||||
super(LearningUnitQuestion, self).full_clean(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_serializer_class(cls):
|
||||
return get_it_serializer_class(cls, field_names=['id', 'title', 'slug', 'type', 'translation_key', ])
|
||||
|
|
@ -240,7 +247,8 @@ class LearningContent(Page):
|
|||
verbose_name = "Learning Content"
|
||||
|
||||
def full_clean(self, *args, **kwargs):
|
||||
self.slug = find_available_slug(LearningContent, slugify(self.title, allow_unicode=True))
|
||||
self.slug = find_available_slug(slugify(f'lc-{self.title}', allow_unicode=True))
|
||||
print(self.slug)
|
||||
super(LearningContent, self).full_clean(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -253,7 +261,7 @@ class LearningContent(Page):
|
|||
return f"{self.title}"
|
||||
|
||||
|
||||
def find_available_slug(model, requested_slug, ignore_page_id=None):
|
||||
def find_available_slug(requested_slug, ignore_page_id=None):
|
||||
"""
|
||||
Finds an available slug within the specified parent.
|
||||
|
||||
|
|
@ -270,8 +278,7 @@ def find_available_slug(model, requested_slug, ignore_page_id=None):
|
|||
treated as in use by another page.
|
||||
"""
|
||||
|
||||
# TODO: In comparison ot wagtails own function, I look for the same model instead of the parent
|
||||
pages = model.objects.filter(slug__startswith=requested_slug)
|
||||
pages = Page.objects.filter(slug__startswith=requested_slug)
|
||||
|
||||
if ignore_page_id:
|
||||
pages = pages.exclude(id=ignore_page_id)
|
||||
|
|
|
|||
Loading…
Reference in New Issue