Merge branch 'feature/competence-circles' into develop
This commit is contained in:
commit
a736d22b21
|
|
@ -106,9 +106,9 @@ const profileDropdownData = [
|
||||||
<nav class="px-8 py-2 mx-auto lg:flex lg:justify-start lg:items-center lg:py-4">
|
<nav class="px-8 py-2 mx-auto lg:flex lg:justify-start lg:items-center lg:py-4">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<a href="https://www.vbv.ch" class="flex">
|
<router-link to="/" class="flex">
|
||||||
<it-icon-vbv class="h-8 w-16 mr-3 -mt-6 -ml-3" />
|
<it-icon-vbv class="h-8 w-16 mr-3 -mt-6 -ml-3" />
|
||||||
</a>
|
</router-link>
|
||||||
<router-link to="/" class="flex">
|
<router-link to="/" class="flex">
|
||||||
<div class="text-white text-2xl pr-10 pl-3 ml-1 border-l border-white">
|
<div class="text-white text-2xl pr-10 pl-3 ml-1 border-l border-white">
|
||||||
myVBV
|
myVBV
|
||||||
|
|
|
||||||
|
|
@ -40,23 +40,28 @@ const clickLink = (to: string) => {
|
||||||
class="mt-2 inline-block flex items-center"
|
class="mt-2 inline-block flex items-center"
|
||||||
@click="clickLink('/profile')"
|
@click="clickLink('/profile')"
|
||||||
>
|
>
|
||||||
<IconSettings class="inline-block" /><span class="ml-3"
|
<IconSettings class="inline-block" />
|
||||||
>Kontoeinstellungen</span
|
<span class="ml-3"> Kontoeinstellungen </span>
|
||||||
>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="learningPathName" class="mt-6 pb-6 border-b border-gray-500">
|
<div v-if="true" class="mt-6 pb-6 border-b border-gray-500">
|
||||||
<h4 class="text-gray-900 text-sm">Kurs: {{ learningPathName }}</h4>
|
<h4 class="text-gray-900 text-sm">Kurs: Versicherungsvermittler/in</h4>
|
||||||
<ul class="mt-6">
|
<ul class="mt-6">
|
||||||
<li>
|
<li>
|
||||||
<button @click="clickLink(`/learningpath/${learningPathSlug}`)">
|
<button @click="clickLink(`/learn/versicherungsvermittlerin-lp`)">
|
||||||
Lernpfad
|
Lernpfad
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="mt-6">Kompetenzprofil</li>
|
<li class="mt-6">
|
||||||
|
<button
|
||||||
|
@click="clickLink(`/competence/versicherungsvermittlerin-competence`)"
|
||||||
|
>
|
||||||
|
Kompetenzprofil
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-6 pb-6 border-b border-gray-500">
|
<div class="mt-6 pb-6 border-b border-gray-500">
|
||||||
|
|
@ -75,7 +80,8 @@ const clickLink = (to: string) => {
|
||||||
class="mt-6 items-center flex"
|
class="mt-6 items-center flex"
|
||||||
@click="userStore.handleLogout()"
|
@click="userStore.handleLogout()"
|
||||||
>
|
>
|
||||||
<IconLogout class="inline-block" /><span class="ml-1">Abmelden</span>
|
<IconLogout class="inline-block" />
|
||||||
|
<span class="ml-1">Abmelden</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,16 @@ const togglePerformanceCriteria = () => {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ComptenceProgress
|
<ComptenceProgress
|
||||||
:status-count="competenceStore.calcStatusCount(competence.children)"
|
:status-count="
|
||||||
|
competenceStore.calcStatusCount(
|
||||||
|
competenceStore.criteriaByCompetence(competence)
|
||||||
|
)
|
||||||
|
"
|
||||||
></ComptenceProgress>
|
></ComptenceProgress>
|
||||||
</div>
|
</div>
|
||||||
<ul v-if="isOpen">
|
<ul v-if="isOpen">
|
||||||
<li
|
<li
|
||||||
v-for="performanceCriteria in competence.children"
|
v-for="performanceCriteria in competenceStore.criteriaByCompetence(competence)"
|
||||||
:key="performanceCriteria.id"
|
:key="performanceCriteria.id"
|
||||||
class="mb-4 pb-4 border-b border-gray-500"
|
class="mb-4 pb-4 border-b border-gray-500"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -30,16 +30,16 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
</h4>
|
</h4>
|
||||||
<p>
|
<p>
|
||||||
Lerneinheit:
|
Lerneinheit:
|
||||||
<a class="link" :href="criteria.learning_unit.frontend_url">
|
<router-link class="link" :to="criteria.learning_unit.frontend_url">
|
||||||
{{ criteria.learning_unit.title }}
|
{{ criteria.learning_unit.title }}
|
||||||
</a>
|
</router-link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="whitespace-nowrap">
|
<span class="whitespace-nowrap">
|
||||||
<a class="link" :href="criteria.learning_unit.evaluate_url">
|
<router-link class="link" :to="criteria.learning_unit.evaluate_url">
|
||||||
Sich nochmals einschätzen
|
Sich nochmals einschätzen
|
||||||
</a>
|
</router-link>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ function render() {
|
||||||
translate = [translate[0], translate[1] + 20];
|
translate = [translate[0], translate[1] + 20];
|
||||||
return "translate(" + translate + ")";
|
return "translate(" + translate + ")";
|
||||||
})
|
})
|
||||||
.attr("class", "circlesText text-xl font-bold")
|
.attr("class", "circlesText text-large font-bold")
|
||||||
.style("text-anchor", "middle");
|
.style("text-anchor", "middle");
|
||||||
|
|
||||||
const iconWidth = 25;
|
const iconWidth = 25;
|
||||||
|
|
@ -194,7 +194,8 @@ function render() {
|
||||||
let translate = wedgeGenerator.centroid(d);
|
let translate = wedgeGenerator.centroid(d);
|
||||||
translate = [translate[0] - iconWidth / 2, translate[1] - iconWidth];
|
translate = [translate[0] - iconWidth / 2, translate[1] - iconWidth];
|
||||||
return "translate(" + translate + ")";
|
return "translate(" + translate + ")";
|
||||||
});
|
})
|
||||||
|
.attr("class", "filter-blue-900");
|
||||||
|
|
||||||
// Create Arrows
|
// Create Arrows
|
||||||
const arrow = d3
|
const arrow = d3
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const props = defineProps<{
|
||||||
<div v-if="circle" class="container-medium">
|
<div v-if="circle" class="container-medium">
|
||||||
<h1 class="">Überblick: Circle "{{ circle.title }}"</h1>
|
<h1 class="">Überblick: Circle "{{ circle.title }}"</h1>
|
||||||
|
|
||||||
<p class="mt-8 text-xl">
|
<p class="mt-8 text-large">
|
||||||
Hier zeigen wir dir, was du in diesem Circle lernen wirst.
|
Hier zeigen wir dir, was du in diesem Circle lernen wirst.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ const props = defineProps<{
|
||||||
<li
|
<li
|
||||||
v-for="goal in circle.goals"
|
v-for="goal in circle.goals"
|
||||||
:key="goal.id"
|
:key="goal.id"
|
||||||
class="text-xl flex items-center"
|
class="text-large flex items-center"
|
||||||
>
|
>
|
||||||
<it-icon-check
|
<it-icon-check
|
||||||
class="mt-4 hidden lg:block w-12 h-12 text-sky-500 flex-none"
|
class="mt-4 hidden lg:block w-12 h-12 text-sky-500 flex-none"
|
||||||
|
|
@ -45,7 +45,7 @@ const props = defineProps<{
|
||||||
<li
|
<li
|
||||||
v-for="jobSituation in circle.job_situations"
|
v-for="jobSituation in circle.job_situations"
|
||||||
:key="jobSituation.id"
|
:key="jobSituation.id"
|
||||||
class="job-situation border border-gray-500 p-4 text-xl flex items-center"
|
class="job-situation border border-gray-500 p-4 text-large flex items-center"
|
||||||
>
|
>
|
||||||
{{ jobSituation.value }}
|
{{ jobSituation.value }}
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,12 @@ import { useCircleStore } from "@/stores/circle";
|
||||||
import type { LearningContent } from "@/types";
|
import type { LearningContent } from "@/types";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
log.debug("LearningContent.vue setup");
|
log.debug("LearningContent.vue setup");
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
const circleStore = useCircleStore();
|
const circleStore = useCircleStore();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
@ -28,7 +31,7 @@ const block = computed(() => {
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn-text inline-flex items-center px-3 py-2 font-normal"
|
class="btn-text inline-flex items-center px-3 py-2"
|
||||||
data-cy="close-learning-content"
|
data-cy="close-learning-content"
|
||||||
@click="circleStore.closeLearningContent(props.learningContent)"
|
@click="circleStore.closeLearningContent(props.learningContent)"
|
||||||
>
|
>
|
||||||
|
|
@ -36,7 +39,7 @@ const block = computed(() => {
|
||||||
<span class="hidden lg:inline">zurück zum Circle</span>
|
<span class="hidden lg:inline">zurück zum Circle</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<h1 class="text-xl hidden lg:block" data-cy="ln-title">
|
<h1 class="text-large hidden lg:block" data-cy="ln-title">
|
||||||
{{ learningContent.title }}
|
{{ learningContent.title }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|
@ -70,18 +73,21 @@ const block = computed(() => {
|
||||||
<div v-else-if="block.type === 'media_library'" class="mt-4 lg:mt-12">
|
<div v-else-if="block.type === 'media_library'" class="mt-4 lg:mt-12">
|
||||||
<h1>{{ learningContent.title }}</h1>
|
<h1>{{ learningContent.title }}</h1>
|
||||||
|
|
||||||
<p class="text-xl my-4 lg:my-8">{{ block.value.description }}</p>
|
<p class="text-large my-4 lg:my-8">{{ block.value.description }}</p>
|
||||||
<a :href="block.value.url" target="_blank" class="button btn-primary">
|
<router-link
|
||||||
|
:to="`${block.value.url}?back=${route.path}`"
|
||||||
|
class="button btn-primary"
|
||||||
|
>
|
||||||
Mediathek öffnen
|
Mediathek öffnen
|
||||||
</a>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="block.type === 'placeholder'" class="mt-4 lg:mt-12">
|
<div v-else-if="block.type === 'placeholder'" class="mt-4 lg:mt-12">
|
||||||
<p class="text-xl my-4">{{ block.value.description }}</p>
|
<p class="text-large my-4">{{ block.value.description }}</p>
|
||||||
<h1>{{ learningContent.title }}</h1>
|
<h1>{{ learningContent.title }}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="text-xl my-4">{{ block.value.description }}</div>
|
<div v-else class="text-large my-4">{{ block.value.description }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -89,14 +89,16 @@ export default {
|
||||||
mounted() {
|
mounted() {
|
||||||
log.debug("LearningPathDiagram mounted");
|
log.debug("LearningPathDiagram mounted");
|
||||||
|
|
||||||
|
const circleWidth = this.vertical ? 60 : 200;
|
||||||
|
const radius = (circleWidth * 0.8) / 2;
|
||||||
|
|
||||||
if (this.vertical) {
|
if (this.vertical) {
|
||||||
this.width = Math.min(960, window.innerWidth - 32);
|
this.width = Math.min(960, window.innerWidth - 32);
|
||||||
this.height = 860;
|
this.height = 860;
|
||||||
|
} else {
|
||||||
|
this.width = circleWidth * this.circles.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
const circleWidth = this.vertical ? 60 : 200;
|
|
||||||
const radius = (circleWidth * 0.8) / 2;
|
|
||||||
|
|
||||||
function getColor(d) {
|
function getColor(d) {
|
||||||
let color = colors.gray[300];
|
let color = colors.gray[300];
|
||||||
if (d.someFinished) {
|
if (d.someFinished) {
|
||||||
|
|
@ -208,7 +210,7 @@ export default {
|
||||||
for (let index = 0; index < i; index++) {
|
for (let index = 0; index < i; index++) {
|
||||||
x += circleWidth * topics[index].circles.length;
|
x += circleWidth * topics[index].circles.length;
|
||||||
}
|
}
|
||||||
return x + 30;
|
return x + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTopicVerticalPosition(i, d, topics) {
|
function getTopicVerticalPosition(i, d, topics) {
|
||||||
|
|
@ -266,7 +268,7 @@ export default {
|
||||||
|
|
||||||
if (this.vertical) {
|
if (this.vertical) {
|
||||||
const Circles_X = radius;
|
const Circles_X = radius;
|
||||||
const Topics_X = Circles_X - radius;
|
const Topics_X = Circles_X - circleWidth;
|
||||||
|
|
||||||
circle_groups.attr("transform", (d, i) => {
|
circle_groups.attr("transform", (d, i) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -302,7 +304,7 @@ export default {
|
||||||
topicTitles.attr("y", 30);
|
topicTitles.attr("y", 30);
|
||||||
} else {
|
} else {
|
||||||
circle_groups.attr("transform", (d, i) => {
|
circle_groups.attr("transform", (d, i) => {
|
||||||
const x_coord = (i + 1) * circleWidth - radius;
|
const x_coord = (i + 1) * circleWidth - circleWidth / 2;
|
||||||
return "translate(" + x_coord + ", 200)";
|
return "translate(" + x_coord + ", 200)";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -386,7 +388,7 @@ export default {
|
||||||
<div class="svg-container h-full content-start">
|
<div class="svg-container h-full content-start">
|
||||||
<svg
|
<svg
|
||||||
:id="identifier"
|
:id="identifier"
|
||||||
class="learning-path-visualization h-full"
|
class="learning-path-visualization h-full -mt-6 lg:mt-0"
|
||||||
:viewBox="viewBox"
|
:viewBox="viewBox"
|
||||||
></svg>
|
></svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ const learningSequenceBorderClass = computed(() => {
|
||||||
<div :id="learningSequence.slug" class="mb-8 learning-sequence">
|
<div :id="learningSequence.slug" class="mb-8 learning-sequence">
|
||||||
<div class="flex items-center gap-4 mb-2 text-blue-900">
|
<div class="flex items-center gap-4 mb-2 text-blue-900">
|
||||||
<component :is="learningSequence.icon" />
|
<component :is="learningSequence.icon" />
|
||||||
<h3 class="text-xl font-semibold">
|
<h3 class="text-large font-semibold">
|
||||||
{{ learningSequence.title }}
|
{{ learningSequence.title }}
|
||||||
</h3>
|
</h3>
|
||||||
<div>{{ humanizeDuration(learningSequence.minutes) }}</div>
|
<div>{{ humanizeDuration(learningSequence.minutes) }}</div>
|
||||||
|
|
|
||||||
|
|
@ -38,14 +38,14 @@ function handleContinue() {
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn-text inline-flex items-center px-3 py-2 font-normal"
|
class="btn-text inline-flex items-center px-3 py-2"
|
||||||
@click="circleStore.closeSelfEvaluation(props.learningUnit)"
|
@click="circleStore.closeSelfEvaluation(props.learningUnit)"
|
||||||
>
|
>
|
||||||
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
||||||
<span class="hidden lg:inline">zurück zum Circle</span>
|
<span class="hidden lg:inline">zurück zum Circle</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<h1 class="text-xl hidden lg:block" data-cy="ln-title">
|
<h1 class="text-large hidden lg:block" data-cy="ln-title">
|
||||||
Selbsteinschätzung {{ learningUnit.title }}
|
Selbsteinschätzung {{ learningUnit.title }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ function handleContinue() {
|
||||||
Schritt {{ state.questionIndex + 1 }} von {{ questions.length }}
|
Schritt {{ state.questionIndex + 1 }} von {{ questions.length }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-xl mt-4">
|
<p class="text-large mt-4">
|
||||||
Überprüfe, ob du in der Lernheinheit
|
Überprüfe, ob du in der Lernheinheit
|
||||||
<span class="font-bold">"{{ learningUnit.title }}"</span> alles verstanden
|
<span class="font-bold">"{{ learningUnit.title }}"</span> alles verstanden
|
||||||
hast.<br />
|
hast.<br />
|
||||||
|
|
@ -88,7 +88,7 @@ function handleContinue() {
|
||||||
@click="circleStore.markCompletion(currentQuestion, 'success')"
|
@click="circleStore.markCompletion(currentQuestion, 'success')"
|
||||||
>
|
>
|
||||||
<it-icon-smiley-happy class="w-16 h-16 mr-4"></it-icon-smiley-happy>
|
<it-icon-smiley-happy class="w-16 h-16 mr-4"></it-icon-smiley-happy>
|
||||||
<span class="font-bold text-xl"> Ja, ich kann das. </span>
|
<span class="font-bold text-large"> Ja, ich kann das. </span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="flex-1 inline-flex items-center text-left p-4 border"
|
class="flex-1 inline-flex items-center text-left p-4 border"
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
<div class="bg-white p-8 flex justify-between">
|
<div class="bg-white p-8 flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="mb-4">{{ title }}</h3>
|
<h3 class="mb-4">{{ title }}</h3>
|
||||||
<p class="mb-4 text-xl">{{ description }}</p>
|
<p class="mb-4">{{ description }}</p>
|
||||||
<router-link :to="link" class="text-xl inline-flex items-center font-normal">
|
<router-link :to="link" class="btn-text inline-flex items-center pl-0 pr-3 py-2">
|
||||||
<span class="inline">{{ call2Action }}</span>
|
<span class="inline">{{ call2Action }}</span>
|
||||||
<it-icon-arrow-right class="ml-1 h-5 w-5"></it-icon-arrow-right>
|
<it-icon-arrow-right class="ml-1 h-5 w-5"></it-icon-arrow-right>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const emit = defineEmits<{
|
||||||
leave-to-class="transform scale-95 opacity-0"
|
leave-to-class="transform scale-95 opacity-0"
|
||||||
>
|
>
|
||||||
<MenuItems
|
<MenuItems
|
||||||
class="absolute mt-2 px-6 w-56 origin-top-right divide-y divide-gray-500 bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
|
class="absolute mt-2 px-6 w-56 w-max-full origin-top-right divide-y divide-gray-500 bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||||
:class="[align === 'left' ? 'left-0' : 'right-0']"
|
:class="[align === 'left' ? 'left-0' : 'right-0']"
|
||||||
>
|
>
|
||||||
<div v-for="section in listItems" :key="section" class="">
|
<div v-for="section in listItems" :key="section" class="">
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ const dropdownSelected = computed({
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Listbox v-model="dropdownSelected" as="div">
|
<Listbox v-model="dropdownSelected" as="div">
|
||||||
<div class="mt-1 relative w-96">
|
<div class="mt-1 relative w-full">
|
||||||
<ListboxButton
|
<ListboxButton
|
||||||
class="bg-white relative w-full border border-gray-500 pl-5 pr-10 py-3 text-left cursor-default font-bold"
|
class="bg-white relative w-full border border-gray-500 pl-5 pr-10 py-3 text-left cursor-default font-bold"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ onMounted(() => {
|
||||||
appElement = document.getElementById("app");
|
appElement = document.getElementById("app");
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted( () => removeNoScroll())
|
onUnmounted(() => removeNoScroll());
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
removeNoScroll();
|
removeNoScroll();
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@ if (url.charAt(url.length - 1) !== "/") {
|
||||||
<template>
|
<template>
|
||||||
<main class="px-4 py-8">
|
<main class="px-4 py-8">
|
||||||
<h1>404 - Not Found as Vue view...</h1>
|
<h1>404 - Not Found as Vue view...</h1>
|
||||||
<div class="text-xl mt-8">Add trailing slash for django view?</div>
|
|
||||||
<div class="mt-8 text-xl">
|
|
||||||
Try this: <a class="link" :href="url">{{ url }}</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ const userStore = useUserStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="px-8 py-8 lg:px-12 lg:py-12 bg-gray-200">
|
<main class="py-4 lg:px-12 lg:py-12 bg-gray-200">
|
||||||
<div class="container-medium">
|
<div class="container-medium">
|
||||||
<h1 data-cy="welcome-message">Willkommen, {{ userStore.first_name }}</h1>
|
<h1 data-cy="welcome-message">Willkommen, {{ userStore.first_name }}</h1>
|
||||||
|
|
||||||
<h2 class="mt-12">Deine Kurse</h2>
|
<h2 class="mt-12">Deine Kurse</h2>
|
||||||
|
|
||||||
<div class="mt-8 p-8 break-words bg-white max-w-xl">
|
<div class="mt-8 p-4 lg:p-8 break-words bg-white max-w-xl">
|
||||||
<h3>Versicherungsvermittler/in</h3>
|
<h3>Versicherungsvermittler/in</h3>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<router-link class="btn-blue" to="/learn/versicherungsvermittlerin-lp">
|
<router-link class="btn-blue" to="/learn/versicherungsvermittlerin-lp">
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const userStore = useUserStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="px-8 py-8 lg:px-12 lg:py-12 bg-gray-200">
|
<main class="lg:px-12 lg:py-12 bg-gray-200">
|
||||||
<div class="container-medium">
|
<div class="container-medium">
|
||||||
<h1 class="mb-8">Login</h1>
|
<h1 class="mb-8">Login</h1>
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ const userStore = useUserStore();
|
||||||
v-model="state.username"
|
v-model="state.username"
|
||||||
type="text"
|
type="text"
|
||||||
name="username"
|
name="username"
|
||||||
class="py-2 px-3 border border-gray-500 mt-1 block w-96"
|
class="py-2 px-3 border border-gray-500 mt-1 block w-96 max-w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
|
|
@ -45,7 +45,7 @@ const userStore = useUserStore();
|
||||||
v-model="state.password"
|
v-model="state.password"
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
class="py-2 px-3 border border-gray-500 mt-1 block w-96"
|
class="py-2 px-3 border border-gray-500 mt-1 block w-96 max-w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -356,15 +356,19 @@ function log(data: any) {
|
||||||
|
|
||||||
<h2 class="mt-8 mb-8">Dropdown (Work-in-progress)</h2>
|
<h2 class="mt-8 mb-8">Dropdown (Work-in-progress)</h2>
|
||||||
|
|
||||||
<ItDropdownSelect v-model="state.dropdownSelected" :items="state.dropdownValues">
|
<ItDropdownSelect
|
||||||
|
v-model="state.dropdownSelected"
|
||||||
|
class="w-full lg:w-96 mt-4 lg:mt-0"
|
||||||
|
:items="state.dropdownValues"
|
||||||
|
>
|
||||||
</ItDropdownSelect>
|
</ItDropdownSelect>
|
||||||
{{ state.dropdownSelected }}
|
{{ state.dropdownSelected }}
|
||||||
|
|
||||||
<h2 class="mt-8 mb-8">Checkbox</h2>
|
<h2 class="mt-8 mb-8">Checkbox</h2>
|
||||||
|
|
||||||
<ItCheckbox v-model="state.checkboxValue" :disabled="false" class=""
|
<ItCheckbox v-model="state.checkboxValue" :disabled="false" class=""
|
||||||
>Label</ItCheckbox
|
>Label
|
||||||
>
|
</ItCheckbox>
|
||||||
|
|
||||||
<ItCheckbox disabled class="mt-4">Disabled</ItCheckbox>
|
<ItCheckbox disabled class="mt-4">Disabled</ItCheckbox>
|
||||||
|
|
||||||
|
|
@ -376,8 +380,8 @@ function log(data: any) {
|
||||||
:list-items="dropdownData"
|
:list-items="dropdownData"
|
||||||
:align="'left'"
|
:align="'left'"
|
||||||
@select="log"
|
@select="log"
|
||||||
>Click Me</ItDropdown
|
>Click Me
|
||||||
>
|
</ItDropdown>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import CompetenceProgress from "@/components/competences/CompetenceProgress.vue";
|
import CompetenceProgress from "@/components/competences/CompetenceProgress.vue";
|
||||||
import PerformanceCriteriaRow from "@/components/competences/PerformanceCriteriaRow.vue";
|
import PerformanceCriteriaRow from "@/components/competences/PerformanceCriteriaRow.vue";
|
||||||
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
import { useCompetenceStore } from "@/stores/competence";
|
import { useCompetenceStore } from "@/stores/competence";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
|
|
@ -19,22 +20,26 @@ const failedCriteria = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const lastUpdatedCompetences = computed(() => {
|
const lastUpdatedCompetences = computed(() => {
|
||||||
if (competenceStore.competenceProfilePage?.children.length) {
|
|
||||||
return _.orderBy(
|
return _.orderBy(
|
||||||
competenceStore.competenceProfilePage.children,
|
competenceStore.competences,
|
||||||
[
|
[
|
||||||
(competence) => {
|
(competence) => {
|
||||||
|
let criteria = competence.children;
|
||||||
|
if (competenceStore.selectedCircle.id != "all") {
|
||||||
|
criteria = criteria.filter((criteria) => {
|
||||||
return (
|
return (
|
||||||
_.maxBy(competence.children, "completion_status_updated_at")
|
criteria.circle.translation_key === competenceStore.selectedCircle.id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
_.maxBy(criteria, "completion_status_updated_at")
|
||||||
?.completion_status_updated_at || ""
|
?.completion_status_updated_at || ""
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
["desc"]
|
["desc"]
|
||||||
).slice(0, 3);
|
).slice(0, 3);
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const countStatus = computed(() => {
|
const countStatus = computed(() => {
|
||||||
|
|
@ -49,9 +54,11 @@ const countStatus = computed(() => {
|
||||||
class="flex flex-col lg:flex-row items-center justify-between mb-10"
|
class="flex flex-col lg:flex-row items-center justify-between mb-10"
|
||||||
>
|
>
|
||||||
<h1>Kompetenzprofil</h1>
|
<h1>Kompetenzprofil</h1>
|
||||||
<!-- <ItDropdownSelect-->
|
<ItDropdownSelect
|
||||||
<!-- v-model="dropdownSelected"-->
|
v-model="competenceStore.selectedCircle"
|
||||||
<!-- :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
|
class="w-full lg:w-96 mt-4 lg:mt-0"
|
||||||
|
:items="competenceStore.availableCircles"
|
||||||
|
></ItDropdownSelect>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-white p-8 mb-8">
|
<div class="bg-white p-8 mb-8">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -66,13 +73,17 @@ const countStatus = computed(() => {
|
||||||
{{ competence.competence_id }} {{ competence.title }}
|
{{ competence.competence_id }} {{ competence.title }}
|
||||||
</p>
|
</p>
|
||||||
<CompetenceProgress
|
<CompetenceProgress
|
||||||
:status-count="competenceStore.calcStatusCount(competence.children)"
|
:status-count="
|
||||||
|
competenceStore.calcStatusCount(
|
||||||
|
competenceStore.criteriaByCompetence(competence)
|
||||||
|
)
|
||||||
|
"
|
||||||
></CompetenceProgress>
|
></CompetenceProgress>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<router-link
|
<router-link
|
||||||
:to="`${competenceStore.competenceProfilePage?.frontend_url}/competences`"
|
:to="`${competenceStore.competenceProfilePage?.frontend_url}/competences`"
|
||||||
class="flex items-center"
|
class="btn-text inline-flex items-center pl-0 py-2"
|
||||||
>
|
>
|
||||||
<span>Alle anschauen</span>
|
<span>Alle anschauen</span>
|
||||||
<it-icon-arrow-right></it-icon-arrow-right>
|
<it-icon-arrow-right></it-icon-arrow-right>
|
||||||
|
|
@ -102,7 +113,7 @@ const countStatus = computed(() => {
|
||||||
</li>
|
</li>
|
||||||
<li class="flex-1">
|
<li class="flex-1">
|
||||||
<h5 class="text-gray-700 mb-4">Nicht eingeschätzt</h5>
|
<h5 class="text-gray-700 mb-4">Nicht eingeschätzt</h5>
|
||||||
<div class="flex flex-row items-center border-r">
|
<div class="flex flex-row items-center">
|
||||||
<it-icon-smiley-neutral class="w-16 h-16"></it-icon-smiley-neutral>
|
<it-icon-smiley-neutral class="w-16 h-16"></it-icon-smiley-neutral>
|
||||||
<p class="text-7xl font-bold inline-block ml-4">
|
<p class="text-7xl font-bold inline-block ml-4">
|
||||||
{{ countStatus.unknown }}
|
{{ countStatus.unknown }}
|
||||||
|
|
@ -112,7 +123,7 @@ const countStatus = computed(() => {
|
||||||
</ul>
|
</ul>
|
||||||
<router-link
|
<router-link
|
||||||
:to="`${competenceStore.competenceProfilePage?.frontend_url}/criteria`"
|
:to="`${competenceStore.competenceProfilePage?.frontend_url}/criteria`"
|
||||||
class="flex items-center"
|
class="btn-text inline-flex items-center pl-0 py-2"
|
||||||
>
|
>
|
||||||
<span>Alle anschauen</span>
|
<span>Alle anschauen</span>
|
||||||
<it-icon-arrow-right></it-icon-arrow-right>
|
<it-icon-arrow-right></it-icon-arrow-right>
|
||||||
|
|
@ -134,7 +145,7 @@ const countStatus = computed(() => {
|
||||||
</ul>
|
</ul>
|
||||||
<router-link
|
<router-link
|
||||||
:to="`${competenceStore.competenceProfilePage?.frontend_url}/criteria`"
|
:to="`${competenceStore.competenceProfilePage?.frontend_url}/criteria`"
|
||||||
class="flex items-center"
|
class="btn-text inline-flex items-center pl-0 py-2"
|
||||||
>
|
>
|
||||||
<span>Alle anschauen</span>
|
<span>Alle anschauen</span>
|
||||||
<it-icon-arrow-right></it-icon-arrow-right>
|
<it-icon-arrow-right></it-icon-arrow-right>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import CompetenceDetail from "@/components/competences/CompetenceDetail.vue";
|
import CompetenceDetail from "@/components/competences/CompetenceDetail.vue";
|
||||||
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
import { useCompetenceStore } from "@/stores/competence";
|
import { useCompetenceStore } from "@/stores/competence";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
|
|
||||||
|
|
@ -10,24 +11,26 @@ const competenceStore = useCompetenceStore();
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container-large">
|
<div class="container-large">
|
||||||
<nav class="lg:mt-4">
|
<nav class="py-4 lg:pb-8">
|
||||||
<a
|
<router-link
|
||||||
class="block mb-8 cursor-pointer flex items-center"
|
class="btn-text inline-flex items-center pl-0"
|
||||||
:href="competenceStore.competenceProfilePage?.frontend_url"
|
:to="competenceStore.competenceProfilePage?.frontend_url"
|
||||||
>
|
>
|
||||||
<it-icon-arrow-left />
|
<it-icon-arrow-left />
|
||||||
<span>zurück</span></a
|
<span>zurück</span>
|
||||||
>
|
</router-link>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">
|
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">
|
||||||
<h1>Kompetenzen</h1>
|
<h1>Kompetenzen</h1>
|
||||||
<!-- <ItDropdownSelect-->
|
<ItDropdownSelect
|
||||||
<!-- v-model="dropdownSelected"-->
|
v-model="competenceStore.selectedCircle"
|
||||||
<!-- :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
|
class="w-full lg:w-96 mt-4 lg:mt-0"
|
||||||
|
:items="competenceStore.availableCircles"
|
||||||
|
></ItDropdownSelect>
|
||||||
</div>
|
</div>
|
||||||
<ul v-if="competenceStore.competenceProfilePage">
|
<ul v-if="competenceStore.competenceProfilePage">
|
||||||
<li
|
<li
|
||||||
v-for="competence in competenceStore.competenceProfilePage.children"
|
v-for="competence in competenceStore.competences"
|
||||||
:key="competence.id"
|
:key="competence.id"
|
||||||
class="bg-white p-8 mb-8"
|
class="bg-white p-8 mb-8"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { default as PerformanceCriteriaRow } from "@/components/competences/PerformanceCriteriaRow.vue";
|
import { default as PerformanceCriteriaRow } from "@/components/competences/PerformanceCriteriaRow.vue";
|
||||||
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
import { useCompetenceStore } from "@/stores/competence";
|
import { useCompetenceStore } from "@/stores/competence";
|
||||||
import type { CourseCompletionStatus } from "@/types";
|
import type { CourseCompletionStatus } from "@/types";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
|
|
@ -20,20 +21,22 @@ const shownCriteria = computed(() => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container-large">
|
<div class="container-large">
|
||||||
<nav class="lg:mt-4">
|
<nav class="py-4 lg:pb-8">
|
||||||
<a
|
<router-link
|
||||||
class="block mb-8 cursor-pointer flex items-center"
|
class="btn-text inline-flex items-center pl-0"
|
||||||
:href="`${competenceStore.competenceProfilePage?.frontend_url}`"
|
:to="`${competenceStore.competenceProfilePage?.frontend_url}`"
|
||||||
>
|
>
|
||||||
<it-icon-arrow-left />
|
<it-icon-arrow-left />
|
||||||
<span>zurück</span></a
|
<span>zurück</span>
|
||||||
>
|
</router-link>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">
|
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">
|
||||||
<h1>Einschätzungen</h1>
|
<h1>Einschätzungen</h1>
|
||||||
<!-- <ItDropdownSelect-->
|
<ItDropdownSelect
|
||||||
<!-- v-model="dropdownSelected"-->
|
v-model="competenceStore.selectedCircle"
|
||||||
<!-- :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
|
class="w-full lg:w-96 mt-4 lg:mt-0"
|
||||||
|
:items="competenceStore.availableCircles"
|
||||||
|
></ItDropdownSelect>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-white p-8">
|
<div class="bg-white p-8">
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ onMounted(async () => {
|
||||||
<div class="flex-initial lg:w-128 px-4 py-4 lg:px-8 lg:pt-4 bg-white">
|
<div class="flex-initial lg:w-128 px-4 py-4 lg:px-8 lg:pt-4 bg-white">
|
||||||
<router-link
|
<router-link
|
||||||
:to="`/learn/${props.learningPathSlug}`"
|
:to="`/learn/${props.learningPathSlug}`"
|
||||||
class="btn-text inline-flex items-center px-3 py-4 font-normal"
|
class="btn-text inline-flex items-center px-3 py-4"
|
||||||
data-cy="back-to-learning-path-button"
|
data-cy="back-to-learning-path-button"
|
||||||
>
|
>
|
||||||
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
<it-icon-arrow-left class="-ml-1 mr-1 h-5 w-5"></it-icon-arrow-left>
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,10 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
|
||||||
|
|
||||||
<div class="learningpath flex flex-col">
|
<div class="learningpath flex flex-col">
|
||||||
<div class="flex flex-col h-max">
|
<div class="flex flex-col h-max">
|
||||||
<div class="bg-white py-8 flex flex-col">
|
<div class="bg-white lg:py-8 flex flex-col">
|
||||||
<div class="flex justify-end p-3">
|
<div class="flex justify-end lg:p-4">
|
||||||
<button
|
<button
|
||||||
class="flex items-center"
|
class="btn-text inline-flex items-center px-3 lg:py-2"
|
||||||
data-cy="show-list-view"
|
data-cy="show-list-view"
|
||||||
@click="learningPathStore.page = 'OVERVIEW'"
|
@click="learningPathStore.page = 'OVERVIEW'"
|
||||||
>
|
>
|
||||||
|
|
@ -68,20 +68,20 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<LearningPathDiagram
|
<LearningPathDiagram
|
||||||
class="max-w-[1680px] w-full"
|
class="mx-auto max-w-[1920px] w-full"
|
||||||
identifier="mainVisualization"
|
identifier="mainVisualization"
|
||||||
:vertical="false"
|
:vertical="false"
|
||||||
></LearningPathDiagram>
|
></LearningPathDiagram>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-large">
|
<div class="container-large pt-0 lg:pt-4">
|
||||||
<h1 data-cy="learning-path-title" class="mt-6 lg:mt-12 mb-6">
|
<h1 data-cy="learning-path-title" class="mt-6 lg:mt-12 mb-6">
|
||||||
{{ learningPathStore.learningPath.title }}
|
{{ learningPathStore.learningPath.title }}
|
||||||
</h1>
|
</h1>
|
||||||
<div
|
<div
|
||||||
class="bg-white p-4 flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-gray-500 justify-start"
|
class="bg-white p-4 flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-gray-500 justify-start"
|
||||||
>
|
>
|
||||||
<div class="p-4 lg:p-8 flex-auto">
|
<div class="p-2 lg:p-8 flex-auto">
|
||||||
<h2>Willkommmen zurück, {{ userStore.first_name }}</h2>
|
<h2>Willkommmen zurück, {{ userStore.first_name }}</h2>
|
||||||
<p class="mt-4 text-xl"></p>
|
<p class="mt-4 text-xl"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,15 @@ import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
|
||||||
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
mediaCategorySlug: string;
|
mediaCategorySlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
log.debug("MediaCategoryDetailView created", props.mediaCategorySlug);
|
const route = useRoute();
|
||||||
|
|
||||||
|
log.debug("MediaCategoryDetailView created", props.mediaCategorySlug, route);
|
||||||
|
|
||||||
const mediaStore = useMediaLibraryStore();
|
const mediaStore = useMediaLibraryStore();
|
||||||
|
|
||||||
|
|
@ -19,6 +22,14 @@ const mediaCategory = computed(() => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const backLink = computed(() => {
|
||||||
|
if (route.query.back) {
|
||||||
|
return route.query.back;
|
||||||
|
} else {
|
||||||
|
return `${mediaStore.mediaLibraryPage?.frontend_url}/category`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const maxCardItems = 4;
|
const maxCardItems = 4;
|
||||||
const maxListItems = 6;
|
const maxListItems = 6;
|
||||||
|
|
||||||
|
|
@ -51,22 +62,19 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
||||||
v-if="mediaCategory"
|
v-if="mediaCategory"
|
||||||
class="fixed top-0 overflow-y-scroll bg-white h-full w-full"
|
class="fixed top-0 overflow-y-scroll bg-white h-full w-full"
|
||||||
>
|
>
|
||||||
<div class="bg-gray-200">
|
<div class="bg-gray-200 pb-4 lg:pb-12">
|
||||||
<div class="container-large">
|
<div class="container-large">
|
||||||
<nav>
|
<nav class="py-4 lg:pb-8">
|
||||||
<a
|
<router-link class="btn-text inline-flex items-center pl-0" :to="backLink">
|
||||||
class="block my-9 cursor-pointer flex items-center"
|
|
||||||
:href="`${mediaStore.mediaLibraryPage.frontend_url}/category`"
|
|
||||||
>
|
|
||||||
<it-icon-arrow-left />
|
<it-icon-arrow-left />
|
||||||
<span>zurück</span></a
|
<span>zurück</span>
|
||||||
>
|
</router-link>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div class="lg:w-6/12">
|
<div class="lg:w-6/12">
|
||||||
<h3 class="font-normal text-large mb-3">Handlungsfeld</h3>
|
<h3 class="font-normal text-large text-gray-900 mb-3">Handlungsfeld</h3>
|
||||||
<h1 class="mb-4 lg:mb-8">{{ mediaCategory.title }}</h1>
|
<h1 class="mb-4 lg:mb-8">{{ mediaCategory.title }}</h1>
|
||||||
<p class="text-xl">{{ mediaCategory.introduction_text }}</p>
|
<p class="text-large">{{ mediaCategory.introduction_text }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<img
|
<img
|
||||||
|
|
@ -108,6 +116,9 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
||||||
content_collection.value.contents[0].type
|
content_collection.value.contents[0].type
|
||||||
),
|
),
|
||||||
'border-t': !displayAsCard(content_collection.value.contents[0].type),
|
'border-t': !displayAsCard(content_collection.value.contents[0].type),
|
||||||
|
'border-gray-500': !displayAsCard(
|
||||||
|
content_collection.value.contents[0].type
|
||||||
|
),
|
||||||
'mb-6': hasMoreItemsForType(
|
'mb-6': hasMoreItemsForType(
|
||||||
content_collection.value.contents[0].type,
|
content_collection.value.contents[0].type,
|
||||||
content_collection.value.contents
|
content_collection.value.contents
|
||||||
|
|
@ -130,13 +141,17 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
||||||
:link-text="mediaItem.value.link_display_text"
|
:link-text="mediaItem.value.link_display_text"
|
||||||
:open-window="mediaItem.value.open_window"
|
:open-window="mediaItem.value.open_window"
|
||||||
/>
|
/>
|
||||||
<div v-else class="flex items-center justify-between border-b py-4">
|
<div
|
||||||
|
v-else
|
||||||
|
class="flex items-center justify-between border-b border-gray-500 py-4"
|
||||||
|
>
|
||||||
<h4 class="text-bold">{{ mediaItem.value.title }}</h4>
|
<h4 class="text-bold">{{ mediaItem.value.title }}</h4>
|
||||||
<media-link
|
<media-link
|
||||||
:blank="mediaItem.value.open_window"
|
:blank="mediaItem.value.open_window"
|
||||||
:to="mediaItem.value.url"
|
:to="mediaItem.value.url"
|
||||||
class="link"
|
class="link"
|
||||||
>{{ mediaItem.value.link_display_text }}
|
>
|
||||||
|
{{ mediaItem.value.link_display_text }}
|
||||||
</media-link>
|
</media-link>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -149,7 +164,7 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
:to="`${mediaCategory.frontend_url}/media`"
|
:to="`${mediaCategory.frontend_url}/media`"
|
||||||
class="flex items-center"
|
class="btn-text inline-flex items-center pl-0 py-2"
|
||||||
>
|
>
|
||||||
<span>Alle anschauen</span>
|
<span>Alle anschauen</span>
|
||||||
<it-icon-arrow-right></it-icon-arrow-right>
|
<it-icon-arrow-right></it-icon-arrow-right>
|
||||||
|
|
|
||||||
|
|
@ -40,14 +40,14 @@ const mediaList = computed(() => {
|
||||||
>
|
>
|
||||||
<div class="bg-gray-200">
|
<div class="bg-gray-200">
|
||||||
<div class="container-large">
|
<div class="container-large">
|
||||||
<nav>
|
<nav class="py-4 lg:pb-8">
|
||||||
<a
|
<router-link
|
||||||
class="block my-9 cursor-pointer flex items-center"
|
class="btn-text inline-flex items-center pl-0"
|
||||||
:href="mediaStore.mediaLibraryPage.frontend_url"
|
:to="mediaStore.mediaLibraryPage.frontend_url"
|
||||||
>
|
>
|
||||||
<it-icon-arrow-left />
|
<it-icon-arrow-left />
|
||||||
<span>zurück</span></a
|
<span>zurück</span>
|
||||||
>
|
</router-link>
|
||||||
</nav>
|
</nav>
|
||||||
<h1 class="mb-4">{{ mediaList.title }}</h1>
|
<h1 class="mb-4">{{ mediaList.title }}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,19 @@ onMounted(async () => {
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-gray-200">
|
<div class="bg-gray-200">
|
||||||
<nav class="px-6 py-4 border-b border-gray-500 bg-white">
|
<nav class="px-6 py-4 border-b border-gray-500 bg-white">
|
||||||
<ul class="flex text-xl flex-col lg:flex-row">
|
<ul v-if="mediaLibraryStore.mediaLibraryPage" class="flex flex-col lg:flex-row">
|
||||||
<li>Übersicht</li>
|
<li>
|
||||||
<li class="lg:ml-12">Handlungsfelder</li>
|
<router-link :to="mediaLibraryStore.mediaLibraryPage.frontend_url">
|
||||||
|
Übersicht
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="lg:ml-12">
|
||||||
|
<router-link
|
||||||
|
:to="`${mediaLibraryStore.mediaLibraryPage.frontend_url}/category`"
|
||||||
|
>
|
||||||
|
Handlungsfelder
|
||||||
|
</router-link>
|
||||||
|
</li>
|
||||||
<li class="lg:ml-12">Allgemeines zu Versicherungen</li>
|
<li class="lg:ml-12">Allgemeines zu Versicherungen</li>
|
||||||
<li class="lg:ml-12">Lernmedien</li>
|
<li class="lg:ml-12">Lernmedien</li>
|
||||||
<li class="lg:ml-12">
|
<li class="lg:ml-12">
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
import { itGet } from "@/fetchHelpers";
|
import { itGet } from "@/fetchHelpers";
|
||||||
import { useCompletionStore } from "@/stores/completion";
|
import { useCompletionStore } from "@/stores/completion";
|
||||||
import type { CompetenceProfilePage, PerformanceCriteria } from "@/types";
|
import type {
|
||||||
|
CompetencePage,
|
||||||
|
CompetenceProfilePage,
|
||||||
|
CourseWagtailPage,
|
||||||
|
PerformanceCriteria,
|
||||||
|
} from "@/types";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
export type CompetenceStoreState = {
|
export type CompetenceStoreState = {
|
||||||
competenceProfilePage: CompetenceProfilePage | undefined;
|
competenceProfilePage: CompetenceProfilePage | undefined;
|
||||||
|
selectedCircle: { id: string; name: string };
|
||||||
|
availableCircles: { id: string; name: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCompetenceStore = defineStore({
|
export const useCompetenceStore = defineStore({
|
||||||
|
|
@ -13,10 +20,12 @@ export const useCompetenceStore = defineStore({
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
competenceProfilePage: undefined,
|
competenceProfilePage: undefined,
|
||||||
|
selectedCircle: { id: "all", name: "Circle: Alle" },
|
||||||
|
availableCircles: [],
|
||||||
} as CompetenceStoreState;
|
} as CompetenceStoreState;
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
flatPerformanceCriteria: (state, circleTitle = "") => {
|
flatPerformanceCriteria: (state) => {
|
||||||
if (!state.competenceProfilePage) {
|
if (!state.competenceProfilePage) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -29,12 +38,40 @@ export const useCompetenceStore = defineStore({
|
||||||
["asc"]
|
["asc"]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (circleTitle) {
|
if (state.selectedCircle.id !== "all") {
|
||||||
criteria = criteria.filter((c) => c.circle === circleTitle);
|
criteria = criteria.filter(
|
||||||
|
(c) => c.circle.translation_key === state.selectedCircle.id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return criteria;
|
return criteria;
|
||||||
},
|
},
|
||||||
|
competences: (state) => {
|
||||||
|
if (state.competenceProfilePage?.children.length) {
|
||||||
|
return state.competenceProfilePage.children.filter((competence) => {
|
||||||
|
let criteria = competence.children;
|
||||||
|
if (state.selectedCircle.id != "all") {
|
||||||
|
criteria = criteria.filter((criteria) => {
|
||||||
|
return criteria.circle.translation_key === state.selectedCircle.id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return criteria.length > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
criteriaByCompetence: (state) => {
|
||||||
|
return (competence: CompetencePage) => {
|
||||||
|
return competence.children.filter((criteria) => {
|
||||||
|
if (state.selectedCircle.id != "all") {
|
||||||
|
return criteria.circle.translation_key === state.selectedCircle.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return competence.children;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
calcStatusCount(criteria: PerformanceCriteria[]) {
|
calcStatusCount(criteria: PerformanceCriteria[]) {
|
||||||
|
|
@ -65,6 +102,12 @@ export const useCompetenceStore = defineStore({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.competenceProfilePage = competenceProfilePageData;
|
this.competenceProfilePage = competenceProfilePageData;
|
||||||
|
|
||||||
|
const circles = competenceProfilePageData.circles.map((c: CourseWagtailPage) => {
|
||||||
|
return { id: c.translation_key, name: `Circle: ${c.title}` };
|
||||||
|
});
|
||||||
|
this.availableCircles = [{ id: "all", name: "Circle: Alle" }, ...circles];
|
||||||
|
|
||||||
await this.parseCompletionData();
|
await this.parseCompletionData();
|
||||||
|
|
||||||
return this.competenceProfilePage;
|
return this.competenceProfilePage;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { itGet } from "@/fetchHelpers";
|
import { itGet } from "@/fetchHelpers";
|
||||||
import type { MediaLibraryPage } from "@/types";
|
import type { MediaLibraryPage } from "@/types";
|
||||||
|
import log from "loglevel";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
export type MediaLibraryStoreState = {
|
export type MediaLibraryStoreState = {
|
||||||
|
|
@ -26,6 +27,7 @@ export const useMediaLibraryStore = defineStore({
|
||||||
if (this.mediaLibraryPage && !reload) {
|
if (this.mediaLibraryPage && !reload) {
|
||||||
return this.mediaLibraryPage;
|
return this.mediaLibraryPage;
|
||||||
}
|
}
|
||||||
|
log.debug("load mediaLibraryPageData");
|
||||||
const mediaLibraryPageData = await itGet(`/api/course/page/${slug}/`);
|
const mediaLibraryPageData = await itGet(`/api/course/page/${slug}/`);
|
||||||
|
|
||||||
if (!mediaLibraryPageData) {
|
if (!mediaLibraryPageData) {
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,13 @@ export interface CourseWagtailPage {
|
||||||
completion_status_updated_at: string;
|
completion_status_updated_at: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CircleLight {
|
||||||
|
readonly id: number;
|
||||||
|
readonly title: string;
|
||||||
|
readonly slug: string;
|
||||||
|
readonly translation_key: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LearningContent extends CourseWagtailPage {
|
export interface LearningContent extends CourseWagtailPage {
|
||||||
type: "learnpath.LearningContent";
|
type: "learnpath.LearningContent";
|
||||||
minutes: number;
|
minutes: number;
|
||||||
|
|
@ -296,7 +303,7 @@ export interface MediaLibraryPage extends CourseWagtailPage {
|
||||||
export interface PerformanceCriteria extends CourseWagtailPage {
|
export interface PerformanceCriteria extends CourseWagtailPage {
|
||||||
type: "competence.PerformanceCriteria";
|
type: "competence.PerformanceCriteria";
|
||||||
competence_id: string;
|
competence_id: string;
|
||||||
circle: string;
|
circle: CircleLight;
|
||||||
course_category: CourseCategory;
|
course_category: CourseCategory;
|
||||||
learning_unit: CourseWagtailPage;
|
learning_unit: CourseWagtailPage;
|
||||||
}
|
}
|
||||||
|
|
@ -310,5 +317,6 @@ export interface CompetencePage extends CourseWagtailPage {
|
||||||
export interface CompetenceProfilePage extends CourseWagtailPage {
|
export interface CompetenceProfilePage extends CourseWagtailPage {
|
||||||
type: "competence.CompetenceProfilePage";
|
type: "competence.CompetenceProfilePage";
|
||||||
course: Course;
|
course: Course;
|
||||||
|
circles: CircleLight[];
|
||||||
children: CompetencePage[];
|
children: CompetencePage[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,11 @@ svg {
|
||||||
.container-large {
|
.container-large {
|
||||||
@apply mx-auto max-w-6xl w-full px-4 lg:px-8 py-4;
|
@apply mx-auto max-w-6xl w-full px-4 lg:px-8 py-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-blue-900 {
|
||||||
|
filter: invert(9%) sepia(38%) saturate(5684%) hue-rotate(200deg) brightness(95%)
|
||||||
|
contrast(105%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
|
|
@ -95,7 +100,8 @@ svg {
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-text {
|
.btn-text {
|
||||||
@apply font-semibold py-2 px-4 align-middle inline-block
|
@apply font-normal py-2 px-4 align-middle inline-block
|
||||||
|
text-blue-900
|
||||||
hover:text-gray-700
|
hover:text-gray-700
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed;
|
disabled:opacity-50 disabled:cursor-not-allowed;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,81 +1,172 @@
|
||||||
# create a new version of this docker image
|
# create a new version of this docker image
|
||||||
# > docker build -t iterativ/vbv-lernwelt-bitbucket .
|
# > docker build --platform linux/amd64 -t iterativ/vbv-lernwelt-bitbucket .
|
||||||
# push new version to Docker Hub
|
# push new version to Docker Hub
|
||||||
# > docker push iterativ/vbv-lernwelt-bitbucket
|
# > docker push iterativ/vbv-lernwelt-bitbucket
|
||||||
# run locally with directory mounted
|
# run locally with directory mounted
|
||||||
# > docker run -v "$(pwd)":/src -it iterativ/vbv-lernwelt-bitbucket /bin/bash
|
# > docker run -v "$(pwd)":/src -it iterativ/vbv-lernwelt-bitbucket /bin/bash
|
||||||
|
|
||||||
FROM python:3.10-bullseye
|
FROM cypress/included:10.9.0
|
||||||
MAINTAINER Daniel Egger <daniel.egger@iterativ.ch>
|
MAINTAINER Daniel Egger <daniel.egger@iterativ.ch>
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
# install python see https://github.com/docker-library/python
|
||||||
|
ENV LANG C.UTF-8
|
||||||
|
ENV LC_ALL C.UTF-8
|
||||||
|
|
||||||
# Install node prereqs, nodejs and yarn
|
|
||||||
# Ref: https://deb.nodesource.com/setup_16.x
|
|
||||||
# Ref: https://yarnpkg.com/en/docs/install
|
|
||||||
# https://github.com/nikolaik/docker-python-nodejs
|
|
||||||
RUN \
|
|
||||||
echo "deb https://deb.nodesource.com/node_16.x bullseye main" > /etc/apt/sources.list.d/nodesource.list && \
|
|
||||||
wget -qO- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \
|
|
||||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list && \
|
|
||||||
wget -qO- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -yqq nodejs yarn && \
|
|
||||||
pip install -U pip && pip install pipenv && \
|
|
||||||
npm i -g npm@^6
|
|
||||||
|
|
||||||
# Install Cypress deps
|
# ensure local python is preferred over distribution python
|
||||||
# https://github.com/cypress-io/cypress-docker-images/blob/master/base/16.5.0/Dockerfile
|
ENV PATH /usr/local/bin:$PATH
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install --no-install-recommends -y \
|
|
||||||
libgtk2.0-0 \
|
|
||||||
libgtk-3-0 \
|
|
||||||
libnotify-dev \
|
|
||||||
libgconf-2-4 \
|
|
||||||
libgbm-dev \
|
|
||||||
libnss3 \
|
|
||||||
libxss1 \
|
|
||||||
libasound2 \
|
|
||||||
libxtst6 \
|
|
||||||
xauth \
|
|
||||||
xvfb \
|
|
||||||
# install text editors
|
|
||||||
vim-tiny \
|
|
||||||
nano \
|
|
||||||
# install emoji font
|
|
||||||
fonts-noto-color-emoji \
|
|
||||||
# install Chinese fonts
|
|
||||||
# this list was copied from https://github.com/jim3ma/docker-leanote
|
|
||||||
fonts-arphic-bkai00mp \
|
|
||||||
fonts-arphic-bsmi00lp \
|
|
||||||
fonts-arphic-gbsn00lp \
|
|
||||||
fonts-arphic-gkai00mp \
|
|
||||||
fonts-arphic-ukai \
|
|
||||||
fonts-arphic-uming \
|
|
||||||
ttf-wqy-zenhei \
|
|
||||||
ttf-wqy-microhei \
|
|
||||||
xfonts-wqy
|
|
||||||
|
|
||||||
RUN npm --version
|
# http://bugs.python.org/issue19846
|
||||||
|
# > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK.
|
||||||
|
ENV LANG C.UTF-8
|
||||||
|
|
||||||
RUN npm install -g yarn@latest --force
|
# runtime dependencies
|
||||||
RUN yarn --version
|
RUN set -eux; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
netbase \
|
||||||
|
tzdata \
|
||||||
|
; \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# a few environment variables to make NPM installs easier
|
ENV GPG_KEY A035C8C19219BA821ECEA86B64E628F8D684696D
|
||||||
# good colors for most applications
|
ENV PYTHON_VERSION 3.10.7
|
||||||
ENV TERM xterm
|
|
||||||
# avoid million NPM install messages
|
|
||||||
ENV npm_config_loglevel warn
|
|
||||||
# allow installing when the main user is root
|
|
||||||
ENV npm_config_unsafe_perm true
|
|
||||||
|
|
||||||
# Node libraries
|
RUN set -eux; \
|
||||||
RUN node -p process.versions
|
\
|
||||||
|
savedAptMark="$(apt-mark showmanual)"; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
dpkg-dev \
|
||||||
|
gcc \
|
||||||
|
gnupg dirmngr \
|
||||||
|
libbluetooth-dev \
|
||||||
|
libbz2-dev \
|
||||||
|
libc6-dev \
|
||||||
|
libexpat1-dev \
|
||||||
|
libffi-dev \
|
||||||
|
libgdbm-dev \
|
||||||
|
liblzma-dev \
|
||||||
|
libncursesw5-dev \
|
||||||
|
libreadline-dev \
|
||||||
|
libsqlite3-dev \
|
||||||
|
libssl-dev \
|
||||||
|
make \
|
||||||
|
tk-dev \
|
||||||
|
uuid-dev \
|
||||||
|
wget \
|
||||||
|
xz-utils \
|
||||||
|
zlib1g-dev \
|
||||||
|
; \
|
||||||
|
\
|
||||||
|
wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz"; \
|
||||||
|
wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc"; \
|
||||||
|
GNUPGHOME="$(mktemp -d)"; export GNUPGHOME; \
|
||||||
|
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$GPG_KEY"; \
|
||||||
|
gpg --batch --verify python.tar.xz.asc python.tar.xz; \
|
||||||
|
command -v gpgconf > /dev/null && gpgconf --kill all || :; \
|
||||||
|
rm -rf "$GNUPGHOME" python.tar.xz.asc; \
|
||||||
|
mkdir -p /usr/src/python; \
|
||||||
|
tar --extract --directory /usr/src/python --strip-components=1 --file python.tar.xz; \
|
||||||
|
rm python.tar.xz; \
|
||||||
|
\
|
||||||
|
cd /usr/src/python; \
|
||||||
|
gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \
|
||||||
|
./configure \
|
||||||
|
--build="$gnuArch" \
|
||||||
|
--enable-loadable-sqlite-extensions \
|
||||||
|
--enable-optimizations \
|
||||||
|
--enable-option-checking=fatal \
|
||||||
|
--enable-shared \
|
||||||
|
--with-lto \
|
||||||
|
--with-system-expat \
|
||||||
|
--without-ensurepip \
|
||||||
|
; \
|
||||||
|
nproc="$(nproc)"; \
|
||||||
|
make -j "$nproc" \
|
||||||
|
LDFLAGS="-Wl,--strip-all" \
|
||||||
|
; \
|
||||||
|
make install; \
|
||||||
|
\
|
||||||
|
cd /; \
|
||||||
|
rm -rf /usr/src/python; \
|
||||||
|
\
|
||||||
|
find /usr/local -depth \
|
||||||
|
\( \
|
||||||
|
\( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \
|
||||||
|
-o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name 'libpython*.a' \) \) \
|
||||||
|
\) -exec rm -rf '{}' + \
|
||||||
|
; \
|
||||||
|
\
|
||||||
|
ldconfig; \
|
||||||
|
\
|
||||||
|
apt-mark auto '.*' > /dev/null; \
|
||||||
|
apt-mark manual $savedAptMark; \
|
||||||
|
find /usr/local -type f -executable -not \( -name '*tkinter*' \) -exec ldd '{}' ';' \
|
||||||
|
| awk '/=>/ { print $(NF-1) }' \
|
||||||
|
| sort -u \
|
||||||
|
| xargs -r dpkg-query --search \
|
||||||
|
| cut -d: -f1 \
|
||||||
|
| sort -u \
|
||||||
|
| xargs -r apt-mark manual \
|
||||||
|
; \
|
||||||
|
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||||
|
rm -rf /var/lib/apt/lists/*; \
|
||||||
|
\
|
||||||
|
python3 --version
|
||||||
|
|
||||||
# Show where Node loads required modules from
|
# make some useful symlinks that are expected to exist ("/usr/local/bin/python" and friends)
|
||||||
RUN node -p 'module.paths'
|
RUN set -eux; \
|
||||||
|
for src in idle3 pydoc3 python3 python3-config; do \
|
||||||
|
dst="$(echo "$src" | tr -d 3)"; \
|
||||||
|
[ -s "/usr/local/bin/$src" ]; \
|
||||||
|
[ ! -e "/usr/local/bin/$dst" ]; \
|
||||||
|
ln -svT "$src" "/usr/local/bin/$dst"; \
|
||||||
|
done
|
||||||
|
|
||||||
# install postgresql
|
# if this is called "PIP_VERSION", pip explodes with "ValueError: invalid truth value '<VERSION>'"
|
||||||
|
ENV PYTHON_PIP_VERSION 22.2.2
|
||||||
|
# https://github.com/docker-library/python/issues/365
|
||||||
|
ENV PYTHON_SETUPTOOLS_VERSION 63.2.0
|
||||||
|
# https://github.com/pypa/get-pip
|
||||||
|
ENV PYTHON_GET_PIP_URL https://github.com/pypa/get-pip/raw/5eaac1050023df1f5c98b173b248c260023f2278/public/get-pip.py
|
||||||
|
ENV PYTHON_GET_PIP_SHA256 5aefe6ade911d997af080b315ebcb7f882212d070465df544e1175ac2be519b4
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
\
|
||||||
|
savedAptMark="$(apt-mark showmanual)"; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends wget; \
|
||||||
|
\
|
||||||
|
wget -O get-pip.py "$PYTHON_GET_PIP_URL"; \
|
||||||
|
echo "$PYTHON_GET_PIP_SHA256 *get-pip.py" | sha256sum -c -; \
|
||||||
|
\
|
||||||
|
apt-mark auto '.*' > /dev/null; \
|
||||||
|
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||||
|
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||||
|
rm -rf /var/lib/apt/lists/*; \
|
||||||
|
\
|
||||||
|
export PYTHONDONTWRITEBYTECODE=1; \
|
||||||
|
\
|
||||||
|
python get-pip.py \
|
||||||
|
--disable-pip-version-check \
|
||||||
|
--no-cache-dir \
|
||||||
|
--no-compile \
|
||||||
|
"pip==$PYTHON_PIP_VERSION" \
|
||||||
|
"setuptools==$PYTHON_SETUPTOOLS_VERSION" \
|
||||||
|
; \
|
||||||
|
rm -f get-pip.py; \
|
||||||
|
\
|
||||||
|
pip --version
|
||||||
|
|
||||||
|
# install postgresql \
|
||||||
|
RUN apt install curl ca-certificates gnupg
|
||||||
|
RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc \
|
||||||
|
| gpg --dearmor \
|
||||||
|
| tee /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg >/dev/null
|
||||||
|
RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" > /etc/apt/sources.list.d/postgresql.list'
|
||||||
|
RUN apt update
|
||||||
RUN apt-get install -y postgresql postgresql-contrib libpq-dev
|
RUN apt-get install -y postgresql postgresql-contrib libpq-dev
|
||||||
|
|
||||||
# Required by python3-saml
|
# Required by python3-saml
|
||||||
|
|
@ -84,13 +175,6 @@ RUN node -p 'module.paths'
|
||||||
# install git-crypt
|
# install git-crypt
|
||||||
RUN apt-get -y install git-crypt
|
RUN apt-get -y install git-crypt
|
||||||
|
|
||||||
ENV LANG C.UTF-8
|
|
||||||
ENV LC_ALL C.UTF-8
|
|
||||||
|
|
||||||
# Install Google Chrome
|
|
||||||
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
|
|
||||||
RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
|
|
||||||
RUN apt-get update && apt-get install -y google-chrome-stable
|
|
||||||
|
|
||||||
# versions of local tools
|
# versions of local tools
|
||||||
RUN echo " node version: $(node -v) \n" \
|
RUN echo " node version: $(node -v) \n" \
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$START_BACKGROUND" = true ]; then
|
if [ "$START_BACKGROUND" = true ]; then
|
||||||
python3 server/manage.py runserver "${DJANGO_PORT}" --settings="$DJANGO_SETTINGS_MODULE" > /dev/null &
|
cd server && python3 manage.py runserver "${DJANGO_PORT}" --settings="$DJANGO_SETTINGS_MODULE" > /dev/null &
|
||||||
else
|
else
|
||||||
python3 server/manage.py runserver "${DJANGO_PORT}" --settings="$DJANGO_SETTINGS_MODULE"
|
cd server && python3 manage.py runserver "${DJANGO_PORT}" --settings="$DJANGO_SETTINGS_MODULE"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -580,7 +580,7 @@ if APP_ENVIRONMENT == "development":
|
||||||
# django-extensions
|
# django-extensions
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
|
# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
|
||||||
INSTALLED_APPS += ["django_extensions"] # noqa F405
|
INSTALLED_APPS += ["django_extensions", "django_watchfiles"] # noqa F405
|
||||||
|
|
||||||
if APP_ENVIRONMENT in ["production", "caprover"] or APP_ENVIRONMENT.startswith(
|
if APP_ENVIRONMENT in ["production", "caprover"] or APP_ENVIRONMENT.startswith(
|
||||||
"caprover"
|
"caprover"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
-r requirements.in
|
-r requirements.in
|
||||||
|
|
||||||
Werkzeug[watchdog] # https://github.com/pallets/werkzeug
|
|
||||||
ipdb # https://github.com/gotcha/ipdb
|
ipdb # https://github.com/gotcha/ipdb
|
||||||
watchgod # https://github.com/samuelcolvin/watchgod
|
|
||||||
pip-tools
|
pip-tools
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|
@ -33,5 +31,8 @@ django-extensions # https://github.com/django-extensions/django-extensions
|
||||||
django-coverage-plugin # https://github.com/nedbat/django_coverage_plugin
|
django-coverage-plugin # https://github.com/nedbat/django_coverage_plugin
|
||||||
pytest-django # https://github.com/pytest-dev/pytest-django
|
pytest-django # https://github.com/pytest-dev/pytest-django
|
||||||
|
|
||||||
|
# django-watchfiles custom PR
|
||||||
|
https://github.com/q0w/django-watchfiles/archive/issue-1.zip
|
||||||
|
|
||||||
# code checking
|
# code checking
|
||||||
truffleHog
|
truffleHog
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
anyascii==0.3.1
|
anyascii==0.3.1
|
||||||
# via wagtail
|
# via wagtail
|
||||||
anyio==3.5.0
|
anyio==3.5.0
|
||||||
# via watchgod
|
# via watchfiles
|
||||||
appnope==0.1.2
|
appnope==0.1.2
|
||||||
# via ipython
|
# via ipython
|
||||||
argon2-cffi==21.3.0
|
argon2-cffi==21.3.0
|
||||||
|
|
@ -15,9 +15,7 @@ argon2-cffi==21.3.0
|
||||||
argon2-cffi-bindings==21.2.0
|
argon2-cffi-bindings==21.2.0
|
||||||
# via argon2-cffi
|
# via argon2-cffi
|
||||||
asgiref==3.5.0
|
asgiref==3.5.0
|
||||||
# via
|
# via django
|
||||||
# django
|
|
||||||
# uvicorn
|
|
||||||
astroid==2.11.2
|
astroid==2.11.2
|
||||||
# via pylint
|
# via pylint
|
||||||
asttokens==2.0.5
|
asttokens==2.0.5
|
||||||
|
|
@ -35,10 +33,12 @@ backcall==0.2.0
|
||||||
# via ipython
|
# via ipython
|
||||||
beautifulsoup4==4.9.3
|
beautifulsoup4==4.9.3
|
||||||
# via wagtail
|
# via wagtail
|
||||||
black==22.8.0
|
black==22.10.0
|
||||||
# via
|
# via
|
||||||
# -r requirements-dev.in
|
# -r requirements-dev.in
|
||||||
# ufmt
|
# ufmt
|
||||||
|
build==0.8.0
|
||||||
|
# via pip-tools
|
||||||
certifi==2021.10.8
|
certifi==2021.10.8
|
||||||
# via
|
# via
|
||||||
# requests
|
# requests
|
||||||
|
|
@ -82,11 +82,12 @@ dill==0.3.4
|
||||||
# via pylint
|
# via pylint
|
||||||
distlib==0.3.4
|
distlib==0.3.4
|
||||||
# via virtualenv
|
# via virtualenv
|
||||||
dj-database-url==0.5.0
|
dj-database-url==1.0.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
django==3.2.13
|
django==3.2.13
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
|
# dj-database-url
|
||||||
# django-cors-headers
|
# django-cors-headers
|
||||||
# django-csp
|
# django-csp
|
||||||
# django-debug-toolbar
|
# django-debug-toolbar
|
||||||
|
|
@ -100,6 +101,7 @@ django==3.2.13
|
||||||
# django-stubs-ext
|
# django-stubs-ext
|
||||||
# django-taggit
|
# django-taggit
|
||||||
# django-treebeard
|
# django-treebeard
|
||||||
|
# django-watchfiles
|
||||||
# djangorestframework
|
# djangorestframework
|
||||||
# drf-spectacular
|
# drf-spectacular
|
||||||
# wagtail
|
# wagtail
|
||||||
|
|
@ -140,6 +142,8 @@ django-taggit==2.1.0
|
||||||
# via wagtail
|
# via wagtail
|
||||||
django-treebeard==4.5.1
|
django-treebeard==4.5.1
|
||||||
# via wagtail
|
# via wagtail
|
||||||
|
django-watchfiles @ https://github.com/q0w/django-watchfiles/archive/issue-1.zip
|
||||||
|
# via -r requirements-dev.in
|
||||||
djangorestframework==3.13.1
|
djangorestframework==3.13.1
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
|
|
@ -222,9 +226,7 @@ libcst==0.4.7
|
||||||
# ufmt
|
# ufmt
|
||||||
# usort
|
# usort
|
||||||
markupsafe==2.1.1
|
markupsafe==2.1.1
|
||||||
# via
|
# via jinja2
|
||||||
# jinja2
|
|
||||||
# werkzeug
|
|
||||||
marshmallow==3.15.0
|
marshmallow==3.15.0
|
||||||
# via environs
|
# via environs
|
||||||
matplotlib-inline==0.1.3
|
matplotlib-inline==0.1.3
|
||||||
|
|
@ -253,6 +255,7 @@ openpyxl==3.0.9
|
||||||
# via tablib
|
# via tablib
|
||||||
packaging==21.3
|
packaging==21.3
|
||||||
# via
|
# via
|
||||||
|
# build
|
||||||
# marshmallow
|
# marshmallow
|
||||||
# pytest
|
# pytest
|
||||||
# pytest-sugar
|
# pytest-sugar
|
||||||
|
|
@ -264,7 +267,7 @@ pathspec==0.9.0
|
||||||
# black
|
# black
|
||||||
# trailrunner
|
# trailrunner
|
||||||
pep517==0.12.0
|
pep517==0.12.0
|
||||||
# via pip-tools
|
# via build
|
||||||
pexpect==4.8.0
|
pexpect==4.8.0
|
||||||
# via ipython
|
# via ipython
|
||||||
pickleshare==0.7.5
|
pickleshare==0.7.5
|
||||||
|
|
@ -273,7 +276,7 @@ pillow==9.0.1
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# wagtail
|
# wagtail
|
||||||
pip-tools==6.6.2
|
pip-tools==6.9.0
|
||||||
# via -r requirements-dev.in
|
# via -r requirements-dev.in
|
||||||
platformdirs==2.5.1
|
platformdirs==2.5.1
|
||||||
# via
|
# via
|
||||||
|
|
@ -318,7 +321,7 @@ pyparsing==3.0.7
|
||||||
# via packaging
|
# via packaging
|
||||||
pyrsistent==0.18.1
|
pyrsistent==0.18.1
|
||||||
# via jsonschema
|
# via jsonschema
|
||||||
pytest==7.1.1
|
pytest==7.1.3
|
||||||
# via
|
# via
|
||||||
# -r requirements-dev.in
|
# -r requirements-dev.in
|
||||||
# pytest-django
|
# pytest-django
|
||||||
|
|
@ -403,6 +406,7 @@ toml==0.10.2
|
||||||
tomli==2.0.1
|
tomli==2.0.1
|
||||||
# via
|
# via
|
||||||
# black
|
# black
|
||||||
|
# build
|
||||||
# django-stubs
|
# django-stubs
|
||||||
# mypy
|
# mypy
|
||||||
# pep517
|
# pep517
|
||||||
|
|
@ -450,7 +454,7 @@ urllib3==1.26.9
|
||||||
# sentry-sdk
|
# sentry-sdk
|
||||||
usort==1.0.5
|
usort==1.0.5
|
||||||
# via ufmt
|
# via ufmt
|
||||||
uvicorn[standard]==0.17.6
|
uvicorn[standard]==0.18.3
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
uvloop==0.16.0
|
uvloop==0.16.0
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
|
|
@ -465,11 +469,9 @@ wagtail-factories==2.0.1
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
wagtail-localize==1.2.1
|
wagtail-localize==1.2.1
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
watchdog==2.1.9
|
watchfiles==0.17.0
|
||||||
# via werkzeug
|
|
||||||
watchgod==0.8.2
|
|
||||||
# via
|
# via
|
||||||
# -r requirements-dev.in
|
# django-watchfiles
|
||||||
# uvicorn
|
# uvicorn
|
||||||
wcwidth==0.2.5
|
wcwidth==0.2.5
|
||||||
# via prompt-toolkit
|
# via prompt-toolkit
|
||||||
|
|
@ -477,8 +479,6 @@ webencodings==0.5.1
|
||||||
# via html5lib
|
# via html5lib
|
||||||
websockets==10.2
|
websockets==10.2
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
werkzeug[watchdog]==2.2.0
|
|
||||||
# via -r requirements-dev.in
|
|
||||||
wheel==0.37.1
|
wheel==0.37.1
|
||||||
# via pip-tools
|
# via pip-tools
|
||||||
whitenoise==6.0.0
|
whitenoise==6.0.0
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,13 @@
|
||||||
anyascii==0.3.1
|
anyascii==0.3.1
|
||||||
# via wagtail
|
# via wagtail
|
||||||
anyio==3.5.0
|
anyio==3.5.0
|
||||||
# via watchgod
|
# via watchfiles
|
||||||
argon2-cffi==21.3.0
|
argon2-cffi==21.3.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
argon2-cffi-bindings==21.2.0
|
argon2-cffi-bindings==21.2.0
|
||||||
# via argon2-cffi
|
# via argon2-cffi
|
||||||
asgiref==3.5.0
|
asgiref==3.5.0
|
||||||
# via
|
# via django
|
||||||
# django
|
|
||||||
# uvicorn
|
|
||||||
async-timeout==4.0.2
|
async-timeout==4.0.2
|
||||||
# via redis
|
# via redis
|
||||||
attrs==21.4.0
|
attrs==21.4.0
|
||||||
|
|
@ -44,15 +42,15 @@ cryptography==36.0.2
|
||||||
# via authlib
|
# via authlib
|
||||||
deprecated==1.2.13
|
deprecated==1.2.13
|
||||||
# via redis
|
# via redis
|
||||||
dj-database-url==0.5.0
|
dj-database-url==1.0.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
django==3.2.13
|
django==3.2.13
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
|
# dj-database-url
|
||||||
# django-cors-headers
|
# django-cors-headers
|
||||||
# django-csp
|
# django-csp
|
||||||
# django-filter
|
# django-filter
|
||||||
# django-htmx
|
|
||||||
# django-model-utils
|
# django-model-utils
|
||||||
# django-modelcluster
|
# django-modelcluster
|
||||||
# django-permissionedforms
|
# django-permissionedforms
|
||||||
|
|
@ -204,7 +202,7 @@ urllib3==1.26.9
|
||||||
# via
|
# via
|
||||||
# requests
|
# requests
|
||||||
# sentry-sdk
|
# sentry-sdk
|
||||||
uvicorn[standard]==0.17.6
|
uvicorn[standard]==0.18.3
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
uvloop==0.16.0
|
uvloop==0.16.0
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
|
|
@ -217,7 +215,7 @@ wagtail-factories==2.0.1
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
wagtail-localize==1.2.1
|
wagtail-localize==1.2.1
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
watchgod==0.8.1
|
watchfiles==0.17.0
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
# via html5lib
|
# via html5lib
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from wagtail.fields import StreamField
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
||||||
from vbv_lernwelt.core.model_utils import find_available_slug
|
from vbv_lernwelt.core.model_utils import find_available_slug
|
||||||
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
|
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
||||||
|
|
||||||
|
|
||||||
class CompetenceProfilePage(Page):
|
class CompetenceProfilePage(Page):
|
||||||
|
|
@ -32,6 +32,7 @@ class CompetenceProfilePage(Page):
|
||||||
cls,
|
cls,
|
||||||
[
|
[
|
||||||
"course",
|
"course",
|
||||||
|
"circles",
|
||||||
"children",
|
"children",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from vbv_lernwelt.competence.models import PerformanceCriteria
|
from vbv_lernwelt.competence.models import PerformanceCriteria
|
||||||
|
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
||||||
from vbv_lernwelt.course.serializers import CourseCategorySerializer
|
from vbv_lernwelt.course.serializers import CourseCategorySerializer
|
||||||
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
|
|
||||||
|
|
||||||
|
|
||||||
class PerformanceCriteriaSerializer(
|
class PerformanceCriteriaSerializer(
|
||||||
|
|
@ -33,7 +33,8 @@ class PerformanceCriteriaSerializer(
|
||||||
return LearningUnitPerformanceCriteriaSerializer(obj.learning_unit).data
|
return LearningUnitPerformanceCriteriaSerializer(obj.learning_unit).data
|
||||||
|
|
||||||
def get_circle(self, obj):
|
def get_circle(self, obj):
|
||||||
return obj.learning_unit.get_parent().specific.title
|
c = obj.learning_unit.get_parent()
|
||||||
|
return {"id": c.id, "title": c.title, "translation_key": c.translation_key}
|
||||||
|
|
||||||
def get_course_category(self, obj):
|
def get_course_category(self, obj):
|
||||||
if obj.learning_unit:
|
if obj.learning_unit:
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
|
||||||
course = SerializerMethodField()
|
course = SerializerMethodField()
|
||||||
course_category = CourseCategorySerializer(read_only=True)
|
course_category = CourseCategorySerializer(read_only=True)
|
||||||
frontend_url = SerializerMethodField()
|
frontend_url = SerializerMethodField()
|
||||||
|
circles = SerializerMethodField()
|
||||||
|
|
||||||
meta_fields = []
|
meta_fields = []
|
||||||
|
|
||||||
|
|
@ -62,6 +63,27 @@ 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_circles(self, obj):
|
||||||
|
course_parent_page = obj.get_ancestors().exact_type(CoursePage).last()
|
||||||
|
|
||||||
|
if course_parent_page:
|
||||||
|
from vbv_lernwelt.learnpath.models import Circle, LearningPath
|
||||||
|
|
||||||
|
circles = (
|
||||||
|
course_parent_page.get_children()
|
||||||
|
.exact_type(LearningPath)
|
||||||
|
.first()
|
||||||
|
.get_children()
|
||||||
|
.exact_type(Circle)
|
||||||
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
{"id": c.id, "title": c.title, "translation_key": c.translation_key}
|
||||||
|
for c in circles
|
||||||
|
]
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def get_frontend_url(self, obj):
|
def get_frontend_url(self, obj):
|
||||||
if hasattr(obj, "get_frontend_url"):
|
if hasattr(obj, "get_frontend_url"):
|
||||||
return obj.get_frontend_url()
|
return obj.get_frontend_url()
|
||||||
|
|
@ -564,7 +564,7 @@ def create_circle_abschluss(lp):
|
||||||
|
|
||||||
def create_circle_betreuen(lp):
|
def create_circle_betreuen(lp):
|
||||||
circle = CircleFactory(
|
circle = CircleFactory(
|
||||||
title="Abschluss",
|
title="Betreuen",
|
||||||
parent=lp,
|
parent=lp,
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Starten", parent=circle, icon="it-icon-ls-start")
|
LearningSequenceFactory(title="Starten", parent=circle, icon="it-icon-ls-start")
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from wagtail.images.blocks import ImageChooserBlock
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
||||||
from vbv_lernwelt.core.model_utils import find_available_slug
|
from vbv_lernwelt.core.model_utils import find_available_slug
|
||||||
|
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
||||||
from vbv_lernwelt.course.models import CoursePage
|
from vbv_lernwelt.course.models import CoursePage
|
||||||
from vbv_lernwelt.learnpath.models_learning_unit_content import (
|
from vbv_lernwelt.learnpath.models_learning_unit_content import (
|
||||||
AssignmentBlock,
|
AssignmentBlock,
|
||||||
|
|
@ -21,7 +22,6 @@ from vbv_lernwelt.learnpath.models_learning_unit_content import (
|
||||||
TestBlock,
|
TestBlock,
|
||||||
VideoBlock,
|
VideoBlock,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
|
|
||||||
|
|
||||||
|
|
||||||
class LearningPath(Page):
|
class LearningPath(Page):
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ from rest_framework.fields import SerializerMethodField
|
||||||
from vbv_lernwelt.competence.serializers import (
|
from vbv_lernwelt.competence.serializers import (
|
||||||
PerformanceCriteriaLearningPathSerializer,
|
PerformanceCriteriaLearningPathSerializer,
|
||||||
)
|
)
|
||||||
|
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
||||||
from vbv_lernwelt.learnpath.models import LearningUnit
|
from vbv_lernwelt.learnpath.models import LearningUnit
|
||||||
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
|
|
||||||
|
|
||||||
|
|
||||||
class LearningUnitSerializer(
|
class LearningUnitSerializer(
|
||||||
|
|
|
||||||
|
|
@ -148,13 +148,15 @@ die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenf
|
||||||
contents=[
|
contents=[
|
||||||
create_relative_link_block(
|
create_relative_link_block(
|
||||||
RelativeLinkBlockFactory(
|
RelativeLinkBlockFactory(
|
||||||
title="VBV 303/12.3 Verkehrsrechtsschutz",
|
title="Rechtsstreitigkeiten",
|
||||||
|
description="VBV 303/12.3 Verkehrsrechtsschutz",
|
||||||
url="/media/versicherungsvermittlerin-media/category/rechtsstreitigkeiten",
|
url="/media/versicherungsvermittlerin-media/category/rechtsstreitigkeiten",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
create_relative_link_block(
|
create_relative_link_block(
|
||||||
RelativeLinkBlockFactory(
|
RelativeLinkBlockFactory(
|
||||||
title="VBV 303/13 Reiseversicherung",
|
title="Reisen",
|
||||||
|
description="VBV 303/13 Reiseversicherung",
|
||||||
url="/media/versicherungsvermittlerin-media/category/reisen",
|
url="/media/versicherungsvermittlerin-media/category/reisen",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
@ -250,19 +252,22 @@ Diese können negative Folgen verschiedener Art nach sich ziehen, darunter recht
|
||||||
contents=[
|
contents=[
|
||||||
create_relative_link_block(
|
create_relative_link_block(
|
||||||
RelativeLinkBlockFactory(
|
RelativeLinkBlockFactory(
|
||||||
title="VBV 303/03 Hausratversicherung",
|
title="Haushalt",
|
||||||
|
description="VBV 303/03 Hausratversicherung",
|
||||||
url="/media/versicherungsvermittlerin-media/category/haushalt",
|
url="/media/versicherungsvermittlerin-media/category/haushalt",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
create_relative_link_block(
|
create_relative_link_block(
|
||||||
RelativeLinkBlockFactory(
|
RelativeLinkBlockFactory(
|
||||||
title="VBV 303/12 Rechtschutzversicherung",
|
title="Rechtsstreitigkeiten",
|
||||||
|
desciption="VBV 303/12 Rechtschutzversicherung",
|
||||||
url="/media/versicherungsvermittlerin-media/category/rechtsstreitigkeiten",
|
url="/media/versicherungsvermittlerin-media/category/rechtsstreitigkeiten",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
create_relative_link_block(
|
create_relative_link_block(
|
||||||
RelativeLinkBlockFactory(
|
RelativeLinkBlockFactory(
|
||||||
title="VBV 304/Teil E Obligatorische Krankenversicherung",
|
title="Gesundheit",
|
||||||
|
description="VBV 304/Teil E Obligatorische Krankenversicherung",
|
||||||
url="/media/versicherungsvermittlerin-media/category/gesundheit",
|
url="/media/versicherungsvermittlerin-media/category/gesundheit",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from wagtail.fields import StreamField
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
||||||
from vbv_lernwelt.core.model_utils import find_available_slug
|
from vbv_lernwelt.core.model_utils import find_available_slug
|
||||||
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
|
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
||||||
from vbv_lernwelt.media_library.content_blocks import MediaContentCollection
|
from vbv_lernwelt.media_library.content_blocks import MediaContentCollection
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@ class RelativeLinkBlockFactory(wagtail_factories.StructBlockFactory):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RelativeLinkBlock
|
model = RelativeLinkBlock
|
||||||
|
|
||||||
title = "Platzhalter Querverweis"
|
title = "Fahrzeug"
|
||||||
description = "Handlungsfeld"
|
description = "Platzhalter Querverweis"
|
||||||
link_display_text = "Handlungsfeld anzeigen"
|
link_display_text = "Handlungsfeld anzeigen"
|
||||||
icon_url = "/static/icons/demo/icon-hf-reisen.svg"
|
icon_url = "/static/icons/demo/icon-hf-reisen.svg"
|
||||||
url = "/media/versicherungsvermittlerin-media/category/fahrzeug"
|
url = "/media/versicherungsvermittlerin-media/category/fahrzeug"
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
"ignore hash 3": "OvBgP9A2JBgiRad/mM36mkzXSXaJE9BEIENnVEmeZdITvwT09xnxLtT4twkCa8m/loMbPHsvPl0T8lRGVBwjlQ==",
|
"ignore hash 3": "OvBgP9A2JBgiRad/mM36mkzXSXaJE9BEIENnVEmeZdITvwT09xnxLtT4twkCa8m/loMbPHsvPl0T8lRGVBwjlQ==",
|
||||||
"ignore hash 4": "1NpUCSvAKLpDZL9e3tqDaUe8Kk2xAuF1tXosFjBanc4lFCgNcfBp02MD3UjB72ZS",
|
"ignore hash 4": "1NpUCSvAKLpDZL9e3tqDaUe8Kk2xAuF1tXosFjBanc4lFCgNcfBp02MD3UjB72ZS",
|
||||||
"ignore hash 5": "1LhwZ0DvP4cGBgbBdCfaBQV7eiaOc4jWKdzO9WEXLFT7AaqBN6jqd0uyaZeAZ19K",
|
"ignore hash 5": "1LhwZ0DvP4cGBgbBdCfaBQV7eiaOc4jWKdzO9WEXLFT7AaqBN6jqd0uyaZeAZ19K",
|
||||||
|
"ignore hash 6": "A035C8C19219BA821ECEA86B64E628F8D684696D",
|
||||||
"json base64 content": "regex:\"content\": \"",
|
"json base64 content": "regex:\"content\": \"",
|
||||||
"img base64 content": "regex:data:image/png;base64,.*"
|
"img base64 content": "regex:data:image/png;base64,.*"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
server/requirements/
|
server/requirements/
|
||||||
env_secrets/
|
env_secrets/
|
||||||
|
env/bitbucket/Dockerfile
|
||||||
env/docker_local.env
|
env/docker_local.env
|
||||||
server/vbv_lernwelt/static/
|
server/vbv_lernwelt/static/
|
||||||
server/vbv_lernwelt/media/
|
server/vbv_lernwelt/media/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue