Load MediaCenter data from api
This commit is contained in:
parent
8941f4ad24
commit
18c3e28ba1
|
|
@ -0,0 +1,33 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import MediaLink from '@/components/mediacenter/MediaLink.vue'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
linkText: string
|
||||||
|
url: string
|
||||||
|
icon: string
|
||||||
|
openWindow?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
icon: '',
|
||||||
|
description: '',
|
||||||
|
openWindow: false,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="border-gray-500 border flex p-4 items-center">
|
||||||
|
<img class="mr-6" :src="icon" />
|
||||||
|
<div>
|
||||||
|
<h4 class="mb-2 text-bold">{{ title }}</h4>
|
||||||
|
<p class="mb-2">{{ description }}</p>
|
||||||
|
<media-link :to="url" :blank="openWindow" class="link">
|
||||||
|
<span class="inline">{{ linkText }}</span>
|
||||||
|
</media-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
// https://router.vuejs.org/guide/advanced/extending-router-link.html
|
||||||
|
// https://vueschool.io/articles/vuejs-tutorials/extending-vue-router-links-in-vue-3/
|
||||||
|
|
||||||
|
import { RouterLink } from 'vue-router'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
...RouterLink.props, // @ts-ignore
|
||||||
|
blank: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const isExternalLink = computed(() => typeof props.to === 'string' && props.to.startsWith('http'))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a v-if="isExternalLink" :target="props.blank ? '_blank' : '_self'" rel="noopener" :href="props.to">
|
||||||
|
<slot />
|
||||||
|
</a>
|
||||||
|
<router-link v-else :target="props.blank ? '_blank' : '_self'" rel="noopener" v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
import MediaLink from "@/components/mediacenter/MediaLink.vue";
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
title: string,
|
|
||||||
description: string,
|
|
||||||
linkText: string,
|
|
||||||
url: string,
|
|
||||||
icon: string,
|
|
||||||
openWindow?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
icon: '',
|
|
||||||
description: '',
|
|
||||||
openWindow: false
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="border-gray-500 border flex p-4 items-center">
|
|
||||||
<img
|
|
||||||
class="mr-6"
|
|
||||||
:src="icon" />
|
|
||||||
<div>
|
|
||||||
<h4 class="mb-2 text-bold">{{title}}</h4>
|
|
||||||
<p class="mb-2">{{description}}</p>
|
|
||||||
<media-link
|
|
||||||
:to="url"
|
|
||||||
:blank="openWindow"
|
|
||||||
class="link"
|
|
||||||
>
|
|
||||||
<span class="inline">{{linkText}}</span>
|
|
||||||
</media-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
// https://router.vuejs.org/guide/advanced/extending-router-link.html
|
|
||||||
// https://vueschool.io/articles/vuejs-tutorials/extending-vue-router-links-in-vue-3/
|
|
||||||
|
|
||||||
import { RouterLink, useLink } from 'vue-router'
|
|
||||||
import {computed} from "vue";
|
|
||||||
|
|
||||||
const props = defineProps(
|
|
||||||
{
|
|
||||||
...RouterLink.props, // @ts-ignore
|
|
||||||
blank: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const isExternalLink = computed(() => typeof props.to === 'string' && props.to.startsWith('http'));
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<a v-if="isExternalLink"
|
|
||||||
:target="props.blank ? '_blank' : '_self'"
|
|
||||||
rel="noopener"
|
|
||||||
:href="props.to">
|
|
||||||
<slot />
|
|
||||||
</a>
|
|
||||||
<router-link
|
|
||||||
v-else
|
|
||||||
:target="props.blank ? '_blank' : '_self'"
|
|
||||||
rel="noopener"
|
|
||||||
v-bind="props"
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import CockpitView from '@/views/CockpitView.vue'
|
import CockpitView from '@/views/CockpitView.vue'
|
||||||
import LoginView from '@/views/LoginView.vue'
|
import LoginView from '@/views/LoginView.vue'
|
||||||
import MediaMainView from '@/views/MediaMainView.vue'
|
|
||||||
import { redirectToLoginIfRequired, updateLoggedIn } from '@/router/guards'
|
import { redirectToLoginIfRequired, updateLoggedIn } from '@/router/guards'
|
||||||
import { useAppStore } from '@/stores/app'
|
import { useAppStore } from '@/stores/app'
|
||||||
|
|
||||||
|
|
@ -26,26 +25,27 @@ const router = createRouter({
|
||||||
component: () => import('@/views/ShopView.vue'),
|
component: () => import('@/views/ShopView.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/mediacenter',
|
path: '/mediacenter/:mediaCenterPageSlug',
|
||||||
component: () => import('@/views/MediaView.vue'),
|
props: true,
|
||||||
|
component: () => import('@/views/MediaCenterView.vue'),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'overview',
|
path: 'overview',
|
||||||
component: () => import('@/views/MediaMainView.vue')
|
component: () => import('@/views/MediaMainView.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'handlungsfelder',
|
path: 'handlungsfelder',
|
||||||
component: () => import('@/views/HandlungsfelderOverview.vue')
|
component: () => import('@/views/HandlungsfelderOverview.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'handlungsfeld',
|
path: 'handlungsfeld',
|
||||||
component: () => import('@/views/Handlungsfeld.vue')
|
component: () => import('@/views/Handlungsfeld.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'handlungsfeldlist',
|
path: 'handlungsfeldlist',
|
||||||
component: () => import('@/views/MediaList.vue')
|
component: () => import('@/views/MediaList.vue'),
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/messages',
|
path: '/messages',
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import type {
|
||||||
LearningSequence,
|
LearningSequence,
|
||||||
LearningUnit,
|
LearningUnit,
|
||||||
LearningUnitQuestion,
|
LearningUnitQuestion,
|
||||||
LearningWagtailPage,
|
CourseWagtailPage,
|
||||||
} from '@/types'
|
} from '@/types'
|
||||||
import type { LearningPath } from '@/services/learningPath'
|
import type { LearningPath } from '@/services/learningPath'
|
||||||
|
|
||||||
|
|
@ -109,7 +109,7 @@ export function parseLearningSequences (circle: Circle, children: CircleChild[])
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Circle implements LearningWagtailPage {
|
export class Circle implements CourseWagtailPage {
|
||||||
readonly type = 'learnpath.Circle';
|
readonly type = 'learnpath.Circle';
|
||||||
readonly learningSequences: LearningSequence[];
|
readonly learningSequences: LearningSequence[];
|
||||||
readonly completed: boolean;
|
readonly completed: boolean;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import * as _ from 'lodash'
|
import * as _ from 'lodash'
|
||||||
|
|
||||||
import type { CircleCompletion, LearningContent, LearningPathChild, LearningWagtailPage, Topic } from '@/types'
|
import type { CircleCompletion, LearningContent, LearningPathChild, CourseWagtailPage, Topic } from '@/types'
|
||||||
import { Circle } from '@/services/circle'
|
import { Circle } from '@/services/circle'
|
||||||
|
|
||||||
function getLastCompleted(learningPathKey: string, completionData: CircleCompletion[]) {
|
function getLastCompleted(learningPathKey: string, completionData: CircleCompletion[]) {
|
||||||
|
|
@ -9,7 +9,7 @@ function getLastCompleted(learningPathKey: string, completionData: CircleComplet
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LearningPath implements LearningWagtailPage {
|
export class LearningPath implements CourseWagtailPage {
|
||||||
readonly type = 'learnpath.LearningPath'
|
readonly type = 'learnpath.LearningPath'
|
||||||
public topics: Topic[]
|
public topics: Topic[]
|
||||||
public circles: Circle[]
|
public circles: Circle[]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { itGet } from '@/fetchHelpers'
|
||||||
|
import type { MediaLibraryPage } from '@/types'
|
||||||
|
|
||||||
|
export type MediaCenterStoreState = {
|
||||||
|
mediaCenterPage: MediaLibraryPage | undefined
|
||||||
|
selectedLearningPath: { id: number; name: string }
|
||||||
|
availableLearningPaths: { id: number; name: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMediaCenterStore = defineStore({
|
||||||
|
id: 'mediaCenter',
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
mediaCenterPage: undefined,
|
||||||
|
selectedLearningPath: { id: 1, name: 'Alle Lehrgänge' },
|
||||||
|
availableLearningPaths: [
|
||||||
|
{ id: 1, name: 'Alle Lehrgänge' },
|
||||||
|
{ id: 2, name: 'Versicherungsvermittler/in' },
|
||||||
|
],
|
||||||
|
} as MediaCenterStoreState
|
||||||
|
},
|
||||||
|
getters: {},
|
||||||
|
actions: {
|
||||||
|
async loadMediaCenterPage(slug: string, reload = false) {
|
||||||
|
if (this.mediaCenterPage && !reload) {
|
||||||
|
return this.mediaCenterPage
|
||||||
|
}
|
||||||
|
const mediaCenterPageData = await itGet(`/api/course/page/${slug}/`)
|
||||||
|
|
||||||
|
if (!mediaCenterPageData) {
|
||||||
|
throw `No mediaCenterPageData found with: ${slug}`
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mediaCenterPage = mediaCenterPageData
|
||||||
|
return this.mediaCenterPage
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
|
|
||||||
export const useMediaCenterStore = defineStore({
|
|
||||||
id: 'mediacenter',
|
|
||||||
state: () => {
|
|
||||||
return {
|
|
||||||
selectedLearningPath: { id: 1, name: 'Alle Lehrgänge' },
|
|
||||||
availableLearningPaths: [
|
|
||||||
{ id: 1, name: 'Alle Lehrgänge' },
|
|
||||||
{ id: 2, name: 'Versicherungsvermittler/in' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getters: {},
|
|
||||||
actions: {},
|
|
||||||
})
|
|
||||||
|
|
@ -47,7 +47,7 @@ export interface CircleJobSituation {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LearningWagtailPage {
|
export interface CourseWagtailPage {
|
||||||
readonly id: number;
|
readonly id: number;
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
readonly slug: string;
|
readonly slug: string;
|
||||||
|
|
@ -55,7 +55,7 @@ export interface LearningWagtailPage {
|
||||||
completed?: boolean;
|
completed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LearningContent extends LearningWagtailPage {
|
export interface LearningContent extends CourseWagtailPage {
|
||||||
type: 'learnpath.LearningContent';
|
type: 'learnpath.LearningContent';
|
||||||
minutes: number;
|
minutes: number;
|
||||||
contents: (LearningContentBlock | VideoBlock | PodcastBlock | DocumentBlock)[];
|
contents: (LearningContentBlock | VideoBlock | PodcastBlock | DocumentBlock)[];
|
||||||
|
|
@ -66,13 +66,13 @@ export interface LearningContent extends LearningWagtailPage {
|
||||||
previousLearningContent?: LearningContent;
|
previousLearningContent?: LearningContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LearningUnitQuestion extends LearningWagtailPage {
|
export interface LearningUnitQuestion extends CourseWagtailPage {
|
||||||
type: 'learnpath.LearningUnitQuestion';
|
type: 'learnpath.LearningUnitQuestion';
|
||||||
parentLearningSequence?: LearningSequence;
|
parentLearningSequence?: LearningSequence;
|
||||||
parentLearningUnit?: LearningUnit;
|
parentLearningUnit?: LearningUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LearningUnit extends LearningWagtailPage {
|
export interface LearningUnit extends CourseWagtailPage {
|
||||||
type: 'learnpath.LearningUnit';
|
type: 'learnpath.LearningUnit';
|
||||||
learningContents: LearningContent[];
|
learningContents: LearningContent[];
|
||||||
minutes: number;
|
minutes: number;
|
||||||
|
|
@ -81,7 +81,7 @@ export interface LearningUnit extends LearningWagtailPage {
|
||||||
last?: boolean;
|
last?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LearningSequence extends LearningWagtailPage {
|
export interface LearningSequence extends CourseWagtailPage {
|
||||||
type: 'learnpath.LearningSequence';
|
type: 'learnpath.LearningSequence';
|
||||||
icon: string;
|
icon: string;
|
||||||
learningUnits: LearningUnit[];
|
learningUnits: LearningUnit[];
|
||||||
|
|
@ -90,13 +90,13 @@ export interface LearningSequence extends LearningWagtailPage {
|
||||||
|
|
||||||
export type CircleChild = LearningContent | LearningUnit | LearningSequence | LearningUnitQuestion;
|
export type CircleChild = LearningContent | LearningUnit | LearningSequence | LearningUnitQuestion;
|
||||||
|
|
||||||
export interface WagtailCircle extends LearningWagtailPage {
|
export interface WagtailCircle extends CourseWagtailPage {
|
||||||
type: 'learnpath.Circle';
|
type: 'learnpath.Circle';
|
||||||
children: CircleChild[];
|
children: CircleChild[];
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Topic extends LearningWagtailPage {
|
export interface Topic extends CourseWagtailPage {
|
||||||
type: 'learnpath.Topic';
|
type: 'learnpath.Topic';
|
||||||
is_visible: boolean;
|
is_visible: boolean;
|
||||||
circles: Circle[];
|
circles: Circle[];
|
||||||
|
|
@ -127,3 +127,55 @@ export interface CircleDiagramData {
|
||||||
arrowEndAngle: number
|
arrowEndAngle: number
|
||||||
done: boolean
|
done: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface Course {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
category_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CourseCategory {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
general: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaDocument {
|
||||||
|
type: "Documents";
|
||||||
|
value: number;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaLink {
|
||||||
|
type: "Links";
|
||||||
|
id: string;
|
||||||
|
value: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
link_display_text: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaContentCollection {
|
||||||
|
type: "content_collection";
|
||||||
|
value: {
|
||||||
|
title: string;
|
||||||
|
contents: (MediaDocument | MediaLink)[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaCategoryPage extends CourseWagtailPage {
|
||||||
|
type: 'media_library.MediaCategoryPage';
|
||||||
|
introduction_text: string;
|
||||||
|
description: string;
|
||||||
|
course_category: CourseCategory;
|
||||||
|
body: MediaContentCollection[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MediaLibraryPage extends CourseWagtailPage {
|
||||||
|
type: 'media_library.MediaLibraryPage';
|
||||||
|
course: Course;
|
||||||
|
children: MediaCategoryPage[];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import * as log from 'loglevel';
|
import * as log from 'loglevel';
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
import {reactive, ref, watch} from "vue";
|
import {reactive, ref, watch} from "vue";
|
||||||
import {useMediaCenterStore} from "@/stores/mediacenter";
|
import {useMediaCenterStore} from "@/stores/mediaCenter";
|
||||||
|
|
||||||
log.debug('HandlungsfelderOverview created');
|
log.debug('HandlungsfelderOverview created');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,25 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as log from 'loglevel';
|
import * as log from 'loglevel'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useMediaCenterStore } from '@/stores/mediaCenter'
|
||||||
|
|
||||||
log.debug('ShopView created');
|
log.debug('MediaCenterView created')
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
mediaCenterPageSlug: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const mediaCenterStore = useMediaCenterStore()
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
log.debug('MediaCenterView mounted', props.mediaCenterPageSlug)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mediaCenterStore.loadMediaCenterPage(props.mediaCenterPageSlug)
|
||||||
|
} catch (error) {
|
||||||
|
log.error(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -22,5 +39,4 @@ log.debug('ShopView created');
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
</style>
|
|
||||||
|
|
@ -3,7 +3,7 @@ import * as log from 'loglevel';
|
||||||
import OverviewCard from '@/components/mediacenter/OverviewCard.vue';
|
import OverviewCard from '@/components/mediacenter/OverviewCard.vue';
|
||||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
import {watch, ref} from "vue";
|
import {watch, ref} from "vue";
|
||||||
import {useMediaCenterStore} from "@/stores/mediacenter";
|
import {useMediaCenterStore} from "@/stores/mediaCenter";
|
||||||
|
|
||||||
log.debug('MediaMainView created');
|
log.debug('MediaMainView created');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue