VBV-80: Refactor next learning content
This commit is contained in:
parent
97f01e0a08
commit
a32c8ccbff
|
|
@ -15,6 +15,7 @@
|
||||||
"@headlessui/vue": "^1.6.7",
|
"@headlessui/vue": "^1.6.7",
|
||||||
"axios": "^0.26.1",
|
"axios": "^0.26.1",
|
||||||
"d3": "^7.6.1",
|
"d3": "^7.6.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"loglevel": "^1.8.0",
|
"loglevel": "^1.8.0",
|
||||||
"pinia": "^2.0.21",
|
"pinia": "^2.0.21",
|
||||||
"vue": "^3.2.38",
|
"vue": "^3.2.38",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ function _createEmptyLearningUnit(parentLearningSequence: LearningSequence): Lea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseLearningSequences (children: CircleChild[]): LearningSequence[] {
|
export function parseLearningSequences (circle: Circle, children: CircleChild[]): LearningSequence[] {
|
||||||
let learningSequence:LearningSequence | undefined;
|
let learningSequence:LearningSequence | undefined;
|
||||||
let learningUnit:LearningUnit | undefined;
|
let learningUnit:LearningUnit | undefined;
|
||||||
let learningContent:LearningContent | undefined;
|
let learningContent:LearningContent | undefined;
|
||||||
|
|
@ -69,6 +69,7 @@ export function parseLearningSequences (children: CircleChild[]): LearningSequen
|
||||||
previousLearningContent = learningContent;
|
previousLearningContent = learningContent;
|
||||||
|
|
||||||
learningContent = Object.assign(child, {
|
learningContent = Object.assign(child, {
|
||||||
|
parentCircle: circle,
|
||||||
parentLearningSequence: learningSequence,
|
parentLearningSequence: learningSequence,
|
||||||
parentLearningUnit: learningUnit,
|
parentLearningUnit: learningUnit,
|
||||||
previousLearningContent: previousLearningContent,
|
previousLearningContent: previousLearningContent,
|
||||||
|
|
@ -111,6 +112,9 @@ export class Circle implements LearningWagtailPage {
|
||||||
readonly learningSequences: LearningSequence[];
|
readonly learningSequences: LearningSequence[];
|
||||||
readonly completed: boolean;
|
readonly completed: boolean;
|
||||||
|
|
||||||
|
nextCircle?: Circle;
|
||||||
|
previousCircle?: Circle;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly id: number,
|
public readonly id: number,
|
||||||
public readonly slug: string,
|
public readonly slug: string,
|
||||||
|
|
@ -121,7 +125,7 @@ export class Circle implements LearningWagtailPage {
|
||||||
public goals: CircleGoal[],
|
public goals: CircleGoal[],
|
||||||
public job_situations: CircleJobSituation[],
|
public job_situations: CircleJobSituation[],
|
||||||
) {
|
) {
|
||||||
this.learningSequences = parseLearningSequences(this.children);
|
this.learningSequences = parseLearningSequences(this, this.children);
|
||||||
this.completed = false;
|
this.completed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,6 +158,18 @@ export class Circle implements LearningWagtailPage {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get flatLearningContents(): LearningContent[] {
|
||||||
|
const result: LearningContent[] = [];
|
||||||
|
this.learningSequences.forEach((learningSequence) => {
|
||||||
|
learningSequence.learningUnits.forEach((learningUnit) => {
|
||||||
|
learningUnit.learningContents.forEach((learningContent) => {
|
||||||
|
result.push(learningContent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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) => {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,19 @@
|
||||||
import type { LearningPathChild, LearningWagtailPage, Topic } from '@/types'
|
import * as _ from 'lodash'
|
||||||
|
|
||||||
|
import type { CircleCompletion, LearningContent, LearningPathChild, LearningWagtailPage, Topic } from '@/types'
|
||||||
import { Circle } from '@/services/circle'
|
import { Circle } from '@/services/circle'
|
||||||
|
|
||||||
|
function getLastCompleted(learningPathKey: string, completionData: CircleCompletion[]) {
|
||||||
|
return _.orderBy(completionData, ['updated_at'], 'desc').find((c: CircleCompletion) => {
|
||||||
|
return c.completed && c.learning_path_key === learningPathKey && c.page_type === 'learnpath.LearningContent'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export class LearningPath implements LearningWagtailPage {
|
export class LearningPath implements LearningWagtailPage {
|
||||||
readonly type = 'learnpath.LearningPath'
|
readonly type = 'learnpath.LearningPath'
|
||||||
public topics: Topic[]
|
public topics: Topic[]
|
||||||
public circles: Circle[]
|
public circles: Circle[]
|
||||||
|
public nextLearningContent?: LearningContent
|
||||||
|
|
||||||
public static fromJson(json: any, completionData: any): LearningPath {
|
public static fromJson(json: any, completionData: any): LearningPath {
|
||||||
return new LearningPath(json.id, json.slug, json.title, json.translation_key, json.children, completionData)
|
return new LearningPath(json.id, json.slug, json.title, json.translation_key, json.children, completionData)
|
||||||
|
|
@ -37,6 +46,11 @@ export class LearningPath implements LearningWagtailPage {
|
||||||
if (topic) {
|
if (topic) {
|
||||||
topic.circles.push(circle)
|
topic.circles.push(circle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
circle.previousCircle = this.circles[this.circles.length - 1]
|
||||||
|
if (circle.previousCircle) {
|
||||||
|
circle.previousCircle.nextCircle = circle
|
||||||
|
}
|
||||||
this.circles.push(circle)
|
this.circles.push(circle)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -44,5 +58,26 @@ export class LearningPath implements LearningWagtailPage {
|
||||||
if (topic) {
|
if (topic) {
|
||||||
this.topics.push(topic)
|
this.topics.push(topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// find next learning content
|
||||||
|
const lastCompletedLearningContent = getLastCompleted(this.translation_key, completionData);
|
||||||
|
|
||||||
|
if (lastCompletedLearningContent) {
|
||||||
|
const lastCircle = this.circles.find(circle => circle.translation_key === lastCompletedLearningContent.circle_key);
|
||||||
|
if (lastCircle) {
|
||||||
|
const lastLearningContent = lastCircle.flatLearningContents.find(learningContent => learningContent.translation_key === lastCompletedLearningContent.page_key);
|
||||||
|
if (lastLearningContent && lastLearningContent.nextLearningContent) {
|
||||||
|
this.nextLearningContent = lastLearningContent.nextLearningContent;
|
||||||
|
} else {
|
||||||
|
if (lastCircle.nextCircle) {
|
||||||
|
this.nextLearningContent = lastCircle.nextCircle.flatLearningContents[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.nextLearningContent = this.circles[0].flatLearningContents[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('################# ', this.nextLearningContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,67 +3,16 @@ import * as log from 'loglevel'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { itGet } from '@/fetchHelpers'
|
import { itGet } from '@/fetchHelpers'
|
||||||
import { LearningPath } from '@/services/learningPath'
|
import { LearningPath } from '@/services/learningPath'
|
||||||
import {defineStore} from 'pinia'
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import type {LearningPath, Topic} from '@/types'
|
|
||||||
import {itGet} from '@/fetchHelpers';
|
|
||||||
import {Circle} from '@/services/circle';
|
|
||||||
import learningPathDiagram from "@/components/circle/LearningPathDiagram.vue";
|
|
||||||
|
|
||||||
export type LearningPathStoreState = {
|
export type LearningPathStoreState = {
|
||||||
learningPath: LearningPath | undefined
|
learningPath: LearningPath | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getLastCompleted(completionData: any) {
|
|
||||||
return _.filter(_.orderBy(completionData, ['updated_at'], 'desc'), c =>{return c.completed && c.page_type === "learnpath.LearningContent" })[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getFirstLearningContent(lastCopleted, learningPathData) {
|
|
||||||
const circles = _.filter(learningPathData.children, {'type': 'learnpath.Circle'})
|
|
||||||
|
|
||||||
let currentCircle = Circle.fromJson(circles[0])
|
|
||||||
const currentLearningUnit = currentCircle.flatChildren[0]
|
|
||||||
let currentLearningSequence = currentLearningUnit.parentLearningSequence
|
|
||||||
return [currentCircle, currentLearningSequence, currentLearningUnit]
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNextLearningContent(lastCopleted, learningPathData) {
|
|
||||||
|
|
||||||
let currentCircle, currentLearningSequence, currentLearningUnit
|
|
||||||
|
|
||||||
currentLearningUnit = getFirstLearningContent(lastCopleted, learningPathData)
|
|
||||||
|
|
||||||
if (lastCopleted) {
|
|
||||||
const circles = _.filter(learningPathData.children, {'type': 'learnpath.Circle'})
|
|
||||||
_.forEach(circles, circle => {
|
|
||||||
_.forEach(Circle.fromJson(circle).learningSequences, learningSequence => {
|
|
||||||
_.forEach(learningSequence.learningUnits, learningUnit => {
|
|
||||||
_.forEach(learningUnit.learningContents, content => {
|
|
||||||
if (lastCopleted.page_key === content.translation_key) {
|
|
||||||
currentCircle = Circle.fromJson(circle)
|
|
||||||
currentLearningSequence = learningSequence
|
|
||||||
currentLearningUnit = content
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
currentLearningUnit = [currentCircle, currentLearningSequence, currentLearningUnit]
|
|
||||||
}
|
|
||||||
return currentLearningUnit
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const useLearningPathStore = defineStore({
|
export const useLearningPathStore = defineStore({
|
||||||
id: 'learningPath',
|
id: 'learningPath',
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
learningPath: undefined,
|
learningPath: undefined,
|
||||||
|
|
||||||
} as LearningPathStoreState;
|
} as LearningPathStoreState;
|
||||||
},
|
},
|
||||||
getters: {},
|
getters: {},
|
||||||
|
|
@ -76,19 +25,7 @@ export const useLearningPathStore = defineStore({
|
||||||
const learningPathData = await itGet(`/learnpath/api/page/${slug}/`);
|
const learningPathData = await itGet(`/learnpath/api/page/${slug}/`);
|
||||||
const completionData = await itGet(`/api/completion/learning_path/${learningPathData.translation_key}/`);
|
const completionData = await itGet(`/api/completion/learning_path/${learningPathData.translation_key}/`);
|
||||||
|
|
||||||
this.learningPath = learningPathData;
|
|
||||||
|
|
||||||
|
|
||||||
if (learningPathData) {
|
if (learningPathData) {
|
||||||
this.learningPath.lastCompleted = getLastCompleted(completionData)
|
|
||||||
const nextLearningContent = getNextLearningContent(this.learningPath.lastCompleted, learningPathData)
|
|
||||||
|
|
||||||
console.log('nextLearningContent', nextLearningContent)
|
|
||||||
this.learningPath.nextCircle = nextLearningContent[0]
|
|
||||||
this.learningPath.nextLearningSequence = nextLearningContent[1]
|
|
||||||
this.learningPath.nextLearningUnit = nextLearningContent[2]
|
|
||||||
|
|
||||||
|
|
||||||
this.learningPath = LearningPath.fromJson(learningPathData, completionData);
|
this.learningPath = LearningPath.fromJson(learningPathData, completionData);
|
||||||
}
|
}
|
||||||
return this.learningPath;
|
return this.learningPath;
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ export interface LearningContent extends LearningWagtailPage {
|
||||||
type: 'learnpath.LearningContent';
|
type: 'learnpath.LearningContent';
|
||||||
minutes: number;
|
minutes: number;
|
||||||
contents: (LearningContentBlock | VideoBlock | PodcastBlock | DocumentBlock)[];
|
contents: (LearningContentBlock | VideoBlock | PodcastBlock | DocumentBlock)[];
|
||||||
|
parentCircle: Circle;
|
||||||
parentLearningSequence?: LearningSequence;
|
parentLearningSequence?: LearningSequence;
|
||||||
parentLearningUnit?: LearningUnit;
|
parentLearningUnit?: LearningUnit;
|
||||||
nextLearningContent?: LearningContent;
|
nextLearningContent?: LearningContent;
|
||||||
|
|
@ -111,6 +112,7 @@ export interface CircleCompletion {
|
||||||
page_key: string;
|
page_key: string;
|
||||||
page_type: string;
|
page_type: string;
|
||||||
circle_key: string;
|
circle_key: string;
|
||||||
|
learning_path_key: string;
|
||||||
completed: boolean;
|
completed: boolean;
|
||||||
json_data: any;
|
json_data: any;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import * as log from 'loglevel';
|
import * as log from 'loglevel';
|
||||||
|
|
||||||
import {computed, onMounted} from 'vue'
|
import {onMounted} from 'vue'
|
||||||
import {useLearningPathStore} from '@/stores/learningPath';
|
import {useLearningPathStore} from '@/stores/learningPath';
|
||||||
import {useUserStore} from '@/stores/user';
|
import {useUserStore} from '@/stores/user';
|
||||||
|
|
||||||
|
|
@ -20,10 +20,6 @@ const learningPathStore = useLearningPathStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
|
||||||
const continueRoute = computed(() => {
|
|
||||||
return "/circle/" + learningPathStore.learningPath.nextCircle.slug + "/";
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.info('LearningPathView mounted');
|
log.info('LearningPathView mounted');
|
||||||
await learningPathStore.loadLearningPath(props.learningPathSlug);
|
await learningPathStore.loadLearningPath(props.learningPathSlug);
|
||||||
|
|
@ -72,10 +68,14 @@ onMounted(async () => {
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-8 flex-2" v-if="learningPathStore.learningPath.nextCircle" translate>
|
<div class="p-8 flex-2" v-if="learningPathStore.learningPath.nextLearningContent" translate>
|
||||||
Nächster Schirtt
|
Nächster Schirtt
|
||||||
<h3>{{ learningPathStore.learningPath.nextCircle.title }}: {{ learningPathStore.learningPath.nextLearningSequence.title }}</h3>
|
<h3>{{ learningPathStore.learningPath.nextLearningContent.parentCircle.title }}: {{ learningPathStore.learningPath.nextLearningContent.parentLearningSequence.title }}</h3>
|
||||||
<router-link class="mt-4 btn-blue" v-bind:to="this.continueRoute" translate>
|
<router-link
|
||||||
|
class="mt-4 btn-blue"
|
||||||
|
:to="`/circle/${learningPathStore.learningPath.nextLearningContent.parentCircle.slug}/`"
|
||||||
|
translate
|
||||||
|
>
|
||||||
Los geht's
|
Los geht's
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export default defineConfig(({ mode }) => {
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
globals: true,
|
globals: true,
|
||||||
environment: 'happy-dom',
|
environment: 'jsdom',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue