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">
|
||||
<div class="flex items-center justify-between">
|
||||
<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" />
|
||||
</a>
|
||||
</router-link>
|
||||
<router-link to="/" class="flex">
|
||||
<div class="text-white text-2xl pr-10 pl-3 ml-1 border-l border-white">
|
||||
myVBV
|
||||
|
|
|
|||
|
|
@ -40,23 +40,28 @@ const clickLink = (to: string) => {
|
|||
class="mt-2 inline-block flex items-center"
|
||||
@click="clickLink('/profile')"
|
||||
>
|
||||
<IconSettings class="inline-block" /><span class="ml-3"
|
||||
>Kontoeinstellungen</span
|
||||
>
|
||||
<IconSettings class="inline-block" />
|
||||
<span class="ml-3"> Kontoeinstellungen </span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="learningPathName" class="mt-6 pb-6 border-b border-gray-500">
|
||||
<h4 class="text-gray-900 text-sm">Kurs: {{ learningPathName }}</h4>
|
||||
<div v-if="true" class="mt-6 pb-6 border-b border-gray-500">
|
||||
<h4 class="text-gray-900 text-sm">Kurs: Versicherungsvermittler/in</h4>
|
||||
<ul class="mt-6">
|
||||
<li>
|
||||
<button @click="clickLink(`/learningpath/${learningPathSlug}`)">
|
||||
<button @click="clickLink(`/learn/versicherungsvermittlerin-lp`)">
|
||||
Lernpfad
|
||||
</button>
|
||||
</li>
|
||||
<li class="mt-6">Kompetenzprofil</li>
|
||||
<li class="mt-6">
|
||||
<button
|
||||
@click="clickLink(`/competence/versicherungsvermittlerin-competence`)"
|
||||
>
|
||||
Kompetenzprofil
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<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"
|
||||
@click="userStore.handleLogout()"
|
||||
>
|
||||
<IconLogout class="inline-block" /><span class="ml-1">Abmelden</span>
|
||||
<IconLogout class="inline-block" />
|
||||
<span class="ml-1">Abmelden</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -34,12 +34,16 @@ const togglePerformanceCriteria = () => {
|
|||
</button>
|
||||
</div>
|
||||
<ComptenceProgress
|
||||
:status-count="competenceStore.calcStatusCount(competence.children)"
|
||||
:status-count="
|
||||
competenceStore.calcStatusCount(
|
||||
competenceStore.criteriaByCompetence(competence)
|
||||
)
|
||||
"
|
||||
></ComptenceProgress>
|
||||
</div>
|
||||
<ul v-if="isOpen">
|
||||
<li
|
||||
v-for="performanceCriteria in competence.children"
|
||||
v-for="performanceCriteria in competenceStore.criteriaByCompetence(competence)"
|
||||
:key="performanceCriteria.id"
|
||||
class="mb-4 pb-4 border-b border-gray-500"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -30,16 +30,16 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
</h4>
|
||||
<p>
|
||||
Lerneinheit:
|
||||
<a class="link" :href="criteria.learning_unit.frontend_url">
|
||||
<router-link class="link" :to="criteria.learning_unit.frontend_url">
|
||||
{{ criteria.learning_unit.title }}
|
||||
</a>
|
||||
</router-link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<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
|
||||
</a>
|
||||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ function render() {
|
|||
translate = [translate[0], translate[1] + 20];
|
||||
return "translate(" + translate + ")";
|
||||
})
|
||||
.attr("class", "circlesText text-xl font-bold")
|
||||
.attr("class", "circlesText text-large font-bold")
|
||||
.style("text-anchor", "middle");
|
||||
|
||||
const iconWidth = 25;
|
||||
|
|
@ -194,7 +194,8 @@ function render() {
|
|||
let translate = wedgeGenerator.centroid(d);
|
||||
translate = [translate[0] - iconWidth / 2, translate[1] - iconWidth];
|
||||
return "translate(" + translate + ")";
|
||||
});
|
||||
})
|
||||
.attr("class", "filter-blue-900");
|
||||
|
||||
// Create Arrows
|
||||
const arrow = d3
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const props = defineProps<{
|
|||
<div v-if="circle" class="container-medium">
|
||||
<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.
|
||||
</p>
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ const props = defineProps<{
|
|||
<li
|
||||
v-for="goal in circle.goals"
|
||||
:key="goal.id"
|
||||
class="text-xl flex items-center"
|
||||
class="text-large flex items-center"
|
||||
>
|
||||
<it-icon-check
|
||||
class="mt-4 hidden lg:block w-12 h-12 text-sky-500 flex-none"
|
||||
|
|
@ -45,7 +45,7 @@ const props = defineProps<{
|
|||
<li
|
||||
v-for="jobSituation in circle.job_situations"
|
||||
: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 }}
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@ import { useCircleStore } from "@/stores/circle";
|
|||
import type { LearningContent } from "@/types";
|
||||
import * as log from "loglevel";
|
||||
import { computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
log.debug("LearningContent.vue setup");
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const circleStore = useCircleStore();
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
@ -28,7 +31,7 @@ const block = computed(() => {
|
|||
>
|
||||
<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"
|
||||
@click="circleStore.closeLearningContent(props.learningContent)"
|
||||
>
|
||||
|
|
@ -36,7 +39,7 @@ const block = computed(() => {
|
|||
<span class="hidden lg:inline">zurück zum Circle</span>
|
||||
</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 }}
|
||||
</h1>
|
||||
|
||||
|
|
@ -70,18 +73,21 @@ const block = computed(() => {
|
|||
<div v-else-if="block.type === 'media_library'" class="mt-4 lg:mt-12">
|
||||
<h1>{{ learningContent.title }}</h1>
|
||||
|
||||
<p class="text-xl my-4 lg:my-8">{{ block.value.description }}</p>
|
||||
<a :href="block.value.url" target="_blank" class="button btn-primary">
|
||||
<p class="text-large my-4 lg:my-8">{{ block.value.description }}</p>
|
||||
<router-link
|
||||
:to="`${block.value.url}?back=${route.path}`"
|
||||
class="button btn-primary"
|
||||
>
|
||||
Mediathek öffnen
|
||||
</a>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</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>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -89,14 +89,16 @@ export default {
|
|||
mounted() {
|
||||
log.debug("LearningPathDiagram mounted");
|
||||
|
||||
const circleWidth = this.vertical ? 60 : 200;
|
||||
const radius = (circleWidth * 0.8) / 2;
|
||||
|
||||
if (this.vertical) {
|
||||
this.width = Math.min(960, window.innerWidth - 32);
|
||||
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) {
|
||||
let color = colors.gray[300];
|
||||
if (d.someFinished) {
|
||||
|
|
@ -208,7 +210,7 @@ export default {
|
|||
for (let index = 0; index < i; index++) {
|
||||
x += circleWidth * topics[index].circles.length;
|
||||
}
|
||||
return x + 30;
|
||||
return x + 10;
|
||||
}
|
||||
|
||||
function getTopicVerticalPosition(i, d, topics) {
|
||||
|
|
@ -266,7 +268,7 @@ export default {
|
|||
|
||||
if (this.vertical) {
|
||||
const Circles_X = radius;
|
||||
const Topics_X = Circles_X - radius;
|
||||
const Topics_X = Circles_X - circleWidth;
|
||||
|
||||
circle_groups.attr("transform", (d, i) => {
|
||||
return (
|
||||
|
|
@ -302,7 +304,7 @@ export default {
|
|||
topicTitles.attr("y", 30);
|
||||
} else {
|
||||
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)";
|
||||
});
|
||||
|
||||
|
|
@ -386,7 +388,7 @@ export default {
|
|||
<div class="svg-container h-full content-start">
|
||||
<svg
|
||||
:id="identifier"
|
||||
class="learning-path-visualization h-full"
|
||||
class="learning-path-visualization h-full -mt-6 lg:mt-0"
|
||||
:viewBox="viewBox"
|
||||
></svg>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ const learningSequenceBorderClass = computed(() => {
|
|||
<div :id="learningSequence.slug" class="mb-8 learning-sequence">
|
||||
<div class="flex items-center gap-4 mb-2 text-blue-900">
|
||||
<component :is="learningSequence.icon" />
|
||||
<h3 class="text-xl font-semibold">
|
||||
<h3 class="text-large font-semibold">
|
||||
{{ learningSequence.title }}
|
||||
</h3>
|
||||
<div>{{ humanizeDuration(learningSequence.minutes) }}</div>
|
||||
|
|
|
|||
|
|
@ -38,14 +38,14 @@ function handleContinue() {
|
|||
>
|
||||
<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)"
|
||||
>
|
||||
<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>
|
||||
</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 }}
|
||||
</h1>
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ function handleContinue() {
|
|||
Schritt {{ state.questionIndex + 1 }} von {{ questions.length }}
|
||||
</div>
|
||||
|
||||
<p class="text-xl mt-4">
|
||||
<p class="text-large mt-4">
|
||||
Überprüfe, ob du in der Lernheinheit
|
||||
<span class="font-bold">"{{ learningUnit.title }}"</span> alles verstanden
|
||||
hast.<br />
|
||||
|
|
@ -88,7 +88,7 @@ function handleContinue() {
|
|||
@click="circleStore.markCompletion(currentQuestion, 'success')"
|
||||
>
|
||||
<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
|
||||
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>
|
||||
<h3 class="mb-4">{{ title }}</h3>
|
||||
<p class="mb-4 text-xl">{{ description }}</p>
|
||||
<router-link :to="link" class="text-xl inline-flex items-center font-normal">
|
||||
<p class="mb-4">{{ description }}</p>
|
||||
<router-link :to="link" class="btn-text inline-flex items-center pl-0 pr-3 py-2">
|
||||
<span class="inline">{{ call2Action }}</span>
|
||||
<it-icon-arrow-right class="ml-1 h-5 w-5"></it-icon-arrow-right>
|
||||
</router-link>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const emit = defineEmits<{
|
|||
leave-to-class="transform scale-95 opacity-0"
|
||||
>
|
||||
<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']"
|
||||
>
|
||||
<div v-for="section in listItems" :key="section" class="">
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ const dropdownSelected = computed({
|
|||
|
||||
<template>
|
||||
<Listbox v-model="dropdownSelected" as="div">
|
||||
<div class="mt-1 relative w-96">
|
||||
<div class="mt-1 relative w-full">
|
||||
<ListboxButton
|
||||
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");
|
||||
});
|
||||
|
||||
onUnmounted( () => removeNoScroll())
|
||||
onUnmounted(() => removeNoScroll());
|
||||
|
||||
const closeModal = () => {
|
||||
removeNoScroll();
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@ if (url.charAt(url.length - 1) !== "/") {
|
|||
<template>
|
||||
<main class="px-4 py-8">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ const userStore = useUserStore();
|
|||
</script>
|
||||
|
||||
<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">
|
||||
<h1 data-cy="welcome-message">Willkommen, {{ userStore.first_name }}</h1>
|
||||
|
||||
<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>
|
||||
<div class="mt-4">
|
||||
<router-link class="btn-blue" to="/learn/versicherungsvermittlerin-lp">
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const userStore = useUserStore();
|
|||
</script>
|
||||
|
||||
<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">
|
||||
<h1 class="mb-8">Login</h1>
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ const userStore = useUserStore();
|
|||
v-model="state.username"
|
||||
type="text"
|
||||
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 class="mb-4">
|
||||
|
|
@ -45,7 +45,7 @@ const userStore = useUserStore();
|
|||
v-model="state.password"
|
||||
type="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>
|
||||
|
||||
|
|
|
|||
|
|
@ -356,15 +356,19 @@ function log(data: any) {
|
|||
|
||||
<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>
|
||||
{{ state.dropdownSelected }}
|
||||
|
||||
<h2 class="mt-8 mb-8">Checkbox</h2>
|
||||
|
||||
<ItCheckbox v-model="state.checkboxValue" :disabled="false" class=""
|
||||
>Label</ItCheckbox
|
||||
>
|
||||
>Label
|
||||
</ItCheckbox>
|
||||
|
||||
<ItCheckbox disabled class="mt-4">Disabled</ItCheckbox>
|
||||
|
||||
|
|
@ -376,8 +380,8 @@ function log(data: any) {
|
|||
:list-items="dropdownData"
|
||||
:align="'left'"
|
||||
@select="log"
|
||||
>Click Me</ItDropdown
|
||||
>
|
||||
>Click Me
|
||||
</ItDropdown>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import CompetenceProgress from "@/components/competences/CompetenceProgress.vue";
|
||||
import PerformanceCriteriaRow from "@/components/competences/PerformanceCriteriaRow.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import _ from "lodash";
|
||||
import * as log from "loglevel";
|
||||
|
|
@ -19,22 +20,26 @@ const failedCriteria = computed(() => {
|
|||
});
|
||||
|
||||
const lastUpdatedCompetences = computed(() => {
|
||||
if (competenceStore.competenceProfilePage?.children.length) {
|
||||
return _.orderBy(
|
||||
competenceStore.competenceProfilePage.children,
|
||||
competenceStore.competences,
|
||||
[
|
||||
(competence) => {
|
||||
let criteria = competence.children;
|
||||
if (competenceStore.selectedCircle.id != "all") {
|
||||
criteria = criteria.filter((criteria) => {
|
||||
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 || ""
|
||||
);
|
||||
},
|
||||
],
|
||||
["desc"]
|
||||
).slice(0, 3);
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
const countStatus = computed(() => {
|
||||
|
|
@ -49,9 +54,11 @@ const countStatus = computed(() => {
|
|||
class="flex flex-col lg:flex-row items-center justify-between mb-10"
|
||||
>
|
||||
<h1>Kompetenzprofil</h1>
|
||||
<!-- <ItDropdownSelect-->
|
||||
<!-- v-model="dropdownSelected"-->
|
||||
<!-- :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
|
||||
<ItDropdownSelect
|
||||
v-model="competenceStore.selectedCircle"
|
||||
class="w-full lg:w-96 mt-4 lg:mt-0"
|
||||
:items="competenceStore.availableCircles"
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
<div class="bg-white p-8 mb-8">
|
||||
<div>
|
||||
|
|
@ -66,13 +73,17 @@ const countStatus = computed(() => {
|
|||
{{ competence.competence_id }} {{ competence.title }}
|
||||
</p>
|
||||
<CompetenceProgress
|
||||
:status-count="competenceStore.calcStatusCount(competence.children)"
|
||||
:status-count="
|
||||
competenceStore.calcStatusCount(
|
||||
competenceStore.criteriaByCompetence(competence)
|
||||
)
|
||||
"
|
||||
></CompetenceProgress>
|
||||
</li>
|
||||
</ul>
|
||||
<router-link
|
||||
: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>
|
||||
<it-icon-arrow-right></it-icon-arrow-right>
|
||||
|
|
@ -102,7 +113,7 @@ const countStatus = computed(() => {
|
|||
</li>
|
||||
<li class="flex-1">
|
||||
<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>
|
||||
<p class="text-7xl font-bold inline-block ml-4">
|
||||
{{ countStatus.unknown }}
|
||||
|
|
@ -112,7 +123,7 @@ const countStatus = computed(() => {
|
|||
</ul>
|
||||
<router-link
|
||||
: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>
|
||||
<it-icon-arrow-right></it-icon-arrow-right>
|
||||
|
|
@ -134,7 +145,7 @@ const countStatus = computed(() => {
|
|||
</ul>
|
||||
<router-link
|
||||
: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>
|
||||
<it-icon-arrow-right></it-icon-arrow-right>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import CompetenceDetail from "@/components/competences/CompetenceDetail.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import * as log from "loglevel";
|
||||
|
||||
|
|
@ -10,24 +11,26 @@ const competenceStore = useCompetenceStore();
|
|||
|
||||
<template>
|
||||
<div class="container-large">
|
||||
<nav class="lg:mt-4">
|
||||
<a
|
||||
class="block mb-8 cursor-pointer flex items-center"
|
||||
:href="competenceStore.competenceProfilePage?.frontend_url"
|
||||
<nav class="py-4 lg:pb-8">
|
||||
<router-link
|
||||
class="btn-text inline-flex items-center pl-0"
|
||||
:to="competenceStore.competenceProfilePage?.frontend_url"
|
||||
>
|
||||
<it-icon-arrow-left />
|
||||
<span>zurück</span></a
|
||||
>
|
||||
<span>zurück</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">
|
||||
<h1>Kompetenzen</h1>
|
||||
<!-- <ItDropdownSelect-->
|
||||
<!-- v-model="dropdownSelected"-->
|
||||
<!-- :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
|
||||
<ItDropdownSelect
|
||||
v-model="competenceStore.selectedCircle"
|
||||
class="w-full lg:w-96 mt-4 lg:mt-0"
|
||||
:items="competenceStore.availableCircles"
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
<ul v-if="competenceStore.competenceProfilePage">
|
||||
<li
|
||||
v-for="competence in competenceStore.competenceProfilePage.children"
|
||||
v-for="competence in competenceStore.competences"
|
||||
:key="competence.id"
|
||||
class="bg-white p-8 mb-8"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { default as PerformanceCriteriaRow } from "@/components/competences/PerformanceCriteriaRow.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import type { CourseCompletionStatus } from "@/types";
|
||||
import * as log from "loglevel";
|
||||
|
|
@ -20,20 +21,22 @@ const shownCriteria = computed(() => {
|
|||
|
||||
<template>
|
||||
<div class="container-large">
|
||||
<nav class="lg:mt-4">
|
||||
<a
|
||||
class="block mb-8 cursor-pointer flex items-center"
|
||||
:href="`${competenceStore.competenceProfilePage?.frontend_url}`"
|
||||
<nav class="py-4 lg:pb-8">
|
||||
<router-link
|
||||
class="btn-text inline-flex items-center pl-0"
|
||||
:to="`${competenceStore.competenceProfilePage?.frontend_url}`"
|
||||
>
|
||||
<it-icon-arrow-left />
|
||||
<span>zurück</span></a
|
||||
>
|
||||
<span>zurück</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
<div class="flex flex-col lg:flex-row items-center justify-between mb-10">
|
||||
<h1>Einschätzungen</h1>
|
||||
<!-- <ItDropdownSelect-->
|
||||
<!-- v-model="dropdownSelected"-->
|
||||
<!-- :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
|
||||
<ItDropdownSelect
|
||||
v-model="competenceStore.selectedCircle"
|
||||
class="w-full lg:w-96 mt-4 lg:mt-0"
|
||||
:items="competenceStore.availableCircles"
|
||||
></ItDropdownSelect>
|
||||
</div>
|
||||
<div class="bg-white p-8">
|
||||
<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">
|
||||
<router-link
|
||||
: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"
|
||||
>
|
||||
<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="flex flex-col h-max">
|
||||
<div class="bg-white py-8 flex flex-col">
|
||||
<div class="flex justify-end p-3">
|
||||
<div class="bg-white lg:py-8 flex flex-col">
|
||||
<div class="flex justify-end lg:p-4">
|
||||
<button
|
||||
class="flex items-center"
|
||||
class="btn-text inline-flex items-center px-3 lg:py-2"
|
||||
data-cy="show-list-view"
|
||||
@click="learningPathStore.page = 'OVERVIEW'"
|
||||
>
|
||||
|
|
@ -68,20 +68,20 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
|
|||
</button>
|
||||
</div>
|
||||
<LearningPathDiagram
|
||||
class="max-w-[1680px] w-full"
|
||||
class="mx-auto max-w-[1920px] w-full"
|
||||
identifier="mainVisualization"
|
||||
:vertical="false"
|
||||
></LearningPathDiagram>
|
||||
</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">
|
||||
{{ learningPathStore.learningPath.title }}
|
||||
</h1>
|
||||
<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"
|
||||
>
|
||||
<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>
|
||||
<p class="mt-4 text-xl"></p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
|
|||
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
||||
import * as log from "loglevel";
|
||||
import { computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const props = defineProps<{
|
||||
mediaCategorySlug: string;
|
||||
}>();
|
||||
|
||||
log.debug("MediaCategoryDetailView created", props.mediaCategorySlug);
|
||||
const route = useRoute();
|
||||
|
||||
log.debug("MediaCategoryDetailView created", props.mediaCategorySlug, route);
|
||||
|
||||
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 maxListItems = 6;
|
||||
|
||||
|
|
@ -51,22 +62,19 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
|||
v-if="mediaCategory"
|
||||
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">
|
||||
<nav>
|
||||
<a
|
||||
class="block my-9 cursor-pointer flex items-center"
|
||||
:href="`${mediaStore.mediaLibraryPage.frontend_url}/category`"
|
||||
>
|
||||
<nav class="py-4 lg:pb-8">
|
||||
<router-link class="btn-text inline-flex items-center pl-0" :to="backLink">
|
||||
<it-icon-arrow-left />
|
||||
<span>zurück</span></a
|
||||
>
|
||||
<span>zurück</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
<div class="flex justify-between">
|
||||
<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>
|
||||
<p class="text-xl">{{ mediaCategory.introduction_text }}</p>
|
||||
<p class="text-large">{{ mediaCategory.introduction_text }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
|
|
@ -108,6 +116,9 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
|||
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(
|
||||
content_collection.value.contents[0].type,
|
||||
content_collection.value.contents
|
||||
|
|
@ -130,13 +141,17 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
|||
: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">
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center justify-between border-b border-gray-500 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 }}
|
||||
>
|
||||
{{ mediaItem.value.link_display_text }}
|
||||
</media-link>
|
||||
</div>
|
||||
</li>
|
||||
|
|
@ -149,7 +164,7 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
|||
)
|
||||
"
|
||||
:to="`${mediaCategory.frontend_url}/media`"
|
||||
class="flex items-center"
|
||||
class="btn-text inline-flex items-center pl-0 py-2"
|
||||
>
|
||||
<span>Alle anschauen</span>
|
||||
<it-icon-arrow-right></it-icon-arrow-right>
|
||||
|
|
|
|||
|
|
@ -40,14 +40,14 @@ const mediaList = computed(() => {
|
|||
>
|
||||
<div class="bg-gray-200">
|
||||
<div class="container-large">
|
||||
<nav>
|
||||
<a
|
||||
class="block my-9 cursor-pointer flex items-center"
|
||||
:href="mediaStore.mediaLibraryPage.frontend_url"
|
||||
<nav class="py-4 lg:pb-8">
|
||||
<router-link
|
||||
class="btn-text inline-flex items-center pl-0"
|
||||
:to="mediaStore.mediaLibraryPage.frontend_url"
|
||||
>
|
||||
<it-icon-arrow-left />
|
||||
<span>zurück</span></a
|
||||
>
|
||||
<span>zurück</span>
|
||||
</router-link>
|
||||
</nav>
|
||||
<h1 class="mb-4">{{ mediaList.title }}</h1>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,9 +25,19 @@ onMounted(async () => {
|
|||
<template>
|
||||
<div class="bg-gray-200">
|
||||
<nav class="px-6 py-4 border-b border-gray-500 bg-white">
|
||||
<ul class="flex text-xl flex-col lg:flex-row">
|
||||
<li>Übersicht</li>
|
||||
<li class="lg:ml-12">Handlungsfelder</li>
|
||||
<ul v-if="mediaLibraryStore.mediaLibraryPage" class="flex flex-col lg:flex-row">
|
||||
<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">Lernmedien</li>
|
||||
<li class="lg:ml-12">
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
import { itGet } from "@/fetchHelpers";
|
||||
import { useCompletionStore } from "@/stores/completion";
|
||||
import type { CompetenceProfilePage, PerformanceCriteria } from "@/types";
|
||||
import type {
|
||||
CompetencePage,
|
||||
CompetenceProfilePage,
|
||||
CourseWagtailPage,
|
||||
PerformanceCriteria,
|
||||
} from "@/types";
|
||||
import _ from "lodash";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export type CompetenceStoreState = {
|
||||
competenceProfilePage: CompetenceProfilePage | undefined;
|
||||
selectedCircle: { id: string; name: string };
|
||||
availableCircles: { id: string; name: string }[];
|
||||
};
|
||||
|
||||
export const useCompetenceStore = defineStore({
|
||||
|
|
@ -13,10 +20,12 @@ export const useCompetenceStore = defineStore({
|
|||
state: () => {
|
||||
return {
|
||||
competenceProfilePage: undefined,
|
||||
selectedCircle: { id: "all", name: "Circle: Alle" },
|
||||
availableCircles: [],
|
||||
} as CompetenceStoreState;
|
||||
},
|
||||
getters: {
|
||||
flatPerformanceCriteria: (state, circleTitle = "") => {
|
||||
flatPerformanceCriteria: (state) => {
|
||||
if (!state.competenceProfilePage) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -29,12 +38,40 @@ export const useCompetenceStore = defineStore({
|
|||
["asc"]
|
||||
);
|
||||
|
||||
if (circleTitle) {
|
||||
criteria = criteria.filter((c) => c.circle === circleTitle);
|
||||
if (state.selectedCircle.id !== "all") {
|
||||
criteria = criteria.filter(
|
||||
(c) => c.circle.translation_key === state.selectedCircle.id
|
||||
);
|
||||
}
|
||||
|
||||
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: {
|
||||
calcStatusCount(criteria: PerformanceCriteria[]) {
|
||||
|
|
@ -65,6 +102,12 @@ export const useCompetenceStore = defineStore({
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
return this.competenceProfilePage;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { itGet } from "@/fetchHelpers";
|
||||
import type { MediaLibraryPage } from "@/types";
|
||||
import log from "loglevel";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export type MediaLibraryStoreState = {
|
||||
|
|
@ -26,6 +27,7 @@ export const useMediaLibraryStore = defineStore({
|
|||
if (this.mediaLibraryPage && !reload) {
|
||||
return this.mediaLibraryPage;
|
||||
}
|
||||
log.debug("load mediaLibraryPageData");
|
||||
const mediaLibraryPageData = await itGet(`/api/course/page/${slug}/`);
|
||||
|
||||
if (!mediaLibraryPageData) {
|
||||
|
|
|
|||
|
|
@ -125,6 +125,13 @@ export interface CourseWagtailPage {
|
|||
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 {
|
||||
type: "learnpath.LearningContent";
|
||||
minutes: number;
|
||||
|
|
@ -296,7 +303,7 @@ export interface MediaLibraryPage extends CourseWagtailPage {
|
|||
export interface PerformanceCriteria extends CourseWagtailPage {
|
||||
type: "competence.PerformanceCriteria";
|
||||
competence_id: string;
|
||||
circle: string;
|
||||
circle: CircleLight;
|
||||
course_category: CourseCategory;
|
||||
learning_unit: CourseWagtailPage;
|
||||
}
|
||||
|
|
@ -310,5 +317,6 @@ export interface CompetencePage extends CourseWagtailPage {
|
|||
export interface CompetenceProfilePage extends CourseWagtailPage {
|
||||
type: "competence.CompetenceProfilePage";
|
||||
course: Course;
|
||||
circles: CircleLight[];
|
||||
children: CompetencePage[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ svg {
|
|||
.container-large {
|
||||
@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 {
|
||||
|
|
@ -95,7 +100,8 @@ svg {
|
|||
}
|
||||
|
||||
.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
|
||||
disabled:opacity-50 disabled:cursor-not-allowed;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +1,172 @@
|
|||
# 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
|
||||
# > docker push iterativ/vbv-lernwelt-bitbucket
|
||||
# run locally with directory mounted
|
||||
# > 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>
|
||||
|
||||
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
|
||||
# https://github.com/cypress-io/cypress-docker-images/blob/master/base/16.5.0/Dockerfile
|
||||
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
|
||||
# ensure local python is preferred over distribution python
|
||||
ENV PATH /usr/local/bin:$PATH
|
||||
|
||||
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
|
||||
RUN yarn --version
|
||||
# runtime dependencies
|
||||
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
|
||||
# good colors for most applications
|
||||
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
|
||||
ENV GPG_KEY A035C8C19219BA821ECEA86B64E628F8D684696D
|
||||
ENV PYTHON_VERSION 3.10.7
|
||||
|
||||
# Node libraries
|
||||
RUN node -p process.versions
|
||||
RUN set -eux; \
|
||||
\
|
||||
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
|
||||
RUN node -p 'module.paths'
|
||||
# make some useful symlinks that are expected to exist ("/usr/local/bin/python" and friends)
|
||||
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
|
||||
|
||||
# Required by python3-saml
|
||||
|
|
@ -84,13 +175,6 @@ RUN node -p 'module.paths'
|
|||
# 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
|
||||
RUN echo " node version: $(node -v) \n" \
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ else
|
|||
fi
|
||||
|
||||
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
|
||||
python3 server/manage.py runserver "${DJANGO_PORT}" --settings="$DJANGO_SETTINGS_MODULE"
|
||||
cd server && python3 manage.py runserver "${DJANGO_PORT}" --settings="$DJANGO_SETTINGS_MODULE"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -580,7 +580,7 @@ if APP_ENVIRONMENT == "development":
|
|||
# django-extensions
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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(
|
||||
"caprover"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
-r requirements.in
|
||||
|
||||
Werkzeug[watchdog] # https://github.com/pallets/werkzeug
|
||||
ipdb # https://github.com/gotcha/ipdb
|
||||
watchgod # https://github.com/samuelcolvin/watchgod
|
||||
pip-tools
|
||||
|
||||
# Testing
|
||||
|
|
@ -33,5 +31,8 @@ django-extensions # https://github.com/django-extensions/django-extensions
|
|||
django-coverage-plugin # https://github.com/nedbat/django_coverage_plugin
|
||||
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
|
||||
truffleHog
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
anyascii==0.3.1
|
||||
# via wagtail
|
||||
anyio==3.5.0
|
||||
# via watchgod
|
||||
# via watchfiles
|
||||
appnope==0.1.2
|
||||
# via ipython
|
||||
argon2-cffi==21.3.0
|
||||
|
|
@ -15,9 +15,7 @@ argon2-cffi==21.3.0
|
|||
argon2-cffi-bindings==21.2.0
|
||||
# via argon2-cffi
|
||||
asgiref==3.5.0
|
||||
# via
|
||||
# django
|
||||
# uvicorn
|
||||
# via django
|
||||
astroid==2.11.2
|
||||
# via pylint
|
||||
asttokens==2.0.5
|
||||
|
|
@ -35,10 +33,12 @@ backcall==0.2.0
|
|||
# via ipython
|
||||
beautifulsoup4==4.9.3
|
||||
# via wagtail
|
||||
black==22.8.0
|
||||
black==22.10.0
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# ufmt
|
||||
build==0.8.0
|
||||
# via pip-tools
|
||||
certifi==2021.10.8
|
||||
# via
|
||||
# requests
|
||||
|
|
@ -82,11 +82,12 @@ dill==0.3.4
|
|||
# via pylint
|
||||
distlib==0.3.4
|
||||
# via virtualenv
|
||||
dj-database-url==0.5.0
|
||||
dj-database-url==1.0.0
|
||||
# via -r requirements.in
|
||||
django==3.2.13
|
||||
# via
|
||||
# -r requirements.in
|
||||
# dj-database-url
|
||||
# django-cors-headers
|
||||
# django-csp
|
||||
# django-debug-toolbar
|
||||
|
|
@ -100,6 +101,7 @@ django==3.2.13
|
|||
# django-stubs-ext
|
||||
# django-taggit
|
||||
# django-treebeard
|
||||
# django-watchfiles
|
||||
# djangorestframework
|
||||
# drf-spectacular
|
||||
# wagtail
|
||||
|
|
@ -140,6 +142,8 @@ django-taggit==2.1.0
|
|||
# via wagtail
|
||||
django-treebeard==4.5.1
|
||||
# via wagtail
|
||||
django-watchfiles @ https://github.com/q0w/django-watchfiles/archive/issue-1.zip
|
||||
# via -r requirements-dev.in
|
||||
djangorestframework==3.13.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
|
|
@ -222,9 +226,7 @@ libcst==0.4.7
|
|||
# ufmt
|
||||
# usort
|
||||
markupsafe==2.1.1
|
||||
# via
|
||||
# jinja2
|
||||
# werkzeug
|
||||
# via jinja2
|
||||
marshmallow==3.15.0
|
||||
# via environs
|
||||
matplotlib-inline==0.1.3
|
||||
|
|
@ -253,6 +255,7 @@ openpyxl==3.0.9
|
|||
# via tablib
|
||||
packaging==21.3
|
||||
# via
|
||||
# build
|
||||
# marshmallow
|
||||
# pytest
|
||||
# pytest-sugar
|
||||
|
|
@ -264,7 +267,7 @@ pathspec==0.9.0
|
|||
# black
|
||||
# trailrunner
|
||||
pep517==0.12.0
|
||||
# via pip-tools
|
||||
# via build
|
||||
pexpect==4.8.0
|
||||
# via ipython
|
||||
pickleshare==0.7.5
|
||||
|
|
@ -273,7 +276,7 @@ pillow==9.0.1
|
|||
# via
|
||||
# -r requirements.in
|
||||
# wagtail
|
||||
pip-tools==6.6.2
|
||||
pip-tools==6.9.0
|
||||
# via -r requirements-dev.in
|
||||
platformdirs==2.5.1
|
||||
# via
|
||||
|
|
@ -318,7 +321,7 @@ pyparsing==3.0.7
|
|||
# via packaging
|
||||
pyrsistent==0.18.1
|
||||
# via jsonschema
|
||||
pytest==7.1.1
|
||||
pytest==7.1.3
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# pytest-django
|
||||
|
|
@ -403,6 +406,7 @@ toml==0.10.2
|
|||
tomli==2.0.1
|
||||
# via
|
||||
# black
|
||||
# build
|
||||
# django-stubs
|
||||
# mypy
|
||||
# pep517
|
||||
|
|
@ -450,7 +454,7 @@ urllib3==1.26.9
|
|||
# sentry-sdk
|
||||
usort==1.0.5
|
||||
# via ufmt
|
||||
uvicorn[standard]==0.17.6
|
||||
uvicorn[standard]==0.18.3
|
||||
# via -r requirements.in
|
||||
uvloop==0.16.0
|
||||
# via uvicorn
|
||||
|
|
@ -465,11 +469,9 @@ wagtail-factories==2.0.1
|
|||
# via -r requirements.in
|
||||
wagtail-localize==1.2.1
|
||||
# via -r requirements.in
|
||||
watchdog==2.1.9
|
||||
# via werkzeug
|
||||
watchgod==0.8.2
|
||||
watchfiles==0.17.0
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# django-watchfiles
|
||||
# uvicorn
|
||||
wcwidth==0.2.5
|
||||
# via prompt-toolkit
|
||||
|
|
@ -477,8 +479,6 @@ webencodings==0.5.1
|
|||
# via html5lib
|
||||
websockets==10.2
|
||||
# via uvicorn
|
||||
werkzeug[watchdog]==2.2.0
|
||||
# via -r requirements-dev.in
|
||||
wheel==0.37.1
|
||||
# via pip-tools
|
||||
whitenoise==6.0.0
|
||||
|
|
|
|||
|
|
@ -7,15 +7,13 @@
|
|||
anyascii==0.3.1
|
||||
# via wagtail
|
||||
anyio==3.5.0
|
||||
# via watchgod
|
||||
# via watchfiles
|
||||
argon2-cffi==21.3.0
|
||||
# via -r requirements.in
|
||||
argon2-cffi-bindings==21.2.0
|
||||
# via argon2-cffi
|
||||
asgiref==3.5.0
|
||||
# via
|
||||
# django
|
||||
# uvicorn
|
||||
# via django
|
||||
async-timeout==4.0.2
|
||||
# via redis
|
||||
attrs==21.4.0
|
||||
|
|
@ -44,15 +42,15 @@ cryptography==36.0.2
|
|||
# via authlib
|
||||
deprecated==1.2.13
|
||||
# via redis
|
||||
dj-database-url==0.5.0
|
||||
dj-database-url==1.0.0
|
||||
# via -r requirements.in
|
||||
django==3.2.13
|
||||
# via
|
||||
# -r requirements.in
|
||||
# dj-database-url
|
||||
# django-cors-headers
|
||||
# django-csp
|
||||
# django-filter
|
||||
# django-htmx
|
||||
# django-model-utils
|
||||
# django-modelcluster
|
||||
# django-permissionedforms
|
||||
|
|
@ -204,7 +202,7 @@ urllib3==1.26.9
|
|||
# via
|
||||
# requests
|
||||
# sentry-sdk
|
||||
uvicorn[standard]==0.17.6
|
||||
uvicorn[standard]==0.18.3
|
||||
# via -r requirements.in
|
||||
uvloop==0.16.0
|
||||
# via uvicorn
|
||||
|
|
@ -217,7 +215,7 @@ wagtail-factories==2.0.1
|
|||
# via -r requirements.in
|
||||
wagtail-localize==1.2.1
|
||||
# via -r requirements.in
|
||||
watchgod==0.8.1
|
||||
watchfiles==0.17.0
|
||||
# via uvicorn
|
||||
webencodings==0.5.1
|
||||
# via html5lib
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from wagtail.fields import StreamField
|
|||
from wagtail.models import Page
|
||||
|
||||
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):
|
||||
|
|
@ -32,6 +32,7 @@ class CompetenceProfilePage(Page):
|
|||
cls,
|
||||
[
|
||||
"course",
|
||||
"circles",
|
||||
"children",
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
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.learnpath.serializer_helpers import get_it_serializer_class
|
||||
|
||||
|
||||
class PerformanceCriteriaSerializer(
|
||||
|
|
@ -33,7 +33,8 @@ class PerformanceCriteriaSerializer(
|
|||
return LearningUnitPerformanceCriteriaSerializer(obj.learning_unit).data
|
||||
|
||||
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):
|
||||
if obj.learning_unit:
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
|
|||
course = SerializerMethodField()
|
||||
course_category = CourseCategorySerializer(read_only=True)
|
||||
frontend_url = SerializerMethodField()
|
||||
circles = SerializerMethodField()
|
||||
|
||||
meta_fields = []
|
||||
|
||||
|
|
@ -62,6 +63,27 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
|
|||
return CourseSerializer(course_parent_page.specific.course).data
|
||||
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):
|
||||
if hasattr(obj, "get_frontend_url"):
|
||||
return obj.get_frontend_url()
|
||||
|
|
@ -564,7 +564,7 @@ def create_circle_abschluss(lp):
|
|||
|
||||
def create_circle_betreuen(lp):
|
||||
circle = CircleFactory(
|
||||
title="Abschluss",
|
||||
title="Betreuen",
|
||||
parent=lp,
|
||||
)
|
||||
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 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.learnpath.models_learning_unit_content import (
|
||||
AssignmentBlock,
|
||||
|
|
@ -21,7 +22,6 @@ from vbv_lernwelt.learnpath.models_learning_unit_content import (
|
|||
TestBlock,
|
||||
VideoBlock,
|
||||
)
|
||||
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
|
||||
|
||||
|
||||
class LearningPath(Page):
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from rest_framework.fields import SerializerMethodField
|
|||
from vbv_lernwelt.competence.serializers import (
|
||||
PerformanceCriteriaLearningPathSerializer,
|
||||
)
|
||||
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
||||
from vbv_lernwelt.learnpath.models import LearningUnit
|
||||
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
|
||||
|
||||
|
||||
class LearningUnitSerializer(
|
||||
|
|
|
|||
|
|
@ -148,13 +148,15 @@ die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenf
|
|||
contents=[
|
||||
create_relative_link_block(
|
||||
RelativeLinkBlockFactory(
|
||||
title="VBV 303/12.3 Verkehrsrechtsschutz",
|
||||
title="Rechtsstreitigkeiten",
|
||||
description="VBV 303/12.3 Verkehrsrechtsschutz",
|
||||
url="/media/versicherungsvermittlerin-media/category/rechtsstreitigkeiten",
|
||||
)
|
||||
),
|
||||
create_relative_link_block(
|
||||
RelativeLinkBlockFactory(
|
||||
title="VBV 303/13 Reiseversicherung",
|
||||
title="Reisen",
|
||||
description="VBV 303/13 Reiseversicherung",
|
||||
url="/media/versicherungsvermittlerin-media/category/reisen",
|
||||
)
|
||||
),
|
||||
|
|
@ -250,19 +252,22 @@ Diese können negative Folgen verschiedener Art nach sich ziehen, darunter recht
|
|||
contents=[
|
||||
create_relative_link_block(
|
||||
RelativeLinkBlockFactory(
|
||||
title="VBV 303/03 Hausratversicherung",
|
||||
title="Haushalt",
|
||||
description="VBV 303/03 Hausratversicherung",
|
||||
url="/media/versicherungsvermittlerin-media/category/haushalt",
|
||||
)
|
||||
),
|
||||
create_relative_link_block(
|
||||
RelativeLinkBlockFactory(
|
||||
title="VBV 303/12 Rechtschutzversicherung",
|
||||
title="Rechtsstreitigkeiten",
|
||||
desciption="VBV 303/12 Rechtschutzversicherung",
|
||||
url="/media/versicherungsvermittlerin-media/category/rechtsstreitigkeiten",
|
||||
)
|
||||
),
|
||||
create_relative_link_block(
|
||||
RelativeLinkBlockFactory(
|
||||
title="VBV 304/Teil E Obligatorische Krankenversicherung",
|
||||
title="Gesundheit",
|
||||
description="VBV 304/Teil E Obligatorische Krankenversicherung",
|
||||
url="/media/versicherungsvermittlerin-media/category/gesundheit",
|
||||
)
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from wagtail.fields import StreamField
|
|||
from wagtail.models import Page
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ class RelativeLinkBlockFactory(wagtail_factories.StructBlockFactory):
|
|||
class Meta:
|
||||
model = RelativeLinkBlock
|
||||
|
||||
title = "Platzhalter Querverweis"
|
||||
description = "Handlungsfeld"
|
||||
title = "Fahrzeug"
|
||||
description = "Platzhalter Querverweis"
|
||||
link_display_text = "Handlungsfeld anzeigen"
|
||||
icon_url = "/static/icons/demo/icon-hf-reisen.svg"
|
||||
url = "/media/versicherungsvermittlerin-media/category/fahrzeug"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"ignore hash 3": "OvBgP9A2JBgiRad/mM36mkzXSXaJE9BEIENnVEmeZdITvwT09xnxLtT4twkCa8m/loMbPHsvPl0T8lRGVBwjlQ==",
|
||||
"ignore hash 4": "1NpUCSvAKLpDZL9e3tqDaUe8Kk2xAuF1tXosFjBanc4lFCgNcfBp02MD3UjB72ZS",
|
||||
"ignore hash 5": "1LhwZ0DvP4cGBgbBdCfaBQV7eiaOc4jWKdzO9WEXLFT7AaqBN6jqd0uyaZeAZ19K",
|
||||
"ignore hash 6": "A035C8C19219BA821ECEA86B64E628F8D684696D",
|
||||
"json base64 content": "regex:\"content\": \"",
|
||||
"img base64 content": "regex:data:image/png;base64,.*"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
server/requirements/
|
||||
env_secrets/
|
||||
env/bitbucket/Dockerfile
|
||||
env/docker_local.env
|
||||
server/vbv_lernwelt/static/
|
||||
server/vbv_lernwelt/media/
|
||||
|
|
|
|||
Loading…
Reference in New Issue