Add courseSessionId query param to url and switch to it
This commit is contained in:
parent
8b4b00170a
commit
6048129507
|
|
@ -3,10 +3,12 @@ import type { CourseSession, DueDate } from "@/types";
|
|||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import { useTranslation } from "i18next-vue";
|
||||
import dayjs from "dayjs";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
dueDate: DueDate;
|
||||
singleLine?: boolean;
|
||||
showCourseSession?: boolean;
|
||||
}>();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -24,6 +26,16 @@ if (!courseSession) {
|
|||
|
||||
const isExpert = courseSessionsStore.hasCockpit(courseSession);
|
||||
const url = isExpert ? props.dueDate.url_expert : props.dueDate.url;
|
||||
|
||||
const courseSessionTitle = computed(() => {
|
||||
if (props.dueDate.course_session) {
|
||||
return (
|
||||
courseSessionsStore.getCourseSessionById(props.dueDate.course_session)?.title ??
|
||||
""
|
||||
);
|
||||
}
|
||||
return "";
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -54,7 +66,12 @@ const url = isExpert ? props.dueDate.url_expert : props.dueDate.url;
|
|||
</a>
|
||||
</div>
|
||||
<div class="text-small text-gray-900">
|
||||
<div>{{ $t("a.Circle") }} «{{ props.dueDate.circle?.title }}»</div>
|
||||
<div>
|
||||
<span v-if="props.showCourseSession ?? courseSessionTitle">
|
||||
{{ courseSessionTitle }}:
|
||||
</span>
|
||||
{{ $t("a.Circle") }} «{{ props.dueDate.circle?.title }}»
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const props = defineProps<{
|
|||
showTopBorder: boolean;
|
||||
showBottomBorder: boolean;
|
||||
showAllDueDatesLink: boolean;
|
||||
showCourseSession: boolean;
|
||||
}>();
|
||||
|
||||
const allDueDates = computed(() => {
|
||||
|
|
@ -28,7 +29,10 @@ const dueDatesDisplayed = computed(() => {
|
|||
:key="dueDate.id"
|
||||
:class="{ 'first:border-t': props.showTopBorder, 'border-b': true }"
|
||||
>
|
||||
<DueDateSingle :due-date="dueDate"></DueDateSingle>
|
||||
<DueDateSingle
|
||||
:due-date="dueDate"
|
||||
:show-course-session="props.showCourseSession"
|
||||
></DueDateSingle>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="allDueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
:show-top-border="props.showTopBorder"
|
||||
show-all-due-dates-link
|
||||
show-bottom-border
|
||||
:show-course-session="false"
|
||||
></DueDatesList>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const logout = () => {
|
|||
userStore.handleLogout();
|
||||
};
|
||||
const selectCourseSession = (courseSession: CourseSession) => {
|
||||
courseSessionsStore.switchCourseSession(courseSession);
|
||||
courseSessionsStore.switchCourseSessionById(courseSession.id);
|
||||
};
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ async function loadAdditionalAppointments() {
|
|||
:show-all-due-dates-link="false"
|
||||
:max-count="numAppointmentsToShow"
|
||||
data-cy="appointments-list"
|
||||
:show-course-session="true"
|
||||
/>
|
||||
<button
|
||||
v-if="canLoadMore"
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ const getNextStepLink = (courseSession: CourseSession) => {
|
|||
:show-top-border="false"
|
||||
:show-all-due-dates-link="true"
|
||||
:show-bottom-border="true"
|
||||
:show-course-session="true"
|
||||
></DueDatesList>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ export const redirectToLoginIfRequired: NavigationGuard = (to) => {
|
|||
if (loginRequired(to) && !userStore.loggedIn) {
|
||||
const appEnv = import.meta.env.VITE_APP_ENVIRONMENT || "local";
|
||||
const ssoLogin = appEnv.startsWith("prod") || appEnv.startsWith("stage");
|
||||
return ssoLogin ? `/login?next=${to.fullPath}` : `/login-local?next=${to.fullPath}`;
|
||||
return ssoLogin
|
||||
? `/login?next=${encodeURIComponent(to.fullPath)}`
|
||||
: `/login-local?next=${encodeURIComponent(to.fullPath)}`;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -46,15 +48,52 @@ export const expertRequired: NavigationGuard = (to: RouteLocationNormalized) =>
|
|||
}
|
||||
};
|
||||
|
||||
export async function handleCourseSessions(to: RouteLocationNormalized) {
|
||||
export async function handleCurrentCourseSession(to: RouteLocationNormalized) {
|
||||
// register after login hooks
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
if (to.params.courseSlug) {
|
||||
courseSessionsStore._currentCourseSlug = to.params.courseSlug as string;
|
||||
} else {
|
||||
courseSessionsStore._currentCourseSlug = "";
|
||||
}
|
||||
if (!courseSessionsStore.loaded) {
|
||||
await courseSessionsStore.loadCourseSessionsData();
|
||||
const userStore = useUserStore();
|
||||
if (userStore.loggedIn) {
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
if (to.params.courseSlug) {
|
||||
courseSessionsStore._currentCourseSlug = to.params.courseSlug as string;
|
||||
} else {
|
||||
courseSessionsStore._currentCourseSlug = "";
|
||||
}
|
||||
if (!courseSessionsStore.loaded) {
|
||||
await courseSessionsStore.loadCourseSessionsData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalized) {
|
||||
/**
|
||||
* switch to course session with id from query param `courseSessionId` if it
|
||||
* is present and valid.
|
||||
*/
|
||||
// register after login hooks
|
||||
const userStore = useUserStore();
|
||||
if (userStore.loggedIn) {
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
if (!courseSessionsStore.loaded) {
|
||||
await courseSessionsStore.loadCourseSessionsData();
|
||||
}
|
||||
|
||||
if (to.query.courseSessionId) {
|
||||
const { courseSessionId, ...restOfQuery } = to.query;
|
||||
const switchSuccessful = courseSessionsStore.switchCourseSessionById(
|
||||
courseSessionId.toString()
|
||||
);
|
||||
if (switchSuccessful) {
|
||||
return {
|
||||
path: to.path,
|
||||
query: restOfQuery,
|
||||
replace: true,
|
||||
};
|
||||
} else {
|
||||
// courseSessionId is invalid for current user -> redirect to home
|
||||
return {
|
||||
path: "/",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import DashboardPage from "@/pages/DashboardPage.vue";
|
||||
import LoginPage from "@/pages/LoginPage.vue";
|
||||
import {
|
||||
handleCourseSessions,
|
||||
handleCourseSessionAsQueryParam,
|
||||
handleCurrentCourseSession,
|
||||
redirectToLoginIfRequired,
|
||||
updateLoggedIn,
|
||||
} from "@/router/guards";
|
||||
|
|
@ -214,7 +215,8 @@ router.beforeEach(updateLoggedIn);
|
|||
router.beforeEach(redirectToLoginIfRequired);
|
||||
|
||||
// register after login hooks
|
||||
router.beforeEach(handleCourseSessions);
|
||||
router.beforeEach(handleCurrentCourseSession);
|
||||
router.beforeEach(handleCourseSessionAsQueryParam);
|
||||
|
||||
router.beforeEach(addToHistory);
|
||||
|
||||
|
|
|
|||
|
|
@ -91,13 +91,31 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
function switchCourseSession(courseSession: CourseSession) {
|
||||
function _switchCourseSession(courseSession: CourseSession) {
|
||||
log.debug("switchCourseSession", courseSession);
|
||||
selectedCourseSessionMap.value.set(courseSession.course.slug, courseSession.id);
|
||||
// Emit event so that the App can re-render with the new courseSession
|
||||
eventBus.emit("switchedCourseSession", courseSession.id);
|
||||
}
|
||||
|
||||
function getCourseSessionById(courseSessionId: number | string) {
|
||||
return allCourseSessions.value.find((cs) => {
|
||||
return courseSessionId.toString() === cs.id.toString();
|
||||
});
|
||||
}
|
||||
|
||||
function switchCourseSessionById(courseSessionId: number | string) {
|
||||
const courseSession = allCourseSessions.value.find((cs) => {
|
||||
return courseSessionId.toString() === cs.id.toString();
|
||||
});
|
||||
if (courseSession) {
|
||||
_switchCourseSession(courseSession);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function courseSessionForCourse(courseSlug: string) {
|
||||
if (courseSlug) {
|
||||
const courseSession = selectedCourseSessionForCourse(courseSlug);
|
||||
|
|
@ -268,7 +286,8 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
uniqueCourseSessionsByCourse,
|
||||
allCurrentCourseSessions,
|
||||
courseSessionForCourse,
|
||||
switchCourseSession,
|
||||
getCourseSessionById,
|
||||
switchCourseSessionById,
|
||||
hasCockpit,
|
||||
hasCourseSessionPreview,
|
||||
currentCourseSessionHasCockpit,
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@ class CourseSessionUserAdmin(admin.ModelAdmin):
|
|||
"user_first_name",
|
||||
"course_session",
|
||||
"role",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"circles",
|
||||
# "created_at",
|
||||
# "updated_at",
|
||||
]
|
||||
search_fields = [
|
||||
"user__first_name",
|
||||
|
|
@ -66,6 +67,9 @@ class CourseSessionUserAdmin(admin.ModelAdmin):
|
|||
user_last_name.short_description = "Last Name"
|
||||
user_last_name.admin_order_field = "user__last_name"
|
||||
|
||||
def circles(self, obj):
|
||||
return ", ".join([c.title for c in obj.expert.all()])
|
||||
|
||||
fieldsets = [
|
||||
(None, {"fields": ("user", "course_session", "role")}),
|
||||
(
|
||||
|
|
|
|||
|
|
@ -63,8 +63,10 @@ class CourseSessionAttendanceCourse(models.Model):
|
|||
)
|
||||
|
||||
if not self.due_date.manual_override_fields:
|
||||
self.due_date.url = self.learning_content.get_frontend_url()
|
||||
self.due_date.url_expert = f"/course/{self.due_date.course_session.course.slug}/cockpit/attendance?id={self.learning_content_id}"
|
||||
self.due_date.url = self.learning_content.get_frontend_url(
|
||||
course_session_id=self.course_session.id
|
||||
)
|
||||
self.due_date.url_expert = f"/course/{self.due_date.course_session.course.slug}/cockpit/attendance?id={self.learning_content_id}&courseSessionId={self.course_session.id}"
|
||||
self.due_date.title = self.learning_content.title
|
||||
self.due_date.page = self.learning_content.page_ptr
|
||||
self.due_date.assignment_type_translation_key = (
|
||||
|
|
@ -123,7 +125,9 @@ class CourseSessionAssignment(models.Model):
|
|||
if self.learning_content_id:
|
||||
title = self.learning_content.title
|
||||
page = self.learning_content.page_ptr
|
||||
url = self.learning_content.get_frontend_url()
|
||||
url = self.learning_content.get_frontend_url(
|
||||
course_session_id=self.course_session.id
|
||||
)
|
||||
assignment_type = self.learning_content.assignment_type
|
||||
assignment_type_translation_keys = {
|
||||
AssignmentType.CASEWORK.value: "learningContentTypes.casework",
|
||||
|
|
@ -131,7 +135,7 @@ class CourseSessionAssignment(models.Model):
|
|||
AssignmentType.REFLECTION.value: "learningContentTypes.reflection",
|
||||
}
|
||||
|
||||
url_expert = f"/course/{self.course_session.course.slug}/cockpit/assignment/{self.learning_content_id}"
|
||||
url_expert = f"/course/{self.course_session.course.slug}/cockpit/assignment/{self.learning_content_id}?courseSessionId={self.course_session.id}"
|
||||
|
||||
if assignment_type in (
|
||||
AssignmentType.CASEWORK.value,
|
||||
|
|
@ -212,10 +216,10 @@ class CourseSessionEdoniqTest(models.Model):
|
|||
)
|
||||
|
||||
if not self.deadline.manual_override_fields:
|
||||
self.deadline.url = self.learning_content.get_frontend_url()
|
||||
self.deadline.url_expert = (
|
||||
f"/course/{self.course_session.course.slug}/cockpit/"
|
||||
self.deadline.url = self.learning_content.get_frontend_url(
|
||||
course_session_id=self.course_session.id
|
||||
)
|
||||
self.deadline.url_expert = f"/course/{self.course_session.course.slug}/cockpit?courseSessionId={self.course_session.id}"
|
||||
self.deadline.title = self.learning_content.title
|
||||
self.deadline.page = self.learning_content.page_ptr
|
||||
self.deadline.assignment_type_translation_key = (
|
||||
|
|
|
|||
|
|
@ -4,56 +4,30 @@ from django.db import migrations, models
|
|||
|
||||
|
||||
def set_url_expert_course_session_assignments(apps):
|
||||
Course = apps.get_model("course", "Course") # noqa
|
||||
|
||||
CourseSessionAssignment = apps.get_model( # noqa
|
||||
"course_session", "CourseSessionAssignment"
|
||||
)
|
||||
# need to load concrete model, so that wagtail page has `specific` instance method...
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAssignment
|
||||
|
||||
for assignment in CourseSessionAssignment.objects.all():
|
||||
for due_date in [
|
||||
assignment.submission_deadline,
|
||||
assignment.evaluation_deadline,
|
||||
]:
|
||||
if due_date and due_date.page:
|
||||
course_slug = due_date.course_session.course.slug
|
||||
content_id = due_date.page.id
|
||||
due_date.url_expert = (
|
||||
f"/course/{course_slug}/cockpit/assignment/{content_id}"
|
||||
)
|
||||
due_date.save()
|
||||
# trigger save to update due_date foreign key fields
|
||||
assignment.save()
|
||||
|
||||
|
||||
def set_url_expert_course_session_edoniq_test(apps):
|
||||
CourseSessionEdoniqTest = apps.get_model( # noqa
|
||||
"course_session", "CourseSessionEdoniqTest"
|
||||
)
|
||||
# need to load concrete model, so that wagtail page has `specific` instance method...
|
||||
from vbv_lernwelt.course_session.models import CourseSessionEdoniqTest
|
||||
|
||||
for edoniq_test in CourseSessionEdoniqTest.objects.all():
|
||||
due_date = edoniq_test.deadline
|
||||
|
||||
if due_date:
|
||||
course_slug = due_date.course_session.course.slug
|
||||
due_date.url_expert = f"/course/{course_slug}/cockpit/"
|
||||
due_date.save()
|
||||
# trigger save to update due_date foreign key fields
|
||||
edoniq_test.save()
|
||||
|
||||
|
||||
def set_url_expert_course_session_attendances(apps):
|
||||
Course = apps.get_model("course", "Course") # noqa
|
||||
|
||||
CourseSessionAttendanceCourse = apps.get_model( # noqa
|
||||
"course_session", "CourseSessionAttendanceCourse"
|
||||
)
|
||||
# need to load concrete model, so that wagtail page has `specific` instance method...
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
||||
|
||||
for attendance in CourseSessionAttendanceCourse.objects.all():
|
||||
due_date = attendance.due_date
|
||||
if due_date and due_date.page:
|
||||
course_slug = due_date.course_session.course.slug
|
||||
content_id = due_date.page.id
|
||||
due_date.url_expert = (
|
||||
f"/course/{course_slug}/cockpit/attendance?id={content_id}"
|
||||
)
|
||||
due_date.save()
|
||||
# trigger save to update due_date foreign key fields
|
||||
attendance.save()
|
||||
|
||||
|
||||
def set_url_expert_default(apps, schema_editor):
|
||||
|
|
|
|||
|
|
@ -268,14 +268,19 @@ class LearningContent(CourseBasePage):
|
|||
<span style="margin-left: 8px;">{self.get_admin_display_title()}</span>
|
||||
</span>"""
|
||||
|
||||
def get_frontend_url(self):
|
||||
def get_frontend_url(self, course_session_id=None):
|
||||
r = re.compile(
|
||||
r"^(?P<coursePart>.+?)-lp-circle-(?P<circlePart>.+?)-lc-(?P<lcPart>.+)$"
|
||||
)
|
||||
m = r.match(self.slug)
|
||||
if m is None:
|
||||
return "ERROR: could not parse slug"
|
||||
return f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}/{m.group('lcPart')}"
|
||||
url = f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}/{m.group('lcPart')}"
|
||||
|
||||
if course_session_id:
|
||||
url += f"?courseSessionId={course_session_id}"
|
||||
|
||||
return url
|
||||
|
||||
def get_parent_circle(self):
|
||||
try:
|
||||
|
|
|
|||
Loading…
Reference in New Issue