Merge branch 'feature/media-library' into develop
|
|
@ -13,6 +13,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
|
|
||||||
|
import AppFooter from "@/components/AppFooter.vue";
|
||||||
import MainNavigationBar from "@/components/MainNavigationBar.vue";
|
import MainNavigationBar from "@/components/MainNavigationBar.vue";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ const profileDropdownData = [
|
||||||
Shop
|
Shop
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link
|
<router-link
|
||||||
to="/media/versicherungsvermittlerin-media/overview"
|
to="/media/versicherungsvermittlerin-media"
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
:class="{ 'nav-item--active': isInRoutePath(['/media']) }"
|
:class="{ 'nav-item--active': isInRoutePath(['/media']) }"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ function render() {
|
||||||
const svg = d3.select(".circle-visualization");
|
const svg = d3.select(".circle-visualization");
|
||||||
// Clean svg before adding new stuff.
|
// Clean svg before adding new stuff.
|
||||||
svg.selectAll("*").remove();
|
svg.selectAll("*").remove();
|
||||||
|
|
||||||
// Append marker as definition to the svg
|
// Append marker as definition to the svg
|
||||||
svg
|
svg
|
||||||
.attr("viewBox", `0 0 ${width} ${height}`)
|
.attr("viewBox", `0 0 ${width} ${height}`)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import colors from "@/colors.json";
|
import colors from "@/colors.json";
|
||||||
import { useLearningPathStore } from "@/stores/learningPath";
|
import { useLearningPathStore } from "@/stores/learningPath";
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
|
import * as _ from "lodash";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -67,11 +68,9 @@ export default {
|
||||||
const newCircle = {};
|
const newCircle = {};
|
||||||
newCircle.pieData = pieData.reverse();
|
newCircle.pieData = pieData.reverse();
|
||||||
newCircle.title = circle.title;
|
newCircle.title = circle.title;
|
||||||
newCircle.slug = circle.slug.replace(
|
newCircle.frontend_url = circle.frontend_url;
|
||||||
`${circle.parentLearningPath.slug}-circle-`,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
newCircle.id = circle.id;
|
newCircle.id = circle.id;
|
||||||
|
newCircle.slug = _.kebabCase(circle.title);
|
||||||
internalCircles.push(newCircle);
|
internalCircles.push(newCircle);
|
||||||
});
|
});
|
||||||
return internalCircles;
|
return internalCircles;
|
||||||
|
|
@ -155,7 +154,7 @@ export default {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.on("click", (d, i) => {
|
.on("click", (d, i) => {
|
||||||
vueRouter.push(`/learn/${this.learningPathStore.learningPath.slug}/${i.slug}`);
|
vueRouter.push(i.frontend_url);
|
||||||
})
|
})
|
||||||
|
|
||||||
.attr("role", "button");
|
.attr("role", "button");
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import { useUserStore } from "@/stores/user";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
|
|
||||||
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
||||||
|
import LearningPathViewVertical from "@/pages/LearningPathViewVertical.vue";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
import type { LearningPath } from "@/services/learningPath";
|
||||||
import LearningPathViewVertical from "@/views/LearningPathViewVertical.vue";
|
|
||||||
|
|
||||||
log.debug("LearningPathView created");
|
log.debug("LearningPathView created");
|
||||||
|
|
||||||
|
|
@ -31,12 +31,9 @@ onMounted(async () => {
|
||||||
const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
|
const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
|
||||||
if (learningPath.nextLearningContent) {
|
if (learningPath.nextLearningContent) {
|
||||||
const circle = learningPath.nextLearningContent.parentCircle;
|
const circle = learningPath.nextLearningContent.parentCircle;
|
||||||
const lsShortSlug =
|
const url =
|
||||||
learningPath.nextLearningContent.parentLearningSequence?.slug.replace(
|
learningPath.nextLearningContent.parentLearningSequence?.frontend_url ||
|
||||||
`${circle.slug}-`,
|
circle.frontend_url;
|
||||||
""
|
|
||||||
);
|
|
||||||
const url = `/learn/${learningPath.slug}/${learningPath.nextLearningContent.parentCircle.slug}#${lsShortSlug}`;
|
|
||||||
const isFirst =
|
const isFirst =
|
||||||
learningPath.nextLearningContent.translation_key ===
|
learningPath.nextLearningContent.translation_key ===
|
||||||
learningPath.circles[0].flatLearningContents[0].translation_key;
|
learningPath.circles[0].flatLearningContents[0].translation_key;
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import LinkCard from "@/components/mediaLibrary/LinkCard.vue";
|
||||||
|
import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
|
||||||
|
import MLCategoryLayout from "@/pages/mediaLibrary/MLCategoryLayout.vue";
|
||||||
|
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
||||||
|
import * as log from "loglevel";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
mediaCategorySlug: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
log.debug("MediaCategoryDetailView created", props.mediaCategorySlug);
|
||||||
|
|
||||||
|
const mediaStore = useMediaLibraryStore();
|
||||||
|
|
||||||
|
const mediaCategory = computed(() => {
|
||||||
|
return mediaStore.mediaLibraryPage?.children.find((category) =>
|
||||||
|
category.slug.endsWith(props.mediaCategorySlug)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxCardItems = 4;
|
||||||
|
const maxListItems = 6;
|
||||||
|
|
||||||
|
const displayAsCard = (itemType: string): boolean => {
|
||||||
|
return itemType === "learn_media" || itemType === "relative_link";
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasMoreItems = (items: object[], maxItems: number): boolean => {
|
||||||
|
return items.length > maxItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMaxDisplayItems = (items: object[], maxItems: number) => {
|
||||||
|
return items.slice(0, maxItems);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMaxDisplayItemsForType = (itemType: string, items: object[]) => {
|
||||||
|
return displayAsCard(itemType)
|
||||||
|
? getMaxDisplayItems(items, maxCardItems)
|
||||||
|
: getMaxDisplayItems(items, maxListItems);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
||||||
|
const maxItems = displayAsCard(itemType) ? maxCardItems : maxListItems;
|
||||||
|
return hasMoreItems(items, maxItems);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Teleport v-if="mediaStore.mediaLibraryPage && mediaCategory" to="body">
|
||||||
|
<MLCategoryLayout>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="w-5/12">
|
||||||
|
<h3 class="font-normal text-large mb-3">Handlungsfeld</h3>
|
||||||
|
<h1 class="mb-4 lg:mb-8">{{ mediaCategory.title }}</h1>
|
||||||
|
<p>{{ mediaCategory.introduction_text }}</p>
|
||||||
|
</div>
|
||||||
|
<img class="w-5/12" :src="mediaCategory.icon" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<section class="mb-20">
|
||||||
|
<h2 class="mb-4">{{ mediaCategory.description_title }}</h2>
|
||||||
|
<p class="mb-4 lg:w-2/3">{{ mediaCategory.description_text }}</p>
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="item in mediaCategory.items"
|
||||||
|
:key="item"
|
||||||
|
class="mb-2 h-10 leading-10 flex items-center"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="text-sky-500 bg-[url('/static/icons/icon-check.svg')] bg-no-repeat h-10 w-10 mr-2"
|
||||||
|
></span>
|
||||||
|
{{ item.value }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-for="content_collection in mediaCategory.body"
|
||||||
|
:key="content_collection.value.title"
|
||||||
|
>
|
||||||
|
<section v-if="content_collection.value?.contents?.length" class="mb-20">
|
||||||
|
<h2 class="mb-4">{{ content_collection.value.title }}</h2>
|
||||||
|
<ul
|
||||||
|
:class="{
|
||||||
|
'grid gap-4 grid-cols-1 lg:grid-cols-2': displayAsCard(
|
||||||
|
content_collection.value.contents[0].type
|
||||||
|
),
|
||||||
|
'border-t': !displayAsCard(content_collection.value.contents[0].type),
|
||||||
|
'mb-6': hasMoreItemsForType(
|
||||||
|
content_collection.value.contents[0].type,
|
||||||
|
content_collection.value.contents
|
||||||
|
),
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="mediaItem in getMaxDisplayItemsForType(
|
||||||
|
content_collection.value.contents[0].type,
|
||||||
|
content_collection.value.contents
|
||||||
|
)"
|
||||||
|
:key="mediaItem.id"
|
||||||
|
>
|
||||||
|
<LinkCard
|
||||||
|
v-if="displayAsCard(mediaItem.type)"
|
||||||
|
:title="mediaItem.value.title"
|
||||||
|
:icon="mediaItem.value.icon_url"
|
||||||
|
:description="mediaItem.value.description"
|
||||||
|
:url="mediaItem.value.url"
|
||||||
|
:link-text="mediaItem.value.link_display_text"
|
||||||
|
:open-window="mediaItem.value.open_window"
|
||||||
|
/>
|
||||||
|
<div v-else class="flex items-center justify-between border-b py-4">
|
||||||
|
<h4 class="text-bold">{{ mediaItem.value.title }}</h4>
|
||||||
|
<media-link
|
||||||
|
:blank="mediaItem.value.open_window"
|
||||||
|
:to="mediaItem.value.url"
|
||||||
|
class="link"
|
||||||
|
>{{ mediaItem.value.link_display_text }}
|
||||||
|
</media-link>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<router-link
|
||||||
|
v-if="
|
||||||
|
hasMoreItemsForType(
|
||||||
|
content_collection.value.contents[0].type,
|
||||||
|
content_collection.value.contents
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:to="`${mediaCategory.frontend_url}/media`"
|
||||||
|
class="flex items-center"
|
||||||
|
>
|
||||||
|
<span>Alle anschauen</span>
|
||||||
|
<it-icon-arrow-right></it-icon-arrow-right>
|
||||||
|
</router-link>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</MLCategoryLayout>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.it-icon-hf {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.it-icon-hf > * {
|
||||||
|
@apply m-auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
||||||
|
import * as log from "loglevel";
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
|
|
||||||
|
log.debug("HandlungsfelderOverview created");
|
||||||
|
|
||||||
|
const mediaStore = useMediaLibraryStore();
|
||||||
|
const dropdownSelected = ref(mediaStore.selectedLearningPath);
|
||||||
|
|
||||||
|
const categories = computed(() => {
|
||||||
|
if (mediaStore.mediaLibraryPage) {
|
||||||
|
return mediaStore.mediaLibraryPage.children.filter(
|
||||||
|
(cat) => cat.course_category.general === false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(dropdownSelected, (newValue) =>
|
||||||
|
mediaStore.$patch({
|
||||||
|
selectedLearningPath: newValue,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mx-auto max-w-5xl">
|
||||||
|
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">
|
||||||
|
<h1>Handlungsfelder</h1>
|
||||||
|
<!-- <ItDropdownSelect v-model="dropdownSelected" :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
|
||||||
|
</div>
|
||||||
|
<div v-if="mediaStore.mediaLibraryPage">
|
||||||
|
<ul class="grid gap-5 grid-cols-1 lg:grid-cols-4">
|
||||||
|
<li v-for="cat in categories" :key="cat.id" class="bg-white p-4">
|
||||||
|
<router-link :to="cat.frontend_url">
|
||||||
|
<img class="m-auto" :src="`/static/icons/demo/${cat.overview_icon}.svg`" />
|
||||||
|
<h3 class="text-base text-center">{{ cat.title }}</h3>
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.it-icon-hf > * {
|
||||||
|
@apply m-auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -2,13 +2,22 @@
|
||||||
import OverviewCard from "@/components/mediaLibrary/OverviewCard.vue";
|
import OverviewCard from "@/components/mediaLibrary/OverviewCard.vue";
|
||||||
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
import { ref, watch } from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
|
|
||||||
log.debug("MediaMainView created");
|
log.debug("MediaMainView created");
|
||||||
|
|
||||||
const mediaStore = useMediaLibraryStore();
|
const mediaStore = useMediaLibraryStore();
|
||||||
const dropdownSelected = ref(mediaStore.selectedLearningPath);
|
const dropdownSelected = ref(mediaStore.selectedLearningPath);
|
||||||
|
|
||||||
|
const generalCategory = computed(() => {
|
||||||
|
if (mediaStore.mediaLibraryPage) {
|
||||||
|
return mediaStore.mediaLibraryPage.children.find(
|
||||||
|
(cat) => cat.course_category.general
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
watch(dropdownSelected, (newValue) =>
|
watch(dropdownSelected, (newValue) =>
|
||||||
mediaStore.$patch({
|
mediaStore.$patch({
|
||||||
selectedLearningPath: newValue,
|
selectedLearningPath: newValue,
|
||||||
|
|
@ -28,17 +37,17 @@ watch(dropdownSelected, (newValue) =>
|
||||||
v-if="mediaStore.mediaLibraryPage"
|
v-if="mediaStore.mediaLibraryPage"
|
||||||
title="Handlungsfelder"
|
title="Handlungsfelder"
|
||||||
call2-action="Anschauen"
|
call2-action="Anschauen"
|
||||||
:link="`/media/${mediaStore.mediaLibraryPage.slug}/handlungsfelder`"
|
:link="`${mediaStore.mediaLibraryPage.frontend_url}/category`"
|
||||||
description="Finde alle Ressourcen der Handlungsfelder wie Lernmedien, Links und andere nützliche Informationen."
|
description="Finde alle Ressourcen der Handlungsfelder wie Lernmedien, Links und andere nützliche Informationen."
|
||||||
icon="handlungsfelder-overview"
|
icon="handlungsfelder-overview"
|
||||||
class="mb-6"
|
class="mb-6"
|
||||||
>
|
>
|
||||||
</OverviewCard>
|
</OverviewCard>
|
||||||
<OverviewCard
|
<OverviewCard
|
||||||
v-if="mediaStore.mediaLibraryPage"
|
v-if="mediaStore.mediaLibraryPage && generalCategory"
|
||||||
title="Lernmedien"
|
title="Lernmedien"
|
||||||
call2-action="Anschauen"
|
call2-action="Anschauen"
|
||||||
:link="`/media/${mediaStore.mediaLibraryPage.slug}/lernmedien`"
|
:link="`${generalCategory.frontend_url}/media`"
|
||||||
description="Finde eine vollständige Liste der Bücher und anderen Medien, auf die im Kurs verwiesen wird."
|
description="Finde eine vollständige Liste der Bücher und anderen Medien, auf die im Kurs verwiesen wird."
|
||||||
icon="lernmedien-overview"
|
icon="lernmedien-overview"
|
||||||
class="mb-6"
|
class="mb-6"
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
|
||||||
|
import MLCategoryLayout from "@/pages/mediaLibrary/MLCategoryLayout.vue";
|
||||||
|
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
||||||
|
import * as log from "loglevel";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
mediaLibraryPageSlug: string;
|
||||||
|
mediaCategorySlug: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
log.debug("MLMediaListPage created", props.mediaCategorySlug);
|
||||||
|
|
||||||
|
const mediaStore = useMediaLibraryStore();
|
||||||
|
|
||||||
|
const mediaCategory = computed(() => {
|
||||||
|
return mediaStore.mediaLibraryPage?.children.find((category) =>
|
||||||
|
category.slug.endsWith(props.mediaCategorySlug)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const mediaList = computed(() => {
|
||||||
|
if (mediaCategory.value) {
|
||||||
|
const learnMediaCollection = mediaCategory.value.body.find((contentCollection) => {
|
||||||
|
if (contentCollection.value.contents.length) {
|
||||||
|
return contentCollection.value.contents[0].type === "learn_media";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(learnMediaCollection);
|
||||||
|
return learnMediaCollection?.value;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Teleport v-if="mediaList" to="body">
|
||||||
|
<MLCategoryLayout>
|
||||||
|
<template #header>
|
||||||
|
<h1 class="mb-4">{{ mediaList.title }}</h1>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<section class="mb-20">
|
||||||
|
<ul class="border-t">
|
||||||
|
<li
|
||||||
|
v-for="item in mediaList.contents"
|
||||||
|
:key="item.id"
|
||||||
|
class="flex items-center justify-between border-b py-4"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div v-if="item.value.icon_url">
|
||||||
|
<img class="mr-6 max-h-[70px]" :src="item.value.icon_url" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 class="text-bold">{{ item.value.title }}</h4>
|
||||||
|
<p v-if="item.value.description" class="mb-2">
|
||||||
|
{{ item.value.description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<media-link
|
||||||
|
:to="item.value.url"
|
||||||
|
:blank="item.value.open_window"
|
||||||
|
class="link"
|
||||||
|
>{{ item.value.link_display_text }}
|
||||||
|
</media-link>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
</MLCategoryLayout>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.it-icon-hf {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.it-icon-hf > * {
|
||||||
|
@apply m-auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
import CockpitView from "@/pages/CockpitView.vue";
|
||||||
|
import LoginView from "@/pages/LoginView.vue";
|
||||||
import { redirectToLoginIfRequired, updateLoggedIn } from "@/router/guards";
|
import { redirectToLoginIfRequired, updateLoggedIn } from "@/router/guards";
|
||||||
import { useAppStore } from "@/stores/app";
|
import { useAppStore } from "@/stores/app";
|
||||||
import CockpitView from "@/views/CockpitView.vue";
|
|
||||||
import LoginView from "@/views/LoginView.vue";
|
|
||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
|
@ -22,70 +22,71 @@ const router = createRouter({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/shop",
|
path: "/shop",
|
||||||
component: () => import("@/views/ShopView.vue"),
|
component: () => import("@/pages/ShopView.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/media/:mediaLibraryPageSlug",
|
path: "/media/:mediaLibraryPageSlug",
|
||||||
props: true,
|
props: true,
|
||||||
component: () => import("@/views/MediaLibraryView.vue"),
|
component: () => import("@/pages/mediaLibrary/MLParentPage.vue"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "overview",
|
path: "",
|
||||||
component: () => import("@/views/MediaLibraryMainView.vue"),
|
component: () => import("@/pages/mediaLibrary/MLIndexPage.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "handlungsfelder/:mediaCategorySlug",
|
path: "category/:mediaCategorySlug/media",
|
||||||
props: true,
|
props: true,
|
||||||
component: () => import("@/views/MediaCategoryDetailView.vue"),
|
component: () => import("@/pages/mediaLibrary/MLMediaListPage.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "handlungsfelder",
|
path: "category/:mediaCategorySlug",
|
||||||
component: () => import("@/views/MediaLibraryCategoryOverview.vue"),
|
props: true,
|
||||||
|
component: () => import("@/pages/mediaLibrary/MLCategoryDetailPage.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "handlungsfeldlist",
|
path: "category",
|
||||||
component: () => import("@/views/MediaList.vue"),
|
component: () => import("@/pages/mediaLibrary/MLCategoryIndexPage.vue"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/messages",
|
path: "/messages",
|
||||||
component: () => import("@/views/MessagesView.vue"),
|
component: () => import("@/pages/MessagesView.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/profile",
|
path: "/profile",
|
||||||
component: () => import("@/views/ProfileView.vue"),
|
component: () => import("@/pages/ProfileView.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug",
|
path: "/learn/:learningPathSlug",
|
||||||
component: () => import("../views/LearningPathView.vue"),
|
component: () => import("../pages/LearningPathView.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug/:circleSlug",
|
path: "/learn/:learningPathSlug/:circleSlug",
|
||||||
component: () => import("../views/CircleView.vue"),
|
component: () => import("../pages/CircleView.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug/:circleSlug/evaluate/:learningUnitSlug",
|
path: "/learn/:learningPathSlug/:circleSlug/evaluate/:learningUnitSlug",
|
||||||
component: () => import("../views/LearningUnitSelfEvaluationView.vue"),
|
component: () => import("../pages/LearningUnitSelfEvaluationView.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug/:circleSlug/:contentSlug",
|
path: "/learn/:learningPathSlug/:circleSlug/:contentSlug",
|
||||||
component: () => import("../views/LearningContentView.vue"),
|
component: () => import("../pages/LearningContentView.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/styleguide",
|
path: "/styleguide",
|
||||||
component: () => import("../views/StyleGuideView.vue"),
|
component: () => import("../pages/StyleGuideView.vue"),
|
||||||
meta: {
|
meta: {
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/:pathMatch(.*)*",
|
path: "/:pathMatch(.*)*",
|
||||||
component: () => import("../views/404View.vue"),
|
component: () => import("../pages/404Page.vue"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ function _createEmptyLearningUnit(
|
||||||
slug: "",
|
slug: "",
|
||||||
translation_key: "",
|
translation_key: "",
|
||||||
type: "learnpath.LearningUnit",
|
type: "learnpath.LearningUnit",
|
||||||
|
frontend_url: "",
|
||||||
learningContents: [],
|
learningContents: [],
|
||||||
minutes: 0,
|
minutes: 0,
|
||||||
parentLearningSequence: parentLearningSequence,
|
parentLearningSequence: parentLearningSequence,
|
||||||
|
|
@ -131,7 +132,8 @@ export class Circle implements CourseWagtailPage {
|
||||||
public readonly slug: string,
|
public readonly slug: string,
|
||||||
public readonly title: string,
|
public readonly title: string,
|
||||||
public readonly translation_key: string,
|
public readonly translation_key: string,
|
||||||
public description: string,
|
public readonly frontend_url: string,
|
||||||
|
public readonly description: string,
|
||||||
public children: CircleChild[],
|
public children: CircleChild[],
|
||||||
public goals: CircleGoal[],
|
public goals: CircleGoal[],
|
||||||
public job_situations: CircleJobSituation[],
|
public job_situations: CircleJobSituation[],
|
||||||
|
|
@ -147,6 +149,7 @@ export class Circle implements CourseWagtailPage {
|
||||||
json.slug,
|
json.slug,
|
||||||
json.title,
|
json.title,
|
||||||
json.translation_key,
|
json.translation_key,
|
||||||
|
json.frontend_url,
|
||||||
json.description,
|
json.description,
|
||||||
json.children,
|
json.children,
|
||||||
json.goals,
|
json.goals,
|
||||||
|
|
@ -241,9 +244,4 @@ export class Circle implements CourseWagtailPage {
|
||||||
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}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ export class LearningPath implements CourseWagtailPage {
|
||||||
json.slug,
|
json.slug,
|
||||||
json.title,
|
json.title,
|
||||||
json.translation_key,
|
json.translation_key,
|
||||||
|
json.frontend_url,
|
||||||
json.course.id,
|
json.course.id,
|
||||||
json.children,
|
json.children,
|
||||||
completionData
|
completionData
|
||||||
|
|
@ -46,6 +47,7 @@ export class LearningPath implements CourseWagtailPage {
|
||||||
public readonly slug: string,
|
public readonly slug: string,
|
||||||
public readonly title: string,
|
public readonly title: string,
|
||||||
public readonly translation_key: string,
|
public readonly translation_key: string,
|
||||||
|
public readonly frontend_url: string,
|
||||||
public readonly courseId: number,
|
public readonly courseId: number,
|
||||||
public children: LearningPathChild[],
|
public children: LearningPathChild[],
|
||||||
completionData?: CourseCompletion[]
|
completionData?: CourseCompletion[]
|
||||||
|
|
|
||||||
|
|
@ -94,25 +94,23 @@ export const useCircleStore = defineStore({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openLearningContent(learningContent: LearningContent) {
|
openLearningContent(learningContent: LearningContent) {
|
||||||
const shortSlug = learningContent.slug.replace(`${this.circle?.slug}-lc-`, "");
|
|
||||||
this.router.push({
|
this.router.push({
|
||||||
path: `${this.circle?.getUrl()}/${shortSlug}`,
|
path: learningContent.frontend_url,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
closeLearningContent() {
|
closeLearningContent() {
|
||||||
this.router.push({
|
this.router.push({
|
||||||
path: `${this.circle?.getUrl()}`,
|
path: `${this.circle?.frontend_url}`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
openSelfEvaluation(learningUnit: LearningUnit) {
|
openSelfEvaluation(learningUnit: LearningUnit) {
|
||||||
const shortSlug = learningUnit.slug.replace(`${this.circle?.slug}-lu-`, "");
|
|
||||||
this.router.push({
|
this.router.push({
|
||||||
path: `${this.circle?.getUrl()}/evaluate/${shortSlug}`,
|
path: learningUnit.frontend_url,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
closeSelfEvaluation() {
|
closeSelfEvaluation() {
|
||||||
this.router.push({
|
this.router.push({
|
||||||
path: `${this.circle?.getUrl()}`,
|
path: `${this.circle?.frontend_url}`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
calcSelfEvaluationStatus(learningUnit: LearningUnit) {
|
calcSelfEvaluationStatus(learningUnit: LearningUnit) {
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ export interface CourseWagtailPage {
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
readonly slug: string;
|
readonly slug: string;
|
||||||
readonly translation_key: string;
|
readonly translation_key: string;
|
||||||
|
readonly frontend_url: string;
|
||||||
completion_status: CourseCompletionStatus;
|
completion_status: CourseCompletionStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,28 +222,49 @@ export interface CourseCategory {
|
||||||
general: boolean;
|
general: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaDocument {
|
export type MediaLibraryContentBlockValue = {
|
||||||
type: "Documents";
|
title: string;
|
||||||
value: number;
|
description: string;
|
||||||
|
icon_url: string;
|
||||||
|
link_display_text: string;
|
||||||
|
url: string;
|
||||||
|
open_window: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface LearnMediaBlock {
|
||||||
|
type: "learn_media";
|
||||||
id: string;
|
id: string;
|
||||||
|
value: MediaLibraryContentBlockValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaLink {
|
export interface ExternalLinkBlock {
|
||||||
type: "Links";
|
type: "external_link";
|
||||||
id: string;
|
id: string;
|
||||||
value: {
|
value: MediaLibraryContentBlockValue;
|
||||||
title: string;
|
}
|
||||||
description: string;
|
|
||||||
link_display_text: string;
|
export interface InternalLinkBlock {
|
||||||
url: string;
|
type: "internal_link";
|
||||||
};
|
id: string;
|
||||||
|
value: MediaLibraryContentBlockValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RelativeLinkBlock {
|
||||||
|
type: "relative_link";
|
||||||
|
id: string;
|
||||||
|
value: MediaLibraryContentBlockValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaContentCollection {
|
export interface MediaContentCollection {
|
||||||
type: "content_collection";
|
type: "content_collection";
|
||||||
value: {
|
value: {
|
||||||
title: string;
|
title: string;
|
||||||
contents: (MediaDocument | MediaLink)[];
|
contents: (
|
||||||
|
| LearnMediaBlock
|
||||||
|
| ExternalLinkBlock
|
||||||
|
| InternalLinkBlock
|
||||||
|
| RelativeLinkBlock
|
||||||
|
)[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,283 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import LinkCard from "@/components/mediaLibrary/LinkCard.vue";
|
|
||||||
import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
|
|
||||||
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
|
||||||
import HandlungsfeldLayout from "@/views/HandlungsfeldLayout.vue";
|
|
||||||
import * as log from "loglevel";
|
|
||||||
import { computed } from "vue";
|
|
||||||
|
|
||||||
const field = {
|
|
||||||
title: "Fahrzeug",
|
|
||||||
description:
|
|
||||||
"Das Auto ist für viele der grösste Stolz! Es birgt aber auch ein grosses Gefahrenpotenzial. Dabei geht es bei den heutigen Fahrzeugpreisen und Reparaturkosten rasch um namhafte Summen, die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.",
|
|
||||||
icon: "/static/icons/demo/icon-hf-fahrzeug-big.svg",
|
|
||||||
summary: {
|
|
||||||
text: "In diesem berufstypischem Handlungsfeld lernst du alles rund um Motorfahrzeugversicherungen, wie man sein Auto optimal schützen kann, wie du vorgehst bei einem Fahrzeugwechsel, welche Aspekte du bei einer Offerte beachten musst und wie du dem Kunden die Lösung präsentierst.",
|
|
||||||
items: [
|
|
||||||
"Motorfahrzeughaftpflichtversicherung",
|
|
||||||
"Motorfahrzeugkaskoversicherung",
|
|
||||||
"Insassenunfallversicherung",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Lernmedien",
|
|
||||||
type: "learnmedia",
|
|
||||||
moreLink: "",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Links",
|
|
||||||
type: "externalLinks",
|
|
||||||
moreLink: "",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Nationales Versicherungsbüro",
|
|
||||||
iconUrl: "",
|
|
||||||
description: "",
|
|
||||||
linkText: "Link öffnen",
|
|
||||||
link: "https://www.nbi-ngf.ch/h",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Adressen der Strassenverkehrsämter",
|
|
||||||
iconUrl: "",
|
|
||||||
description: "",
|
|
||||||
linkText: "Link öffnen",
|
|
||||||
link: "https://asa.ch/strassenverkehrsaemter/adressen/",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Verankerung im Lernpfad",
|
|
||||||
type: "internalLinks",
|
|
||||||
moreLink: "",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Circle: Einstieg – Lernsequenz: Anwenden",
|
|
||||||
iconUrl: "",
|
|
||||||
description: "",
|
|
||||||
linkText: "Lerineinheit anzeigen",
|
|
||||||
link: "http://localhost:8000/learn/versicherungsvermittlerin/versicherungsvermittlerin-circle-analyse",
|
|
||||||
openWindow: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Circle: Einstieg – Lernsequenz: Anwenden",
|
|
||||||
iconUrl: "",
|
|
||||||
description: "",
|
|
||||||
linkText: "Lerineinheit anzeigen",
|
|
||||||
link: "http://localhost:8000/learn/versicherungsvermittlerin/versicherungsvermittlerin-circle-analyse",
|
|
||||||
openWindow: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Circle: Einstieg – Lernsequenz: Anwenden",
|
|
||||||
iconUrl: "",
|
|
||||||
description: "",
|
|
||||||
linkText: "Lerineinheit anzeigen",
|
|
||||||
link: "http://localhost:8000/learn/versicherungsvermittlerin/versicherungsvermittlerin-circle-analyse",
|
|
||||||
openWindow: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Circle: Einstieg – Lernsequenz: Anwenden",
|
|
||||||
iconUrl: "",
|
|
||||||
description: "",
|
|
||||||
linkText: "Lerineinheit anzeigen",
|
|
||||||
link: "http://localhost:8000/learn/versicherungsvermittlerin/versicherungsvermittlerin-circle-analyse",
|
|
||||||
openWindow: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Querverweise",
|
|
||||||
type: "realtiveLinks",
|
|
||||||
moreLink: "",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Rechtsstreigkeiten",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-einkommenssicherung.svg",
|
|
||||||
description:
|
|
||||||
"Lernmedium: Verkehrsrechtsschutz – Buch «Sach- und Vermögensversicherungen/Kapitel 12.3»",
|
|
||||||
linkText: "Handlungsfeldanzeigen",
|
|
||||||
link: "http://localhost:8000/media/handlungsfeld",
|
|
||||||
openWindow: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Rechtsstreigkeiten",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-einkommenssicherung.svg",
|
|
||||||
description:
|
|
||||||
"Lernmedium: Verkehrsrechtsschutz – Buch «Sach- und Vermögensversicherungen/Kapitel 12.3»",
|
|
||||||
linkText: "Handlungsfeldanzeigen",
|
|
||||||
link: "http://localhost:8000/media/handlungsfeld",
|
|
||||||
openWindow: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
mediaCategorySlug: string;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
log.debug("MediaCategoryDetailView created", props.mediaCategorySlug);
|
|
||||||
|
|
||||||
const mediaStore = useMediaLibraryStore();
|
|
||||||
|
|
||||||
const mediaCategory = computed(() => {
|
|
||||||
return mediaStore.mediaLibraryPage?.children.find(
|
|
||||||
(category) => category.slug === props.mediaCategorySlug
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const maxCardItems = 4;
|
|
||||||
const maxListItems = 6;
|
|
||||||
|
|
||||||
const displayAsCard = (itemType: string): boolean => {
|
|
||||||
return itemType === "learnmedia" || itemType === "realtiveLinks";
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasMoreItems = (items: object[], maxItems: number): boolean => {
|
|
||||||
return items.length > maxItems;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMaxDisplayItems = (items: object[], maxItems: number) => {
|
|
||||||
return items.slice(0, maxItems);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMaxDisplayItemsForType = (itemType: string, items: object[]) => {
|
|
||||||
return displayAsCard(itemType)
|
|
||||||
? getMaxDisplayItems(items, maxCardItems)
|
|
||||||
: getMaxDisplayItems(items, maxListItems);
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
|
||||||
const maxItems = displayAsCard(itemType) ? maxCardItems : maxListItems;
|
|
||||||
return hasMoreItems(items, maxItems);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Teleport v-if="mediaStore.mediaLibraryPage && mediaCategory" to="body">
|
|
||||||
<HandlungsfeldLayout>
|
|
||||||
<template #header>
|
|
||||||
<div class="flex justify-between">
|
|
||||||
<div class="w-5/12">
|
|
||||||
<h3 class="font-normal text-large mb-3">Handlungsfeld</h3>
|
|
||||||
<h1 class="mb-4 lg:mb-8">{{ mediaCategory.title }}</h1>
|
|
||||||
<p>{{ mediaCategory.introduction_text }}</p>
|
|
||||||
</div>
|
|
||||||
<img class="w-5/12" :src="field.icon" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #body>
|
|
||||||
<section class="mb-20">
|
|
||||||
<h2 class="mb-4">{{ mediaCategory.description_title }}</h2>
|
|
||||||
<p class="mb-4 lg:w-2/3">{{ mediaCategory.description_text }}</p>
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="item in mediaCategory.items"
|
|
||||||
:key="item"
|
|
||||||
class="mb-2 h-10 leading-10 flex items-center"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="text-sky-500 bg-[url('/static/icons/icon-check.svg')] bg-no-repeat h-10 w-10 mr-2"
|
|
||||||
></span>
|
|
||||||
{{ item.value }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<section v-for="item in field.items" :key="item.title" class="mb-20">
|
|
||||||
<h2 class="mb-4">{{ item.title }}</h2>
|
|
||||||
<ul
|
|
||||||
:class="{
|
|
||||||
'grid gap-4 grid-cols-1 lg:grid-cols-2': displayAsCard(item.type),
|
|
||||||
'border-t': !displayAsCard(item.type),
|
|
||||||
'mb-6': hasMoreItemsForType(item.type, item.items),
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
v-for="subItem in getMaxDisplayItemsForType(item.type, item.items)"
|
|
||||||
:key="subItem.link"
|
|
||||||
>
|
|
||||||
<LinkCard
|
|
||||||
v-if="displayAsCard(item.type)"
|
|
||||||
:title="subItem.title"
|
|
||||||
:icon="subItem.iconUrl"
|
|
||||||
:description="subItem.description"
|
|
||||||
:url="subItem.link"
|
|
||||||
:link-text="subItem.linkText"
|
|
||||||
:open-window="subItem.openWindow"
|
|
||||||
/>
|
|
||||||
<div v-else class="flex items-center justify-between border-b py-4">
|
|
||||||
<h4 class="text-bold">{{ subItem.title }}</h4>
|
|
||||||
<media-link :blank="subItem.openWindow" :to="subItem.link" class="link"
|
|
||||||
>{{ subItem.linkText }}
|
|
||||||
</media-link>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<router-link
|
|
||||||
v-if="hasMoreItemsForType(item.type, item.items)"
|
|
||||||
to="/media/handlungsfeldlist"
|
|
||||||
class="flex items-center"
|
|
||||||
>
|
|
||||||
<span>Alle anschauen</span>
|
|
||||||
<it-icon-arrow-right></it-icon-arrow-right>
|
|
||||||
</router-link>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
</HandlungsfeldLayout>
|
|
||||||
</Teleport>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.it-icon-hf {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.it-icon-hf > * {
|
|
||||||
@apply m-auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
|
||||||
import * as log from "loglevel";
|
|
||||||
import { ref, watch } from "vue";
|
|
||||||
|
|
||||||
log.debug("HandlungsfelderOverview created");
|
|
||||||
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: "Fahrzeug",
|
|
||||||
icon: "icon-hf-fahrzeug",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Reisen",
|
|
||||||
icon: "icon-hf-reisen",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Einkommenssicherung",
|
|
||||||
icon: "icon-hf-einkommenssicherung",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Gesundheit",
|
|
||||||
icon: "icon-hf-fahrzeug",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Haushalt",
|
|
||||||
icon: "icon-hf-reisen",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Sparen",
|
|
||||||
icon: "icon-hf-einkommenssicherung",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Pensionierung",
|
|
||||||
icon: "icon-hf-fahrzeug",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "KMU",
|
|
||||||
icon: "icon-hf-reisen",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Wohneigentum",
|
|
||||||
icon: "icon-hf-einkommenssicherung",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Rechtsstreitigkeiten",
|
|
||||||
icon: "icon-hf-fahrzeug",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Erben / Vererben",
|
|
||||||
icon: "icon-hf-reisen",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Selbstständigkeit",
|
|
||||||
icon: "icon-hf-einkommenssicherung",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const mediaStore = useMediaLibraryStore();
|
|
||||||
const dropdownSelected = ref(mediaStore.selectedLearningPath);
|
|
||||||
|
|
||||||
watch(dropdownSelected, (newValue) =>
|
|
||||||
mediaStore.$patch({
|
|
||||||
selectedLearningPath: newValue,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="mx-auto max-w-5xl">
|
|
||||||
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">
|
|
||||||
<h1>Handlungsfelder</h1>
|
|
||||||
<!-- <ItDropdownSelect v-model="dropdownSelected" :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
|
|
||||||
</div>
|
|
||||||
<div v-if="mediaStore.mediaLibraryPage">
|
|
||||||
<ul class="grid gap-5 grid-cols-1 lg:grid-cols-4">
|
|
||||||
<li
|
|
||||||
v-for="cat in mediaStore.mediaLibraryPage.children"
|
|
||||||
:key="cat.id"
|
|
||||||
class="bg-white p-4"
|
|
||||||
>
|
|
||||||
<router-link
|
|
||||||
:to="`/media/${mediaStore.mediaLibraryPage.slug}/handlungsfelder/${cat.slug}`"
|
|
||||||
>
|
|
||||||
<img class="m-auto" :src="`/static/icons/demo/${cat.overview_icon}.svg`" />
|
|
||||||
<h3 class="text-base text-center">{{ cat.title }}</h3>
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.it-icon-hf > * {
|
|
||||||
@apply m-auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
|
|
||||||
import HandlungsfeldLayout from "@/views/HandlungsfeldLayout.vue";
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
title: "Fahrzeug: Lernmedien",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Die Motorfahrzeughaftpflicht",
|
|
||||||
iconUrl: "/static/icons/demo/icon-hf-book.png",
|
|
||||||
description: "Buch «Sach- und Vermögensversicherungen» – Kapitel 16",
|
|
||||||
linkText: "PDF anzeigen",
|
|
||||||
link: "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf",
|
|
||||||
openWindow: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Teleport to="body">
|
|
||||||
<HandlungsfeldLayout>
|
|
||||||
<template #header>
|
|
||||||
<h1 class="mb-4">{{ data.title }}</h1>
|
|
||||||
</template>
|
|
||||||
<template #body>
|
|
||||||
<section class="mb-20">
|
|
||||||
<ul class="border-t">
|
|
||||||
<li
|
|
||||||
v-for="item in data.items"
|
|
||||||
:key="item.link"
|
|
||||||
class="flex items-center justify-between border-b py-4"
|
|
||||||
>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div v-if="item.iconUrl">
|
|
||||||
<img class="mr-6 max-h-[70px]" :src="item.iconUrl" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4 class="text-bold">{{ item.title }}</h4>
|
|
||||||
<p v-if="item.description" class="mb-2">{{ item.description }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="">
|
|
||||||
<media-link :to="item.link" :blank="item.openWindow" class="link"
|
|
||||||
>{{ item.linkText }}
|
|
||||||
</media-link>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
</HandlungsfeldLayout>
|
|
||||||
</Teleport>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.it-icon-hf {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.it-icon-hf > * {
|
|
||||||
@apply m-auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
# script should fail when any process returns non zero code
|
# script should fail when any process returns non zero code
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# set location to script directory
|
||||||
|
cd "${0%/*}"
|
||||||
|
|
||||||
echo 'format client code'
|
echo 'format client code'
|
||||||
npm run prettier
|
npm run prettier
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,3 @@
|
||||||
# script should fail when any process returns non zero code
|
# script should fail when any process returns non zero code
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo 'prettier:check'
|
|
||||||
(cd client && npm run prettier:check)
|
|
||||||
|
|
||||||
echo 'lint'
|
|
||||||
(cd client && npm run lint)
|
|
||||||
|
|
||||||
echo 'python ufmt check'
|
|
||||||
ufmt check server
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,15 @@
|
||||||
# script should fail when any process returns non zero code
|
# script should fail when any process returns non zero code
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
echo 'prettier:check'
|
||||||
|
(cd client && npm run prettier:check)
|
||||||
|
|
||||||
|
echo 'lint'
|
||||||
|
(cd client && npm run lint)
|
||||||
|
|
||||||
|
echo 'python ufmt check'
|
||||||
|
ufmt check server
|
||||||
|
|
||||||
echo 'check git-crypt files diff'
|
echo 'check git-crypt files diff'
|
||||||
git-crypt status -e | sort > git-crypt-encrypted-files-check.txt && diff git-crypt-encrypted-files.txt git-crypt-encrypted-files-check.txt
|
git-crypt status -e | sort > git-crypt-encrypted-files-check.txt && diff git-crypt-encrypted-files.txt git-crypt-encrypted-files-check.txt
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[MASTER]
|
[MASTER]
|
||||||
load-plugins=pylint_django
|
# load-plugins=pylint_django
|
||||||
django-settings-module=config.settings.base
|
django-settings-module=config.settings.base
|
||||||
[FORMAT]
|
[FORMAT]
|
||||||
max-line-length=120
|
max-line-length=120
|
||||||
|
|
@ -84,4 +84,4 @@ disable=
|
||||||
# R0915: Too many statements
|
# R0915: Too many statements
|
||||||
too-many-statements,
|
too-many-statements,
|
||||||
duplicate-code,
|
duplicate-code,
|
||||||
cyclic-impor
|
cyclic-import
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ STATICFILES_FINDERS = [
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#media-root
|
# https://docs.djangoproject.com/en/dev/ref/settings/#media-root
|
||||||
MEDIA_ROOT = str(APPS_DIR / "media")
|
MEDIA_ROOT = str(APPS_DIR / "media")
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#media-url
|
# https://docs.djangoproject.com/en/dev/ref/settings/#media-url
|
||||||
MEDIA_URL = "/media/"
|
MEDIA_URL = "/server/media/"
|
||||||
|
|
||||||
IT_SERVE_VUE = env.bool("IT_SERVE_VUE", DEBUG)
|
IT_SERVE_VUE = env.bool("IT_SERVE_VUE", DEBUG)
|
||||||
IT_SERVE_VUE_URL = env("IT_SERVE_VUE_URL", "http://localhost:5173")
|
IT_SERVE_VUE_URL = env("IT_SERVE_VUE_URL", "http://localhost:5173")
|
||||||
|
|
@ -213,7 +213,6 @@ LANGUAGES = [
|
||||||
|
|
||||||
WAGTAILDOCS_DOCUMENT_MODEL = "media_library.LibraryDocument"
|
WAGTAILDOCS_DOCUMENT_MODEL = "media_library.LibraryDocument"
|
||||||
|
|
||||||
|
|
||||||
WAGTAIL_CONTENT_LANGUAGES = [
|
WAGTAIL_CONTENT_LANGUAGES = [
|
||||||
("fr-CH", "Swiss French"),
|
("fr-CH", "Swiss French"),
|
||||||
("de-CH", "Swiss German"),
|
("de-CH", "Swiss German"),
|
||||||
|
|
@ -424,7 +423,6 @@ else:
|
||||||
cache_logger_on_first_use=True,
|
cache_logger_on_first_use=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# django-rest-framework
|
# django-rest-framework
|
||||||
# -------------------------------------------------------------------------------
|
# -------------------------------------------------------------------------------
|
||||||
# django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/
|
# django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/
|
||||||
|
|
@ -464,8 +462,6 @@ CSP_DEFAULT_SRC = [
|
||||||
]
|
]
|
||||||
CSP_FRAME_ANCESTORS = ("'self'",)
|
CSP_FRAME_ANCESTORS = ("'self'",)
|
||||||
|
|
||||||
# By Default swagger ui is available only to admin user. You can change permission classs to change that
|
|
||||||
# See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings
|
|
||||||
SPECTACULAR_SETTINGS = {
|
SPECTACULAR_SETTINGS = {
|
||||||
"TITLE": "VBV Lernwelt API",
|
"TITLE": "VBV Lernwelt API",
|
||||||
"DESCRIPTION": "Documentation of API endpoints of VBV Lernwelt",
|
"DESCRIPTION": "Documentation of API endpoints of VBV Lernwelt",
|
||||||
|
|
@ -492,7 +488,6 @@ ALLOWED_HOSTS = env.list(
|
||||||
"IT_DJANGO_ALLOWED_HOSTS", default=["localhost", "0.0.0.0", "127.0.0.1"]
|
"IT_DJANGO_ALLOWED_HOSTS", default=["localhost", "0.0.0.0", "127.0.0.1"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# CACHES
|
# CACHES
|
||||||
CACHES = {
|
CACHES = {
|
||||||
"default": {
|
"default": {
|
||||||
|
|
@ -533,8 +528,6 @@ OAUTH = {
|
||||||
"client_name": env("IT_OAUTH_CLIENT_NAME", default="lernetz"),
|
"client_name": env("IT_OAUTH_CLIENT_NAME", default="lernetz"),
|
||||||
"client_id": env("IT_OAUTH_CLIENT_ID", default="iterativ"),
|
"client_id": env("IT_OAUTH_CLIENT_ID", default="iterativ"),
|
||||||
"client_secret": env("IT_OAUTH_CLIENT_SECRET", default=""),
|
"client_secret": env("IT_OAUTH_CLIENT_SECRET", default=""),
|
||||||
# "access_token_url": env("IT_OAUTH_ACCESS_TOKEN_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token"),
|
|
||||||
# "authorize_url": env("IT_OAUTH_AUTHORIZE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/auth"),
|
|
||||||
"authorize_params": IT_OAUTH_AUTHORIZE_PARAMS,
|
"authorize_params": IT_OAUTH_AUTHORIZE_PARAMS,
|
||||||
"access_token_params": IT_OAUTH_AUTHORIZE_PARAMS,
|
"access_token_params": IT_OAUTH_AUTHORIZE_PARAMS,
|
||||||
"api_base_url": env(
|
"api_base_url": env(
|
||||||
|
|
|
||||||
|
|
@ -50,19 +50,25 @@ urlpatterns = [
|
||||||
# user management
|
# user management
|
||||||
path("sso/", include("vbv_lernwelt.sso.urls")),
|
path("sso/", include("vbv_lernwelt.sso.urls")),
|
||||||
re_path(r'api/core/me/$', me_user_view, name='me_user_view'),
|
re_path(r'api/core/me/$', me_user_view, name='me_user_view'),
|
||||||
re_path(r'api/core/login/$', django_view_authentication_exempt(vue_login), name='vue_login'),
|
re_path(r'api/core/login/$', django_view_authentication_exempt(vue_login),
|
||||||
|
name='vue_login'),
|
||||||
re_path(r'api/core/logout/$', vue_logout, name='vue_logout'),
|
re_path(r'api/core/logout/$', vue_logout, name='vue_logout'),
|
||||||
|
|
||||||
# core
|
# core
|
||||||
re_path(r"server/core/icons/$", generate_web_component_icons, name="generate_web_component_icons"),
|
re_path(r"server/core/icons/$", generate_web_component_icons,
|
||||||
|
name="generate_web_component_icons"),
|
||||||
|
|
||||||
# course
|
# course
|
||||||
path(r"api/course/page/<slug:slug>/", page_api_view, name="page_api_view"),
|
path(r"api/course/page/<slug:slug>/", page_api_view, name="page_api_view"),
|
||||||
path(r"api/course/completion/mark/", mark_course_completion, name="mark_course_completion"),
|
path(r"api/course/completion/mark/", mark_course_completion,
|
||||||
path(r"api/course/completion/<course_id>/", request_course_completion, name="request_course_completion"),
|
name="mark_course_completion"),
|
||||||
|
path(r"api/course/completion/<course_id>/", request_course_completion,
|
||||||
|
name="request_course_completion"),
|
||||||
|
|
||||||
# testing and debug
|
# testing and debug
|
||||||
path('server/raise_error/', user_passes_test(lambda u: u.is_superuser, login_url='/login/')(raise_example_error), ),
|
path('server/raise_error/',
|
||||||
|
user_passes_test(lambda u: u.is_superuser, login_url='/login/')(
|
||||||
|
raise_example_error), ),
|
||||||
path("server/checkratelimit/", check_rate_limit),
|
path("server/checkratelimit/", check_rate_limit),
|
||||||
]
|
]
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
@ -73,8 +79,11 @@ if settings.DEBUG:
|
||||||
|
|
||||||
if settings.APP_ENVIRONMENT != 'production':
|
if settings.APP_ENVIRONMENT != 'production':
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
re_path(r'api/core/cypressreset/$', cypress_reset_view, name='cypress_reset_view'),
|
re_path(r'api/core/cypressreset/$', cypress_reset_view,
|
||||||
|
name='cypress_reset_view'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -88,7 +97,6 @@ def handler403(request, exception=None):
|
||||||
|
|
||||||
handler500 = "vbv_lernwelt.core.views.server_json_error"
|
handler500 = "vbv_lernwelt.core.views.server_json_error"
|
||||||
|
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
# This allows the error pages to be debugged during development, just visit
|
# This allows the error pages to be debugged during development, just visit
|
||||||
# these url in browser to see how these error pages look like.
|
# these url in browser to see how these error pages look like.
|
||||||
|
|
@ -115,6 +123,5 @@ if settings.DEBUG:
|
||||||
|
|
||||||
urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns
|
urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns
|
||||||
|
|
||||||
|
|
||||||
# serve everything else via the vue app
|
# serve everything else via the vue app
|
||||||
urlpatterns += [re_path(r"^(?!.*(server/|api/|sso/)).*$", vue_home, name="home")]
|
urlpatterns += [re_path(r"^(?!.*(server/|api/|sso/)).*$", vue_home, name="home")]
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,6 @@ class CompetenceProfilePage(Page):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(
|
||||||
cls,
|
cls,
|
||||||
[
|
[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"course",
|
"course",
|
||||||
"children",
|
"children",
|
||||||
],
|
],
|
||||||
|
|
@ -69,11 +64,6 @@ class CompetencePage(Page):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(
|
||||||
cls,
|
cls,
|
||||||
[
|
[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"children",
|
"children",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,11 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
||||||
TopicFactory,
|
TopicFactory,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.media_library.tests.media_library_factories import (
|
from vbv_lernwelt.media_library.tests.media_library_factories import (
|
||||||
create_document_collection,
|
create_external_link_block,
|
||||||
create_link_collection,
|
create_learn_media_block,
|
||||||
create_media_content_link,
|
create_media_collection,
|
||||||
LinkBlockFactory,
|
ExternalLinkBlockFactory,
|
||||||
|
LearnMediaBlockFactory,
|
||||||
MediaCategoryPageFactory,
|
MediaCategoryPageFactory,
|
||||||
MediaLibraryPageFactory,
|
MediaLibraryPageFactory,
|
||||||
)
|
)
|
||||||
|
|
@ -321,22 +322,31 @@ welche Aspekte du bei einer Offerte beachten musst und wie du dem Kunden die Lö
|
||||||
]
|
]
|
||||||
body_data = json.dumps(
|
body_data = json.dumps(
|
||||||
[
|
[
|
||||||
create_document_collection(),
|
create_media_collection(
|
||||||
create_link_collection(
|
title="Lernmedien",
|
||||||
links_dict=[
|
contents=[
|
||||||
create_media_content_link(
|
create_learn_media_block(LearnMediaBlockFactory()),
|
||||||
LinkBlockFactory(
|
create_learn_media_block(LearnMediaBlockFactory()),
|
||||||
|
create_learn_media_block(LearnMediaBlockFactory()),
|
||||||
|
create_learn_media_block(LearnMediaBlockFactory()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
create_media_collection(
|
||||||
|
title="Links",
|
||||||
|
contents=[
|
||||||
|
create_external_link_block(
|
||||||
|
ExternalLinkBlockFactory(
|
||||||
title="Nationales Versicherungsbüro",
|
title="Nationales Versicherungsbüro",
|
||||||
url="https://www.vbv.ch/",
|
url="https://www.vbv.ch/",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
create_media_content_link(
|
create_external_link_block(
|
||||||
LinkBlockFactory(
|
ExternalLinkBlockFactory(
|
||||||
title="Adressen der Strassenverkehrsämter",
|
title="Adressen der Strassenverkehrsämter",
|
||||||
url="https://www.vbv.ch/",
|
url="https://www.vbv.ch/",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import structlog
|
import structlog
|
||||||
|
from django.views.decorators.cache import cache_page
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
@ -11,7 +12,7 @@ logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
# @cache_page(60 * 60 * 8, cache="api_page_cache")
|
@cache_page(60 * 60 * 8, cache="api_page_cache")
|
||||||
def page_api_view(request, slug):
|
def page_api_view(request, slug):
|
||||||
try:
|
try:
|
||||||
page = Page.objects.get(slug=slug, locale__language_code="de-CH")
|
page = Page.objects.get(slug=slug, locale__language_code="de-CH")
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,17 @@ class LearningPath(Page):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.title}"
|
return f"{self.title}"
|
||||||
|
|
||||||
|
def get_frontend_url(self):
|
||||||
|
return f"/learn/{self.slug}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_serializer_class(cls):
|
def get_serializer_class(cls):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(
|
||||||
cls,
|
cls,
|
||||||
["id", "title", "slug", "type", "translation_key", "children", "course"],
|
[
|
||||||
|
"children",
|
||||||
|
"course",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -76,11 +82,6 @@ class Topic(Page):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(
|
||||||
cls,
|
cls,
|
||||||
field_names=[
|
field_names=[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"is_visible",
|
"is_visible",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -143,11 +144,6 @@ class Circle(Page):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(
|
||||||
cls,
|
cls,
|
||||||
field_names=[
|
field_names=[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"children",
|
"children",
|
||||||
"description",
|
"description",
|
||||||
"job_situations",
|
"job_situations",
|
||||||
|
|
@ -156,6 +152,10 @@ class Circle(Page):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_frontend_url(self):
|
||||||
|
short_slug = self.slug.replace(f"{self.get_parent().slug}-circle-", "")
|
||||||
|
return f"{self.get_parent().specific.get_frontend_url()}/{short_slug}"
|
||||||
|
|
||||||
def full_clean(self, *args, **kwargs):
|
def full_clean(self, *args, **kwargs):
|
||||||
self.slug = find_slug_with_parent_prefix(self, "circle")
|
self.slug = find_slug_with_parent_prefix(self, "circle")
|
||||||
super(Circle, self).full_clean(*args, **kwargs)
|
super(Circle, self).full_clean(*args, **kwargs)
|
||||||
|
|
@ -185,9 +185,7 @@ class LearningSequence(Page):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_serializer_class(cls):
|
def get_serializer_class(cls):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(cls, field_names=["icon"])
|
||||||
cls, field_names=["id", "title", "slug", "type", "translation_key", "icon"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_admin_display_title(self):
|
def get_admin_display_title(self):
|
||||||
return f"{self.icon} {self.draft_title}"
|
return f"{self.icon} {self.draft_title}"
|
||||||
|
|
@ -203,6 +201,10 @@ class LearningSequence(Page):
|
||||||
self.slug = find_slug_with_parent_prefix(self, "ls")
|
self.slug = find_slug_with_parent_prefix(self, "ls")
|
||||||
super(LearningSequence, self).full_clean(*args, **kwargs)
|
super(LearningSequence, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_frontend_url(self):
|
||||||
|
short_slug = self.slug.replace(f"{self.get_parent().slug}-", "")
|
||||||
|
return f"{self.get_parent().specific.get_frontend_url()}#{short_slug}"
|
||||||
|
|
||||||
|
|
||||||
class LearningUnit(Page):
|
class LearningUnit(Page):
|
||||||
parent_page_types = ["learnpath.Circle"]
|
parent_page_types = ["learnpath.Circle"]
|
||||||
|
|
@ -240,6 +242,10 @@ class LearningUnit(Page):
|
||||||
)
|
)
|
||||||
super(LearningUnit, self).full_clean(*args, **kwargs)
|
super(LearningUnit, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_frontend_url(self):
|
||||||
|
short_slug = self.slug.replace(f"{self.get_parent().slug}-lu-", "")
|
||||||
|
return f"{self.get_parent().specific.get_frontend_url()}/evaluate/{short_slug}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_serializer_class(cls):
|
def get_serializer_class(cls):
|
||||||
from vbv_lernwelt.learnpath.serializers import LearningUnitSerializer
|
from vbv_lernwelt.learnpath.serializers import LearningUnitSerializer
|
||||||
|
|
@ -298,6 +304,10 @@ class LearningContent(Page):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Learning Content"
|
verbose_name = "Learning Content"
|
||||||
|
|
||||||
|
def get_frontend_url(self):
|
||||||
|
short_slug = self.slug.replace(f"{self.get_parent().slug}-lc-", "")
|
||||||
|
return f"{self.get_parent().specific.get_frontend_url()}/{short_slug}"
|
||||||
|
|
||||||
def full_clean(self, *args, **kwargs):
|
def full_clean(self, *args, **kwargs):
|
||||||
self.slug = find_slug_with_parent_prefix(self, "lc")
|
self.slug = find_slug_with_parent_prefix(self, "lc")
|
||||||
super(LearningContent, self).full_clean(*args, **kwargs)
|
super(LearningContent, self).full_clean(*args, **kwargs)
|
||||||
|
|
@ -307,11 +317,6 @@ class LearningContent(Page):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(
|
||||||
cls,
|
cls,
|
||||||
field_names=[
|
field_names=[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"minutes",
|
"minutes",
|
||||||
"contents",
|
"contents",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,19 @@ from vbv_lernwelt.learnpath.utils import get_wagtail_type
|
||||||
|
|
||||||
|
|
||||||
def get_it_serializer_class(model, field_names):
|
def get_it_serializer_class(model, field_names):
|
||||||
|
base_field_names = [
|
||||||
|
"id",
|
||||||
|
"title",
|
||||||
|
"slug",
|
||||||
|
"type",
|
||||||
|
"translation_key",
|
||||||
|
"frontend_url",
|
||||||
|
]
|
||||||
return wagtail_serializers.get_serializer_class(
|
return wagtail_serializers.get_serializer_class(
|
||||||
model, field_names=field_names, meta_fields=[], base=ItBaseSerializer
|
model,
|
||||||
|
field_names=base_field_names + field_names,
|
||||||
|
meta_fields=[],
|
||||||
|
base=ItBaseSerializer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -23,6 +34,7 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
|
||||||
children = SerializerMethodField()
|
children = SerializerMethodField()
|
||||||
course = SerializerMethodField()
|
course = SerializerMethodField()
|
||||||
course_category = CourseCategorySerializer(read_only=True)
|
course_category = CourseCategorySerializer(read_only=True)
|
||||||
|
frontend_url = SerializerMethodField()
|
||||||
|
|
||||||
meta_fields = []
|
meta_fields = []
|
||||||
|
|
||||||
|
|
@ -50,6 +62,11 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
|
||||||
return CourseSerializer(course_parent_page.specific.course).data
|
return CourseSerializer(course_parent_page.specific.course).data
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def get_frontend_url(self, obj):
|
||||||
|
if hasattr(obj, "get_frontend_url"):
|
||||||
|
return obj.get_frontend_url()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _get_descendants(pages, obj):
|
def _get_descendants(pages, obj):
|
||||||
return [c for c in pages if c.path.startswith(obj.path) and c.depth >= obj.depth]
|
return [c for c in pages if c.path.startswith(obj.path) and c.depth >= obj.depth]
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,6 @@ class LearningUnitSerializer(
|
||||||
get_it_serializer_class(
|
get_it_serializer_class(
|
||||||
LearningUnit,
|
LearningUnit,
|
||||||
[
|
[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"course_category",
|
"course_category",
|
||||||
"children",
|
"children",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,32 @@
|
||||||
from django.db import models
|
|
||||||
from wagtail import blocks
|
from wagtail import blocks
|
||||||
from wagtail.admin.panels import FieldPanel
|
|
||||||
from wagtail.documents.blocks import DocumentChooserBlock
|
|
||||||
from wagtail.snippets.models import register_snippet
|
|
||||||
|
|
||||||
|
|
||||||
@register_snippet
|
class MediaLibraryContentBlock(blocks.StructBlock):
|
||||||
class MediaLibraryContent(models.Model):
|
|
||||||
title = models.TextField()
|
|
||||||
description = models.TextField()
|
|
||||||
link_display_text = models.CharField(max_length=255)
|
|
||||||
# TODO: Revisions only work with wagtail 4.0, can not migrate since wagtail localize is not ready yet.
|
|
||||||
# _revisions = GenericRelation("wagtailcore.Revision", related_query_name="media_library_content")
|
|
||||||
|
|
||||||
panels = [
|
|
||||||
FieldPanel("title"),
|
|
||||||
FieldPanel("description"),
|
|
||||||
FieldPanel("link_display_text"),
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def revisions(self):
|
|
||||||
return self._revisions
|
|
||||||
|
|
||||||
|
|
||||||
class AnchorBlock(blocks.PageChooserBlock):
|
|
||||||
"""
|
|
||||||
Verankerung im Lernpfad. Link to a Learning Content.
|
|
||||||
"""
|
|
||||||
|
|
||||||
page_type = "learnpath.LearningUnit"
|
|
||||||
|
|
||||||
|
|
||||||
class LinkBlock(blocks.StructBlock):
|
|
||||||
title = blocks.TextBlock(blank=False, null=False)
|
title = blocks.TextBlock(blank=False, null=False)
|
||||||
description = blocks.TextBlock(default="")
|
description = blocks.TextBlock(default="", required=False)
|
||||||
|
icon_url = blocks.TextBlock(default="", required=False)
|
||||||
link_display_text = blocks.CharBlock(max_length=255, default="Link öffnen")
|
link_display_text = blocks.CharBlock(max_length=255, default="Link öffnen")
|
||||||
url = blocks.URLBlock()
|
url = blocks.TextBlock(default="", required=False)
|
||||||
|
open_window = blocks.BooleanBlock(default=False)
|
||||||
|
page = blocks.PageChooserBlock(
|
||||||
|
page_type=["learnpath.LearningContent"], required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CrossReferenceBlock(blocks.StructBlock):
|
class ExternalLinkBlock(MediaLibraryContentBlock):
|
||||||
title = models.TextField(blank=False, null=False)
|
pass
|
||||||
description = blocks.TextBlock(default="")
|
|
||||||
link_display_text = blocks.CharBlock(max_length=255, default="Link öffnen")
|
|
||||||
category = blocks.PageChooserBlock(page_type="media_library.MediaCategoryPage")
|
class InternalLinkBlock(MediaLibraryContentBlock):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RelativeLinkBlock(MediaLibraryContentBlock):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LearnMediaBlock(MediaLibraryContentBlock):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MediaContentCollection(blocks.StructBlock):
|
class MediaContentCollection(blocks.StructBlock):
|
||||||
|
|
@ -54,10 +37,10 @@ class MediaContentCollection(blocks.StructBlock):
|
||||||
title = blocks.TextBlock()
|
title = blocks.TextBlock()
|
||||||
contents = blocks.StreamBlock(
|
contents = blocks.StreamBlock(
|
||||||
[
|
[
|
||||||
("Links", LinkBlock()),
|
("learn_media", MediaLibraryContentBlock()),
|
||||||
("Documents", DocumentChooserBlock()),
|
("external_link", ExternalLinkBlock()),
|
||||||
("Ankers", AnchorBlock()),
|
("internal_link", InternalLinkBlock()),
|
||||||
("CrossReference", CrossReferenceBlock()),
|
("relative_link", RelativeLinkBlock()),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,17 @@ import json
|
||||||
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
|
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
|
||||||
from vbv_lernwelt.course.models import Course, CoursePage
|
from vbv_lernwelt.course.models import Course, CoursePage
|
||||||
from vbv_lernwelt.media_library.tests.media_library_factories import (
|
from vbv_lernwelt.media_library.tests.media_library_factories import (
|
||||||
create_document_collection,
|
create_external_link_block,
|
||||||
create_link_collection,
|
create_internal_link_block,
|
||||||
create_media_content_link,
|
create_learn_media_block,
|
||||||
LinkBlockFactory,
|
create_media_collection,
|
||||||
|
create_relative_link_block,
|
||||||
|
ExternalLinkBlockFactory,
|
||||||
|
InternalLinkBlockFactory,
|
||||||
|
LearnMediaBlockFactory,
|
||||||
MediaCategoryPageFactory,
|
MediaCategoryPageFactory,
|
||||||
MediaLibraryPageFactory,
|
MediaLibraryPageFactory,
|
||||||
|
RelativeLinkBlockFactory,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,49 +33,210 @@ def create_default_media_library():
|
||||||
]
|
]
|
||||||
for idx, cat in enumerate(course.coursecategory_set.all()):
|
for idx, cat in enumerate(course.coursecategory_set.all()):
|
||||||
overview_icon = icons[(idx + 2) % len(icons)]
|
overview_icon = icons[(idx + 2) % len(icons)]
|
||||||
introduction_text = """
|
|
||||||
Das Auto ist für viele der grösste Stolz! Es birgt aber auch ein grosses Gefahrenpotenzial.
|
if cat.title == "Fahrzeug":
|
||||||
|
media_category = MediaCategoryPageFactory(
|
||||||
|
overview_icon=overview_icon,
|
||||||
|
title=cat.title,
|
||||||
|
course_category=cat,
|
||||||
|
parent=media_lib_page,
|
||||||
|
introduction_text="""
|
||||||
|
Das Auto ist für viele der grösste Stolz. Es birgt aber auch ein grosses Gefahrenpotenzial.
|
||||||
Dabei geht es bei den heutigen Fahrzeugpreisen und Reparaturkosten rasch um namhafte Summen,
|
Dabei geht es bei den heutigen Fahrzeugpreisen und Reparaturkosten rasch um namhafte Summen,
|
||||||
die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.""".strip()
|
die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.
|
||||||
description_title = "Das erwartet dich in diesem Handlungsfeld"
|
""".strip(),
|
||||||
description_text = """
|
items=[
|
||||||
In diesem berufstypischem Handlungsfeld lernst du alles rund um Motorfahrzeugversicherungen,
|
("item", text)
|
||||||
wie man sein Auto optimal schützen kann, wie du vorgehst bei einem Fahrzeugwechsel,
|
for text in [
|
||||||
welche Aspekte du bei einer Offerte beachten musst und wie du dem Kunden die Lösung präsentierst.""".strip()
|
"Motorfahrzeughaftpflichtversicherung",
|
||||||
items = [
|
"Motorfahrzeugkaskoversicherung",
|
||||||
("item", "Motorfahrzeughaftpflichtversicherung"),
|
"Insassenunfallversicherung",
|
||||||
("item", "Motorfahrzeugkaskoversicherung"),
|
"(Verkehrsrechtsschutzversicherung)",
|
||||||
("item", "Insassenunfallversicherung"),
|
"(Fahrzeugassistance)",
|
||||||
]
|
"Überblick und gesetzliche Grundlagen",
|
||||||
body_data = json.dumps(
|
"Versicherungsschutz: versicherte Personen und Sachen, örtlicher Geltungsbereich, versicherte Gefahren, versicherte Schäden, wichtigste Ausschlüsse, Garantie- und Versicherungssumme",
|
||||||
[
|
"Versicherungsleistung",
|
||||||
create_document_collection(),
|
"Bonus-/Malus-System",
|
||||||
create_link_collection(
|
"Mögliche Zusatzversicherungen wie Bonusschutz, Grobfahrlässigkeit, persönliche Effekten, Parkschaden, Scheinwerfer, Ersatzfahrzeug",
|
||||||
links_dict=[
|
"Abgrenzungen zur Hausratversicherung (u.a. einfacher Diebstahl auswärts)",
|
||||||
create_media_content_link(
|
"Abgrenzung zu KVG und UVG",
|
||||||
LinkBlockFactory(
|
"Nutzen Insassenunfall",
|
||||||
title="Nationales Versicherungsbüro",
|
]
|
||||||
url="https://www.vbv.ch/",
|
],
|
||||||
)
|
body=json.dumps(
|
||||||
|
[
|
||||||
|
create_media_collection(
|
||||||
|
title="Lernmedien",
|
||||||
|
contents=[
|
||||||
|
create_learn_media_block(
|
||||||
|
LearnMediaBlockFactory(
|
||||||
|
title="VBV 303/7 Motorfahrzeugkasko",
|
||||||
|
description="PDF",
|
||||||
|
url="/static/media/demo_oktober/07_Motorfahrzeugkaskoversicherung.pdf",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
create_learn_media_block(
|
||||||
|
LearnMediaBlockFactory(
|
||||||
|
title="VBV 303/16 Motorfahrzeughaftpflicht",
|
||||||
|
description="PDF",
|
||||||
|
url="/static/media/demo_oktober/16_Motorfahrzeughaftpflichtversicherung.pdf",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
create_media_content_link(
|
create_media_collection(
|
||||||
LinkBlockFactory(
|
title="Links",
|
||||||
title="Adressen der Strassenverkehrsämter",
|
contents=[
|
||||||
url="https://www.vbv.ch/",
|
create_external_link_block(
|
||||||
)
|
ExternalLinkBlockFactory(
|
||||||
|
title="Nationales Versicherungsbüro",
|
||||||
|
url="https://www.nbi-ngf.ch/de/ngf",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
create_external_link_block(
|
||||||
|
ExternalLinkBlockFactory(
|
||||||
|
title="Adressen der Strassenverkehrsämter",
|
||||||
|
url="https://asa.ch/strassenverkehrsaemter/adressen/",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
create_external_link_block(
|
||||||
|
ExternalLinkBlockFactory(
|
||||||
|
title="Bundesamt für Statistik – Strassenverkehrsunfälle",
|
||||||
|
url="https://www.bfs.admin.ch/bfs/de/home/statistiken/mobilitaet-verkehr/unfaelle-umweltauswirkungen/verkehrsunfaelle/strassenverkehr.html",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
create_external_link_block(
|
||||||
|
ExternalLinkBlockFactory(
|
||||||
|
title="Beratungsstelle für Unfallverhütung – Unfallursachen",
|
||||||
|
url="https://www.bfu.ch/de/dossiers/risiken-im-strassenverkehr",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
create_media_collection(
|
||||||
|
title="Verankerung im Lernpfad",
|
||||||
|
contents=[
|
||||||
|
create_internal_link_block(
|
||||||
|
InternalLinkBlockFactory(
|
||||||
|
title="Rafael kauft einen Ford Mustang",
|
||||||
|
description="Anhand der Story von Rafael Fasel und seinem Ford Mustang lernst du in diesem berufstypischem Handlungsfeld alles rund um Motorfahrzeugversicherungen, wie man sein Auto optimal schützen kann, wie du vorgehst bei einem Fahrzeugwechsel, welche Aspekte du bei einer Offerte beachten musst und wie du dem Kunden die Lösung präsentierst.",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
create_media_collection(
|
||||||
|
title="Querverweise",
|
||||||
|
contents=[
|
||||||
|
create_relative_link_block(
|
||||||
|
RelativeLinkBlockFactory(
|
||||||
|
title="VBV 303/12.3 Verkehrsrechtsschutz",
|
||||||
|
url="/media/versicherungsvermittlerin-media/category/rechtsstreitigkeiten",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
create_relative_link_block(
|
||||||
|
RelativeLinkBlockFactory(
|
||||||
|
title="VBV 303/13 Reiseversicherung",
|
||||||
|
url="/media/versicherungsvermittlerin-media/category/reisen",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
)
|
||||||
)
|
elif cat.title == "Reisen":
|
||||||
media_category = MediaCategoryPageFactory(
|
media_category = MediaCategoryPageFactory(
|
||||||
overview_icon=overview_icon,
|
overview_icon=overview_icon,
|
||||||
title=cat.title,
|
title=cat.title,
|
||||||
course_category=cat,
|
course_category=cat,
|
||||||
parent=media_lib_page,
|
parent=media_lib_page,
|
||||||
introduction_text=introduction_text,
|
introduction_text="""
|
||||||
description_title=description_title,
|
Auf keine Zeit im Jahr freuen wir uns mehr als auf unsere Ferien.
|
||||||
description_text=description_text,
|
Neue Orte, neue Bekanntschaften, neue Erfahrungen oder einfach mal abschalten – es gibt viele Gründe, sich fürs Reisen zu begeistern.
|
||||||
items=items,
|
|
||||||
body=body_data,
|
Bereits während der Vorbereitung und Planung, aber auch während der Reise selbst,
|
||||||
)
|
gehen wir bewusst und unbewusst verschiedene Risiken ein.
|
||||||
|
Diese können negative Folgen verschiedener Art nach sich ziehen, darunter rechtliche, finanzielle oder gesundheitliche Folgen.
|
||||||
|
""".strip(),
|
||||||
|
items=[
|
||||||
|
("item", text)
|
||||||
|
for text in [
|
||||||
|
"Annullierungskosten",
|
||||||
|
"Personenassistance",
|
||||||
|
"Fahrzeugassistance",
|
||||||
|
"Evtl. Reisegepäck",
|
||||||
|
"(Hausratversicherung)",
|
||||||
|
"(Krankenversicherung)",
|
||||||
|
"(Einzelunfallversicherung)",
|
||||||
|
"(Privat- und Verkehrsrechtsschutz)",
|
||||||
|
"Überblick und gesetzliche Grundlagen",
|
||||||
|
"Versicherungsschutz: versicherte Personen, örtlicher Geltungsbereich, versicherte Ereignisse, Ausschlüsse, Garantiesumme, Dauer",
|
||||||
|
"Versicherungsleistung",
|
||||||
|
]
|
||||||
|
],
|
||||||
|
body=json.dumps(
|
||||||
|
[
|
||||||
|
create_media_collection(
|
||||||
|
title="Lernmedien",
|
||||||
|
contents=[
|
||||||
|
create_learn_media_block(
|
||||||
|
LearnMediaBlockFactory(
|
||||||
|
title="VBV 303/13 Reiseversicherung",
|
||||||
|
description="PDF",
|
||||||
|
url="/static/media/demo_oktober/13_Reiseversicherung.pdf",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
create_learn_media_block(
|
||||||
|
LearnMediaBlockFactory(
|
||||||
|
title="Fach-Check «Reisen»",
|
||||||
|
description="Applikation",
|
||||||
|
link_display_text="Zum Fach-Check",
|
||||||
|
url="/static/media/demo_oktober/fach_check_reisen/index.html",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
create_media_collection(
|
||||||
|
title="Verankerung im Lernpfad",
|
||||||
|
contents=[
|
||||||
|
create_internal_link_block(
|
||||||
|
InternalLinkBlockFactory(
|
||||||
|
title="Emma und Ayla campen durch Amerika",
|
||||||
|
description="Begleite Emma Durand und Ayla Yilmaz bei den Vorbereitungen auf ihre grosse Reise durch Amerika und lerne dabei, welche Risiken durch welche Versicherungen abgedeckt werden können.",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
create_media_collection(
|
||||||
|
title="Querverweise",
|
||||||
|
contents=[
|
||||||
|
create_relative_link_block(
|
||||||
|
RelativeLinkBlockFactory(
|
||||||
|
title="VBV 303/03 Hausratversicherung",
|
||||||
|
url="/media/versicherungsvermittlerin-media/category/haushalt",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
create_relative_link_block(
|
||||||
|
RelativeLinkBlockFactory(
|
||||||
|
title="VBV 303/12 Rechtschutzversicherung",
|
||||||
|
url="/media/versicherungsvermittlerin-media/category/rechtsstreitigkeiten",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
create_relative_link_block(
|
||||||
|
RelativeLinkBlockFactory(
|
||||||
|
title="VBV 304/Teil E Obligatorische Krankenversicherung",
|
||||||
|
url="/media/versicherungsvermittlerin-media/category/gesundheit",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
media_category = MediaCategoryPageFactory(
|
||||||
|
overview_icon=overview_icon,
|
||||||
|
title=cat.title,
|
||||||
|
course_category=cat,
|
||||||
|
parent=media_lib_page,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
# Generated by Django 3.2.13 on 2022-09-28 12:51
|
# Generated by Django 3.2.13 on 2022-10-03 14:18
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import taggit.managers
|
import taggit.managers
|
||||||
import wagtail.blocks
|
import wagtail.blocks
|
||||||
import wagtail.documents.blocks
|
|
||||||
import wagtail.fields
|
import wagtail.fields
|
||||||
import wagtail.models.collections
|
import wagtail.models.collections
|
||||||
import wagtail.search.index
|
import wagtail.search.index
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
import vbv_lernwelt.media_library.content_blocks
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
|
@ -19,29 +16,12 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("course", "0001_initial"),
|
("course", "0001_initial"),
|
||||||
("wagtailcore", "0069_log_entry_jsonfield"),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
("taggit", "0004_alter_taggeditem_content_type_alter_taggeditem_tag"),
|
("taggit", "0004_alter_taggeditem_content_type_alter_taggeditem_tag"),
|
||||||
|
("wagtailcore", "0069_log_entry_jsonfield"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
|
||||||
name="MediaLibraryContent",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.BigAutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("title", models.TextField()),
|
|
||||||
("description", models.TextField()),
|
|
||||||
("link_display_text", models.CharField(max_length=255)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="MediaLibraryPage",
|
name="MediaLibraryPage",
|
||||||
fields=[
|
fields=[
|
||||||
|
|
@ -108,7 +88,7 @@ class Migration(migrations.Migration):
|
||||||
wagtail.blocks.StreamBlock(
|
wagtail.blocks.StreamBlock(
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
"Links",
|
"learn_media",
|
||||||
wagtail.blocks.StructBlock(
|
wagtail.blocks.StructBlock(
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
|
|
@ -121,7 +101,15 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"description",
|
"description",
|
||||||
wagtail.blocks.TextBlock(
|
wagtail.blocks.TextBlock(
|
||||||
default=""
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"icon_url",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
@ -133,27 +121,52 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"url",
|
"url",
|
||||||
wagtail.blocks.URLBlock(),
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"open_window",
|
||||||
|
wagtail.blocks.BooleanBlock(
|
||||||
|
default=False
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"page",
|
||||||
|
wagtail.blocks.PageChooserBlock(
|
||||||
|
page_type=[
|
||||||
|
"learnpath.LearningContent"
|
||||||
|
],
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Documents",
|
"external_link",
|
||||||
wagtail.documents.blocks.DocumentChooserBlock(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"Ankers",
|
|
||||||
vbv_lernwelt.media_library.content_blocks.AnchorBlock(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"CrossReference",
|
|
||||||
wagtail.blocks.StructBlock(
|
wagtail.blocks.StructBlock(
|
||||||
[
|
[
|
||||||
|
(
|
||||||
|
"title",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
blank=False,
|
||||||
|
null=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"description",
|
"description",
|
||||||
wagtail.blocks.TextBlock(
|
wagtail.blocks.TextBlock(
|
||||||
default=""
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"icon_url",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
@ -164,11 +177,139 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"category",
|
"url",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"open_window",
|
||||||
|
wagtail.blocks.BooleanBlock(
|
||||||
|
default=False
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"page",
|
||||||
wagtail.blocks.PageChooserBlock(
|
wagtail.blocks.PageChooserBlock(
|
||||||
page_type=[
|
page_type=[
|
||||||
"media_library.MediaCategoryPage"
|
"learnpath.LearningContent"
|
||||||
]
|
],
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"internal_link",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"title",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
blank=False,
|
||||||
|
null=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"icon_url",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"link_display_text",
|
||||||
|
wagtail.blocks.CharBlock(
|
||||||
|
default="Link öffnen",
|
||||||
|
max_length=255,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"url",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"open_window",
|
||||||
|
wagtail.blocks.BooleanBlock(
|
||||||
|
default=False
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"page",
|
||||||
|
wagtail.blocks.PageChooserBlock(
|
||||||
|
page_type=[
|
||||||
|
"learnpath.LearningContent"
|
||||||
|
],
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"relative_link",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"title",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
blank=False,
|
||||||
|
null=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"icon_url",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"link_display_text",
|
||||||
|
wagtail.blocks.CharBlock(
|
||||||
|
default="Link öffnen",
|
||||||
|
max_length=255,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"url",
|
||||||
|
wagtail.blocks.TextBlock(
|
||||||
|
default="",
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"open_window",
|
||||||
|
wagtail.blocks.BooleanBlock(
|
||||||
|
default=False
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"page",
|
||||||
|
wagtail.blocks.PageChooserBlock(
|
||||||
|
page_type=[
|
||||||
|
"learnpath.LearningContent"
|
||||||
|
],
|
||||||
|
required=False,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,14 @@ class MediaLibraryPage(Page):
|
||||||
)
|
)
|
||||||
super(MediaLibraryPage, self).full_clean(*args, **kwargs)
|
super(MediaLibraryPage, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_frontend_url(self):
|
||||||
|
return f"/media/{self.slug}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_serializer_class(cls):
|
def get_serializer_class(cls):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(
|
||||||
cls,
|
cls,
|
||||||
[
|
[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"course",
|
"course",
|
||||||
"children",
|
"children",
|
||||||
],
|
],
|
||||||
|
|
@ -82,20 +80,19 @@ class MediaCategoryPage(Page):
|
||||||
|
|
||||||
def full_clean(self, *args, **kwargs):
|
def full_clean(self, *args, **kwargs):
|
||||||
self.slug = find_available_slug(
|
self.slug = find_available_slug(
|
||||||
slugify(f"{self.get_parent()}-cat-{self.title}", allow_unicode=True)
|
slugify(f"{self.get_parent().slug}-cat-{self.title}", allow_unicode=True)
|
||||||
)
|
)
|
||||||
super(MediaCategoryPage, self).full_clean(*args, **kwargs)
|
super(MediaCategoryPage, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_frontend_url(self):
|
||||||
|
short_slug = self.slug.replace(f"{self.get_parent().slug}-cat-", "")
|
||||||
|
return f"{self.get_parent().specific.get_frontend_url()}/category/{short_slug}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_serializer_class(cls):
|
def get_serializer_class(cls):
|
||||||
return get_it_serializer_class(
|
return get_it_serializer_class(
|
||||||
cls,
|
cls,
|
||||||
field_names=[
|
field_names=[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"course_category",
|
"course_category",
|
||||||
"introduction_text",
|
"introduction_text",
|
||||||
"overview_icon",
|
"overview_icon",
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import wagtail_factories
|
import wagtail_factories
|
||||||
|
|
||||||
from vbv_lernwelt.media_library.content_blocks import (
|
from vbv_lernwelt.media_library.content_blocks import (
|
||||||
AnchorBlock,
|
ExternalLinkBlock,
|
||||||
CrossReferenceBlock,
|
InternalLinkBlock,
|
||||||
LinkBlock,
|
LearnMediaBlock,
|
||||||
MediaContentCollection,
|
RelativeLinkBlock,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.media_library.models import (
|
from vbv_lernwelt.media_library.models import (
|
||||||
LibraryDocument,
|
LibraryDocument,
|
||||||
|
|
@ -30,57 +31,102 @@ class MediaLibraryPageFactory(wagtail_factories.PageFactory):
|
||||||
model = MediaLibraryPage
|
model = MediaLibraryPage
|
||||||
|
|
||||||
|
|
||||||
class AnchorBlockFactory(wagtail_factories.StructBlockFactory):
|
class LearnMediaBlockFactory(wagtail_factories.StructBlockFactory):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AnchorBlock
|
model = LearnMediaBlock
|
||||||
|
|
||||||
|
title = "Platzhalter PDF"
|
||||||
|
icon_url = "/static/icons/demo/icon-hf-book.png"
|
||||||
|
description = "Buch «Platzhalter» – Kapitel 16"
|
||||||
|
link_display_text = "PDF anzeigen"
|
||||||
|
url = "/static/media/documents/01a_Motorfahrzeughaftpflicht.pdf"
|
||||||
|
open_window = True
|
||||||
|
|
||||||
|
|
||||||
class LinkBlockFactory(wagtail_factories.StructBlockFactory):
|
def create_learn_media_block(link_block=None):
|
||||||
title = "Interesting link"
|
if link_block is None:
|
||||||
|
link_block = LearnMediaBlockFactory()
|
||||||
|
return {
|
||||||
|
"id": str(uuid.uuid4()),
|
||||||
|
"type": "learn_media",
|
||||||
|
"value": dict(link_block.items()),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ExternalLinkBlockFactory(wagtail_factories.StructBlockFactory):
|
||||||
|
class Meta:
|
||||||
|
model = ExternalLinkBlock
|
||||||
|
|
||||||
|
title = "Platzhalter externer Link"
|
||||||
description = "This link is really interesting..."
|
description = "This link is really interesting..."
|
||||||
url = "https://www.vbv.ch/"
|
url = "https://www.vbv.ch/"
|
||||||
|
open_window = True
|
||||||
class Meta:
|
link_display_text = "Link öffnen"
|
||||||
model = LinkBlock
|
|
||||||
|
|
||||||
|
|
||||||
class CrossReferenceBlockFactory(wagtail_factories.StructBlockFactory):
|
def create_external_link_block(link_block=None):
|
||||||
class Meta:
|
|
||||||
model = CrossReferenceBlock
|
|
||||||
|
|
||||||
|
|
||||||
class MediaContentCollectionFactory(wagtail_factories.StructBlockFactory):
|
|
||||||
title = "Links"
|
|
||||||
contents = wagtail_factories.StreamFieldFactory(
|
|
||||||
{"Links": LinkBlockFactory, "Documents": LibraryDocumentFactory}
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = MediaContentCollection
|
|
||||||
|
|
||||||
|
|
||||||
class MediaCategoryPageFactory(wagtail_factories.PageFactory):
|
|
||||||
title = "Fahrzeug"
|
|
||||||
introduction_text = "Das Auto ist für viele der grösste Stolz! Es birgt aber ..."
|
|
||||||
description_title = "Das erwartet dich in diesem Handlungsfeld"
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = MediaCategoryPage
|
|
||||||
|
|
||||||
|
|
||||||
def create_media_content_link(link_block=None):
|
|
||||||
if link_block is None:
|
if link_block is None:
|
||||||
link_block = LinkBlockFactory()
|
link_block = ExternalLinkBlockFactory()
|
||||||
return {"id": str(uuid.uuid4()), "type": "Links", "value": dict(link_block.items())}
|
return {
|
||||||
|
"id": str(uuid.uuid4()),
|
||||||
|
"type": "external_link",
|
||||||
|
"value": dict(link_block.items()),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def create_link_collection(links_dict=None):
|
class InternalLinkBlockFactory(wagtail_factories.StructBlockFactory):
|
||||||
|
class Meta:
|
||||||
|
model = InternalLinkBlock
|
||||||
|
|
||||||
|
title = "Platzhalter interner Link"
|
||||||
|
description = "Link to a Learning Content"
|
||||||
|
link_display_text = "Lerneinheit anzeigen"
|
||||||
|
url = "/learn/versicherungsvermittlerin-lp/basis/einleitung-circle-basis"
|
||||||
|
# TODO: page = blocks.PageChooserBlock mit Titel etc
|
||||||
|
|
||||||
|
|
||||||
|
def create_internal_link_block(link_block=None):
|
||||||
|
if link_block is None:
|
||||||
|
link_block = InternalLinkBlockFactory()
|
||||||
|
return {
|
||||||
|
"id": str(uuid.uuid4()),
|
||||||
|
"type": "internal_link",
|
||||||
|
"value": dict(link_block.items()),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RelativeLinkBlockFactory(wagtail_factories.StructBlockFactory):
|
||||||
|
class Meta:
|
||||||
|
model = RelativeLinkBlock
|
||||||
|
|
||||||
|
title = "Platzhalter Querverweis"
|
||||||
|
description = "Lernmedium: Verkehrsrechtsschutz – Buch «Sach- und Vermögensversicherungen/Kapitel 12.3»"
|
||||||
|
link_display_text = "Handlungsfeld anzeigen"
|
||||||
|
icon_url = "/static/icons/demo/icon-hf-reisen.svg"
|
||||||
|
url = "/media/versicherungsvermittlerin-media/category/fahrzeug"
|
||||||
|
# TODO: page = blocks.PageChooserBlock zu Handlungsfeld
|
||||||
|
|
||||||
|
|
||||||
|
def create_relative_link_block(link_block=None):
|
||||||
|
if link_block is None:
|
||||||
|
link_block = RelativeLinkBlockFactory()
|
||||||
|
return {
|
||||||
|
"id": str(uuid.uuid4()),
|
||||||
|
"type": "relative_link",
|
||||||
|
"value": dict(link_block.items()),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_media_collection(title, contents=None):
|
||||||
|
if contents is None:
|
||||||
|
contents = []
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": str(uuid.uuid4()),
|
"id": str(uuid.uuid4()),
|
||||||
"type": "content_collection",
|
"type": "content_collection",
|
||||||
"value": {
|
"value": {
|
||||||
"title": "Links",
|
"title": title,
|
||||||
"contents": [link_dict for link_dict in links_dict],
|
"contents": [content_dict for content_dict in contents],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,3 +146,49 @@ def create_document_collection(document_ids=None):
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MediaCategoryPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = "Fahrzeug (Platzhalter)"
|
||||||
|
introduction_text = """
|
||||||
|
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.
|
||||||
|
Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
||||||
|
Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.
|
||||||
|
Nulla consequat massa quis enim. Donec.
|
||||||
|
""".strip()
|
||||||
|
description_title = "Das erwartet dich in diesem Handlungsfeld"
|
||||||
|
description_text = """
|
||||||
|
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.
|
||||||
|
Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
||||||
|
Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.
|
||||||
|
Nulla consequat massa quis enim. Donec.
|
||||||
|
""".strip()
|
||||||
|
overview_icon = "icon-hf-fahrzeug"
|
||||||
|
body = json.dumps(
|
||||||
|
[
|
||||||
|
create_media_collection(
|
||||||
|
title="Lernmedien",
|
||||||
|
contents=[create_learn_media_block() for _ in range(8)],
|
||||||
|
),
|
||||||
|
create_media_collection(
|
||||||
|
title="Links",
|
||||||
|
contents=[create_external_link_block() for _ in range(4)],
|
||||||
|
),
|
||||||
|
create_media_collection(
|
||||||
|
title="Links",
|
||||||
|
contents=[create_internal_link_block() for _ in range(3)],
|
||||||
|
),
|
||||||
|
create_media_collection(
|
||||||
|
title="Querverweise",
|
||||||
|
contents=[create_relative_link_block() for _ in range(2)],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
items = [
|
||||||
|
("item", "Versicherung 1"),
|
||||||
|
("item", "Versicherung 2"),
|
||||||
|
("item", "Versicherung 3"),
|
||||||
|
]
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MediaCategoryPage
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 362 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 986 B |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 261 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 235 B |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 273 B |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 468 B |
|
After Width: | Height: | Size: 247 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 42 B |
|
|
@ -0,0 +1,46 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||||
|
<script src="js/underscore.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
|
||||||
|
<link rel="stylesheet" type="text/css" media="all" href="css/style.css" />
|
||||||
|
<script>
|
||||||
|
setTimeout(function() {
|
||||||
|
window.print();
|
||||||
|
}, 1000 );
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="certificate">
|
||||||
|
<div class="certification-header">
|
||||||
|
<div class="logo">
|
||||||
|
<div class="logo_text"></div>
|
||||||
|
<img src="images/header.png" class="bgHeader">
|
||||||
|
<a target="_blank" href="http://education.crealogix.com/clxtestpool/autoren-tool-clxtestpool/">
|
||||||
|
<img height="60px" src="images/logo.png" class="logo" alt="logo" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<h1>TITLE</h1>
|
||||||
|
</div>
|
||||||
|
<div class="certification-body">
|
||||||
|
<div class="certification-textarea">
|
||||||
|
<h2>TITLE</h2>
|
||||||
|
<div class="overview">OVERVIEW</div>
|
||||||
|
<div class="passedText">PASSED TEXT</div>
|
||||||
|
<div class="table-container">QUESTION TABLE</div>
|
||||||
|
<div class="certification-footer">
|
||||||
|
<div class="footerBg"></div>
|
||||||
|
<div class="date">DATE</div>
|
||||||
|
<div class="link">
|
||||||
|
<a target="_blank" href="https://www.vbv.ch/">© VBV</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DOM is ready here -->
|
||||||
|
<script src="js/certificate.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,207 @@
|
||||||
|
body {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#certificate {
|
||||||
|
background-color: #FFF;
|
||||||
|
font-family: Arial, Verdana;
|
||||||
|
margin: 10px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 13px;
|
||||||
|
max-width: 768px;
|
||||||
|
min-width: 768px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certification-header {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certification-header h1 {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
font-family: 'Arial';
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
padding-top: 26px;
|
||||||
|
color: #FFF;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certification-header h2 {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 33px;
|
||||||
|
font-family: 'Arial';
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 0 0 14px 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo .bgHeader {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 0;
|
||||||
|
width: 768px;
|
||||||
|
background-color: #00224d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certification-textarea {
|
||||||
|
padding-top: 100px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview, .certification-footer {
|
||||||
|
font-family: Arial, Verdana;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.certification-body h2 {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.overview .column-1 {
|
||||||
|
float: left;
|
||||||
|
width: 35%;
|
||||||
|
}
|
||||||
|
.overview .column-2 {
|
||||||
|
float: left;
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
.overview .column-3 {
|
||||||
|
float: left;
|
||||||
|
text-align: right;
|
||||||
|
width: 20%;
|
||||||
|
|
||||||
|
}
|
||||||
|
.overview .column-4 {
|
||||||
|
float: left;
|
||||||
|
text-align: right;
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
border-bottom: 1px solid rgb(196, 196, 196);
|
||||||
|
margin: 15px 0 8px 0;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
font-weight: bold;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header.chapter-header {
|
||||||
|
border-bottom: 1px solid rgb(196, 196, 196);
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-footer {
|
||||||
|
height: auto;
|
||||||
|
overflow: auto;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 5px;
|
||||||
|
border-top: 1px solid rgb(196, 196, 196);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-footer .column-1 {
|
||||||
|
float: right;
|
||||||
|
width: 20%;
|
||||||
|
text-align: right;
|
||||||
|
word-break: break-all;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-footer .column-2 {
|
||||||
|
float: right;
|
||||||
|
width: 20%;
|
||||||
|
word-break: break-all;
|
||||||
|
text-align: right;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-row {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header .column-1,
|
||||||
|
.table-row .column-1 {
|
||||||
|
text-align: left;
|
||||||
|
float: left;
|
||||||
|
width: 7%;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.table-header .column-2,
|
||||||
|
.table-row .column-2 {
|
||||||
|
float: left;
|
||||||
|
width: 53%;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.table-header .column-3,
|
||||||
|
.table-row .column-3 {
|
||||||
|
float: left;
|
||||||
|
text-align: right;
|
||||||
|
width: 20%;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header .column-4,
|
||||||
|
.table-row .column-4 {
|
||||||
|
float: left;
|
||||||
|
text-align: right;
|
||||||
|
width: 20%;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 1px dashed gray;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certification-footer {
|
||||||
|
position: relative;
|
||||||
|
border-top: 1px solid rgb(196, 196, 196);
|
||||||
|
padding-top: 7px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
.certification-footer .date {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 5px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.certification-footer .link {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
.certification-footer .link a,
|
||||||
|
.certification-footer .link a:hover {
|
||||||
|
color: black;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certification-footer .footerBg {
|
||||||
|
border-bottom: 40px solid white;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||