Merge branch 'feature/refactor-typescript' into develop
This commit is contained in:
commit
4635435ce3
|
|
@ -54,6 +54,7 @@ pipelines:
|
||||||
- npm install
|
- npm install
|
||||||
- npm run prettier:check
|
- npm run prettier:check
|
||||||
- npm run lint
|
- npm run lint
|
||||||
|
- npm run typecheck
|
||||||
- step:
|
- step:
|
||||||
name: cypress tests
|
name: cypress tests
|
||||||
max-time: 45
|
max-time: 45
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/vue": "^1.6.7",
|
"@headlessui/vue": "^1.6.7",
|
||||||
"axios": "^0.26.1",
|
|
||||||
"d3": "^7.6.1",
|
"d3": "^7.6.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"loglevel": "^1.8.0",
|
"loglevel": "^1.8.0",
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ function learninPathSlug(): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDropdownSelect(data: DropdownData) {
|
function handleDropdownSelect(data: DropdownData) {
|
||||||
log.debug("Selected action:", data.action);
|
|
||||||
switch (data.action) {
|
switch (data.action) {
|
||||||
case "settings":
|
case "settings":
|
||||||
router.push("/profile");
|
router.push("/profile");
|
||||||
|
|
@ -76,8 +75,7 @@ onMounted(() => {
|
||||||
log.debug("MainNavigationBar mounted");
|
log.debug("MainNavigationBar mounted");
|
||||||
});
|
});
|
||||||
|
|
||||||
const profileDropdownData: DropdownListItem[][] = [
|
const profileDropdownData: DropdownListItem[] = [
|
||||||
[
|
|
||||||
{
|
{
|
||||||
title: "Kontoeinstellungen",
|
title: "Kontoeinstellungen",
|
||||||
icon: IconSettings as Component,
|
icon: IconSettings as Component,
|
||||||
|
|
@ -85,8 +83,6 @@ const profileDropdownData: DropdownListItem[][] = [
|
||||||
action: "settings",
|
action: "settings",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
{
|
||||||
title: "Abmelden",
|
title: "Abmelden",
|
||||||
icon: IconLogout as Component,
|
icon: IconLogout as Component,
|
||||||
|
|
@ -94,7 +90,6 @@ const profileDropdownData: DropdownListItem[][] = [
|
||||||
action: "logout",
|
action: "logout",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -102,7 +97,6 @@ const profileDropdownData: DropdownListItem[][] = [
|
||||||
<div>
|
<div>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<MobileMenu
|
<MobileMenu
|
||||||
:user-store="userStore"
|
|
||||||
:show="state.showMenu"
|
:show="state.showMenu"
|
||||||
:learning-path-slug="learninPathSlug()"
|
:learning-path-slug="learninPathSlug()"
|
||||||
:learning-path-name="learningPathName()"
|
:learning-path-name="learningPathName()"
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
import IconLogout from "@/components/icons/IconLogout.vue";
|
import IconLogout from "@/components/icons/IconLogout.vue";
|
||||||
import IconSettings from "@/components/icons/IconSettings.vue";
|
import IconSettings from "@/components/icons/IconSettings.vue";
|
||||||
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
|
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
|
||||||
|
import { useUserStore } from "@/stores/user";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
show: boolean;
|
show: boolean;
|
||||||
userStore: object;
|
|
||||||
learningPathName: string;
|
learningPathName: string;
|
||||||
learningPathSlug: string;
|
learningPathSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
criteria: undefined,
|
|
||||||
showState: false,
|
showState: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,14 @@ import * as _ from "lodash";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
import { computed, onMounted } from "vue";
|
import { computed, onMounted } from "vue";
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
import colors from "@/colors.json";
|
import colors from "@/colors.json";
|
||||||
|
import type { LearningSequence } from "@/types";
|
||||||
|
import type { DefaultArcObject } from "d3";
|
||||||
|
|
||||||
const circleStore = useCircleStore();
|
const circleStore = useCircleStore();
|
||||||
|
|
||||||
function someFinished(learningSequence) {
|
function someFinished(learningSequence: LearningSequence) {
|
||||||
if (circleStore.circle) {
|
if (circleStore.circle) {
|
||||||
return circleStore.circle.someFinishedInLearningSequence(
|
return circleStore.circle.someFinishedInLearningSequence(
|
||||||
learningSequence.translation_key
|
learningSequence.translation_key
|
||||||
|
|
@ -18,7 +21,7 @@ function someFinished(learningSequence) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function allFinished(learningSequence) {
|
function allFinished(learningSequence: LearningSequence) {
|
||||||
if (circleStore.circle) {
|
if (circleStore.circle) {
|
||||||
return circleStore.circle.allFinishedInLearningSequence(
|
return circleStore.circle.allFinishedInLearningSequence(
|
||||||
learningSequence.translation_key
|
learningSequence.translation_key
|
||||||
|
|
@ -32,6 +35,17 @@ onMounted(async () => {
|
||||||
render();
|
render();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface CirclePie extends d3.PieArcDatum<number> {
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
slug: string;
|
||||||
|
translation_key: string;
|
||||||
|
arrowStartAngle: number;
|
||||||
|
arrowEndAngle: number;
|
||||||
|
someFinished: boolean;
|
||||||
|
allFinished: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const pieData = computed(() => {
|
const pieData = computed(() => {
|
||||||
const circle = circleStore.circle;
|
const circle = circleStore.circle;
|
||||||
console.log("initial of compute pie data ", circle);
|
console.log("initial of compute pie data ", circle);
|
||||||
|
|
@ -41,37 +55,63 @@ const pieData = computed(() => {
|
||||||
|
|
||||||
const pieWeights = new Array(Math.max(circle.learningSequences.length, 1)).fill(1);
|
const pieWeights = new Array(Math.max(circle.learningSequences.length, 1)).fill(1);
|
||||||
const pieGenerator = d3.pie();
|
const pieGenerator = d3.pie();
|
||||||
let angles = pieGenerator(pieWeights);
|
const angles = pieGenerator(pieWeights);
|
||||||
_.forEach(angles, (pie) => {
|
let result = angles.map((angle) => {
|
||||||
const thisLearningSequence = circle.learningSequences[parseInt(pie.index)];
|
const thisLearningSequence = circle.learningSequences[angle.index];
|
||||||
pie.title = thisLearningSequence.title;
|
|
||||||
pie.icon = thisLearningSequence.icon;
|
return Object.assign(
|
||||||
pie.startAngle = pie.startAngle + Math.PI;
|
{
|
||||||
pie.endAngle = pie.endAngle + Math.PI;
|
startAngle: angle.startAngle + Math.PI,
|
||||||
pie.arrowStartAngle = pie.endAngle + (pie.startAngle - pie.endAngle) / 2;
|
endAngle: angle.endAngle + Math.PI,
|
||||||
pie.arrowEndAngle = pie.startAngle + (pie.startAngle - pie.endAngle) / 2;
|
..._.pick(thisLearningSequence, ["title", "icon", "translation_key", "slug"]),
|
||||||
pie.translation_key = thisLearningSequence.translation_key;
|
arrowStartAngle: angle.endAngle + (angle.startAngle - angle.endAngle) / 2,
|
||||||
pie.slug = thisLearningSequence.slug;
|
arrowEndAngle: angle.startAngle + (angle.startAngle - angle.endAngle) / 2,
|
||||||
pie.someFinished = someFinished(thisLearningSequence);
|
someFinished: someFinished(thisLearningSequence),
|
||||||
pie.allFinished = allFinished(thisLearningSequence);
|
allFinished: allFinished(thisLearningSequence),
|
||||||
|
},
|
||||||
|
angle
|
||||||
|
);
|
||||||
});
|
});
|
||||||
angles = angles.reverse();
|
result = result.reverse();
|
||||||
return angles;
|
return result as CirclePie[];
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
const width = 450;
|
const width = 450;
|
||||||
const height = 450;
|
const height = 450;
|
||||||
const radius = Math.min(width, height) / 2.4;
|
const radius = Math.min(width, height) / 2.4;
|
||||||
|
|
||||||
function render() {
|
function getColor(d: CirclePie) {
|
||||||
const arrowStrokeWidth = 2;
|
let color = colors.gray[300];
|
||||||
|
if (d.someFinished) {
|
||||||
|
color = colors.sky[500];
|
||||||
|
}
|
||||||
|
if (d.allFinished) {
|
||||||
|
color = colors.green[500];
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHoverColor(d: CirclePie) {
|
||||||
|
let color = colors.gray[200];
|
||||||
|
if (d.someFinished) {
|
||||||
|
color = colors.sky[400];
|
||||||
|
}
|
||||||
|
if (d.allFinished) {
|
||||||
|
color = colors.green[400];
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
const svg = d3.select(".circle-visualization");
|
const svg = d3.select(".circle-visualization");
|
||||||
// Clean svg before adding new stuff.
|
// Clean svg before adding new stuff.
|
||||||
svg.selectAll("*").remove();
|
svg.selectAll("*").remove();
|
||||||
|
|
||||||
|
if (pieData.value) {
|
||||||
|
const arrowStrokeWidth = 2;
|
||||||
// Append marker as definition to the svg
|
// Append marker as definition to the svg
|
||||||
svg
|
svg
|
||||||
.attr("viewBox", `0 0 ${width} ${height}`)
|
.attr("viewBox", `0 0 ${width} ${height}`)
|
||||||
|
|
@ -94,28 +134,6 @@ function render() {
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||||
|
|
||||||
function getColor(d) {
|
|
||||||
let color = colors.gray[300];
|
|
||||||
if (d.someFinished) {
|
|
||||||
color = colors.sky[500];
|
|
||||||
}
|
|
||||||
if (d.allFinished) {
|
|
||||||
color = colors.green[500];
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHoverColor(d) {
|
|
||||||
let color = colors.gray[200];
|
|
||||||
if (d.someFinished) {
|
|
||||||
color = colors.sky[400];
|
|
||||||
}
|
|
||||||
if (d.allFinished) {
|
|
||||||
color = colors.green[400];
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the pie diagram wede
|
// Generate the pie diagram wede
|
||||||
const wedgeGenerator = d3
|
const wedgeGenerator = d3
|
||||||
.arc()
|
.arc()
|
||||||
|
|
@ -141,6 +159,7 @@ function render() {
|
||||||
.transition()
|
.transition()
|
||||||
.duration(200)
|
.duration(200)
|
||||||
.attr("fill", (d) => {
|
.attr("fill", (d) => {
|
||||||
|
// @ts-ignore
|
||||||
return getHoverColor(d);
|
return getHoverColor(d);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -149,6 +168,7 @@ function render() {
|
||||||
.transition()
|
.transition()
|
||||||
.duration(200)
|
.duration(200)
|
||||||
.attr("fill", (d) => {
|
.attr("fill", (d) => {
|
||||||
|
// @ts-ignore
|
||||||
return getColor(d);
|
return getColor(d);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -164,6 +184,7 @@ function render() {
|
||||||
return getColor(d);
|
return getColor(d);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
learningSequences.append("path").attr("d", wedgeGenerator);
|
learningSequences.append("path").attr("d", wedgeGenerator);
|
||||||
|
|
||||||
const learningSequenceText = learningSequences
|
const learningSequenceText = learningSequences
|
||||||
|
|
@ -174,7 +195,7 @@ function render() {
|
||||||
return d.title;
|
return d.title;
|
||||||
})
|
})
|
||||||
.attr("transform", function (d) {
|
.attr("transform", function (d) {
|
||||||
let translate = wedgeGenerator.centroid(d);
|
let translate = wedgeGenerator.centroid(d as unknown as DefaultArcObject);
|
||||||
translate = [translate[0], translate[1] + 20];
|
translate = [translate[0], translate[1] + 20];
|
||||||
return "translate(" + translate + ")";
|
return "translate(" + translate + ")";
|
||||||
})
|
})
|
||||||
|
|
@ -191,7 +212,7 @@ function render() {
|
||||||
.attr("width", iconWidth)
|
.attr("width", iconWidth)
|
||||||
.attr("height", iconWidth)
|
.attr("height", iconWidth)
|
||||||
.attr("transform", function (d) {
|
.attr("transform", function (d) {
|
||||||
let translate = wedgeGenerator.centroid(d);
|
let translate = wedgeGenerator.centroid(d as unknown as DefaultArcObject);
|
||||||
translate = [translate[0] - iconWidth / 2, translate[1] - iconWidth];
|
translate = [translate[0] - iconWidth / 2, translate[1] - iconWidth];
|
||||||
return "translate(" + translate + ")";
|
return "translate(" + translate + ")";
|
||||||
})
|
})
|
||||||
|
|
@ -204,10 +225,10 @@ function render() {
|
||||||
.outerRadius(arrowRadius + arrowStrokeWidth)
|
.outerRadius(arrowRadius + arrowStrokeWidth)
|
||||||
.padAngle(20 / 360)
|
.padAngle(20 / 360)
|
||||||
.startAngle((d) => {
|
.startAngle((d) => {
|
||||||
return d.arrowStartAngle;
|
return (d as unknown as CirclePie).arrowStartAngle;
|
||||||
})
|
})
|
||||||
.endAngle((d) => {
|
.endAngle((d) => {
|
||||||
return d.arrowEndAngle;
|
return (d as unknown as CirclePie).arrowEndAngle;
|
||||||
});
|
});
|
||||||
|
|
||||||
const arrows = g
|
const arrows = g
|
||||||
|
|
@ -224,10 +245,13 @@ function render() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const all_arows = g.selectAll(".arrow");
|
const all_arows = g.selectAll(".arrow");
|
||||||
|
// @ts-ignore
|
||||||
all_arows.last().remove();
|
all_arows.last().remove();
|
||||||
|
|
||||||
//Draw arrow paths
|
//Draw arrow paths
|
||||||
|
// @ts-ignore
|
||||||
arrows.append("path").attr("fill", colors.gray[500]).attr("d", arrow);
|
arrows.append("path").attr("fill", colors.gray[500]).attr("d", arrow);
|
||||||
|
}
|
||||||
return svg;
|
return svg;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ const emit = defineEmits(["back", "next"]);
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn-text inline-flex items-center px-3 py-2"
|
class="btn-text inline-flex items-center px-3 py-2"
|
||||||
|
data-cy="close-learning-content"
|
||||||
@click="$emit('back')"
|
@click="$emit('back')"
|
||||||
>
|
>
|
||||||
<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>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { LearningContentType } from "@/types";
|
import type { LearningContentType } from "@/types";
|
||||||
import { learningContentTypesToName } from "@/utils/typeMaps";
|
import { learningContentTypeData } from "@/utils/typeMaps";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningContentType: LearningContentType;
|
learningContentType: LearningContentType;
|
||||||
|
|
@ -11,39 +11,12 @@ const props = defineProps<{
|
||||||
<div
|
<div
|
||||||
class="flex bg-gray-200 rounded-full px-2.5 py-0.5 gap-2 items-center w-min h-min"
|
class="flex bg-gray-200 rounded-full px-2.5 py-0.5 gap-2 items-center w-min h-min"
|
||||||
>
|
>
|
||||||
<it-icon-lc-assignment
|
<component
|
||||||
v-if="props.learningContentType === 'assignment'"
|
:is="learningContentTypeData(props.learningContentType).icon"
|
||||||
class="w-6 h-6"
|
class="w-6 h-6"
|
||||||
/>
|
></component>
|
||||||
<it-icon-lc-exercise
|
|
||||||
v-else-if="props.learningContentType === 'exercise'"
|
|
||||||
class="w-6 h-6"
|
|
||||||
/>
|
|
||||||
<it-icon-lc-book v-else-if="props.learningContentType === 'book'" class="w-6 h-6" />
|
|
||||||
<it-icon-lc-video
|
|
||||||
v-else-if="props.learningContentType === 'video'"
|
|
||||||
class="w-6 h-6"
|
|
||||||
/>
|
|
||||||
<it-icon-lc-media-library
|
|
||||||
v-else-if="props.learningContentType === 'media_library'"
|
|
||||||
class="w-6 h-6"
|
|
||||||
/>
|
|
||||||
<it-icon-lc-test v-else-if="props.learningContentType === 'test'" class="w-6 h-6" />
|
|
||||||
<it-icon-lc-online-training
|
|
||||||
v-else-if="props.learningContentType === 'online_training'"
|
|
||||||
class="w-6 h-6"
|
|
||||||
/>
|
|
||||||
<it-icon-lc-resource
|
|
||||||
v-else-if="props.learningContentType === 'resource'"
|
|
||||||
class="w-6 h-6"
|
|
||||||
/>
|
|
||||||
<it-icon-lc-resource
|
|
||||||
v-else-if="props.learningContentType === 'document'"
|
|
||||||
class="w-6 h-6"
|
|
||||||
/>
|
|
||||||
<it-icon-lc-document v-else class="w-6 h-6" />
|
|
||||||
<p class="whitespace-nowrap">
|
<p class="whitespace-nowrap">
|
||||||
{{ learningContentTypesToName.get(props.learningContentType) }}
|
{{ learningContentTypeData(props.learningContentType).title }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -121,8 +121,8 @@ const learningSequenceBorderClass = computed(() => {
|
||||||
<span class="flex gap-4 items-center xl:h-10">
|
<span class="flex gap-4 items-center xl:h-10">
|
||||||
<button
|
<button
|
||||||
class="cursor-pointer w-full sm:w-auto text-left"
|
class="cursor-pointer w-full sm:w-auto text-left"
|
||||||
@click.stop="circleStore.openLearningContent(learningContent)"
|
|
||||||
:data-cy="`${learningContent.slug}`"
|
:data-cy="`${learningContent.slug}`"
|
||||||
|
@click.stop="circleStore.openLearningContent(learningContent)"
|
||||||
>
|
>
|
||||||
{{ learningContent.title }}
|
{{ learningContent.title }}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
<div>
|
<div>
|
||||||
<h4 class="mb-2 text-bold">{{ title }}</h4>
|
<h4 class="mb-2 text-bold">{{ title }}</h4>
|
||||||
<p class="mb-2">{{ description }}</p>
|
<p class="mb-2">{{ description }}</p>
|
||||||
<media-link :to="url" :blank="openWindow" class="link">
|
<MediaLink :to="url" :blank="openWindow" class="link">
|
||||||
<span class="inline">{{ linkText }}</span>
|
<span class="inline">{{ linkText }}</span>
|
||||||
</media-link>
|
</MediaLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ import { computed } from "vue";
|
||||||
import { RouterLink } from "vue-router";
|
import { RouterLink } from "vue-router";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
...RouterLink.props, // @ts-ignore
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
...RouterLink.props,
|
||||||
blank: {
|
blank: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ defineEmits(["update:modelValue"]);
|
||||||
'opacity-50': disabled,
|
'opacity-50': disabled,
|
||||||
'cursor-not-allowed': disabled,
|
'cursor-not-allowed': disabled,
|
||||||
}"
|
}"
|
||||||
@click="$emit('update:modelValue', !modelValue)"
|
|
||||||
class="w-8 h-8 cursor-pointer"
|
class="w-8 h-8 cursor-pointer"
|
||||||
|
@click="$emit('update:modelValue', !modelValue)"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ import type { DropdownListItem } from "@/types";
|
||||||
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/vue";
|
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
buttonClasses: [string];
|
buttonClasses: [string] | [];
|
||||||
listItems: [[DropdownListItem]];
|
listItems: DropdownListItem[];
|
||||||
align: "left" | "right";
|
align: "left" | "right";
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "select", data: object): void;
|
(e: "select", data: any): void;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -33,8 +33,7 @@ const emit = defineEmits<{
|
||||||
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="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="item in listItems" :key="item.title" class="px-1 py-1">
|
||||||
<div v-for="item in section" :key="item" class="px-1 py-1">
|
|
||||||
<MenuItem>
|
<MenuItem>
|
||||||
<button
|
<button
|
||||||
class="text-black group flex w-full items-center px-0 py-2 text-sm"
|
class="text-black group flex w-full items-center px-0 py-2 text-sm"
|
||||||
|
|
@ -47,7 +46,6 @@ const emit = defineEmits<{
|
||||||
</button>
|
</button>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</MenuItems>
|
</MenuItems>
|
||||||
</transition>
|
</transition>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,7 @@ export const itPost = (url: RequestInfo, data: unknown, options: RequestInit = {
|
||||||
"Content-Type": "application/json;charset=UTF-8",
|
"Content-Type": "application/json;charset=UTF-8",
|
||||||
},
|
},
|
||||||
options?.headers
|
options?.headers
|
||||||
);
|
) as HeadersInit;
|
||||||
|
|
||||||
if (options?.headers) {
|
|
||||||
delete options.headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
options = Object.assign(
|
options = Object.assign(
|
||||||
{
|
{
|
||||||
|
|
@ -43,7 +39,6 @@ export const itPost = (url: RequestInfo, data: unknown, options: RequestInit = {
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
options.headers["X-CSRFToken"] = getCookieValue("csrftoken");
|
options.headers["X-CSRFToken"] = getCookieValue("csrftoken");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export function setI18nLanguage(i18n: any, locale: string) {
|
||||||
*
|
*
|
||||||
* axios.defaults.headers.common['Accept-Language'] = locale
|
* axios.defaults.headers.common['Accept-Language'] = locale
|
||||||
*/
|
*/
|
||||||
document.querySelector("html").setAttribute("lang", locale);
|
document.querySelector("html")?.setAttribute("lang", locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadLocaleMessages(i18n: any, locale: any) {
|
export async function loadLocaleMessages(i18n: any, locale: any) {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,11 @@ const userStore = useUserStore();
|
||||||
<form
|
<form
|
||||||
class="bg-white p-4 lg:p-8"
|
class="bg-white p-4 lg:p-8"
|
||||||
@submit.prevent="
|
@submit.prevent="
|
||||||
userStore.handleLogin(state.username, state.password, route.query.next)
|
userStore.handleLogin(
|
||||||
|
state.username,
|
||||||
|
state.password,
|
||||||
|
route.query.next as string
|
||||||
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ const state = reactive({
|
||||||
});
|
});
|
||||||
|
|
||||||
const dropdownData = [
|
const dropdownData = [
|
||||||
[
|
|
||||||
{
|
{
|
||||||
title: "Option 1",
|
title: "Option 1",
|
||||||
icon: IconLogout,
|
icon: IconLogout,
|
||||||
|
|
@ -35,13 +34,11 @@ const dropdownData = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Option 2",
|
title: "Option 2",
|
||||||
icon: null,
|
icon: IconLogout,
|
||||||
data: {
|
data: {
|
||||||
test: 12,
|
test: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
{
|
||||||
title: "Option 3",
|
title: "Option 3",
|
||||||
icon: IconSettings,
|
icon: IconSettings,
|
||||||
|
|
@ -49,7 +46,6 @@ const dropdownData = [
|
||||||
amount: 34,
|
amount: 34,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// TODO: die CSS-Klasse für die Farben wird hier in der StyleGuideView.vue generiert.
|
// TODO: die CSS-Klasse für die Farben wird hier in der StyleGuideView.vue generiert.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ const competenceStore = useCompetenceStore();
|
||||||
<div class="container-large">
|
<div class="container-large">
|
||||||
<nav class="py-4 lg:pb-8">
|
<nav class="py-4 lg:pb-8">
|
||||||
<router-link
|
<router-link
|
||||||
|
v-if="competenceStore.competenceProfilePage"
|
||||||
class="btn-text inline-flex items-center pl-0"
|
class="btn-text inline-flex items-center pl-0"
|
||||||
:to="competenceStore.competenceProfilePage?.frontend_url"
|
:to="competenceStore.competenceProfilePage?.frontend_url"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ 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";
|
||||||
import { computed, Ref, ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
|
||||||
log.debug("CompetencesMainView created");
|
log.debug("CompetencesMainView created");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ let competencePage: CompetencePage | undefined;
|
||||||
const findCriteria = () => {
|
const findCriteria = () => {
|
||||||
for (const page of competenceStore.competenceProfilePage
|
for (const page of competenceStore.competenceProfilePage
|
||||||
?.children as CompetencePage[]) {
|
?.children as CompetencePage[]) {
|
||||||
for (let criteria of page.children) {
|
for (const criteria of page.children) {
|
||||||
if (criteria.slug === route.params["criteriaSlug"]) {
|
if (criteria.slug === route.params["criteriaSlug"]) {
|
||||||
currentQuestion = criteria;
|
currentQuestion = criteria;
|
||||||
competencePage = page;
|
competencePage = page;
|
||||||
|
|
@ -63,7 +63,7 @@ findCriteria();
|
||||||
@back="router.back()"
|
@back="router.back()"
|
||||||
@next="router.back()"
|
@next="router.back()"
|
||||||
>
|
>
|
||||||
<div class="container-medium">
|
<div v-if="currentQuestion" class="container-medium">
|
||||||
<div class="mt-4 lg:mt-8 p-6 lg:p-12 border">
|
<div class="mt-4 lg:mt-8 p-6 lg:p-12 border">
|
||||||
<h2 class="heading-2">
|
<h2 class="heading-2">
|
||||||
{{ currentQuestion.competence_id }} {{ currentQuestion.title }}
|
{{ currentQuestion.competence_id }} {{ currentQuestion.title }}
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,12 @@ onMounted(async () => {
|
||||||
const learningUnits = circleStore.circle?.learningSequences.flatMap(
|
const learningUnits = circleStore.circle?.learningSequences.flatMap(
|
||||||
(ls) => ls.learningUnits
|
(ls) => ls.learningUnits
|
||||||
);
|
);
|
||||||
|
if (learningUnits) {
|
||||||
wagtailPage = learningUnits.find((lu) => {
|
wagtailPage = learningUnits.find((lu) => {
|
||||||
return lu.slug.endsWith(slugEnd);
|
return lu.slug.endsWith(slugEnd);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (wagtailPage) {
|
if (wagtailPage) {
|
||||||
document
|
document
|
||||||
.getElementById(wagtailPage.slug)
|
.getElementById(wagtailPage.slug)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import LinkCard from "@/components/mediaLibrary/LinkCard.vue";
|
import LinkCard from "@/components/mediaLibrary/LinkCard.vue";
|
||||||
import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
|
import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
|
||||||
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
|
||||||
|
import type { MediaBlockType } 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";
|
import { useRoute } from "vue-router";
|
||||||
|
|
@ -33,28 +34,28 @@ const backLink = computed(() => {
|
||||||
const maxCardItems = 4;
|
const maxCardItems = 4;
|
||||||
const maxListItems = 6;
|
const maxListItems = 6;
|
||||||
|
|
||||||
const displayAsCard = (itemType: string): boolean => {
|
const displayAsCard = (itemType: MediaBlockType): boolean => {
|
||||||
return itemType === "learn_media" || itemType === "relative_link";
|
return itemType === "learn_media" || itemType === "relative_link";
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasMoreItems = (items: object[], maxItems: number): boolean => {
|
function hasMoreItems<T>(items: T[], maxItems: number): boolean {
|
||||||
return items.length > maxItems;
|
return items.length > maxItems;
|
||||||
};
|
}
|
||||||
|
|
||||||
const getMaxDisplayItems = (items: object[], maxItems: number) => {
|
function getMaxDisplayItems<T>(items: T[], maxItems: number) {
|
||||||
return items.slice(0, maxItems);
|
return items.slice(0, maxItems);
|
||||||
};
|
}
|
||||||
|
|
||||||
const getMaxDisplayItemsForType = (itemType: string, items: object[]) => {
|
function getMaxDisplayItemsForType<T>(itemType: MediaBlockType, items: T[]) {
|
||||||
return displayAsCard(itemType)
|
return displayAsCard(itemType)
|
||||||
? getMaxDisplayItems(items, maxCardItems)
|
? getMaxDisplayItems(items, maxCardItems)
|
||||||
: getMaxDisplayItems(items, maxListItems);
|
: getMaxDisplayItems(items, maxListItems);
|
||||||
};
|
}
|
||||||
|
|
||||||
const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
function hasMoreItemsForType<T>(itemType: MediaBlockType, items: T[]) {
|
||||||
const maxItems = displayAsCard(itemType) ? maxCardItems : maxListItems;
|
const maxItems = displayAsCard(itemType) ? maxCardItems : maxListItems;
|
||||||
return hasMoreItems(items, maxItems);
|
return hasMoreItems(items, maxItems);
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -65,7 +66,10 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
||||||
<div class="bg-gray-200 pb-4 lg:pb-12">
|
<div class="bg-gray-200 pb-4 lg:pb-12">
|
||||||
<div class="container-large">
|
<div class="container-large">
|
||||||
<nav class="py-4 lg:pb-8">
|
<nav class="py-4 lg:pb-8">
|
||||||
<router-link class="btn-text inline-flex items-center pl-0" :to="backLink">
|
<router-link
|
||||||
|
class="btn-text inline-flex items-center pl-0"
|
||||||
|
:to="(backLink as string)"
|
||||||
|
>
|
||||||
<it-icon-arrow-left />
|
<it-icon-arrow-left />
|
||||||
<span>zurück</span>
|
<span>zurück</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
@ -93,7 +97,7 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
||||||
<ul>
|
<ul>
|
||||||
<li
|
<li
|
||||||
v-for="item in mediaCategory.items"
|
v-for="item in mediaCategory.items"
|
||||||
:key="item"
|
:key="item.id"
|
||||||
class="mb-2 flex items-center"
|
class="mb-2 flex items-center"
|
||||||
>
|
>
|
||||||
<it-icon-check class="h-8 w-8 text-sky-500 mr-4 flex-none"></it-icon-check>
|
<it-icon-check class="h-8 w-8 text-sky-500 mr-4 flex-none"></it-icon-check>
|
||||||
|
|
@ -141,13 +145,13 @@ const hasMoreItemsForType = (itemType: string, items: object[]) => {
|
||||||
/>
|
/>
|
||||||
<div v-else class="flex items-center justify-between border-b py-4">
|
<div v-else class="flex items-center justify-between border-b py-4">
|
||||||
<h4 class="text-bold">{{ mediaItem.value.title }}</h4>
|
<h4 class="text-bold">{{ mediaItem.value.title }}</h4>
|
||||||
<media-link
|
<MediaLink
|
||||||
: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>
|
</MediaLink>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ const mediaList = computed(() => {
|
||||||
return contentCollection.value.contents[0].type === "learn_media";
|
return contentCollection.value.contents[0].type === "learn_media";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log(learnMediaCollection);
|
|
||||||
return learnMediaCollection?.value;
|
return learnMediaCollection?.value;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
@ -35,7 +34,7 @@ const mediaList = computed(() => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="mediaCategory"
|
v-if="mediaCategory && mediaStore.mediaLibraryPage && mediaList"
|
||||||
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">
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { WagtailCircle } from "@/types";
|
||||||
import { describe, it } from "vitest";
|
import { describe, it } from "vitest";
|
||||||
import { Circle } from "../circle";
|
import { Circle } from "../circle";
|
||||||
import data from "./learning_path_json.json";
|
import data from "./learning_path_json.json";
|
||||||
|
|
@ -6,7 +7,7 @@ describe("Circle.parseJson", () => {
|
||||||
it("can parse circle from api response", () => {
|
it("can parse circle from api response", () => {
|
||||||
const cirleData = data.children.find(
|
const cirleData = data.children.find(
|
||||||
(c) => c.slug === "test-lehrgang-lp-circle-analyse"
|
(c) => c.slug === "test-lehrgang-lp-circle-analyse"
|
||||||
);
|
) as unknown as WagtailCircle;
|
||||||
const circle = Circle.fromJson(cirleData, undefined);
|
const circle = Circle.fromJson(cirleData, undefined);
|
||||||
expect(circle.learningSequences.length).toBe(3);
|
expect(circle.learningSequences.length).toBe(3);
|
||||||
expect(circle.flatLearningContents.length).toBe(7);
|
expect(circle.flatLearningContents.length).toBe(7);
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,59 @@
|
||||||
{
|
{
|
||||||
"id": 372,
|
"id": 362,
|
||||||
"title": "Test Lernpfad",
|
"title": "Test Lernpfad",
|
||||||
"slug": "test-lehrgang-lp",
|
"slug": "test-lehrgang-lp",
|
||||||
"type": "learnpath.LearningPath",
|
"type": "learnpath.LearningPath",
|
||||||
"translation_key": "42e559ca-970f-4a08-9e5e-63860585ee1e",
|
"translation_key": "8a230aa1-075e-4ac1-a8d6-87642c4f33ba",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 373,
|
"id": 363,
|
||||||
"title": "Basis",
|
"title": "Basis",
|
||||||
"slug": "test-lehrgang-lp-topic-basis",
|
"slug": "test-lehrgang-lp-topic-basis",
|
||||||
"type": "learnpath.Topic",
|
"type": "learnpath.Topic",
|
||||||
"translation_key": "d68c1544-cf22-4a59-a81c-8cb977440cd0",
|
"translation_key": "d6e14156-2fb9-4f1b-83ce-6879e364f9a2",
|
||||||
|
"frontend_url": "",
|
||||||
"is_visible": false
|
"is_visible": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 374,
|
"id": 364,
|
||||||
"title": "Basis",
|
"title": "Basis",
|
||||||
"slug": "test-lehrgang-lp-circle-basis",
|
"slug": "test-lehrgang-lp-circle-basis",
|
||||||
"type": "learnpath.Circle",
|
"type": "learnpath.Circle",
|
||||||
"translation_key": "ec62a2af-6f74-4031-b971-c3287bbbc573",
|
"translation_key": "8034e867-4b05-4509-a9bc-99f9f3619e88",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/basis",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 375,
|
"id": 365,
|
||||||
"title": "Starten",
|
"title": "Starten",
|
||||||
"slug": "test-lehrgang-lp-circle-basis-ls-starten",
|
"slug": "test-lehrgang-lp-circle-basis-ls-starten",
|
||||||
"type": "learnpath.LearningSequence",
|
"type": "learnpath.LearningSequence",
|
||||||
"translation_key": "c5fdada9-036d-4516-a50f-6656a1c6b009",
|
"translation_key": "868bc4cb-c5b5-423e-a890-433184cd06e0",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/basis#ls-starten",
|
||||||
"icon": "it-icon-ls-start"
|
"icon": "it-icon-ls-start"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 376,
|
"id": 366,
|
||||||
|
"title": "Einf\u00fchrung",
|
||||||
|
"slug": "test-lehrgang-lp-circle-basis-lu-einf\u00fchrung",
|
||||||
|
"type": "learnpath.LearningUnit",
|
||||||
|
"translation_key": "6b0a4794-9861-4ea4-b422-99261a4347a6",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/basis#lu-einf\u00fchrung",
|
||||||
|
"evaluate_url": "/learn/test-lehrgang-lp/basis/evaluate/einf\u00fchrung",
|
||||||
|
"course_category": {
|
||||||
|
"id": 14,
|
||||||
|
"title": "Allgemein",
|
||||||
|
"general": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 367,
|
||||||
"title": "Einf\u00fchrung",
|
"title": "Einf\u00fchrung",
|
||||||
"slug": "test-lehrgang-lp-circle-basis-lc-einf\u00fchrung",
|
"slug": "test-lehrgang-lp-circle-basis-lc-einf\u00fchrung",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "01de5131-28ce-4b1f-805f-8643384bfd6b",
|
"translation_key": "d1d1b923-f597-4de7-ac44-d02c2f0a1a59",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/basis/einf\u00fchrung",
|
||||||
"minutes": 15,
|
"minutes": 15,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -42,24 +62,41 @@
|
||||||
"description": "Beispiel Dokument",
|
"description": "Beispiel Dokument",
|
||||||
"url": null
|
"url": null
|
||||||
},
|
},
|
||||||
"id": "bd05f721-3e9d-4a11-8fe2-7c04e2365f52"
|
"id": "9f22d0b7-643a-4e97-816a-a41141befc95"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 377,
|
"id": 368,
|
||||||
"title": "Beenden",
|
"title": "Beenden",
|
||||||
"slug": "test-lehrgang-lp-circle-basis-ls-beenden",
|
"slug": "test-lehrgang-lp-circle-basis-ls-beenden",
|
||||||
"type": "learnpath.LearningSequence",
|
"type": "learnpath.LearningSequence",
|
||||||
"translation_key": "128c0162-025f-41be-9842-60016a77cdbc",
|
"translation_key": "338208db-7c85-470e-872f-850e34747873",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/basis#ls-beenden",
|
||||||
"icon": "it-icon-ls-end"
|
"icon": "it-icon-ls-end"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 378,
|
"id": 369,
|
||||||
|
"title": "Beenden",
|
||||||
|
"slug": "test-lehrgang-lp-circle-basis-lu-beenden",
|
||||||
|
"type": "learnpath.LearningUnit",
|
||||||
|
"translation_key": "c14b63d6-3144-41fa-8a3c-2eada6ddd5ea",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/basis#lu-beenden",
|
||||||
|
"evaluate_url": "/learn/test-lehrgang-lp/basis/evaluate/beenden",
|
||||||
|
"course_category": {
|
||||||
|
"id": 14,
|
||||||
|
"title": "Allgemein",
|
||||||
|
"general": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 370,
|
||||||
"title": "Jetzt kann es losgehen!",
|
"title": "Jetzt kann es losgehen!",
|
||||||
"slug": "test-lehrgang-lp-circle-basis-lc-jetzt-kann-es-losgehen",
|
"slug": "test-lehrgang-lp-circle-basis-lc-jetzt-kann-es-losgehen",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "271896b9-6082-4fd4-9d70-6093ec9cc6ea",
|
"translation_key": "6920bcac-597b-462a-9458-32aa5dc8d3f7",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/basis/jetzt-kann-es-losgehen",
|
||||||
"minutes": 30,
|
"minutes": 30,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -68,45 +105,130 @@
|
||||||
"description": "Beispiel Dokument",
|
"description": "Beispiel Dokument",
|
||||||
"url": null
|
"url": null
|
||||||
},
|
},
|
||||||
"id": "204fc13b-a9ae-40de-8e09-f1e922c4fdd9"
|
"id": "1422a7c3-0a9a-4321-88a0-d82d0ed26ba2"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Basis",
|
"description": "Basis",
|
||||||
"job_situations": [],
|
"goal_description": "('In diesem Circle baust du deine Handlungskompetenzen f\u00fcr diese Themen aus:',)",
|
||||||
"goals": [],
|
"goals": [
|
||||||
"experts": []
|
{
|
||||||
|
"type": "goal",
|
||||||
|
"value": "... hier ein Beispieltext f\u00fcr ein Ziel 1",
|
||||||
|
"id": "38afbda4-7b7e-4f5c-88e8-c595c43e1659"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 379,
|
"type": "goal",
|
||||||
|
"value": "... hier ein Beispieltext f\u00fcr ein Ziel 2",
|
||||||
|
"id": "4d00ac58-0499-4316-9af2-356c37dedc35"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "goal",
|
||||||
|
"value": "... hier ein Beispieltext f\u00fcr ein Ziel 3",
|
||||||
|
"id": "945eb104-8cc1-45cd-a07a-a4d3ec4f39a3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"job_situation_description": "Du triffst in diesem Circle auf die folgenden berufstypischen Handlungsfelder:",
|
||||||
|
"job_situations": [
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Job Situation 1",
|
||||||
|
"id": "02fb807a-0d07-4353-81ec-8b8b383954d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Job Situation 2",
|
||||||
|
"id": "371952f6-5871-4bf1-b423-d3dab7371001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Job Situation 3",
|
||||||
|
"id": "116bfa7b-65e8-44a1-8c82-e8b05fd86a01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Job Situation 4",
|
||||||
|
"id": "08baf7dd-8801-4af9-8af8-714989775ddb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Job Situation 5",
|
||||||
|
"id": "93ade4b8-c4fb-4941-98c5-e58336fca4bb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Job Situation 6",
|
||||||
|
"id": "1fac4ee4-6d86-4e9e-9fa4-a99c6659bc8b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Job Situation 7",
|
||||||
|
"id": "06d1e273-dec8-4a0b-ae2c-2baeb7a19ec7"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"experts": [
|
||||||
|
{
|
||||||
|
"type": "person",
|
||||||
|
"value": {
|
||||||
|
"first_name": "Patrizia",
|
||||||
|
"last_name": "Mustermann",
|
||||||
|
"email": "patrizia.mustermann@example.com",
|
||||||
|
"photo": null,
|
||||||
|
"biography": ""
|
||||||
|
},
|
||||||
|
"id": "83490f33-da54-4548-baac-af75ea36651e"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 371,
|
||||||
"title": "Beraten der Kunden",
|
"title": "Beraten der Kunden",
|
||||||
"slug": "test-lehrgang-lp-topic-beraten-der-kunden",
|
"slug": "test-lehrgang-lp-topic-beraten-der-kunden",
|
||||||
"type": "learnpath.Topic",
|
"type": "learnpath.Topic",
|
||||||
"translation_key": "91918780-75f8-4db3-8fb8-91b63f08b9b9",
|
"translation_key": "728a2578-a22c-41df-9079-43a5318c5030",
|
||||||
|
"frontend_url": "",
|
||||||
"is_visible": true
|
"is_visible": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 380,
|
"id": 372,
|
||||||
"title": "Analyse",
|
"title": "Analyse",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse",
|
"slug": "test-lehrgang-lp-circle-analyse",
|
||||||
"type": "learnpath.Circle",
|
"type": "learnpath.Circle",
|
||||||
"translation_key": "50f11be3-a56d-412d-be25-3d272fb5df40",
|
"translation_key": "e429adf5-dd5d-4699-b471-40c782fb507e",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 381,
|
"id": 373,
|
||||||
"title": "Starten",
|
"title": "Starten",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-ls-starten",
|
"slug": "test-lehrgang-lp-circle-analyse-ls-starten",
|
||||||
"type": "learnpath.LearningSequence",
|
"type": "learnpath.LearningSequence",
|
||||||
"translation_key": "07ac0eb9-3671-4b62-8053-1d0c43a1f0fb",
|
"translation_key": "40e977e0-3668-418d-b838-d3774a5cbe7d",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse#ls-starten",
|
||||||
"icon": "it-icon-ls-start"
|
"icon": "it-icon-ls-start"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 382,
|
"id": 374,
|
||||||
|
"title": "Einf\u00fchrung",
|
||||||
|
"slug": "test-lehrgang-lp-circle-analyse-lu-einf\u00fchrung",
|
||||||
|
"type": "learnpath.LearningUnit",
|
||||||
|
"translation_key": "badfd186-26c1-433e-90ad-8cac52eb599f",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse#lu-einf\u00fchrung",
|
||||||
|
"evaluate_url": "/learn/test-lehrgang-lp/analyse/evaluate/einf\u00fchrung",
|
||||||
|
"course_category": {
|
||||||
|
"id": 14,
|
||||||
|
"title": "Allgemein",
|
||||||
|
"general": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 375,
|
||||||
"title": "Einleitung Circle \"Analyse\"",
|
"title": "Einleitung Circle \"Analyse\"",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lc-einleitung-circle-analyse",
|
"slug": "test-lehrgang-lp-circle-analyse-lc-einleitung-circle-analyse",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "00ed0ab2-fdb0-4ee6-a7d2-42a219b849a8",
|
"translation_key": "5e8d6478-6287-4658-94c5-ecbd5d624962",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse/einleitung-circle-analyse",
|
||||||
"minutes": 15,
|
"minutes": 15,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -115,24 +237,27 @@
|
||||||
"description": "Beispiel Dokument",
|
"description": "Beispiel Dokument",
|
||||||
"url": null
|
"url": null
|
||||||
},
|
},
|
||||||
"id": "892a9a4a-8e1e-4f7e-8c35-9bf3bbe5371b"
|
"id": "8b7f183e-1879-4391-953f-52d9a621f435"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 383,
|
"id": 376,
|
||||||
"title": "Beobachten",
|
"title": "Beobachten",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-ls-beobachten",
|
"slug": "test-lehrgang-lp-circle-analyse-ls-beobachten",
|
||||||
"type": "learnpath.LearningSequence",
|
"type": "learnpath.LearningSequence",
|
||||||
"translation_key": "4cb08bc2-d101-43cc-b006-8f2bbb1a0579",
|
"translation_key": "35df96df-2e8d-4f16-aee1-8d72990f63a0",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse#ls-beobachten",
|
||||||
"icon": "it-icon-ls-watch"
|
"icon": "it-icon-ls-watch"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 384,
|
"id": 377,
|
||||||
"title": "Fahrzeug",
|
"title": "Fahrzeug",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lu-fahrzeug",
|
"slug": "test-lehrgang-lp-circle-analyse-lu-fahrzeug",
|
||||||
"type": "learnpath.LearningUnit",
|
"type": "learnpath.LearningUnit",
|
||||||
"translation_key": "8f4afa40-c27e-48f7-a2d7-0e713479a55e",
|
"translation_key": "405d42e4-ee10-4453-8e5f-82e49bb4d597",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse#lu-fahrzeug",
|
||||||
|
"evaluate_url": "/learn/test-lehrgang-lp/analyse/evaluate/fahrzeug",
|
||||||
"course_category": {
|
"course_category": {
|
||||||
"id": 15,
|
"id": 15,
|
||||||
"title": "Fahrzeug",
|
"title": "Fahrzeug",
|
||||||
|
|
@ -140,29 +265,32 @@
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 397,
|
"id": 391,
|
||||||
"title": "Innerhalb des Handlungsfelds \u00abFahrzeug\u00bb bin ich f\u00e4hig, die Ziele und Pl\u00e4ne des Kunden zu ergr\u00fcnden (SOLL).",
|
"title": "Innerhalb des Handlungsfelds \u00abFahrzeug\u00bb bin ich f\u00e4hig, die Ziele und Pl\u00e4ne des Kunden zu ergr\u00fcnden (SOLL).",
|
||||||
"slug": "test-lehrgang-competence-crit-y13-fahrzeug",
|
"slug": "test-lehrgang-competence-crit-y13-fahrzeug",
|
||||||
"type": "competence.PerformanceCriteria",
|
"type": "competence.PerformanceCriteria",
|
||||||
"translation_key": "e9d49552-7d18-418a-94b6-ebb4ee6bf187",
|
"translation_key": "3b714984-afdb-4456-9c01-a59064724929",
|
||||||
|
"frontend_url": "",
|
||||||
"competence_id": "Y1.3"
|
"competence_id": "Y1.3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 398,
|
"id": 392,
|
||||||
"title": "Innerhalb des Handlungsfelds \u00abFahrzeug\u00bb bin ich f\u00e4hig, die IST-Situation des Kunden mit der geeigneten Gespr\u00e4chs-/Fragetechnik zu erfassen.",
|
"title": "Innerhalb des Handlungsfelds \u00abFahrzeug\u00bb bin ich f\u00e4hig, die IST-Situation des Kunden mit der geeigneten Gespr\u00e4chs-/Fragetechnik zu erfassen.",
|
||||||
"slug": "test-lehrgang-competence-crit-y21-fahrzeug",
|
"slug": "test-lehrgang-competence-crit-y21-fahrzeug",
|
||||||
"type": "competence.PerformanceCriteria",
|
"type": "competence.PerformanceCriteria",
|
||||||
"translation_key": "5f257b35-c6ca-49e4-9401-a5d02d53926d",
|
"translation_key": "c2850a27-60c5-471b-9fec-ba0baf152e91",
|
||||||
|
"frontend_url": "",
|
||||||
"competence_id": "Y2.1"
|
"competence_id": "Y2.1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 385,
|
"id": 378,
|
||||||
"title": "Rafael Fasel wechselt sein Auto",
|
"title": "Rafael Fasel wechselt sein Auto",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lc-rafael-fasel-wechselt-sein-auto",
|
"slug": "test-lehrgang-lp-circle-analyse-lc-rafael-fasel-wechselt-sein-auto",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "fda4f870-9307-414d-b07f-eea607a9afb7",
|
"translation_key": "b7779d45-adf4-41fc-a4a5-e95c732b2224",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse/rafael-fasel-wechselt-sein-auto",
|
||||||
"minutes": 30,
|
"minutes": 30,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -171,16 +299,17 @@
|
||||||
"description": "In diesem Online-Training lernst du, wie du den Kundenbedarf ermittelst.",
|
"description": "In diesem Online-Training lernst du, wie du den Kundenbedarf ermittelst.",
|
||||||
"url": ""
|
"url": ""
|
||||||
},
|
},
|
||||||
"id": "700a0f64-0892-4fa5-9e08-3bd34e99edeb"
|
"id": "c79d34cb-0e7e-403d-a672-03d94cf6bdc7"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 386,
|
"id": 379,
|
||||||
"title": "Fachcheck Fahrzeug",
|
"title": "Fachcheck Fahrzeug",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lc-fachcheck-fahrzeug",
|
"slug": "test-lehrgang-lp-circle-analyse-lc-fachcheck-fahrzeug",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "dce0847f-4593-4bba-bd0c-a09c71eb0344",
|
"translation_key": "e395e05c-81bf-4bc6-98e8-3833bebb551c",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse/fachcheck-fahrzeug",
|
||||||
"minutes": 30,
|
"minutes": 30,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -189,16 +318,18 @@
|
||||||
"description": "Beispiel Test",
|
"description": "Beispiel Test",
|
||||||
"url": null
|
"url": null
|
||||||
},
|
},
|
||||||
"id": "9f674aaa-ebf0-4a01-adcc-c0c46394fb10"
|
"id": "ac4c67bc-7de9-4e5c-a35e-e13f5766d6cc"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 387,
|
"id": 380,
|
||||||
"title": "Reisen",
|
"title": "Reisen",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lu-reisen",
|
"slug": "test-lehrgang-lp-circle-analyse-lu-reisen",
|
||||||
"type": "learnpath.LearningUnit",
|
"type": "learnpath.LearningUnit",
|
||||||
"translation_key": "c3f6d33f-8dbc-4d88-9a81-3c602c4f9cc8",
|
"translation_key": "d0c956cc-3c86-4e08-9990-ed4e85d03219",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse#lu-reisen",
|
||||||
|
"evaluate_url": "/learn/test-lehrgang-lp/analyse/evaluate/reisen",
|
||||||
"course_category": {
|
"course_category": {
|
||||||
"id": 16,
|
"id": 16,
|
||||||
"title": "Reisen",
|
"title": "Reisen",
|
||||||
|
|
@ -206,21 +337,23 @@
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 399,
|
"id": 393,
|
||||||
"title": "Innerhalb des Handlungsfelds \u00abReisen\u00bb bin ich f\u00e4hig, die Ziele und Pl\u00e4ne des Kunden zu ergr\u00fcnden (SOLL).",
|
"title": "Innerhalb des Handlungsfelds \u00abReisen\u00bb bin ich f\u00e4hig, die Ziele und Pl\u00e4ne des Kunden zu ergr\u00fcnden (SOLL).",
|
||||||
"slug": "test-lehrgang-competence-crit-y13-reisen",
|
"slug": "test-lehrgang-competence-crit-y13-reisen",
|
||||||
"type": "competence.PerformanceCriteria",
|
"type": "competence.PerformanceCriteria",
|
||||||
"translation_key": "1e488b69-8a3e-4acc-9547-48c103e0d038",
|
"translation_key": "1df45a12-41f2-4ff5-8580-d5a7caf5dd56",
|
||||||
|
"frontend_url": "",
|
||||||
"competence_id": "Y1.3"
|
"competence_id": "Y1.3"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 388,
|
"id": 381,
|
||||||
"title": "Reiseversicherung",
|
"title": "Reiseversicherung",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lc-reiseversicherung",
|
"slug": "test-lehrgang-lp-circle-analyse-lc-reiseversicherung",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "ff513aae-efe1-4974-b67f-7a292b8aef86",
|
"translation_key": "bad7439a-8b0c-4877-8d6c-78f292be83d4",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse/reiseversicherung",
|
||||||
"minutes": 240,
|
"minutes": 240,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -229,16 +362,17 @@
|
||||||
"description": "Beispiel \u00dcbung",
|
"description": "Beispiel \u00dcbung",
|
||||||
"url": null
|
"url": null
|
||||||
},
|
},
|
||||||
"id": "f35f213e-1a33-49fe-97c5-26e15161719f"
|
"id": "7e1ee533-7f75-495b-a2bc-8bbd2b1311c9"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 389,
|
"id": 382,
|
||||||
"title": "Emma und Ayla campen durch Amerika",
|
"title": "Emma und Ayla campen durch Amerika",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lc-emma-und-ayla-campen-durch-amerika",
|
"slug": "test-lehrgang-lp-circle-analyse-lc-emma-und-ayla-campen-durch-amerika",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "a77b0f9d-9a70-47bd-8e62-7580d70a4306",
|
"translation_key": "27f9d8f3-209c-4d55-94d9-2e70fbfe163b",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse/emma-und-ayla-campen-durch-amerika",
|
||||||
"minutes": 120,
|
"minutes": 120,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -247,24 +381,41 @@
|
||||||
"description": "Beispiel \u00dcbung",
|
"description": "Beispiel \u00dcbung",
|
||||||
"url": "/static/media/web_based_trainings/story-06-a-01-emma-und-ayla-campen-durch-amerika-einstieg/scormcontent/index.html"
|
"url": "/static/media/web_based_trainings/story-06-a-01-emma-und-ayla-campen-durch-amerika-einstieg/scormcontent/index.html"
|
||||||
},
|
},
|
||||||
"id": "60f087ff-fa3a-4da2-820f-4fcdf449f70d"
|
"id": "b08e1851-8583-4428-b1bc-402c7095130b"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 390,
|
"id": 383,
|
||||||
"title": "Beenden",
|
"title": "Beenden",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-ls-beenden",
|
"slug": "test-lehrgang-lp-circle-analyse-ls-beenden",
|
||||||
"type": "learnpath.LearningSequence",
|
"type": "learnpath.LearningSequence",
|
||||||
"translation_key": "06f1e998-b827-41cc-8129-d72d731719c1",
|
"translation_key": "68be244d-0e00-4700-834c-57b4db366fc1",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse#ls-beenden",
|
||||||
"icon": "it-icon-ls-end"
|
"icon": "it-icon-ls-end"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 391,
|
"id": 384,
|
||||||
|
"title": "Beenden",
|
||||||
|
"slug": "test-lehrgang-lp-circle-analyse-lu-beenden",
|
||||||
|
"type": "learnpath.LearningUnit",
|
||||||
|
"translation_key": "d594db87-ad78-491b-bf1b-410adfa3a0ba",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse#lu-beenden",
|
||||||
|
"evaluate_url": "/learn/test-lehrgang-lp/analyse/evaluate/beenden",
|
||||||
|
"course_category": {
|
||||||
|
"id": 14,
|
||||||
|
"title": "Allgemein",
|
||||||
|
"general": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 385,
|
||||||
"title": "KompetenzNavi anschauen",
|
"title": "KompetenzNavi anschauen",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lc-kompetenzprofil-anschauen",
|
"slug": "test-lehrgang-lp-circle-analyse-lc-kompetenznavi-anschauen",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "6cc47dc1-a74f-4cbf-afa6-23885891c82f",
|
"translation_key": "8ee57ba5-e09e-4058-937a-b733ea72b969",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse/kompetenznavi-anschauen",
|
||||||
"minutes": 30,
|
"minutes": 30,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -273,16 +424,17 @@
|
||||||
"description": "Beispiel Dokument",
|
"description": "Beispiel Dokument",
|
||||||
"url": null
|
"url": null
|
||||||
},
|
},
|
||||||
"id": "3f685055-4e3e-4ca9-93af-bac19236931d"
|
"id": "3ef87e69-5e5c-415a-934c-ed47ad9fdd93"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 392,
|
"id": 386,
|
||||||
"title": "Circle \"Analyse\" abschliessen",
|
"title": "Circle \"Analyse\" abschliessen",
|
||||||
"slug": "test-lehrgang-lp-circle-analyse-lc-circle-analyse-abschliessen",
|
"slug": "test-lehrgang-lp-circle-analyse-lc-circle-analyse-abschliessen",
|
||||||
"type": "learnpath.LearningContent",
|
"type": "learnpath.LearningContent",
|
||||||
"translation_key": "9b32e2cd-1368-4885-a79b-906b45ba04bc",
|
"translation_key": "90d9ab63-cc0f-492f-aad1-f7d448ee5b2c",
|
||||||
|
"frontend_url": "/learn/test-lehrgang-lp/analyse/circle-analyse-abschliessen",
|
||||||
"minutes": 30,
|
"minutes": 30,
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
|
|
@ -291,34 +443,36 @@
|
||||||
"description": "Beispiel Dokument",
|
"description": "Beispiel Dokument",
|
||||||
"url": null
|
"url": null
|
||||||
},
|
},
|
||||||
"id": "650b7b15-b522-4df7-ac5b-6a654f12334f"
|
"id": "21415232-862b-488c-9987-4f4ee369a854"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Unit-Test Circle",
|
"description": "Unit-Test Circle",
|
||||||
"job_situations": [
|
"goal_description": "('In diesem Circle baust du deine Handlungskompetenzen f\u00fcr diese Themen aus:',)",
|
||||||
{
|
|
||||||
"type": "job_situation",
|
|
||||||
"value": "Autoversicherung",
|
|
||||||
"id": "c5a6b365-0a18-47d5-b6e1-6cb8b8ec7d35"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "job_situation",
|
|
||||||
"value": "Autokauf",
|
|
||||||
"id": "e969d2a2-b383-482c-a721-88552af086a6"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"goals": [
|
"goals": [
|
||||||
{
|
{
|
||||||
"type": "goal",
|
"type": "goal",
|
||||||
"value": "... die heutige Versicherungssituation von Privat- oder Gesch\u00e4ftskunden einzusch\u00e4tzen.",
|
"value": "... die heutige Versicherungssituation von Privat- oder Gesch\u00e4ftskunden einzusch\u00e4tzen.",
|
||||||
"id": "d9ad8aed-d7d6-42c7-b6d4-65102c8ddf10"
|
"id": "d1f001fa-f7b8-41a3-90c7-632260ff7054"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "goal",
|
"type": "goal",
|
||||||
"value": "... deinem Kunden seine optimale L\u00f6sung aufzuzeigen",
|
"value": "... deinem Kunden seine optimale L\u00f6sung aufzuzeigen",
|
||||||
"id": "2506950c-45cb-474f-acb9-45e83e9ebe1b"
|
"id": "8f73bb0f-e898-4961-ab28-dd34caca2c0b"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"job_situation_description": "Du triffst in diesem Circle auf die folgenden berufstypischen Handlungsfelder:",
|
||||||
|
"job_situations": [
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Autoversicherung",
|
||||||
|
"id": "df46930b-2911-4161-a677-75b4b156dff3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "job_situation",
|
||||||
|
"value": "Autokauf",
|
||||||
|
"id": "17a6d252-e942-44cc-920f-015e38e727be"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"experts": [
|
"experts": [
|
||||||
|
|
@ -331,14 +485,14 @@
|
||||||
"photo": null,
|
"photo": null,
|
||||||
"biography": ""
|
"biography": ""
|
||||||
},
|
},
|
||||||
"id": "b7b0ff2e-f840-4d74-99c1-c7a5ee6dc14e"
|
"id": "b0633305-5e74-43eb-93b8-ebbcfb1b17d1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"course": {
|
"course": {
|
||||||
"id": -1,
|
"id": -1,
|
||||||
"title": "Test Lerngang",
|
"title": "Test Lehrgang",
|
||||||
"category_name": "Handlungsfeld"
|
"category_name": "Handlungsfeld"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,25 @@ import requests
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
client = requests.session()
|
client = requests.session()
|
||||||
client.get('http://localhost:8000/')
|
client.get("http://localhost:8001/")
|
||||||
|
|
||||||
client.post(
|
client.post(
|
||||||
'http://localhost:8000/api/core/login/',
|
"http://localhost:8001/api/core/login/",
|
||||||
json={
|
json={
|
||||||
'username': 'admin',
|
"username": "admin",
|
||||||
'password': 'test',
|
"password": "test",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
response = client.get(
|
response = client.get(
|
||||||
'http://localhost:8000/api/course/page/test-lehrgang-lp/',
|
"http://localhost:8001/api/course/page/test-lehrgang-lp/",
|
||||||
)
|
)
|
||||||
print(response.status_code)
|
print(response.status_code)
|
||||||
print(response.json())
|
print(response.json())
|
||||||
|
|
||||||
with open('learning_path_json.json', 'w') as f:
|
with open("learning_path_json.json", "w") as f:
|
||||||
f.write(json.dumps(response.json(), indent=4))
|
f.write(json.dumps(response.json(), indent=4))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -4,34 +4,13 @@ import type {
|
||||||
CircleGoal,
|
CircleGoal,
|
||||||
CircleJobSituation,
|
CircleJobSituation,
|
||||||
CourseCompletion,
|
CourseCompletion,
|
||||||
CourseCompletionStatus,
|
|
||||||
CourseWagtailPage,
|
|
||||||
LearningContent,
|
LearningContent,
|
||||||
LearningSequence,
|
LearningSequence,
|
||||||
LearningUnit,
|
LearningUnit,
|
||||||
LearningUnitPerformanceCriteria,
|
LearningUnitPerformanceCriteria,
|
||||||
|
WagtailCircle,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
|
|
||||||
function _createEmptyLearningUnit(
|
|
||||||
parentLearningSequence: LearningSequence
|
|
||||||
): LearningUnit {
|
|
||||||
return {
|
|
||||||
id: 0,
|
|
||||||
title: "",
|
|
||||||
slug: "",
|
|
||||||
translation_key: "",
|
|
||||||
type: "learnpath.LearningUnit",
|
|
||||||
frontend_url: "",
|
|
||||||
learningContents: [],
|
|
||||||
minutes: 0,
|
|
||||||
parentLearningSequence: parentLearningSequence,
|
|
||||||
children: [],
|
|
||||||
last: true,
|
|
||||||
completion_status: "unknown",
|
|
||||||
evaluate_url: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseLearningSequences(
|
export function parseLearningSequences(
|
||||||
circle: Circle,
|
circle: Circle,
|
||||||
children: CircleChild[]
|
children: CircleChild[]
|
||||||
|
|
@ -47,23 +26,15 @@ export function parseLearningSequences(
|
||||||
if (learningSequence) {
|
if (learningSequence) {
|
||||||
if (learningUnit) {
|
if (learningUnit) {
|
||||||
learningUnit.last = true;
|
learningUnit.last = true;
|
||||||
learningSequence.learningUnits.push(learningUnit);
|
|
||||||
}
|
}
|
||||||
result.push(learningSequence);
|
|
||||||
}
|
}
|
||||||
learningSequence = Object.assign(child, { learningUnits: [] });
|
learningSequence = Object.assign(child, { learningUnits: [] });
|
||||||
|
result.push(learningSequence);
|
||||||
// initialize empty learning unit if there will not come a learning unit next
|
|
||||||
learningUnit = _createEmptyLearningUnit(learningSequence);
|
|
||||||
} else if (child.type === "learnpath.LearningUnit") {
|
} else if (child.type === "learnpath.LearningUnit") {
|
||||||
if (!learningSequence) {
|
if (!learningSequence) {
|
||||||
throw new Error("LearningUnit found before LearningSequence");
|
throw new Error("LearningUnit found before LearningSequence");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (learningUnit && learningUnit.learningContents.length) {
|
|
||||||
learningSequence.learningUnits.push(learningUnit);
|
|
||||||
}
|
|
||||||
|
|
||||||
learningUnit = Object.assign(child, {
|
learningUnit = Object.assign(child, {
|
||||||
learningContents: [],
|
learningContents: [],
|
||||||
parentLearningSequence: learningSequence,
|
parentLearningSequence: learningSequence,
|
||||||
|
|
@ -74,9 +45,10 @@ export function parseLearningSequences(
|
||||||
return c;
|
return c;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
learningSequence.learningUnits.push(learningUnit);
|
||||||
} else if (child.type === "learnpath.LearningContent") {
|
} else if (child.type === "learnpath.LearningContent") {
|
||||||
if (!learningUnit) {
|
if (!learningUnit) {
|
||||||
throw new Error("LearningContent found before LearningUnit");
|
throw new Error(`LearningContent found before LearningUnit ${child.slug}`);
|
||||||
}
|
}
|
||||||
previousLearningContent = learningContent;
|
previousLearningContent = learningContent;
|
||||||
|
|
||||||
|
|
@ -92,14 +64,13 @@ export function parseLearningSequences(
|
||||||
}
|
}
|
||||||
|
|
||||||
learningUnit.learningContents.push(child);
|
learningUnit.learningContents.push(child);
|
||||||
|
} else {
|
||||||
|
throw new Error("Unknown CircleChild found...");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (learningUnit && learningSequence) {
|
if (learningUnit) {
|
||||||
// TypeScript does not get it here...
|
|
||||||
learningUnit.last = true;
|
learningUnit.last = true;
|
||||||
(learningSequence as LearningSequence).learningUnits.push(learningUnit);
|
|
||||||
result.push(learningSequence);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Finished with LearningContent but there is no LearningSequence and LearningUnit"
|
"Finished with LearningContent but there is no LearningSequence and LearningUnit"
|
||||||
|
|
@ -121,10 +92,9 @@ export function parseLearningSequences(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Circle implements CourseWagtailPage {
|
export class Circle implements WagtailCircle {
|
||||||
readonly type = "learnpath.Circle";
|
readonly type = "learnpath.Circle";
|
||||||
readonly learningSequences: LearningSequence[];
|
readonly learningSequences: LearningSequence[];
|
||||||
completion_status: CourseCompletionStatus = "unknown";
|
|
||||||
|
|
||||||
nextCircle?: Circle;
|
nextCircle?: Circle;
|
||||||
previousCircle?: Circle;
|
previousCircle?: Circle;
|
||||||
|
|
@ -136,30 +106,33 @@ export class Circle implements CourseWagtailPage {
|
||||||
public readonly translation_key: string,
|
public readonly translation_key: string,
|
||||||
public readonly frontend_url: string,
|
public readonly frontend_url: string,
|
||||||
public readonly description: string,
|
public readonly description: string,
|
||||||
public children: CircleChild[],
|
public readonly children: CircleChild[],
|
||||||
public goal_description: string,
|
public readonly goal_description: string,
|
||||||
public goals: CircleGoal[],
|
public readonly goals: CircleGoal[],
|
||||||
public job_situation_description: string,
|
public readonly job_situation_description: string,
|
||||||
public job_situations: CircleJobSituation[],
|
public readonly job_situations: CircleJobSituation[],
|
||||||
public readonly parentLearningPath?: LearningPath
|
public readonly parentLearningPath?: LearningPath
|
||||||
) {
|
) {
|
||||||
this.learningSequences = parseLearningSequences(this, this.children);
|
this.learningSequences = parseLearningSequences(this, this.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static fromJson(json: any, learningPath?: LearningPath): Circle {
|
public static fromJson(
|
||||||
|
wagtailCircle: WagtailCircle,
|
||||||
|
learningPath?: LearningPath
|
||||||
|
): Circle {
|
||||||
// TODO add error checking when the data does not conform to the schema
|
// TODO add error checking when the data does not conform to the schema
|
||||||
return new Circle(
|
return new Circle(
|
||||||
json.id,
|
wagtailCircle.id,
|
||||||
json.slug,
|
wagtailCircle.slug,
|
||||||
json.title,
|
wagtailCircle.title,
|
||||||
json.translation_key,
|
wagtailCircle.translation_key,
|
||||||
json.frontend_url,
|
wagtailCircle.frontend_url,
|
||||||
json.description,
|
wagtailCircle.description,
|
||||||
json.children,
|
wagtailCircle.children,
|
||||||
json.goal_description,
|
wagtailCircle.goal_description,
|
||||||
json.goals,
|
wagtailCircle.goals,
|
||||||
json.job_situation_description,
|
wagtailCircle.job_situation_description,
|
||||||
json.job_situations,
|
wagtailCircle.job_situations,
|
||||||
learningPath
|
learningPath
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ import * as _ from "lodash";
|
||||||
|
|
||||||
import { Circle } from "@/services/circle";
|
import { Circle } from "@/services/circle";
|
||||||
import type {
|
import type {
|
||||||
|
Course,
|
||||||
CourseCompletion,
|
CourseCompletion,
|
||||||
CourseCompletionStatus,
|
|
||||||
CourseWagtailPage,
|
|
||||||
LearningContent,
|
LearningContent,
|
||||||
LearningPathChild,
|
LearningPathChild,
|
||||||
Topic,
|
Topic,
|
||||||
|
WagtailLearningPath,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
|
|
||||||
function getLastCompleted(courseId: number, completionData: CourseCompletion[]) {
|
function getLastCompleted(courseId: number, completionData: CourseCompletion[]) {
|
||||||
|
|
@ -22,21 +22,23 @@ function getLastCompleted(courseId: number, completionData: CourseCompletion[])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LearningPath implements CourseWagtailPage {
|
export class LearningPath implements WagtailLearningPath {
|
||||||
readonly type = "learnpath.LearningPath";
|
readonly type = "learnpath.LearningPath";
|
||||||
public topics: Topic[];
|
public topics: Topic[];
|
||||||
public circles: Circle[];
|
public circles: Circle[];
|
||||||
public nextLearningContent?: LearningContent;
|
public nextLearningContent?: LearningContent;
|
||||||
readonly completion_status: CourseCompletionStatus = "unknown";
|
|
||||||
|
|
||||||
public static fromJson(json: any, completionData: CourseCompletion[]): LearningPath {
|
public static fromJson(
|
||||||
|
json: WagtailLearningPath,
|
||||||
|
completionData: CourseCompletion[]
|
||||||
|
): LearningPath {
|
||||||
return new LearningPath(
|
return new LearningPath(
|
||||||
json.id,
|
json.id,
|
||||||
json.slug,
|
json.slug,
|
||||||
json.course.title,
|
json.course.title,
|
||||||
json.translation_key,
|
json.translation_key,
|
||||||
json.frontend_url,
|
json.frontend_url,
|
||||||
json.course.id,
|
json.course,
|
||||||
json.children,
|
json.children,
|
||||||
completionData
|
completionData
|
||||||
);
|
);
|
||||||
|
|
@ -48,7 +50,7 @@ export class LearningPath implements CourseWagtailPage {
|
||||||
public readonly title: string,
|
public readonly title: string,
|
||||||
public readonly translation_key: string,
|
public readonly translation_key: string,
|
||||||
public readonly frontend_url: string,
|
public readonly frontend_url: string,
|
||||||
public readonly courseId: number,
|
public readonly course: Course,
|
||||||
public children: LearningPathChild[],
|
public children: LearningPathChild[],
|
||||||
completionData?: CourseCompletion[]
|
completionData?: CourseCompletion[]
|
||||||
) {
|
) {
|
||||||
|
|
@ -95,7 +97,7 @@ export class LearningPath implements CourseWagtailPage {
|
||||||
this.nextLearningContent = undefined;
|
this.nextLearningContent = undefined;
|
||||||
|
|
||||||
const lastCompletedLearningContent = getLastCompleted(
|
const lastCompletedLearningContent = getLastCompleted(
|
||||||
this.courseId,
|
this.course.id,
|
||||||
completionData
|
completionData
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import type {
|
||||||
LearningContent,
|
LearningContent,
|
||||||
LearningUnit,
|
LearningUnit,
|
||||||
LearningUnitPerformanceCriteria,
|
LearningUnitPerformanceCriteria,
|
||||||
|
PerformanceCriteria,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
|
|
@ -85,17 +86,23 @@ export const useCircleStore = defineStore({
|
||||||
return learningUnit;
|
return learningUnit;
|
||||||
},
|
},
|
||||||
async markCompletion(
|
async markCompletion(
|
||||||
page: LearningContent | LearningUnitPerformanceCriteria,
|
page:
|
||||||
|
| LearningContent
|
||||||
|
| LearningUnitPerformanceCriteria
|
||||||
|
| PerformanceCriteria
|
||||||
|
| undefined,
|
||||||
completion_status: CourseCompletionStatus = "success"
|
completion_status: CourseCompletionStatus = "success"
|
||||||
) {
|
) {
|
||||||
const completionStore = useCompletionStore();
|
const completionStore = useCompletionStore();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (page) {
|
||||||
page.completion_status = completion_status;
|
page.completion_status = completion_status;
|
||||||
const completionData = await completionStore.markPage(page);
|
const completionData = await completionStore.markPage(page);
|
||||||
if (this.circle) {
|
if (this.circle) {
|
||||||
this.circle.parseCompletionData(completionData);
|
this.circle.parseCompletionData(completionData);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
return error;
|
return error;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { itGet } from "@/fetchHelpers";
|
import { itGet } from "@/fetchHelpers";
|
||||||
import { useCompletionStore } from "@/stores/completion";
|
import { useCompletionStore } from "@/stores/completion";
|
||||||
import type {
|
import type {
|
||||||
|
BaseCourseWagtailPage,
|
||||||
CompetencePage,
|
CompetencePage,
|
||||||
CompetenceProfilePage,
|
CompetenceProfilePage,
|
||||||
CourseWagtailPage,
|
|
||||||
PerformanceCriteria,
|
PerformanceCriteria,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
@ -103,9 +103,11 @@ export const useCompetenceStore = defineStore({
|
||||||
|
|
||||||
this.competenceProfilePage = competenceProfilePageData;
|
this.competenceProfilePage = competenceProfilePageData;
|
||||||
|
|
||||||
const circles = competenceProfilePageData.circles.map((c: CourseWagtailPage) => {
|
const circles = competenceProfilePageData.circles.map(
|
||||||
|
(c: BaseCourseWagtailPage) => {
|
||||||
return { id: c.translation_key, name: `Circle: ${c.title}` };
|
return { id: c.translation_key, name: `Circle: ${c.title}` };
|
||||||
});
|
}
|
||||||
|
);
|
||||||
this.availableCircles = [{ id: "all", name: "Circle: Alle" }, ...circles];
|
this.availableCircles = [{ id: "all", name: "Circle: Alle" }, ...circles];
|
||||||
|
|
||||||
await this.parseCompletionData();
|
await this.parseCompletionData();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { itGet, itPost } from "@/fetchHelpers";
|
import { itGet, itPost } from "@/fetchHelpers";
|
||||||
import type { CourseCompletion, CourseWagtailPage } from "@/types";
|
import type { BaseCourseWagtailPage, CourseCompletion } from "@/types";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
export type CompletionStoreState = {
|
export type CompletionStoreState = {
|
||||||
|
|
@ -28,7 +28,7 @@ export const useCompletionStore = defineStore({
|
||||||
this.completionData = completionData;
|
this.completionData = completionData;
|
||||||
return this.completionData || [];
|
return this.completionData || [];
|
||||||
},
|
},
|
||||||
async markPage(page: CourseWagtailPage) {
|
async markPage(page: BaseCourseWagtailPage) {
|
||||||
const completionData = await itPost("/api/course/completion/mark/", {
|
const completionData = await itPost("/api/course/completion/mark/", {
|
||||||
page_key: page.translation_key,
|
page_key: page.translation_key,
|
||||||
completion_status: page.completion_status,
|
completion_status: page.completion_status,
|
||||||
|
|
|
||||||
|
|
@ -3,105 +3,132 @@ import type { Component } from "vue";
|
||||||
|
|
||||||
export type CourseCompletionStatus = "unknown" | "fail" | "success";
|
export type CourseCompletionStatus = "unknown" | "fail" | "success";
|
||||||
|
|
||||||
export type LearningContentType =
|
export interface BaseCourseWagtailPage {
|
||||||
| "assignment"
|
readonly id: number;
|
||||||
| "book"
|
readonly title: string;
|
||||||
| "document"
|
readonly slug: string;
|
||||||
| "exercise"
|
readonly type: string;
|
||||||
| "media_library"
|
readonly translation_key: string;
|
||||||
| "online_training"
|
readonly frontend_url: string;
|
||||||
| "resource"
|
completion_status?: CourseCompletionStatus;
|
||||||
| "test"
|
completion_status_updated_at?: string;
|
||||||
| "video"
|
|
||||||
| "placeholder";
|
|
||||||
|
|
||||||
export interface LearningContentBlock {
|
|
||||||
type: LearningContentType;
|
|
||||||
value: {
|
|
||||||
description: string;
|
|
||||||
};
|
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AssignmentBlock {
|
export interface CircleLight {
|
||||||
type: "assignment";
|
readonly id: number;
|
||||||
value: {
|
readonly title: string;
|
||||||
description: string;
|
readonly translation_key: string;
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BookBlock {
|
export interface BaseLearningContentBlock {
|
||||||
type: "book";
|
readonly type: string;
|
||||||
value: {
|
readonly id: string;
|
||||||
|
readonly value: {
|
||||||
description: string;
|
description: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
text?: string;
|
||||||
};
|
};
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocumentBlock {
|
export interface AssignmentBlock extends BaseLearningContentBlock {
|
||||||
type: "document";
|
readonly type: "assignment";
|
||||||
value: {
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExerciseBlock {
|
export interface BookBlock extends BaseLearningContentBlock {
|
||||||
type: "exercise";
|
readonly type: "book";
|
||||||
value: {
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaLibraryBlock {
|
export interface DocumentBlock extends BaseLearningContentBlock {
|
||||||
type: "media_library";
|
readonly type: "document";
|
||||||
value: {
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OnlineTrainingBlock {
|
export interface ExerciseBlock extends BaseLearningContentBlock {
|
||||||
type: "online_training";
|
readonly type: "exercise";
|
||||||
value: {
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResourceBlock {
|
export interface MediaLibraryBlock extends BaseLearningContentBlock {
|
||||||
type: "resource";
|
readonly type: "media_library";
|
||||||
value: {
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TestBlock {
|
export interface OnlineTrainingBlock extends BaseLearningContentBlock {
|
||||||
type: "test";
|
readonly type: "online_training";
|
||||||
value: {
|
|
||||||
description: string;
|
|
||||||
url: string;
|
|
||||||
};
|
|
||||||
id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoBlock {
|
export interface ResourceBlock extends BaseLearningContentBlock {
|
||||||
type: "video";
|
readonly type: "resource";
|
||||||
value: {
|
}
|
||||||
description: string;
|
|
||||||
url: string;
|
export interface TestBlock extends BaseLearningContentBlock {
|
||||||
};
|
readonly type: "test";
|
||||||
id: string;
|
}
|
||||||
|
|
||||||
|
export interface VideoBlock extends BaseLearningContentBlock {
|
||||||
|
readonly type: "video";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlaceholderBlock extends BaseLearningContentBlock {
|
||||||
|
readonly type: "placeholder";
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LearningContentBlock =
|
||||||
|
| AssignmentBlock
|
||||||
|
| BookBlock
|
||||||
|
| DocumentBlock
|
||||||
|
| ExerciseBlock
|
||||||
|
| MediaLibraryBlock
|
||||||
|
| OnlineTrainingBlock
|
||||||
|
| ResourceBlock
|
||||||
|
| TestBlock
|
||||||
|
| VideoBlock
|
||||||
|
| PlaceholderBlock;
|
||||||
|
|
||||||
|
export type LearningContentType = LearningContentBlock["type"];
|
||||||
|
|
||||||
|
export interface LearningContent extends BaseCourseWagtailPage {
|
||||||
|
readonly type: "learnpath.LearningContent";
|
||||||
|
minutes: number;
|
||||||
|
contents: LearningContentBlock[];
|
||||||
|
parentCircle: Circle;
|
||||||
|
parentLearningSequence?: LearningSequence;
|
||||||
|
parentLearningUnit?: LearningUnit;
|
||||||
|
nextLearningContent?: LearningContent;
|
||||||
|
previousLearningContent?: LearningContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LearningUnitPerformanceCriteria extends BaseCourseWagtailPage {
|
||||||
|
readonly type: "competence.PerformanceCriteria";
|
||||||
|
readonly competence_id: string;
|
||||||
|
parentLearningSequence?: LearningSequence;
|
||||||
|
parentLearningUnit?: LearningUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LearningUnit extends BaseCourseWagtailPage {
|
||||||
|
readonly type: "learnpath.LearningUnit";
|
||||||
|
readonly evaluate_url: string;
|
||||||
|
readonly course_category: CourseCategory;
|
||||||
|
children: LearningUnitPerformanceCriteria[];
|
||||||
|
|
||||||
|
// additional frontend fields
|
||||||
|
learningContents: LearningContent[];
|
||||||
|
minutes: number;
|
||||||
|
parentLearningSequence?: LearningSequence;
|
||||||
|
parentCircle?: Circle;
|
||||||
|
last?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LearningSequence extends BaseCourseWagtailPage {
|
||||||
|
readonly type: "learnpath.LearningSequence";
|
||||||
|
icon: string;
|
||||||
|
learningUnits: LearningUnit[];
|
||||||
|
minutes: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CircleChild = LearningContent | LearningUnit | LearningSequence;
|
||||||
|
|
||||||
|
export interface WagtailLearningPath extends BaseCourseWagtailPage {
|
||||||
|
readonly type: "learnpath.LearningPath";
|
||||||
|
course: Course;
|
||||||
|
children: LearningPathChild[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CircleGoal {
|
export interface CircleGoal {
|
||||||
|
|
@ -116,83 +143,19 @@ export interface CircleJobSituation {
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CourseWagtailPage {
|
export interface WagtailCircle extends BaseCourseWagtailPage {
|
||||||
readonly id: number;
|
readonly type: "learnpath.Circle";
|
||||||
readonly title: string;
|
readonly description: string;
|
||||||
readonly slug: string;
|
readonly goal_description: string;
|
||||||
readonly translation_key: string;
|
readonly goals: CircleGoal[];
|
||||||
readonly frontend_url: string;
|
readonly job_situation_description: string;
|
||||||
completion_status: CourseCompletionStatus;
|
readonly job_situations: CircleJobSituation[];
|
||||||
completion_status_updated_at?: string;
|
readonly children: CircleChild[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CircleLight {
|
export interface Topic extends BaseCourseWagtailPage {
|
||||||
readonly id: number;
|
readonly type: "learnpath.Topic";
|
||||||
readonly title: string;
|
readonly is_visible: boolean;
|
||||||
readonly slug: string;
|
|
||||||
readonly translation_key: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LearningContent extends CourseWagtailPage {
|
|
||||||
type: "learnpath.LearningContent";
|
|
||||||
minutes: number;
|
|
||||||
contents: (
|
|
||||||
| AssignmentBlock
|
|
||||||
| BookBlock
|
|
||||||
| DocumentBlock
|
|
||||||
| ExerciseBlock
|
|
||||||
| MediaLibraryBlock
|
|
||||||
| OnlineTrainingBlock
|
|
||||||
| ResourceBlock
|
|
||||||
| TestBlock
|
|
||||||
| VideoBlock
|
|
||||||
)[];
|
|
||||||
parentCircle: Circle;
|
|
||||||
parentLearningSequence?: LearningSequence;
|
|
||||||
parentLearningUnit?: LearningUnit;
|
|
||||||
nextLearningContent?: LearningContent;
|
|
||||||
previousLearningContent?: LearningContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LearningUnitPerformanceCriteria extends CourseWagtailPage {
|
|
||||||
type: "competence.PerformanceCriteria";
|
|
||||||
parentLearningSequence?: LearningSequence;
|
|
||||||
parentLearningUnit?: LearningUnit;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LearningUnit extends CourseWagtailPage {
|
|
||||||
type: "learnpath.LearningUnit";
|
|
||||||
learningContents: LearningContent[];
|
|
||||||
evaluate_url: string;
|
|
||||||
minutes: number;
|
|
||||||
parentLearningSequence?: LearningSequence;
|
|
||||||
parentCircle?: Circle;
|
|
||||||
children: LearningUnitPerformanceCriteria[];
|
|
||||||
last?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LearningSequence extends CourseWagtailPage {
|
|
||||||
type: "learnpath.LearningSequence";
|
|
||||||
icon: string;
|
|
||||||
learningUnits: LearningUnit[];
|
|
||||||
minutes: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CircleChild =
|
|
||||||
| LearningContent
|
|
||||||
| LearningUnit
|
|
||||||
| LearningSequence
|
|
||||||
| LearningUnitPerformanceCriteria;
|
|
||||||
|
|
||||||
export interface WagtailCircle extends CourseWagtailPage {
|
|
||||||
type: "learnpath.Circle";
|
|
||||||
children: CircleChild[];
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Topic extends CourseWagtailPage {
|
|
||||||
type: "learnpath.Topic";
|
|
||||||
is_visible: boolean;
|
|
||||||
circles: Circle[];
|
circles: Circle[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,7 +171,7 @@ export interface CourseCompletion {
|
||||||
page_slug: string;
|
page_slug: string;
|
||||||
course: number;
|
course: number;
|
||||||
completion_status: CourseCompletionStatus;
|
completion_status: CourseCompletionStatus;
|
||||||
additional_json_data: any;
|
additional_json_data: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CircleDiagramData {
|
export interface CircleDiagramData {
|
||||||
|
|
@ -224,7 +187,7 @@ export interface CircleDiagramData {
|
||||||
|
|
||||||
export interface Course {
|
export interface Course {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
title: string;
|
||||||
category_name: string;
|
category_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,20 +230,24 @@ export interface RelativeLinkBlock {
|
||||||
value: MediaLibraryContentBlockValue;
|
value: MediaLibraryContentBlockValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MediaBlock =
|
||||||
|
| LearnMediaBlock
|
||||||
|
| ExternalLinkBlock
|
||||||
|
| InternalLinkBlock
|
||||||
|
| RelativeLinkBlock;
|
||||||
|
|
||||||
|
export type MediaBlockType = MediaBlock["type"];
|
||||||
|
|
||||||
export interface MediaContentCollection {
|
export interface MediaContentCollection {
|
||||||
type: "content_collection";
|
type: "content_collection";
|
||||||
value: {
|
value: {
|
||||||
title: string;
|
title: string;
|
||||||
contents: (
|
description: string;
|
||||||
| LearnMediaBlock
|
contents: MediaBlock[];
|
||||||
| ExternalLinkBlock
|
|
||||||
| InternalLinkBlock
|
|
||||||
| RelativeLinkBlock
|
|
||||||
)[];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaCategoryPage extends CourseWagtailPage {
|
export interface MediaCategoryPage extends BaseCourseWagtailPage {
|
||||||
type: "media_library.MediaCategoryPage";
|
type: "media_library.MediaCategoryPage";
|
||||||
overview_icon: string;
|
overview_icon: string;
|
||||||
introduction_text: string;
|
introduction_text: string;
|
||||||
|
|
@ -290,36 +257,36 @@ export interface MediaCategoryPage extends CourseWagtailPage {
|
||||||
type: "item";
|
type: "item";
|
||||||
value: string;
|
value: string;
|
||||||
id: string;
|
id: string;
|
||||||
};
|
}[];
|
||||||
course_category: CourseCategory;
|
course_category: CourseCategory;
|
||||||
body: MediaContentCollection[];
|
body: MediaContentCollection[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MediaLibraryPage extends CourseWagtailPage {
|
export interface MediaLibraryPage extends BaseCourseWagtailPage {
|
||||||
type: "media_library.MediaLibraryPage";
|
readonly type: "media_library.MediaLibraryPage";
|
||||||
course: Course;
|
readonly course: Course;
|
||||||
children: MediaCategoryPage[];
|
readonly children: MediaCategoryPage[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PerformanceCriteria extends CourseWagtailPage {
|
export interface PerformanceCriteria extends BaseCourseWagtailPage {
|
||||||
type: "competence.PerformanceCriteria";
|
readonly type: "competence.PerformanceCriteria";
|
||||||
competence_id: string;
|
readonly competence_id: string;
|
||||||
circle: CircleLight;
|
readonly circle: CircleLight;
|
||||||
course_category: CourseCategory;
|
readonly course_category: CourseCategory;
|
||||||
learning_unit: CourseWagtailPage;
|
readonly learning_unit: BaseCourseWagtailPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompetencePage extends CourseWagtailPage {
|
export interface CompetencePage extends BaseCourseWagtailPage {
|
||||||
type: "competence.CompetencePage";
|
readonly type: "competence.CompetencePage";
|
||||||
competence_id: string;
|
readonly competence_id: string;
|
||||||
children: PerformanceCriteria[];
|
readonly children: PerformanceCriteria[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CompetenceProfilePage extends CourseWagtailPage {
|
export interface CompetenceProfilePage extends BaseCourseWagtailPage {
|
||||||
type: "competence.CompetenceProfilePage";
|
readonly type: "competence.CompetenceProfilePage";
|
||||||
course: Course;
|
readonly course: Course;
|
||||||
circles: CircleLight[];
|
readonly circles: CircleLight[];
|
||||||
children: CompetencePage[];
|
readonly children: CompetencePage[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropdown
|
// dropdown
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,32 @@
|
||||||
import type { LearningContentType } from "@/types";
|
import type { LearningContentType } from "@/types";
|
||||||
|
import { assertUnreachable } from "@/utils/utils";
|
||||||
|
|
||||||
export const learningContentTypesToName = new Map<LearningContentType, string>([
|
export function learningContentTypeData(t: LearningContentType): {
|
||||||
["assignment", "Transferauftrag"],
|
title: string;
|
||||||
["book", "Buch"],
|
icon: string;
|
||||||
["document", "Dokument"],
|
} {
|
||||||
["exercise", "Übung"],
|
switch (t) {
|
||||||
["media_library", "Mediathek"],
|
case "assignment":
|
||||||
["online_training", "Online-Training"],
|
return { title: "Transferauftrag", icon: "it-icon-lc-assignment" };
|
||||||
["video", "Video"],
|
case "book":
|
||||||
["test", "Test"],
|
return { title: "Buch", icon: "it-icon-lc-book" };
|
||||||
["resource", "Seite"],
|
case "document":
|
||||||
["placeholder", "In Umsetzung"],
|
return { title: "Dokument", icon: "it-icon-lc-document" };
|
||||||
]);
|
case "exercise":
|
||||||
|
return { title: "Übung", icon: "it-icon-lc-exercise" };
|
||||||
|
case "media_library":
|
||||||
|
return { title: "Mediathek", icon: "it-icon-lc-media-library" };
|
||||||
|
case "online_training":
|
||||||
|
return { title: "Online-Training", icon: "it-icon-lc-online-training" };
|
||||||
|
case "video":
|
||||||
|
return { title: "Video", icon: "it-icon-lc-video" };
|
||||||
|
case "test":
|
||||||
|
return { title: "Test", icon: "it-icon-lc-test" };
|
||||||
|
case "resource":
|
||||||
|
return { title: "Ressource", icon: "it-icon-lc-resource" };
|
||||||
|
case "placeholder":
|
||||||
|
return { title: "In Umsetzung", icon: "it-icon-lc-document" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return assertUnreachable(t);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function assertUnreachable(x: never): never {
|
||||||
|
throw new Error("Didn't expect to get here");
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,8 @@ set -e
|
||||||
echo 'prettier:check'
|
echo 'prettier:check'
|
||||||
(cd client && npm run prettier:check)
|
(cd client && npm run prettier:check)
|
||||||
|
|
||||||
echo 'lint'
|
echo 'lint and typecheck'
|
||||||
(cd client && npm run lint)
|
(cd client && npm run lint && npm run typecheck)
|
||||||
|
|
||||||
echo 'python ufmt check'
|
echo 'python ufmt check'
|
||||||
ufmt check server
|
ufmt check server
|
||||||
|
|
|
||||||
|
|
@ -185,8 +185,6 @@ gunicorn==20.1.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
h11==0.13.0
|
h11==0.13.0
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
hiredis==2.0.0
|
|
||||||
# via -r requirements.in
|
|
||||||
html5lib==1.1
|
html5lib==1.1
|
||||||
# via wagtail
|
# via wagtail
|
||||||
httptools==0.4.0
|
httptools==0.4.0
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ Pillow # https://github.com/python-pillow/Pillow
|
||||||
argon2-cffi # https://github.com/hynek/argon2_cffi
|
argon2-cffi # https://github.com/hynek/argon2_cffi
|
||||||
whitenoise # https://github.com/evansd/whitenoise
|
whitenoise # https://github.com/evansd/whitenoise
|
||||||
redis # https://github.com/redis/redis-py
|
redis # https://github.com/redis/redis-py
|
||||||
hiredis # https://github.com/redis/hiredis-py
|
|
||||||
uvicorn[standard] # https://github.com/encode/uvicorn
|
uvicorn[standard] # https://github.com/encode/uvicorn
|
||||||
environs
|
environs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,6 @@ gunicorn==20.1.0
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
h11==0.13.0
|
h11==0.13.0
|
||||||
# via uvicorn
|
# via uvicorn
|
||||||
hiredis==2.0.0
|
|
||||||
# via -r requirements.in
|
|
||||||
html5lib==1.1
|
html5lib==1.1
|
||||||
# via wagtail
|
# via wagtail
|
||||||
httptools==0.4.0
|
httptools==0.4.0
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class CompetenceAPITestCase(APITestCase):
|
||||||
self.user = User.objects.get(username="student")
|
self.user = User.objects.get(username="student")
|
||||||
self.client.login(username="student", password="test")
|
self.client.login(username="student", password="test")
|
||||||
|
|
||||||
def test_get_learnpathPage(self):
|
def test_get_compentence_page(self):
|
||||||
slug = "test-lehrgang-competence"
|
slug = "test-lehrgang-competence"
|
||||||
competence_profile = CompetenceProfilePage.objects.get(slug=slug)
|
competence_profile = CompetenceProfilePage.objects.get(slug=slug)
|
||||||
response = self.client.get(f"/api/course/page/{slug}/")
|
response = self.client.get(f"/api/course/page/{slug}/")
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ def create_test_learning_path(user=None, skip_locales=True):
|
||||||
LearningSequenceFactory(
|
LearningSequenceFactory(
|
||||||
title="Starten", parent=circle_basis, icon="it-icon-ls-start"
|
title="Starten", parent=circle_basis, icon="it-icon-ls-start"
|
||||||
)
|
)
|
||||||
|
LearningUnitFactory(title="Einführung", parent=circle_basis)
|
||||||
LearningContentFactory(
|
LearningContentFactory(
|
||||||
title="Einführung",
|
title="Einführung",
|
||||||
parent=circle_basis,
|
parent=circle_basis,
|
||||||
|
|
@ -103,6 +104,7 @@ def create_test_learning_path(user=None, skip_locales=True):
|
||||||
contents=[("document", DocumentBlockFactory())],
|
contents=[("document", DocumentBlockFactory())],
|
||||||
)
|
)
|
||||||
LearningSequenceFactory(title="Beenden", parent=circle_basis, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Beenden", parent=circle_basis, icon="it-icon-ls-end")
|
||||||
|
LearningUnitFactory(title="Beenden", parent=circle_basis)
|
||||||
LearningContentFactory(
|
LearningContentFactory(
|
||||||
title="Jetzt kann es losgehen!",
|
title="Jetzt kann es losgehen!",
|
||||||
parent=circle_basis,
|
parent=circle_basis,
|
||||||
|
|
@ -140,6 +142,7 @@ def create_test_learning_path(user=None, skip_locales=True):
|
||||||
)
|
)
|
||||||
|
|
||||||
LearningSequenceFactory(title="Starten", parent=circle, icon="it-icon-ls-start")
|
LearningSequenceFactory(title="Starten", parent=circle, icon="it-icon-ls-start")
|
||||||
|
LearningUnitFactory(title="Einführung", parent=circle)
|
||||||
LearningContentFactory(
|
LearningContentFactory(
|
||||||
title=f'Einleitung Circle "Analyse"',
|
title=f'Einleitung Circle "Analyse"',
|
||||||
parent=circle,
|
parent=circle,
|
||||||
|
|
@ -204,6 +207,7 @@ def create_test_learning_path(user=None, skip_locales=True):
|
||||||
)
|
)
|
||||||
|
|
||||||
LearningSequenceFactory(title="Beenden", parent=circle, icon="it-icon-ls-end")
|
LearningSequenceFactory(title="Beenden", parent=circle, icon="it-icon-ls-end")
|
||||||
|
LearningUnitFactory(title="Beenden", parent=circle)
|
||||||
LearningContentFactory(
|
LearningContentFactory(
|
||||||
title="KompetenzNavi anschauen",
|
title="KompetenzNavi anschauen",
|
||||||
parent=circle,
|
parent=circle,
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,6 @@ class DocumentBlock(blocks.StructBlock):
|
||||||
icon = "media"
|
icon = "media"
|
||||||
|
|
||||||
|
|
||||||
class PlaceholderBlock(blocks.StructBlock):
|
|
||||||
description = blocks.TextBlock()
|
|
||||||
url = blocks.TextBlock()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
icon = "media"
|
|
||||||
|
|
||||||
|
|
||||||
class ExerciseBlock(blocks.StructBlock):
|
class ExerciseBlock(blocks.StructBlock):
|
||||||
description = blocks.TextBlock()
|
description = blocks.TextBlock()
|
||||||
url = blocks.TextBlock()
|
url = blocks.TextBlock()
|
||||||
|
|
@ -82,3 +74,11 @@ class VideoBlock(blocks.StructBlock):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
icon = "media"
|
icon = "media"
|
||||||
|
|
||||||
|
|
||||||
|
class PlaceholderBlock(blocks.StructBlock):
|
||||||
|
description = blocks.TextBlock()
|
||||||
|
url = blocks.TextBlock()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
icon = "media"
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class TestRetrieveLearingPathContents(APITestCase):
|
||||||
self.user = User.objects.get(username="student")
|
self.user = User.objects.get(username="student")
|
||||||
self.client.login(username="student", password="test")
|
self.client.login(username="student", password="test")
|
||||||
|
|
||||||
def test_get_learnpathPage(self):
|
def test_get_learnpath_page(self):
|
||||||
slug = "test-lehrgang-lp"
|
slug = "test-lehrgang-lp"
|
||||||
learning_path = LearningPath.objects.get(slug=slug)
|
learning_path = LearningPath.objects.get(slug=slug)
|
||||||
response = self.client.get(f"/api/course/page/{slug}/")
|
response = self.client.get(f"/api/course/page/{slug}/")
|
||||||
|
|
@ -25,4 +25,4 @@ class TestRetrieveLearingPathContents(APITestCase):
|
||||||
# topics and circles
|
# topics and circles
|
||||||
self.assertEqual(4, len(data["children"]))
|
self.assertEqual(4, len(data["children"]))
|
||||||
# circle "analyse" contents
|
# circle "analyse" contents
|
||||||
self.assertEqual(12, len(data["children"][3]["children"]))
|
self.assertEqual(14, len(data["children"][3]["children"]))
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class MediaLibraryAPITestCase(APITestCase):
|
||||||
self.user = User.objects.get(username="student")
|
self.user = User.objects.get(username="student")
|
||||||
self.client.login(username="student", password="test")
|
self.client.login(username="student", password="test")
|
||||||
|
|
||||||
def test_get_learnpathPage(self):
|
def test_get_media_library_page(self):
|
||||||
slug = "test-lehrgang-media"
|
slug = "test-lehrgang-media"
|
||||||
media_library = MediaLibraryPage.objects.get(slug=slug)
|
media_library = MediaLibraryPage.objects.get(slug=slug)
|
||||||
response = self.client.get(f"/api/course/page/{slug}/")
|
response = self.client.get(f"/api/course/page/{slug}/")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue