Merge branch 'feature/learning-content-urls' into develop

This commit is contained in:
Daniel Egger 2022-09-09 18:08:22 +02:00
commit 384999619c
13 changed files with 319 additions and 143 deletions

View File

@ -1,32 +1,29 @@
<script setup lang="ts"> <script setup lang="ts">
import * as log from 'loglevel'; import * as log from 'loglevel'
import {computed} from 'vue'; import { computed } from 'vue'
import {useCircleStore} from '@/stores/circle'; import type { LearningContent } from '@/types'
import { useCircleStore } from '@/stores/circle'
log.debug('LearningContent.vue setup'); log.debug('LearningContent.vue setup')
const circleStore = useCircleStore(); const circleStore = useCircleStore()
const learningContent = computed(() => circleStore.currentLearningContent); const props = defineProps<{
learningContent: LearningContent
}>()
const block = computed(() => { const block = computed(() => {
if (learningContent.value) { if (props.learningContent?.contents?.length) {
return learningContent.value.contents[0]; return props.learningContent.contents[0]
} }
})
return undefined
})
</script> </script>
<template> <template>
<div> <div v-if="block">
<nav <nav class="px-4 lg:px-8 py-4 flex justify-between items-center border-b border-gray-500">
class="
px-4 lg:px-8
py-4
flex justify-between items-center
border-b border-gray-500
"
>
<button <button
type="button" type="button"
class="btn-text inline-flex items-center px-3 py-2 font-normal" class="btn-text inline-flex items-center px-3 py-2 font-normal"
@ -43,22 +40,16 @@ const block = computed(() => {
type="button" type="button"
class="btn-blue" class="btn-blue"
data-cy="complete-and-continue" data-cy="complete-and-continue"
@click="circleStore.continueFromLearningContent()" @click="circleStore.continueFromLearningContent(this.learningContent)"
> >
Abschliessen und weiter Abschliessen und weiter
</button> </button>
</nav> </nav>
<div v-if="block.type === 'exercise'" class="h-screen"> <div v-if="block.type === 'exercise'" class="h-screen">
<iframe <iframe width="100%" height="100%" scrolling="no" :src="block.value.url" />
width="100%"
height="100%"
scrolling="no"
:src="block.value.url"
/>
</div> </div>
<div v-else class="mx-auto max-w-5xl px-4 lg:px-8 py-4"> <div v-else class="mx-auto max-w-5xl px-4 lg:px-8 py-4">
<p>{{ block.value.description }}</p> <p>{{ block.value.description }}</p>
@ -69,24 +60,19 @@ const block = computed(() => {
:title="learningContent.title" :title="learningContent.title"
frameborder="0" frameborder="0"
allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen> allowfullscreen
>
</iframe> </iframe>
</div> </div>
<div <div v-if="block.type === 'podcast'">
v-if="block.type === 'podcast'"
>
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="" :src="block.value.url"></iframe> <iframe width="100%" height="300" scrolling="no" frameborder="no" allow="" :src="block.value.url"></iframe>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
$header-height: 77px; $header-height: 77px;
$footer-height: 57px; $footer-height: 57px;

View File

@ -30,6 +30,26 @@ const allFinished = computed(() => {
return false return false
}) })
const continueTranslationKey = computed(() => {
if (props.learningSequence && circleStore.circle) {
const childrenReversed = [...circleStore.circle.flatLearningContents].reverse()
const lastFinished = childrenReversed.find((learningContent) => {
return learningContent.completed
})
if (!lastFinished) {
// must be the first
return circleStore.circle.flatLearningContents[0].translation_key
}
if (lastFinished && lastFinished.nextLearningContent) {
return lastFinished.nextLearningContent.translation_key
}
}
return ''
})
const learningSequenceBorderClass = computed(() => { const learningSequenceBorderClass = computed(() => {
let result = [] let result = []
if (props.learningSequence && circleStore.circle) { if (props.learningSequence && circleStore.circle) {
@ -77,6 +97,14 @@ const learningSequenceBorderClass = computed(() => {
>{{ learningContent.contents[0].type }}: {{ learningContent.title }}</span >{{ learningContent.contents[0].type }}: {{ learningContent.title }}</span
> >
</ItCheckbox> </ItCheckbox>
<button
v-if="learningContent.translation_key === continueTranslationKey"
class="btn-blue -my-4"
@click.stop="circleStore.openLearningContent(learningContent)"
>
Weiter gehts
</button>
</div> </div>
<div v-if="learningUnit.id" class="hover:cursor-pointer" @click="circleStore.openSelfEvaluation(learningUnit)"> <div v-if="learningUnit.id" class="hover:cursor-pointer" @click="circleStore.openSelfEvaluation(learningUnit)">

View File

@ -1,42 +1,39 @@
<script setup lang="ts"> <script setup lang="ts">
import * as log from 'loglevel'; import * as log from 'loglevel'
import {computed, reactive} from 'vue'; import { computed, reactive } from 'vue'
import {useCircleStore} from '@/stores/circle'; import { useCircleStore } from '@/stores/circle'
import { LearningUnit } from '@/types'
log.debug('LearningContent.vue setup'); log.debug('LearningContent.vue setup')
const circleStore = useCircleStore(); const circleStore = useCircleStore()
const state = reactive({ const state = reactive({
questionIndex: 0, questionIndex: 0,
}); })
const questions = computed(() => circleStore.currentSelfEvaluation!.children); const props = defineProps<{
const currentQuestion = computed(() => questions.value[state.questionIndex]); learningUnit: LearningUnit
}>()
const questions = computed(() => props.learningUnit?.children)
const currentQuestion = computed(() => questions.value[state.questionIndex])
function handleContinue() { function handleContinue() {
log.debug('handleContinue'); log.debug('handleContinue')
if (state.questionIndex + 1 < questions.value.length) { if (state.questionIndex + 1 < questions.value.length) {
log.debug('increment questionIndex', state.questionIndex); log.debug('increment questionIndex', state.questionIndex)
state.questionIndex += 1; state.questionIndex += 1
} else { } else {
log.debug('continue to next learning content'); log.debug('continue to next learning content')
circleStore.continueFromSelfEvaluation(); circleStore.continueFromSelfEvaluation()
} }
} }
</script> </script>
<template> <template>
<div> <div v-if="learningUnit">
<nav <nav class="px-4 lg:px-8 py-4 flex justify-between items-center border-b border-gray-500">
class="
px-4 lg:px-8
py-4
flex justify-between items-center
border-b border-gray-500
"
>
<button <button
type="button" type="button"
class="btn-text inline-flex items-center px-3 py-2 font-normal" class="btn-text inline-flex items-center px-3 py-2 font-normal"
@ -46,24 +43,17 @@ function handleContinue() {
<span class="hidden lg:inline">zurück zum Circle</span> <span class="hidden lg:inline">zurück zum Circle</span>
</button> </button>
<h1 class="text-xl hidden lg:block">{{ circleStore.currentSelfEvaluation.title }}</h1> <h1 class="text-xl hidden lg:block">{{ learningUnit.title }}</h1>
<button <button type="button" class="btn-blue" @click="handleContinue()">Weiter</button>
type="button"
class="btn-blue"
@click="handleContinue()"
>
Weiter
</button>
</nav> </nav>
<div class="mx-auto max-w-6xl px-4 lg:px-8 py-4"> <div class="mx-auto max-w-6xl px-4 lg:px-8 py-4">
<div class="mt-2 lg:mt-8 text-gray-700">Schritt {{ state.questionIndex + 1 }} von {{ questions.length }}</div> <div class="mt-2 lg:mt-8 text-gray-700">Schritt {{ state.questionIndex + 1 }} von {{ questions.length }}</div>
<p class="text-xl mt-4"> <p class="text-xl mt-4">
Überprüfe, ob du in der Lernheinheit <span class="font-bold">"{{ circleStore.currentSelfEvaluation.title }}"</span> alles verstanden hast.<br> Überprüfe, ob du in der Lernheinheit
<span class="font-bold">"{{ learningUnit.title }}"</span> alles verstanden hast.<br />
Lies die folgende Aussage und bewerte sie: Lies die folgende Aussage und bewerte sie:
</p> </p>
@ -81,9 +71,7 @@ function handleContinue() {
}" }"
> >
<it-icon-smiley-happy class="w-16 h-16 mr-4"></it-icon-smiley-happy> <it-icon-smiley-happy class="w-16 h-16 mr-4"></it-icon-smiley-happy>
<span class="font-bold text-xl"> <span class="font-bold text-xl"> Ja, ich kann das. </span>
Ja, ich kann das.
</span>
</button> </button>
<button <button
@click="circleStore.markCompletion(currentQuestion, false)" @click="circleStore.markCompletion(currentQuestion, false)"
@ -95,20 +83,14 @@ function handleContinue() {
}" }"
> >
<it-icon-smiley-thinking class="w-16 h-16 mr-4"></it-icon-smiley-thinking> <it-icon-smiley-thinking class="w-16 h-16 mr-4"></it-icon-smiley-thinking>
<span class="font-bold text-xl"> <span class="font-bold text-xl"> Das muss ich nochmals anschauen. </span>
Das muss ich nochmals anschauen.
</span>
</button> </button>
</div> </div>
<div class="mt-6 lg:mt-12">Schau dein Fortschritt in deinem Kompetenzprofil: Kompetenzprofil öffnen</div> <div class="mt-6 lg:mt-12">Schau dein Fortschritt in deinem Kompetenzprofil: Kompetenzprofil öffnen</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped></style>
</style>

View File

@ -1,4 +1,4 @@
import { createApp } from 'vue' import { createApp, markRaw } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import * as log from 'loglevel' import * as log from 'loglevel'
@ -7,6 +7,7 @@ import App from './App.vue'
import router from './router' import router from './router'
import '../tailwind.css' import '../tailwind.css'
import type { Router } from 'vue-router'
if (window.location.href.indexOf('localhost') >= 0) { if (window.location.href.indexOf('localhost') >= 0) {
log.setLevel('trace') log.setLevel('trace')
@ -21,8 +22,19 @@ const app = createApp(App)
// todo: define lang setup // todo: define lang setup
// await loadLocaleMessages(i18n, 'de') // await loadLocaleMessages(i18n, 'de')
app.use(createPinia())
app.use(router) app.use(router)
declare module 'pinia' {
export interface PiniaCustomProperties {
router: Router
}
}
const pinia = createPinia();
pinia.use(({ store }) => {
store.router = markRaw(router)
})
app.use(pinia)
// app.use(i18n) // app.use(i18n)
app.mount('#app') app.mount('#app')

View File

@ -46,6 +46,16 @@ const router = createRouter({
component: () => import('../views/CircleView.vue'), component: () => import('../views/CircleView.vue'),
props: true, props: true,
}, },
{
path: '/learn/:learningPathSlug/:circleSlug/evaluate/:learningUnitSlug',
component: () => import('../views/LearningUnitSelfEvaluationView.vue'),
props: true,
},
{
path: '/learn/:learningPathSlug/:circleSlug/:contentSlug',
component: () => import('../views/LearningContentView.vue'),
props: true,
},
{ {
path: '/styleguide', path: '/styleguide',
component: () => import('../views/StyleGuideView.vue'), component: () => import('../views/StyleGuideView.vue'),

View File

@ -174,6 +174,16 @@ export class Circle implements LearningWagtailPage {
return result; return result;
} }
public get flatLearningUnits(): LearningUnit[] {
const result: LearningUnit[] = [];
this.learningSequences.forEach((learningSequence) => {
learningSequence.learningUnits.forEach((learningUnit) => {
result.push(learningUnit);
});
});
return result;
}
public someFinishedInLearningSequence(translationKey: string): boolean { public someFinishedInLearningSequence(translationKey: string): boolean {
if (translationKey) { if (translationKey) {
return this.flatChildren.filter((lc) => { return this.flatChildren.filter((lc) => {
@ -215,4 +225,9 @@ export class Circle implements LearningWagtailPage {
this.parentLearningPath.calcNextLearningContent(completionData); this.parentLearningPath.calcNextLearningContent(completionData);
} }
} }
public getUrl(): string {
const shortSlug = this.slug.replace(`${this.parentLearningPath?.slug}-circle-`, '')
return `/learn/${this.parentLearningPath?.slug}/${shortSlug}`;
}
} }

View File

@ -6,10 +6,28 @@ export type AppState = {
showMainNavigationBar: boolean showMainNavigationBar: boolean
} }
const showMainNavigationBarInitialState = () => {
let path = window.location.pathname;
// remove dangling slash
if (path.endsWith('/')) {
path = path.slice(0, -1);
}
const numberOfSlashes = (path.match(/\//g) || []).length;
// it should hide main navigation bar when on learning content page
if (path.startsWith('/learn/') && numberOfSlashes >= 4) {
return false
}
return true;
}
export const useAppStore = defineStore({ export const useAppStore = defineStore({
id: 'app', id: 'app',
state: () => ({ state: () => ({
showMainNavigationBar: true, showMainNavigationBar: showMainNavigationBarInitialState(),
userLoaded: false, userLoaded: false,
routingFinished: false, routingFinished: false,
} as AppState), } as AppState),

View File

@ -5,14 +5,10 @@ import { defineStore } from 'pinia'
import type { LearningContent, LearningUnit, LearningUnitQuestion } from '@/types' import type { LearningContent, LearningUnit, LearningUnitQuestion } from '@/types'
import type { Circle } from '@/services/circle' import type { Circle } from '@/services/circle'
import { itPost } from '@/fetchHelpers' import { itPost } from '@/fetchHelpers'
import { useAppStore } from '@/stores/app'
import { useLearningPathStore } from '@/stores/learningPath' import { useLearningPathStore } from '@/stores/learningPath'
export type CircleStoreState = { export type CircleStoreState = {
circle: Circle | undefined circle: Circle | undefined
currentLearningContent: LearningContent | undefined
currentSelfEvaluation: LearningUnit | undefined
page: 'INDEX' | 'OVERVIEW' | 'LEARNING_CONTENT' | 'SELF_EVALUATION'
} }
export const useCircleStore = defineStore({ export const useCircleStore = defineStore({
@ -20,15 +16,12 @@ export const useCircleStore = defineStore({
state: () => { state: () => {
return { return {
circle: undefined, circle: undefined,
currentLearningContent: undefined,
currentSelfEvaluation: undefined,
page: 'INDEX',
} as CircleStoreState; } as CircleStoreState;
}, },
getters: { getters: {
}, },
actions: { actions: {
async loadCircle(learningPathSlug: string, circleSlug: string) { async loadCircle(learningPathSlug: string, circleSlug: string): Promise<Circle> {
this.circle = undefined; this.circle = undefined;
const learningPathStore = useLearningPathStore(); const learningPathStore = useLearningPathStore();
await learningPathStore.loadLearningPath(learningPathSlug); await learningPathStore.loadLearningPath(learningPathSlug);
@ -44,6 +37,30 @@ export const useCircleStore = defineStore({
return this.circle return this.circle
}, },
async loadLearningContent(learningPathSlug: string, circleSlug: string, learningContentSlug: string) {
const circle = await this.loadCircle(learningPathSlug, circleSlug);
const result = circle.flatLearningContents.find((learningContent) => {
return learningContent.slug.endsWith(learningContentSlug);
});
if (!result) {
throw `No learning content found with slug: ${learningContentSlug}`;
}
return result
},
async loadSelfEvaluation(learningPathSlug: string, circleSlug: string, learningUnitSlug: string) {
const circle = await this.loadCircle(learningPathSlug, circleSlug);
const learningUnit = circle.flatLearningUnits.find((child) => {
return child.slug.endsWith(learningUnitSlug)
});
if (!learningUnit) {
throw `No self evaluation found with slug: ${learningUnitSlug}`;
}
return learningUnit
},
async markCompletion(page: LearningContent | LearningUnitQuestion, flag = true) { async markCompletion(page: LearningContent | LearningUnitQuestion, flag = true) {
try { try {
page.completed = flag; page.completed = flag;
@ -60,28 +77,26 @@ export const useCircleStore = defineStore({
} }
}, },
openLearningContent(learningContent: LearningContent) { openLearningContent(learningContent: LearningContent) {
this.currentLearningContent = learningContent; const shortSlug = learningContent.slug.replace(`${this.circle?.slug}-lc-`, '');
const appStore = useAppStore(); this.router.push({
appStore.showMainNavigationBar = false; path: `${this.circle?.getUrl()}/${shortSlug}`,
this.page = 'LEARNING_CONTENT'; });
}, },
closeLearningContent() { closeLearningContent() {
this.currentLearningContent = undefined; this.router.push({
const appStore = useAppStore(); path: `${this.circle?.getUrl()}`
appStore.showMainNavigationBar = true; });
this.page = 'INDEX';
}, },
openSelfEvaluation(learningUnit: LearningUnit) { openSelfEvaluation(learningUnit: LearningUnit) {
this.page = 'SELF_EVALUATION'; const shortSlug = learningUnit.slug.replace(`${this.circle?.slug}-lu-`, '');
const appStore = useAppStore(); this.router.push({
appStore.showMainNavigationBar = false; path: `${this.circle?.getUrl()}/evaluate/${shortSlug}`,
this.currentSelfEvaluation = learningUnit; });
}, },
closeSelfEvaluation() { closeSelfEvaluation() {
this.currentSelfEvaluation = undefined; this.router.push({
const appStore = useAppStore(); path: `${this.circle?.getUrl()}`
appStore.showMainNavigationBar = true; });
this.page = 'INDEX';
}, },
calcSelfEvaluationStatus(learningUnit: LearningUnit) { calcSelfEvaluationStatus(learningUnit: LearningUnit) {
if (learningUnit.children.length > 0) { if (learningUnit.children.length > 0) {
@ -94,12 +109,12 @@ export const useCircleStore = defineStore({
} }
return undefined; return undefined;
}, },
continueFromLearningContent() { continueFromLearningContent(currentLearningContent: LearningContent) {
if (this.currentLearningContent) { if (currentLearningContent) {
this.markCompletion(this.currentLearningContent, true); this.markCompletion(currentLearningContent, true);
const nextLearningContent = this.currentLearningContent.nextLearningContent; const nextLearningContent = currentLearningContent.nextLearningContent;
const currentParent = this.currentLearningContent.parentLearningUnit; const currentParent = currentLearningContent.parentLearningUnit;
const nextParent = nextLearningContent?.parentLearningUnit; const nextParent = nextLearningContent?.parentLearningUnit;
if ( if (
@ -108,13 +123,14 @@ export const useCircleStore = defineStore({
currentParent.children.length > 0 currentParent.children.length > 0
) { ) {
// go to self evaluation // go to self evaluation
this.openSelfEvaluation(currentParent); // this.openSelfEvaluation(currentParent);
} else if (this.currentLearningContent.nextLearningContent) { this.closeLearningContent();
} else if (currentLearningContent.nextLearningContent) {
if ( if (
this.currentLearningContent.parentLearningSequence && currentLearningContent.parentLearningSequence &&
this.currentLearningContent.parentLearningSequence.id === nextLearningContent?.parentLearningSequence?.id currentLearningContent.parentLearningSequence.id === nextLearningContent?.parentLearningSequence?.id
) { ) {
this.openLearningContent(this.currentLearningContent.nextLearningContent); this.openLearningContent(currentLearningContent.nextLearningContent);
} else { } else {
this.closeLearningContent(); this.closeLearningContent();
} }
@ -126,21 +142,22 @@ export const useCircleStore = defineStore({
} }
}, },
continueFromSelfEvaluation() { continueFromSelfEvaluation() {
if (this.currentSelfEvaluation) { this.closeSelfEvaluation()
const nextContent = this.currentSelfEvaluation.learningContents[this.currentSelfEvaluation.learningContents.length - 1].nextLearningContent; // if (this.currentSelfEvaluation) {
// const nextContent = this.currentSelfEvaluation.learningContents[this.currentSelfEvaluation.learningContents.length - 1].nextLearningContent;
if (nextContent) { //
if (this.currentSelfEvaluation?.parentLearningSequence?.id === nextContent?.parentLearningSequence?.id) { // if (nextContent) {
this.openLearningContent(nextContent); // if (this.currentSelfEvaluation?.parentLearningSequence?.id === nextContent?.parentLearningSequence?.id) {
} else { // this.openLearningContent(nextContent);
this.closeSelfEvaluation(); // } else {
} // this.closeSelfEvaluation();
} else { // }
this.closeSelfEvaluation(); // } else {
} // this.closeSelfEvaluation();
} else { // }
log.error('currentSelfEvaluation is undefined'); // } else {
} // log.error('currentSelfEvaluation is undefined');
// }
} }
} }
}) })

View File

@ -7,7 +7,7 @@ import LearningContent from '@/components/circle/LearningContent.vue'
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { useCircleStore } from '@/stores/circle' import { useCircleStore } from '@/stores/circle'
import SelfEvaluation from '@/components/circle/SelfEvaluation.vue' import { useAppStore } from '@/stores/app'
log.debug('CircleView.vue created') log.debug('CircleView.vue created')
@ -16,6 +16,9 @@ const props = defineProps<{
circleSlug: string circleSlug: string
}>() }>()
const appStore = useAppStore()
appStore.showMainNavigationBar = true
const circleStore = useCircleStore() const circleStore = useCircleStore()
onMounted(async () => { onMounted(async () => {
@ -42,9 +45,6 @@ onMounted(async () => {
<div v-if="circleStore.page === 'LEARNING_CONTENT'"> <div v-if="circleStore.page === 'LEARNING_CONTENT'">
<LearningContent :key="circleStore.currentLearningContent.translation_key" /> <LearningContent :key="circleStore.currentLearningContent.translation_key" />
</div> </div>
<div v-else-if="circleStore.page === 'SELF_EVALUATION'">
<SelfEvaluation :key="circleStore.currentSelfEvaluation.translation_key" />
</div>
<div v-else> <div v-else>
<div class="circle-container"> <div class="circle-container">
<div class="circle"> <div class="circle">

View File

@ -0,0 +1,59 @@
<script setup lang="ts">
import * as log from 'loglevel'
import { onMounted, reactive, watch } from 'vue'
import { useCircleStore } from '@/stores/circle'
import { useAppStore } from '@/stores/app'
import LearningContent from '@/components/circle/LearningContent.vue'
import { LearningContent as LearningContentType } from '@/types'
log.debug('LearningContentView created')
const props = defineProps<{
learningPathSlug: string
circleSlug: string
contentSlug: string
}>()
const state: { learningContent?: LearningContentType } = reactive({})
const appStore = useAppStore()
appStore.showMainNavigationBar = false
const circleStore = useCircleStore()
const loadLearningContent = async () => {
try {
state.learningContent = await circleStore.loadLearningContent(
props.learningPathSlug,
props.circleSlug,
props.contentSlug
)
} catch (error) {
log.error(error)
}
}
watch(
() => props.contentSlug,
async () => {
log.debug(
'LearningContentView props.contentSlug changed',
props.learningPathSlug,
props.circleSlug,
props.contentSlug
)
await loadLearningContent()
}
)
onMounted(async () => {
log.debug('LearningContentView mounted', props.learningPathSlug, props.circleSlug, props.contentSlug)
await loadLearningContent()
})
</script>
<template>
<LearningContent v-if="state.learningContent" :learning-content="state.learningContent" />
</template>
<style lang="postcss" scoped></style>

View File

@ -0,0 +1,45 @@
<script setup lang="ts">
import * as log from 'loglevel'
import SelfEvaluation from '@/components/circle/SelfEvaluation.vue'
import { onMounted, reactive } from 'vue'
import { useAppStore } from '@/stores/app'
import { useCircleStore } from '@/stores/circle'
import type { LearningUnit } from '@/types'
log.debug('LearningUnitSelfEvaluationView created')
const props = defineProps<{
learningPathSlug: string
circleSlug: string
learningUnitSlug: string
}>()
const appStore = useAppStore()
appStore.showMainNavigationBar = false
const circleStore = useCircleStore()
const state: { learningUnit?: LearningUnit } = reactive({})
onMounted(async () => {
log.debug('LearningUnitSelfEvaluationView mounted', props.learningPathSlug, props.circleSlug, props.learningUnitSlug)
try {
state.learningUnit = await circleStore.loadSelfEvaluation(
props.learningPathSlug,
props.circleSlug,
props.learningUnitSlug
)
} catch (error) {
log.error(error)
}
})
</script>
<template>
<SelfEvaluation v-if="state.learningUnit" :learning-unit="state.learningUnit" />
</template>
<style lang="postcss" scoped></style>

View File

@ -1,9 +1,9 @@
import {fileURLToPath, URL} from 'url' import { fileURLToPath, URL } from "url";
import {defineConfig, loadEnv} from 'vite' import { defineConfig, loadEnv } from "vite";
import vue from '@vitejs/plugin-vue' import vue from "@vitejs/plugin-vue";
// import vueI18n from '@intlify/vite-plugin-vue-i18n' // import vueI18n from '@intlify/vite-plugin-vue-i18n'
import alias from '@rollup/plugin-alias' import alias from "@rollup/plugin-alias";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(({ mode }) => { export default defineConfig(({ mode }) => {
@ -33,6 +33,10 @@ export default defineConfig(({ mode }) => {
// ] // ]
}), }),
], ],
server: {
port: 5173,
hmr: { port: 5173 }
},
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)), '@': fileURLToPath(new URL('./src', import.meta.url)),

View File

@ -109,7 +109,7 @@ django-csp==3.7
# via -r requirements.in # via -r requirements.in
django-debug-toolbar==3.2.4 django-debug-toolbar==3.2.4
# via -r requirements-dev.in # via -r requirements-dev.in
django-extensions==3.1.5 django-extensions==3.2.0
# via -r requirements-dev.in # via -r requirements-dev.in
django-filter==21.1 django-filter==21.1
# via wagtail # via wagtail
@ -428,9 +428,9 @@ wagtail-factories==2.0.1
# via -r requirements.in # via -r requirements.in
wagtail-localize==1.2.1 wagtail-localize==1.2.1
# via -r requirements.in # via -r requirements.in
watchdog==2.1.7 watchdog==2.1.9
# via werkzeug # via werkzeug
watchgod==0.8.1 watchgod==0.8.2
# via # via
# -r requirements-dev.in # -r requirements-dev.in
# uvicorn # uvicorn
@ -440,7 +440,7 @@ webencodings==0.5.1
# via html5lib # via html5lib
websockets==10.2 websockets==10.2
# via uvicorn # via uvicorn
werkzeug[watchdog]==2.1.0 werkzeug[watchdog]==2.2.0
# via -r requirements-dev.in # via -r requirements-dev.in
wheel==0.37.1 wheel==0.37.1
# via pip-tools # via pip-tools