Make `useCurrentCourseSession` reactive

This commit is contained in:
Daniel Egger 2023-05-19 09:52:11 +02:00
parent eaf12fbc87
commit bcc42eaf83
16 changed files with 44 additions and 36 deletions

View File

@ -31,7 +31,7 @@ onMounted(() => {
eventBus.on("switchedCourseSession", () => { eventBus.on("switchedCourseSession", () => {
// Rerender the component tree, when the course session gets switched. // Rerender the component tree, when the course session gets switched.
// Mainly so that the `useCurrentCourseSession` composable gets re-evaluated // So that the current learning path completion data gets updated.
componentKey.value++; componentKey.value++;
log.info("Switched course session, re-evaluate component tree"); log.info("Switched course session, re-evaluate component tree");
}); });

View File

@ -1,17 +1,19 @@
import { useCourseSessionsStore } from "@/stores/courseSessions"; import { useCourseSessionsStore } from "@/stores/courseSessions";
import type { CourseSession } from "@/types"; import type { CourseSession } from "@/types";
import log from "loglevel"; import log from "loglevel";
import type { ComputedRef } from "vue";
import { computed } from "vue";
export function useCurrentCourseSession() { export function useCurrentCourseSession() {
/** /**
* We often need the current course session in our components. * We often need the current course session in our components.
* With this composable we can get it easily. * With this composable we can get it easily.
* ATTENTION: The value is not reactive! So the components need to be re-rendered
* when the value changes. This is currently done with the "switchedCourseSession"
* event handler in the App.vue component.
*/ */
const store = useCourseSessionsStore(); const store = useCourseSessionsStore();
const result: ComputedRef<CourseSession> = computed(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
() => {
if (!store.currentCourseSession) { if (!store.currentCourseSession) {
log.error( log.error(
"currentCourseSession is only defined in pages with :courseSlug in the route" "currentCourseSession is only defined in pages with :courseSlug in the route"
@ -21,7 +23,8 @@ export function useCurrentCourseSession() {
It is only defined in pages with :courseSlug in the route` It is only defined in pages with :courseSlug in the route`
); );
} }
return store.currentCourseSession;
// return the current NON-REACTIVE course session }
return store.currentCourseSession as CourseSession; );
return result;
} }

View File

@ -22,7 +22,7 @@ onMounted(async () => {
log.debug("CockpitParentPage mounted", props.courseSlug); log.debug("CockpitParentPage mounted", props.courseSlug);
try { try {
await cockpitStore.loadCourseSessionUsers(courseSession.id); await cockpitStore.loadCourseSessionUsers(courseSession.value.id);
cockpitStore.courseSessionUsers?.forEach((csu) => { cockpitStore.courseSessionUsers?.forEach((csu) => {
competenceStore.loadCompetenceProfilePage( competenceStore.loadCompetenceProfilePage(
props.courseSlug + "-competence", props.courseSlug + "-competence",

View File

@ -144,7 +144,7 @@ const feedbackData = reactive<FeedbackData>({ amount: 0, questions: {} });
onMounted(async () => { onMounted(async () => {
log.debug("FeedbackPage mounted"); log.debug("FeedbackPage mounted");
const data = await itGet( const data = await itGet(
`/api/core/feedback/${courseSession.course.id}/${props.circleId}` `/api/core/feedback/${courseSession.value.course.id}/${props.circleId}`
); );
Object.assign(feedbackData, data); Object.assign(feedbackData, data);
}); });

View File

@ -39,7 +39,7 @@ const router = useRouter();
const queryResult = useQuery({ const queryResult = useQuery({
query: ASSIGNMENT_COMPLETION_QUERY, query: ASSIGNMENT_COMPLETION_QUERY,
variables: { variables: {
courseSessionId: courseSession.id.toString(), courseSessionId: courseSession.value.id.toString(),
assignmentId: props.assignmentId, assignmentId: props.assignmentId,
assignmentUserId: props.userId, assignmentUserId: props.userId,
}, },
@ -48,7 +48,7 @@ const queryResult = useQuery({
onMounted(async () => { onMounted(async () => {
log.debug("AssignmentView mounted", props.assignmentId, props.userId); log.debug("AssignmentView mounted", props.assignmentId, props.userId);
state.assignmentUser = courseSession.users.find( state.assignmentUser = courseSession.value.users.find(
(user) => user.user_id === Number(props.userId) (user) => user.user_id === Number(props.userId)
); );
}); });

View File

@ -29,7 +29,7 @@ async function startEvaluation() {
// noinspection TypeScriptValidateTypes // noinspection TypeScriptValidateTypes
upsertAssignmentCompletionMutation.executeMutation({ upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignment.id.toString(), assignmentId: props.assignment.id.toString(),
courseSessionId: courseSession.id.toString(), courseSessionId: courseSession.value.id.toString(),
assignmentUserId: props.assignmentUser.user_id.toString(), assignmentUserId: props.assignmentUser.user_id.toString(),
completionStatus: "EVALUATION_IN_PROGRESS", completionStatus: "EVALUATION_IN_PROGRESS",
completionDataString: JSON.stringify({}), completionDataString: JSON.stringify({}),

View File

@ -44,7 +44,7 @@ async function submitEvaluation() {
// noinspection TypeScriptValidateTypes // noinspection TypeScriptValidateTypes
upsertAssignmentCompletionMutation.executeMutation({ upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignment.id.toString(), assignmentId: props.assignment.id.toString(),
courseSessionId: courseSession.id.toString(), courseSessionId: courseSession.value.id.toString(),
assignmentUserId: props.assignmentUser.user_id.toString(), assignmentUserId: props.assignmentUser.user_id.toString(),
completionStatus: "EVALUATION_SUBMITTED", completionStatus: "EVALUATION_SUBMITTED",
completionDataString: JSON.stringify({}), completionDataString: JSON.stringify({}),
@ -87,7 +87,7 @@ const grade = computed(() => {
const evaluationUser = computed(() => { const evaluationUser = computed(() => {
if (props.assignmentCompletion.evaluation_user) { if (props.assignmentCompletion.evaluation_user) {
return (courseSession.users ?? []).find( return (courseSession.value.users ?? []).find(
(user) => user.user_id === Number(props.assignmentCompletion.evaluation_user) (user) => user.user_id === Number(props.assignmentCompletion.evaluation_user)
) as CourseSessionUser; ) as CourseSessionUser;
} }

View File

@ -68,7 +68,7 @@ async function evaluateAssignmentCompletion(completionData: AssignmentCompletion
// noinspection TypeScriptValidateTypes // noinspection TypeScriptValidateTypes
upsertAssignmentCompletionMutation.executeMutation({ upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignment.id.toString(), assignmentId: props.assignment.id.toString(),
courseSessionId: courseSession.id.toString(), courseSessionId: courseSession.value.id.toString(),
assignmentUserId: props.assignmentUser.user_id.toString(), assignmentUserId: props.assignmentUser.user_id.toString(),
completionStatus: "EVALUATION_IN_PROGRESS", completionStatus: "EVALUATION_IN_PROGRESS",
completionDataString: JSON.stringify(completionData), completionDataString: JSON.stringify(completionData),

View File

@ -23,7 +23,7 @@ onMounted(async () => {
const assignments = computed(() => { const assignments = computed(() => {
return calcAssignmentLearningContents( return calcAssignmentLearningContents(
learningPathStore.learningPathForUser(courseSession.course.slug, userStore.id) learningPathStore.learningPathForUser(courseSession.value.course.slug, userStore.id)
); );
}); });
</script> </script>

View File

@ -62,7 +62,7 @@ const onSubmit = async () => {
// noinspection TypeScriptValidateTypes // noinspection TypeScriptValidateTypes
upsertAssignmentCompletionMutation.executeMutation({ upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignment.id.toString(), assignmentId: props.assignment.id.toString(),
courseSessionId: courseSession.id.toString(), courseSessionId: courseSession.value.id.toString(),
completionDataString: JSON.stringify({}), completionDataString: JSON.stringify({}),
completionStatus: "SUBMITTED", completionStatus: "SUBMITTED",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment

View File

@ -34,7 +34,7 @@ async function upsertAssignmentCompletion(completion_data: AssignmentCompletionD
// noinspection TypeScriptValidateTypes // noinspection TypeScriptValidateTypes
await upsertAssignmentCompletionMutation.executeMutation({ await upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.assignmentId.toString(), assignmentId: props.assignmentId.toString(),
courseSessionId: courseSession.id.toString(), courseSessionId: courseSession.value.id.toString(),
completionDataString: JSON.stringify(completion_data), completionDataString: JSON.stringify(completion_data),
completionStatus: "IN_PROGRESS", completionStatus: "IN_PROGRESS",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment

View File

@ -43,7 +43,7 @@ const props = defineProps<{
const queryResult = useQuery({ const queryResult = useQuery({
query: ASSIGNMENT_COMPLETION_QUERY, query: ASSIGNMENT_COMPLETION_QUERY,
variables: { variables: {
courseSessionId: courseSession.id.toString(), courseSessionId: courseSession.value.id.toString(),
assignmentId: props.learningContent.content_assignment_id.toString(), assignmentId: props.learningContent.content_assignment_id.toString(),
}, },
pause: true, pause: true,
@ -80,7 +80,7 @@ onMounted(async () => {
// noinspection TypeScriptValidateTypes // noinspection TypeScriptValidateTypes
await upsertAssignmentCompletionMutation.executeMutation({ await upsertAssignmentCompletionMutation.executeMutation({
assignmentId: props.learningContent.content_assignment_id.toString(), assignmentId: props.learningContent.content_assignment_id.toString(),
courseSessionId: courseSession.id.toString(), courseSessionId: courseSession.value.id.toString(),
completionDataString: JSON.stringify({}), completionDataString: JSON.stringify({}),
completionStatus: "IN_PROGRESS", completionStatus: "IN_PROGRESS",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -151,7 +151,7 @@ const getTitle = () => {
}; };
const assignmentUser = computed(() => { const assignmentUser = computed(() => {
return courseSession.users.find( return courseSession.value.users.find(
(user) => user.user_id === Number(userStore.id) (user) => user.user_id === Number(userStore.id)
) as CourseSessionUser; ) as CourseSessionUser;
}); });

View File

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import * as log from "loglevel"; import * as log from "loglevel";
import { useCurrentCourseSession } from "@/composables";
import LearningPathAppointmentsMock from "@/pages/learningPath/learningPathPage/LearningPathAppointmentsMock.vue"; import LearningPathAppointmentsMock from "@/pages/learningPath/learningPathPage/LearningPathAppointmentsMock.vue";
import LearningPathListView from "@/pages/learningPath/learningPathPage/LearningPathListView.vue"; import LearningPathListView from "@/pages/learningPath/learningPathPage/LearningPathListView.vue";
import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue"; import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue";
@ -20,6 +21,7 @@ const props = defineProps<{
const breakpoints = useBreakpoints(breakpointsTailwind); const breakpoints = useBreakpoints(breakpointsTailwind);
const learningPathStore = useLearningPathStore(); const learningPathStore = useLearningPathStore();
const userStore = useUserStore(); const userStore = useUserStore();
const courseSession = useCurrentCourseSession();
// Layout state // Layout state
const useMobileLayout = breakpoints.smaller("sm"); const useMobileLayout = breakpoints.smaller("sm");
@ -80,6 +82,7 @@ const changeViewType = (viewType: ViewType) => {
<p class="font-bold"> <p class="font-bold">
{{ $t("learningPathPage.welcomeBack", { name: userStore.first_name }) }} {{ $t("learningPathPage.welcomeBack", { name: userStore.first_name }) }}
</p> </p>
<pre>{{ courseSession.title }}</pre>
<h2 data-cy="learning-path-title"> <h2 data-cy="learning-path-title">
{{ learningPath?.title }} {{ learningPath?.title }}
</h2> </h2>

View File

@ -1,6 +1,6 @@
import { useCurrentCourseSession } from "@/composables";
import { itGetCached } from "@/fetchHelpers"; import { itGetCached } from "@/fetchHelpers";
import { useCompletionStore } from "@/stores/completion"; import { useCompletionStore } from "@/stores/completion";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useUserStore } from "@/stores/user"; import { useUserStore } from "@/stores/user";
import type { import type {
CircleLight, CircleLight,
@ -166,11 +166,10 @@ export const useCompetenceStore = defineStore({
if (competenceProfilePage) { if (competenceProfilePage) {
const completionStore = useCompletionStore(); const completionStore = useCompletionStore();
const courseSessionsStore = useCourseSessionsStore(); const courseSession = useCurrentCourseSession();
const courseSession = courseSessionsStore.currentCourseSession;
if (courseSession) { if (courseSession) {
const completionData = await completionStore.loadCourseSessionCompletionData( const completionData = await completionStore.loadCourseSessionCompletionData(
courseSession.id, courseSession.value.id,
userId userId
); );

View File

@ -228,7 +228,6 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
return { return {
uniqueCourseSessionsByCourse, uniqueCourseSessionsByCourse,
currentCourseSession,
allCurrentCourseSessions, allCurrentCourseSessions,
courseSessionForCourse, courseSessionForCourse,
switchCourseSession, switchCourseSession,
@ -242,9 +241,12 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
findAttendanceDay, findAttendanceDay,
findAssignmentDetails, findAssignmentDetails,
// only used so that `router.afterEach` can switch it // use `useCurrentCourseSession` whenever possible
currentCourseSession,
loadCourseSessionsData, loadCourseSessionsData,
loaded, loaded,
// only used so that `router.afterEach` can switch it
_currentCourseSlug, _currentCourseSlug,
// only used for unit testing // only used for unit testing

View File

@ -1,7 +1,8 @@
import mitt from "mitt"; import mitt from "mitt";
export type MittEvents = { export type MittEvents = {
// event needed so that the App components do re-render when the course session changes // event needed so that the App components do re-render
// and reload the current course session
switchedCourseSession: number; switchedCourseSession: number;
finishedLearningContent: boolean; finishedLearningContent: boolean;