Refactor to pinia store

This commit is contained in:
Daniel Egger 2022-06-20 16:26:44 +02:00
parent 626e336db9
commit 1483ec8be0
6 changed files with 149 additions and 123 deletions

View File

@ -1,22 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import ItCheckbox from '@/components/ui/ItCheckbox.vue'; import ItCheckbox from '@/components/ui/ItCheckbox.vue';
import type {LearningContent, LearningSequence} from '@/services/circle'; import type {LearningContent, LearningSequence} from '@/types';
import {useCircleStore} from '@/stores/circle';
const props = defineProps<{ const props = defineProps<{
learningSequence: LearningSequence learningSequence: LearningSequence
completionData: any
}>() }>()
const contentCompleted = (learningContent: LearningContent) => { const circleStore = useCircleStore();
if (props.completionData?.completed_learning_contents) {
return props.completionData.completed_learning_contents.findIndex((e) => {
return e.learning_content_key === learningContent.translation_key && e.completed;
}) >= 0;
}
return false; function toggleCompleted(learningContent: LearningContent) {
circleStore.toggleCompleted(learningContent, !learningContent.completed);
} }
</script> </script>
<template> <template>
@ -44,8 +41,8 @@ const contentCompleted = (learningContent: LearningContent) => {
class="flex items-center gap-4 pb-3" class="flex items-center gap-4 pb-3"
> >
<ItCheckbox <ItCheckbox
:modelValue="contentCompleted(learningContent)" :modelValue="learningContent.completed"
@click="$emit('toggleLearningContentCheckbox', learningContent, !contentCompleted(learningContent))" @click="toggleCompleted(learningContent)"
> >
{{ learningContent.contents[0].type }}: {{ learningContent.title }} {{ learningContent.contents[0].type }}: {{ learningContent.title }}
</ItCheckbox> </ItCheckbox>

View File

@ -1,6 +1,6 @@
import {describe, it} from 'vitest' import {describe, it} from 'vitest'
import type {Circle} from '../circle';
import {parseLearningSequences} from '../circle'; import {parseLearningSequences} from '../circle';
import type {WagtailCircle} from '@/types';
describe('circleService.parseLearningSequences', () => { describe('circleService.parseLearningSequences', () => {
it('can parse learning sequences from api response', () => { it('can parse learning sequences from api response', () => {
@ -117,7 +117,7 @@ describe('circleService.parseLearningSequences', () => {
"job_situations": [], "job_situations": [],
"goals": [], "goals": [],
"experts": [] "experts": []
} as Circle; } as WagtailCircle;
const learningSequences = parseLearningSequences(input.children); const learningSequences = parseLearningSequences(input.children);

View File

@ -1,66 +1,6 @@
export interface LearningContentBlock { import type {CircleChild, LearningSequence, LearningUnit} from '@/types';
type: 'video' | 'web-based-training' | 'podcast' | 'competence' | 'exercise' | 'document' | 'knowledge';
value: {
description: string;
url?: string;
},
id: string;
}
export interface CircleGoal {
type: 'goal';
value: string;
id: string;
}
export interface CircleJobSituation {
type: 'job_situation';
value: string;
id: string;
}
export interface LearningWagtailPage {
id: number;
title: string;
slug: string;
translation_key: string;
}
export interface LearningContent extends LearningWagtailPage {
type: 'learnpath.LearningContent';
minutes: number;
contents: LearningContentBlock[];
}
export interface LearningUnit extends LearningWagtailPage {
type: 'learnpath.LearningUnit';
questions: [];
learningContents: LearningContent[];
minutes: number;
}
export interface LearningSequence extends LearningWagtailPage {
type: 'learnpath.LearningSequence';
icon: string;
learningUnits: LearningUnit[];
minutes: number;
}
type CircleChild = LearningContent | LearningUnit | LearningSequence;
export interface Circle extends LearningWagtailPage {
type: 'learnpath.Circle';
children: CircleChild[];
description: string;
}
function createEmptyLearningUnit(): LearningUnit { function createEmptyLearningUnit(): LearningUnit {
return { return {
id: -1, id: -1,
@ -132,3 +72,4 @@ export function parseLearningSequences (children: CircleChild[]): LearningSequen
return result; return result;
} }

View File

@ -0,0 +1,59 @@
import {defineStore} from 'pinia'
import type {Circle, LearningContent} from '@/types'
import {itGet, itPost} from '@/fetchHelpers';
import {parseLearningSequences} from '@/services/circle';
export type CircleStoreState = {
circleData: Circle;
completionData: any;
}
export const useCircleStore = defineStore({
id: 'circle',
state: () => {
return {
circleData: {},
completionData: {},
} as CircleStoreState;
},
getters: {},
actions: {
async loadCircle(slug: string) {
try {
this.circleData = await itGet(`/learnpath/api/circle/${slug}/`);
console.log(this.circleData);
this.circleData.learningSequences = parseLearningSequences(this.circleData.children);
this.completionData = await itGet(`/api/completion/circle/${this.circleData.translation_key}/`);
this.parseCompletionData();
} catch (error) {
console.warn(error);
return error
}
},
async toggleCompleted(learningContent: LearningContent, flag = true) {
try {
learningContent.completed = flag;
this.completionData = await itPost('/api/completion/complete_learning_content/', {
learning_content_key: learningContent.translation_key,
completed: learningContent.completed,
});
this.parseCompletionData();
} catch (error) {
console.warn(error);
return error
}
},
parseCompletionData() {
this.circleData.learningSequences.forEach((learningSequence) => {
learningSequence.learningUnits.forEach((learningUnit) => {
learningUnit.learningContents.forEach((learningContent) => {
learningContent.completed = this.completionData.completed_learning_contents.findIndex((e) => {
return e.learning_content_key === learningContent.translation_key && e.completed;
}) >= 0;
});
});
});
}
}
})

63
client/src/types.ts Normal file
View File

@ -0,0 +1,63 @@
export interface LearningContentBlock {
type: 'video' | 'web-based-training' | 'podcast' | 'competence' | 'exercise' | 'document' | 'knowledge';
value: {
description: string;
url?: string;
},
id: string;
}
export interface CircleGoal {
type: 'goal';
value: string;
id: string;
}
export interface CircleJobSituation {
type: 'job_situation';
value: string;
id: string;
}
export interface LearningWagtailPage {
id: number;
title: string;
slug: string;
translation_key: string;
}
export interface LearningContent extends LearningWagtailPage {
type: 'learnpath.LearningContent';
minutes: number;
contents: LearningContentBlock[];
completed?: boolean;
}
export interface LearningUnit extends LearningWagtailPage {
type: 'learnpath.LearningUnit';
questions: [];
learningContents: LearningContent[];
minutes: number;
}
export interface LearningSequence extends LearningWagtailPage {
type: 'learnpath.LearningSequence';
icon: string;
learningUnits: LearningUnit[];
minutes: number;
}
export type CircleChild = LearningContent | LearningUnit | LearningSequence;
export interface WagtailCircle extends LearningWagtailPage {
type: 'learnpath.Circle';
children: CircleChild[];
description: string;
}
export interface Circle extends LearningWagtailPage {
type: 'learnpath.Circle';
children: CircleChild[];
description: string;
learningSequences: LearningSequence[];
}

View File

@ -1,57 +1,23 @@
<script lang="ts"> <script setup lang="ts">
import * as log from 'loglevel'; import * as log from 'loglevel';
import MainNavigationBar from '../components/MainNavigationBar.vue'; import MainNavigationBar from '../components/MainNavigationBar.vue';
import LearningSequence from '../components/circle/LearningSequence.vue'; import LearningSequence from '../components/circle/LearningSequence.vue';
import type {Circle, LearningContent} from '../services/circle';
import {parseLearningSequences} from '../services/circle';
import {defineComponent} from 'vue' import {onMounted} from 'vue'
import {itGet, itPost} from '@/fetchHelpers'; import {useCircleStore} from '@/stores/circle';
export default defineComponent({ const props = defineProps<{
components: { LearningSequence, MainNavigationBar }, circleSlug: string
props: { }>()
circleSlug: {
type: String,
required: true,
},
},
data() {
return {
count: 0,
circleData: {} as Circle,
learningSequences: [] as LearningSequence[],
completionData: {},
}
},
methods: {
toggleLearningContentCheckbox(learningContent: LearningContent, completed=true) {
log.debug('toggleLearningContentCheckbox', learningContent, completed);
console.log(learningContent);
itPost('/api/completion/complete_learning_content/', { const circleStore = useCircleStore();
learning_content_key: learningContent.translation_key,
completed: completed,
}).then((data) => {
log.warn(data);
this.completionData = data;
});
},
},
mounted() {
log.debug('CircleView mounted', this.circleSlug);
itGet(`/learnpath/api/circle/${this.circleSlug}/`).then((data) => {
this.circleData = data;
this.learningSequences = parseLearningSequences(this.circleData.children);
log.debug(this.learningSequences);
itGet(`/api/completion/circle/${this.circleData.translation_key}/`).then((completionData) => { onMounted(() => {
this.completionData = completionData; log.info('CircleView.vue mounted');
}); circleStore.loadCircle(props.circleSlug);
});
}
}); });
</script> </script>
<template> <template>
@ -61,7 +27,7 @@ export default defineComponent({
<div class="flex flex-col lg:flex-row"> <div class="flex flex-col lg:flex-row">
<div class="flex-initial lg:w-128 px-4 py-8 lg:px-8"> <div class="flex-initial lg:w-128 px-4 py-8 lg:px-8">
<h1 class="text-blue-dark text-7xl"> <h1 class="text-blue-dark text-7xl">
{{ circleData.title }} {{ circleStore.circleData.title }}
</h1> </h1>
<div class="mt-8"> <div class="mt-8">
@ -71,7 +37,7 @@ export default defineComponent({
<div class="outcome border border-gray-500 mt-8 p-6"> <div class="outcome border border-gray-500 mt-8 p-6">
<h3 class="text-blue-dark">Das lernst du in diesem Circle.</h3> <h3 class="text-blue-dark">Das lernst du in diesem Circle.</h3>
<div class="prose mt-4"> <div class="prose mt-4">
{{ circleData.description }} {{ circleStore.circleData.description }}
</div> </div>
<button class="btn-primary mt-4">Erfahre mehr dazu</button> <button class="btn-primary mt-4">Erfahre mehr dazu</button>
@ -88,12 +54,12 @@ export default defineComponent({
<div class="flex-auto bg-gray-100 px-4 py-8 lg:px-24"> <div class="flex-auto bg-gray-100 px-4 py-8 lg:px-24">
<div <div
v-for="learningSequence in learningSequences" v-for="learningSequence in circleStore.circleData.learningSequences"
:key="learningSequence.translation_key" :key="learningSequence.translation_key"
> >
<LearningSequence <LearningSequence
:learning-sequence="learningSequence" @toggleLearningContentCheckbox="toggleLearningContentCheckbox" :learning-sequence="learningSequence" @toggleLearningContentCheckbox="toggleLearningContentCheckbox"
:completion-data="completionData" :completion-data="circleStore.completionData"
></LearningSequence> ></LearningSequence>
</div> </div>