Refactor urls for courses, learning paths and competence profile

This commit is contained in:
Daniel Egger 2022-12-02 11:10:12 +01:00
parent cb9505b54c
commit 00d2527b29
16 changed files with 80 additions and 40 deletions

View File

@ -35,13 +35,22 @@ function toggleNav() {
state.showMenu = !state.showMenu;
}
function isInRoutePath(checkPaths: string[]) {
log.debug("isInRoutePath", checkPaths, route.path);
return checkPaths.some((path) => route.path.startsWith(path));
function inCourse() {
return route.path.startsWith("/course/");
}
function inCourse() {
return isInRoutePath(["/learn", "/competence"]);
function inLearningPath() {
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) {
@ -149,7 +158,7 @@ const profileDropdownData: DropdownListItem[] = [
v-if="inCourse() && courseSessionsStore.courseSessionForRoute"
:to="courseSessionsStore.courseSessionForRoute.learning_path_url"
class="nav-item"
:class="{ 'nav-item--active': isInRoutePath(['/learn']) }"
:class="{ 'nav-item--active': inLearningPath() }"
>
{{ $t("general.learningPath") }}
</router-link>
@ -158,7 +167,7 @@ const profileDropdownData: DropdownListItem[] = [
v-if="inCourse() && courseSessionsStore.courseSessionForRoute"
:to="courseSessionsStore.courseSessionForRoute.competence_url"
class="nav-item"
:class="{ 'nav-item--active': isInRoutePath(['/competence']) }"
:class="{ 'nav-item--active': inCompetenceProfile() }"
>
KompetenzNavi
</router-link>
@ -173,7 +182,7 @@ const profileDropdownData: DropdownListItem[] = [
<router-link
to="/media/versicherungsvermittlerin-media"
class="nav-item"
:class="{ 'nav-item--active': isInRoutePath(['/media']) }"
:class="{ 'nav-item--active': inMediaLibrary() }"
data-cy="medialibrary-link"
>
{{ $t("mediaLibrary.title") }}

View File

@ -17,7 +17,11 @@ const learningPathData = ref<LearningPath | undefined>(undefined);
const learningPathStore = useLearningPathStore();
learningPathStore
.loadLearningPath(props.learningPathUrl.replace("/learn/", ""), false, false)
.loadLearningPath(
props.learningPathUrl.replace(/\/learn$/, "-lp").replace(/^\/course\//, ""),
false,
false
)
.then((data) => {
learningPathData.value = data;
});

View File

@ -6,16 +6,17 @@ import { onMounted } from "vue";
log.debug("CompetenceParentPage created");
const props = defineProps<{
competenceProfilePageSlug: string;
courseSlug: string;
}>();
const competenceStore = useCompetenceStore();
onMounted(async () => {
log.debug("CompetencesView mounted", props.competenceProfilePageSlug);
log.debug("CompetencesView mounted", props.courseSlug);
try {
await competenceStore.loadCompetenceProfilePage(props.competenceProfilePageSlug);
const competencePageSlug = props.courseSlug + "-competence";
await competenceStore.loadCompetenceProfilePage(competencePageSlug);
} catch (error) {
log.error(error);
}

View File

@ -16,7 +16,7 @@ const route = useRoute();
log.debug("CircleView.vue created", route);
const props = defineProps<{
learningPathSlug: string;
courseSlug: string;
circleSlug: string;
}>();
@ -35,10 +35,10 @@ const duration = computed(() => {
});
onMounted(async () => {
log.debug("CircleView.vue mounted", props.learningPathSlug, props.circleSlug);
log.debug("CircleView.vue mounted", props.courseSlug, props.circleSlug);
try {
await circleStore.loadCircle(props.learningPathSlug, props.circleSlug);
await circleStore.loadCircle(props.courseSlug, props.circleSlug);
if (route.hash.startsWith("#ls-") || route.hash.startsWith("#lu-")) {
const slugEnd = route.hash.replace("#", "");
@ -86,7 +86,7 @@ onMounted(async () => {
<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">
<router-link
:to="`/learn/${props.learningPathSlug}`"
:to="`/course/${props.courseSlug}/learn`"
class="btn-text inline-flex items-center px-3 py-4"
data-cy="back-to-learning-path-button"
>

View File

@ -9,7 +9,7 @@ import { onMounted, reactive, watch } from "vue";
log.debug("LearningContentView created");
const props = defineProps<{
learningPathSlug: string;
courseSlug: string;
circleSlug: string;
contentSlug: string;
}>();
@ -24,7 +24,7 @@ const circleStore = useCircleStore();
const loadLearningContent = async () => {
try {
state.learningContent = await circleStore.loadLearningContent(
props.learningPathSlug,
props.courseSlug,
props.circleSlug,
props.contentSlug
);
@ -38,7 +38,7 @@ watch(
async () => {
log.debug(
"LearningContentView props.contentSlug changed",
props.learningPathSlug,
props.courseSlug,
props.circleSlug,
props.contentSlug
);
@ -49,7 +49,7 @@ watch(
onMounted(async () => {
log.debug(
"LearningContentView mounted",
props.learningPathSlug,
props.courseSlug,
props.circleSlug,
props.contentSlug
);

View File

@ -12,7 +12,7 @@ import type { LearningPath } from "@/services/learningPath";
log.debug("LearningPathView created");
const props = defineProps<{
learningPathSlug: string;
courseSlug: string;
}>();
const learningPathStore = useLearningPathStore();
@ -22,7 +22,7 @@ onMounted(async () => {
log.debug("LearningPathView mounted");
try {
await learningPathStore.loadLearningPath(props.learningPathSlug);
await learningPathStore.loadLearningPath(props.courseSlug + "-lp");
} catch (error) {
log.error(error);
}
@ -49,7 +49,7 @@ const createContinueUrl = (learningPath: LearningPath): [string, boolean] => {
<Teleport to="body">
<LearningPathViewVertical
:show="learningPathStore.page === 'OVERVIEW'"
:learning-path-slug="props.learningPathSlug"
:learning-path-slug="props.learningPathSlug + '-lp'"
@closemodal="learningPathStore.page = 'INDEX'"
/>
</Teleport>

View File

@ -11,7 +11,7 @@ import { onMounted, reactive } from "vue";
log.debug("LearningUnitSelfEvaluationView created");
const props = defineProps<{
learningPathSlug: string;
courseSlug: string;
circleSlug: string;
learningUnitSlug: string;
}>();
@ -26,14 +26,14 @@ const state: { learningUnit?: LearningUnit } = reactive({});
onMounted(async () => {
log.debug(
"LearningUnitSelfEvaluationView mounted",
props.learningPathSlug,
props.courseSlug,
props.circleSlug,
props.learningUnitSlug
);
try {
state.learningUnit = await circleStore.loadSelfEvaluation(
props.learningPathSlug,
props.courseSlug,
props.circleSlug,
props.learningUnitSlug
);

View File

@ -53,7 +53,7 @@ const router = createRouter({
],
},
{
path: "/competence/:competenceProfilePageSlug",
path: "/course/:courseSlug/competence",
props: true,
component: () => import("@/pages/competence/CompetenceParentPage.vue"),
children: [
@ -77,27 +77,27 @@ const router = createRouter({
],
},
{
path: "/learn/:learningPathSlug",
path: "/course/:courseSlug/learn",
component: () => import("../pages/learningPath/LearningPathPage.vue"),
props: true,
},
{
path: "/learn/:learningPathSlug/:circleSlug",
path: "/course/:courseSlug/learn/:circleSlug",
component: () => import("../pages/learningPath/CirclePage.vue"),
props: true,
},
{
path: "/learn/:learningPathSlug/:circleSlug/evaluate/:learningUnitSlug",
path: "/course/:courseSlug/learn/:circleSlug/evaluate/:learningUnitSlug",
component: () => import("../pages/learningPath/SelfEvaluationPage.vue"),
props: true,
},
{
path: "/learn/:learningPathSlug/:circleSlug/:contentSlug",
path: "/course/:courseSlug/learn/:circleSlug/:contentSlug",
component: () => import("../pages/learningPath/LearningContentPage.vue"),
props: true,
},
{
path: "/learn/:learningPathSlug/cockpit",
path: "/course/:courseSlug/cockpit",
component: () => import("../pages/cockpit/CockpitPage.vue"),
props: true,
},

View File

@ -37,8 +37,9 @@ export const useCircleStore = defineStore({
},
getters: {},
actions: {
async loadCircle(learningPathSlug: string, circleSlug: string): Promise<Circle> {
async loadCircle(courseSlug: string, circleSlug: string): Promise<Circle> {
this.circle = undefined;
const learningPathSlug = courseSlug + "-lp";
const learningPathStore = useLearningPathStore();
await learningPathStore.loadLearningPath(learningPathSlug);
if (learningPathStore.learningPath) {
@ -54,11 +55,11 @@ export const useCircleStore = defineStore({
return this.circle;
},
async loadLearningContent(
learningPathSlug: string,
courseSlug: string,
circleSlug: string,
learningContentSlug: string
) {
const circle = await this.loadCircle(learningPathSlug, circleSlug);
const circle = await this.loadCircle(courseSlug, circleSlug);
const result = circle.flatLearningContents.find((learningContent) => {
return learningContent.slug.endsWith(learningContentSlug);
});
@ -70,11 +71,11 @@ export const useCircleStore = defineStore({
return result;
},
async loadSelfEvaluation(
learningPathSlug: string,
courseSlug: string,
circleSlug: string,
learningUnitSlug: string
) {
const circle = await this.loadCircle(learningPathSlug, circleSlug);
const circle = await this.loadCircle(courseSlug, circleSlug);
const learningUnit = circle.flatLearningUnits.find((child) => {
return child.slug.endsWith(learningUnitSlug);
});

View File

@ -30,7 +30,7 @@ class CompetenceProfilePage(CourseBasePage):
super(CompetenceProfilePage, self).full_clean(*args, **kwargs)
def get_frontend_url(self):
return f"/competence/{self.slug}"
return f"/course/{self.get_parent().slug}/competence"
class CompetencePage(CourseBasePage):

View File

@ -80,6 +80,8 @@ def create_test_course_with_categories(apps=None, schema_editor=None):
parent=site.root_page,
course=course,
)
course.slug = course_page.slug
course.save()
def create_test_learning_path(user=None, skip_locales=True):

View File

@ -57,3 +57,5 @@ def create_versicherungsvermittlerin_with_categories(
parent=site.root_page,
course=course,
)
course.slug = course_page.slug
course.save()

View File

@ -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'),
),
]

View File

@ -14,6 +14,9 @@ class Course(models.Model):
category_name = models.CharField(
_("Kategorie-Name"), max_length=255, default="Kategorie"
)
slug = models.SlugField(
_("Slug"), max_length=255, unique=True, blank=True, allow_unicode=True
)
class Meta:
verbose_name = _("Lehrgang")

View File

@ -65,7 +65,7 @@ class CourseSessionSerializer(serializers.ModelSerializer):
def get_experts(self, obj):
expert_relations = CourseSessionUser.objects.filter(
expert__in=Circle.objects.descendant_of(obj.course.coursepage)
)
).distinct()
expert_result = []
for er in expert_relations:
for circle in er.expert.all():

View File

@ -43,7 +43,7 @@ class LearningPath(CourseBasePage):
return f"{self.title}"
def get_frontend_url(self):
return f"/learn/{self.slug}"
return f"/course/{self.get_parent().slug}/learn"
class Topic(CourseBasePage):