Refactor urls for courses, learning paths and competence profile
This commit is contained in:
parent
cb9505b54c
commit
00d2527b29
|
|
@ -35,13 +35,22 @@ function toggleNav() {
|
||||||
state.showMenu = !state.showMenu;
|
state.showMenu = !state.showMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInRoutePath(checkPaths: string[]) {
|
function inCourse() {
|
||||||
log.debug("isInRoutePath", checkPaths, route.path);
|
return route.path.startsWith("/course/");
|
||||||
return checkPaths.some((path) => route.path.startsWith(path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function inCourse() {
|
function inLearningPath() {
|
||||||
return isInRoutePath(["/learn", "/competence"]);
|
const regex = new RegExp("/course/[^/]+/learn");
|
||||||
|
return regex.test(route.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function inCompetenceProfile() {
|
||||||
|
const regex = new RegExp("/course/[^/]+/competence");
|
||||||
|
return regex.test(route.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function inMediaLibrary() {
|
||||||
|
return route.path.startsWith("/media/");
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDropdownSelect(data: DropdownData) {
|
function handleDropdownSelect(data: DropdownData) {
|
||||||
|
|
@ -149,7 +158,7 @@ const profileDropdownData: DropdownListItem[] = [
|
||||||
v-if="inCourse() && courseSessionsStore.courseSessionForRoute"
|
v-if="inCourse() && courseSessionsStore.courseSessionForRoute"
|
||||||
:to="courseSessionsStore.courseSessionForRoute.learning_path_url"
|
:to="courseSessionsStore.courseSessionForRoute.learning_path_url"
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
:class="{ 'nav-item--active': isInRoutePath(['/learn']) }"
|
:class="{ 'nav-item--active': inLearningPath() }"
|
||||||
>
|
>
|
||||||
{{ $t("general.learningPath") }}
|
{{ $t("general.learningPath") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
@ -158,7 +167,7 @@ const profileDropdownData: DropdownListItem[] = [
|
||||||
v-if="inCourse() && courseSessionsStore.courseSessionForRoute"
|
v-if="inCourse() && courseSessionsStore.courseSessionForRoute"
|
||||||
:to="courseSessionsStore.courseSessionForRoute.competence_url"
|
:to="courseSessionsStore.courseSessionForRoute.competence_url"
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
:class="{ 'nav-item--active': isInRoutePath(['/competence']) }"
|
:class="{ 'nav-item--active': inCompetenceProfile() }"
|
||||||
>
|
>
|
||||||
KompetenzNavi
|
KompetenzNavi
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
@ -173,7 +182,7 @@ const profileDropdownData: DropdownListItem[] = [
|
||||||
<router-link
|
<router-link
|
||||||
to="/media/versicherungsvermittlerin-media"
|
to="/media/versicherungsvermittlerin-media"
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
:class="{ 'nav-item--active': isInRoutePath(['/media']) }"
|
:class="{ 'nav-item--active': inMediaLibrary() }"
|
||||||
data-cy="medialibrary-link"
|
data-cy="medialibrary-link"
|
||||||
>
|
>
|
||||||
{{ $t("mediaLibrary.title") }}
|
{{ $t("mediaLibrary.title") }}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,11 @@ const learningPathData = ref<LearningPath | undefined>(undefined);
|
||||||
const learningPathStore = useLearningPathStore();
|
const learningPathStore = useLearningPathStore();
|
||||||
|
|
||||||
learningPathStore
|
learningPathStore
|
||||||
.loadLearningPath(props.learningPathUrl.replace("/learn/", ""), false, false)
|
.loadLearningPath(
|
||||||
|
props.learningPathUrl.replace(/\/learn$/, "-lp").replace(/^\/course\//, ""),
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
learningPathData.value = data;
|
learningPathData.value = data;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,17 @@ import { onMounted } from "vue";
|
||||||
log.debug("CompetenceParentPage created");
|
log.debug("CompetenceParentPage created");
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
competenceProfilePageSlug: string;
|
courseSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const competenceStore = useCompetenceStore();
|
const competenceStore = useCompetenceStore();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug("CompetencesView mounted", props.competenceProfilePageSlug);
|
log.debug("CompetencesView mounted", props.courseSlug);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await competenceStore.loadCompetenceProfilePage(props.competenceProfilePageSlug);
|
const competencePageSlug = props.courseSlug + "-competence";
|
||||||
|
await competenceStore.loadCompetenceProfilePage(competencePageSlug);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const route = useRoute();
|
||||||
log.debug("CircleView.vue created", route);
|
log.debug("CircleView.vue created", route);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPathSlug: string;
|
courseSlug: string;
|
||||||
circleSlug: string;
|
circleSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
|
@ -35,10 +35,10 @@ const duration = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug("CircleView.vue mounted", props.learningPathSlug, props.circleSlug);
|
log.debug("CircleView.vue mounted", props.courseSlug, props.circleSlug);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await circleStore.loadCircle(props.learningPathSlug, props.circleSlug);
|
await circleStore.loadCircle(props.courseSlug, props.circleSlug);
|
||||||
|
|
||||||
if (route.hash.startsWith("#ls-") || route.hash.startsWith("#lu-")) {
|
if (route.hash.startsWith("#ls-") || route.hash.startsWith("#lu-")) {
|
||||||
const slugEnd = route.hash.replace("#", "");
|
const slugEnd = route.hash.replace("#", "");
|
||||||
|
|
@ -86,7 +86,7 @@ onMounted(async () => {
|
||||||
<div class="flex flex-col lg:flex-row">
|
<div class="flex flex-col lg:flex-row">
|
||||||
<div class="flex-initial lg:w-128 px-4 py-4 lg:px-8 lg:pt-4 bg-white">
|
<div class="flex-initial lg:w-128 px-4 py-4 lg:px-8 lg:pt-4 bg-white">
|
||||||
<router-link
|
<router-link
|
||||||
:to="`/learn/${props.learningPathSlug}`"
|
:to="`/course/${props.courseSlug}/learn`"
|
||||||
class="btn-text inline-flex items-center px-3 py-4"
|
class="btn-text inline-flex items-center px-3 py-4"
|
||||||
data-cy="back-to-learning-path-button"
|
data-cy="back-to-learning-path-button"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { onMounted, reactive, watch } from "vue";
|
||||||
log.debug("LearningContentView created");
|
log.debug("LearningContentView created");
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPathSlug: string;
|
courseSlug: string;
|
||||||
circleSlug: string;
|
circleSlug: string;
|
||||||
contentSlug: string;
|
contentSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
@ -24,7 +24,7 @@ const circleStore = useCircleStore();
|
||||||
const loadLearningContent = async () => {
|
const loadLearningContent = async () => {
|
||||||
try {
|
try {
|
||||||
state.learningContent = await circleStore.loadLearningContent(
|
state.learningContent = await circleStore.loadLearningContent(
|
||||||
props.learningPathSlug,
|
props.courseSlug,
|
||||||
props.circleSlug,
|
props.circleSlug,
|
||||||
props.contentSlug
|
props.contentSlug
|
||||||
);
|
);
|
||||||
|
|
@ -38,7 +38,7 @@ watch(
|
||||||
async () => {
|
async () => {
|
||||||
log.debug(
|
log.debug(
|
||||||
"LearningContentView props.contentSlug changed",
|
"LearningContentView props.contentSlug changed",
|
||||||
props.learningPathSlug,
|
props.courseSlug,
|
||||||
props.circleSlug,
|
props.circleSlug,
|
||||||
props.contentSlug
|
props.contentSlug
|
||||||
);
|
);
|
||||||
|
|
@ -49,7 +49,7 @@ watch(
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug(
|
log.debug(
|
||||||
"LearningContentView mounted",
|
"LearningContentView mounted",
|
||||||
props.learningPathSlug,
|
props.courseSlug,
|
||||||
props.circleSlug,
|
props.circleSlug,
|
||||||
props.contentSlug
|
props.contentSlug
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import type { LearningPath } from "@/services/learningPath";
|
||||||
log.debug("LearningPathView created");
|
log.debug("LearningPathView created");
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPathSlug: string;
|
courseSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const learningPathStore = useLearningPathStore();
|
const learningPathStore = useLearningPathStore();
|
||||||
|
|
@ -22,7 +22,7 @@ onMounted(async () => {
|
||||||
log.debug("LearningPathView mounted");
|
log.debug("LearningPathView mounted");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await learningPathStore.loadLearningPath(props.learningPathSlug);
|
await learningPathStore.loadLearningPath(props.courseSlug + "-lp");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<LearningPathViewVertical
|
<LearningPathViewVertical
|
||||||
:show="learningPathStore.page === 'OVERVIEW'"
|
:show="learningPathStore.page === 'OVERVIEW'"
|
||||||
:learning-path-slug="props.learningPathSlug"
|
:learning-path-slug="props.learningPathSlug + '-lp'"
|
||||||
@closemodal="learningPathStore.page = 'INDEX'"
|
@closemodal="learningPathStore.page = 'INDEX'"
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { onMounted, reactive } from "vue";
|
||||||
log.debug("LearningUnitSelfEvaluationView created");
|
log.debug("LearningUnitSelfEvaluationView created");
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPathSlug: string;
|
courseSlug: string;
|
||||||
circleSlug: string;
|
circleSlug: string;
|
||||||
learningUnitSlug: string;
|
learningUnitSlug: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
@ -26,14 +26,14 @@ const state: { learningUnit?: LearningUnit } = reactive({});
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
log.debug(
|
log.debug(
|
||||||
"LearningUnitSelfEvaluationView mounted",
|
"LearningUnitSelfEvaluationView mounted",
|
||||||
props.learningPathSlug,
|
props.courseSlug,
|
||||||
props.circleSlug,
|
props.circleSlug,
|
||||||
props.learningUnitSlug
|
props.learningUnitSlug
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
state.learningUnit = await circleStore.loadSelfEvaluation(
|
state.learningUnit = await circleStore.loadSelfEvaluation(
|
||||||
props.learningPathSlug,
|
props.courseSlug,
|
||||||
props.circleSlug,
|
props.circleSlug,
|
||||||
props.learningUnitSlug
|
props.learningUnitSlug
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ const router = createRouter({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/competence/:competenceProfilePageSlug",
|
path: "/course/:courseSlug/competence",
|
||||||
props: true,
|
props: true,
|
||||||
component: () => import("@/pages/competence/CompetenceParentPage.vue"),
|
component: () => import("@/pages/competence/CompetenceParentPage.vue"),
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -77,27 +77,27 @@ const router = createRouter({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug",
|
path: "/course/:courseSlug/learn",
|
||||||
component: () => import("../pages/learningPath/LearningPathPage.vue"),
|
component: () => import("../pages/learningPath/LearningPathPage.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug/:circleSlug",
|
path: "/course/:courseSlug/learn/:circleSlug",
|
||||||
component: () => import("../pages/learningPath/CirclePage.vue"),
|
component: () => import("../pages/learningPath/CirclePage.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug/:circleSlug/evaluate/:learningUnitSlug",
|
path: "/course/:courseSlug/learn/:circleSlug/evaluate/:learningUnitSlug",
|
||||||
component: () => import("../pages/learningPath/SelfEvaluationPage.vue"),
|
component: () => import("../pages/learningPath/SelfEvaluationPage.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug/:circleSlug/:contentSlug",
|
path: "/course/:courseSlug/learn/:circleSlug/:contentSlug",
|
||||||
component: () => import("../pages/learningPath/LearningContentPage.vue"),
|
component: () => import("../pages/learningPath/LearningContentPage.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learn/:learningPathSlug/cockpit",
|
path: "/course/:courseSlug/cockpit",
|
||||||
component: () => import("../pages/cockpit/CockpitPage.vue"),
|
component: () => import("../pages/cockpit/CockpitPage.vue"),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,9 @@ export const useCircleStore = defineStore({
|
||||||
},
|
},
|
||||||
getters: {},
|
getters: {},
|
||||||
actions: {
|
actions: {
|
||||||
async loadCircle(learningPathSlug: string, circleSlug: string): Promise<Circle> {
|
async loadCircle(courseSlug: string, circleSlug: string): Promise<Circle> {
|
||||||
this.circle = undefined;
|
this.circle = undefined;
|
||||||
|
const learningPathSlug = courseSlug + "-lp";
|
||||||
const learningPathStore = useLearningPathStore();
|
const learningPathStore = useLearningPathStore();
|
||||||
await learningPathStore.loadLearningPath(learningPathSlug);
|
await learningPathStore.loadLearningPath(learningPathSlug);
|
||||||
if (learningPathStore.learningPath) {
|
if (learningPathStore.learningPath) {
|
||||||
|
|
@ -54,11 +55,11 @@ export const useCircleStore = defineStore({
|
||||||
return this.circle;
|
return this.circle;
|
||||||
},
|
},
|
||||||
async loadLearningContent(
|
async loadLearningContent(
|
||||||
learningPathSlug: string,
|
courseSlug: string,
|
||||||
circleSlug: string,
|
circleSlug: string,
|
||||||
learningContentSlug: string
|
learningContentSlug: string
|
||||||
) {
|
) {
|
||||||
const circle = await this.loadCircle(learningPathSlug, circleSlug);
|
const circle = await this.loadCircle(courseSlug, circleSlug);
|
||||||
const result = circle.flatLearningContents.find((learningContent) => {
|
const result = circle.flatLearningContents.find((learningContent) => {
|
||||||
return learningContent.slug.endsWith(learningContentSlug);
|
return learningContent.slug.endsWith(learningContentSlug);
|
||||||
});
|
});
|
||||||
|
|
@ -70,11 +71,11 @@ export const useCircleStore = defineStore({
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
async loadSelfEvaluation(
|
async loadSelfEvaluation(
|
||||||
learningPathSlug: string,
|
courseSlug: string,
|
||||||
circleSlug: string,
|
circleSlug: string,
|
||||||
learningUnitSlug: string
|
learningUnitSlug: string
|
||||||
) {
|
) {
|
||||||
const circle = await this.loadCircle(learningPathSlug, circleSlug);
|
const circle = await this.loadCircle(courseSlug, circleSlug);
|
||||||
const learningUnit = circle.flatLearningUnits.find((child) => {
|
const learningUnit = circle.flatLearningUnits.find((child) => {
|
||||||
return child.slug.endsWith(learningUnitSlug);
|
return child.slug.endsWith(learningUnitSlug);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class CompetenceProfilePage(CourseBasePage):
|
||||||
super(CompetenceProfilePage, self).full_clean(*args, **kwargs)
|
super(CompetenceProfilePage, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
def get_frontend_url(self):
|
def get_frontend_url(self):
|
||||||
return f"/competence/{self.slug}"
|
return f"/course/{self.get_parent().slug}/competence"
|
||||||
|
|
||||||
|
|
||||||
class CompetencePage(CourseBasePage):
|
class CompetencePage(CourseBasePage):
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,8 @@ def create_test_course_with_categories(apps=None, schema_editor=None):
|
||||||
parent=site.root_page,
|
parent=site.root_page,
|
||||||
course=course,
|
course=course,
|
||||||
)
|
)
|
||||||
|
course.slug = course_page.slug
|
||||||
|
course.save()
|
||||||
|
|
||||||
|
|
||||||
def create_test_learning_path(user=None, skip_locales=True):
|
def create_test_learning_path(user=None, skip_locales=True):
|
||||||
|
|
|
||||||
|
|
@ -57,3 +57,5 @@ def create_versicherungsvermittlerin_with_categories(
|
||||||
parent=site.root_page,
|
parent=site.root_page,
|
||||||
course=course,
|
course=course,
|
||||||
)
|
)
|
||||||
|
course.slug = course_page.slug
|
||||||
|
course.save()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.2.13 on 2022-12-02 09:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('course', '0005_alter_coursesessionuser_expert'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='course',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(allow_unicode=True, blank=True, max_length=255, unique=True, verbose_name='Slug'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -14,6 +14,9 @@ class Course(models.Model):
|
||||||
category_name = models.CharField(
|
category_name = models.CharField(
|
||||||
_("Kategorie-Name"), max_length=255, default="Kategorie"
|
_("Kategorie-Name"), max_length=255, default="Kategorie"
|
||||||
)
|
)
|
||||||
|
slug = models.SlugField(
|
||||||
|
_("Slug"), max_length=255, unique=True, blank=True, allow_unicode=True
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Lehrgang")
|
verbose_name = _("Lehrgang")
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ class CourseSessionSerializer(serializers.ModelSerializer):
|
||||||
def get_experts(self, obj):
|
def get_experts(self, obj):
|
||||||
expert_relations = CourseSessionUser.objects.filter(
|
expert_relations = CourseSessionUser.objects.filter(
|
||||||
expert__in=Circle.objects.descendant_of(obj.course.coursepage)
|
expert__in=Circle.objects.descendant_of(obj.course.coursepage)
|
||||||
)
|
).distinct()
|
||||||
expert_result = []
|
expert_result = []
|
||||||
for er in expert_relations:
|
for er in expert_relations:
|
||||||
for circle in er.expert.all():
|
for circle in er.expert.all():
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class LearningPath(CourseBasePage):
|
||||||
return f"{self.title}"
|
return f"{self.title}"
|
||||||
|
|
||||||
def get_frontend_url(self):
|
def get_frontend_url(self):
|
||||||
return f"/learn/{self.slug}"
|
return f"/course/{self.get_parent().slug}/learn"
|
||||||
|
|
||||||
|
|
||||||
class Topic(CourseBasePage):
|
class Topic(CourseBasePage):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue