vbv/client/src/stores/courseSessions.ts

204 lines
6.2 KiB
TypeScript

import { itGetCached } from "@/fetchHelpers";
import type { CourseSession, DueDate } from "@/types";
import eventBus from "@/utils/eventBus";
import { useRouteLookups } from "@/utils/route";
import { useLocalStorage } from "@vueuse/core";
import dayjs from "dayjs";
import uniqBy from "lodash/uniqBy";
import log from "loglevel";
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { useUserStore } from "./user";
const SELECTED_COURSE_SESSIONS_KEY = "selectedCourseSessionMap";
export const useCourseSessionsStore = defineStore("courseSessions", () => {
const loaded = ref(false);
const allCourseSessions = ref<CourseSession[]>([]);
const { inCompetenceProfile, inLearningPath } = useRouteLookups();
async function loadCourseSessionsData(reload = false) {
log.debug("loadCourseSessionsData called");
allCourseSessions.value = await itGetCached(`/api/course/sessions/`, {
reload: reload,
});
const userStore = useUserStore();
if (userStore.loggedIn) {
// TODO: refactor after implementing of Klassenkonzept
await Promise.all(
allCourseSessions.value.map(async (cs) => {
sortDueDates(cs.due_dates);
})
);
if (!allCourseSessions.value) {
throw `No courseSessionData found for user`;
}
}
loaded.value = true;
}
const selectedCourseSessionMap = useLocalStorage(
SELECTED_COURSE_SESSIONS_KEY,
new Map<string, string>()
);
const _currentCourseSlug = ref("");
const uniqueCourseSessionsByCourse = computed(() =>
// Im Dashboard wird aktuell ein Widget pro Kurs dargestellt
// mit dem Fortschritt jeweils einer Durchführung.
// Dieser Getter wird nur dort benutzt.
// TODO: Das Dashboard verschwindet evtl. in Zukunft, dann kann dieser Getter weg.
// @ts-ignore
uniqBy(allCourseSessions.value, "course.id")
);
function selectedCourseSessionForCourse(courseSlug: string) {
// Wir wollen pro Kurs wissen, welche Durchführung der User zuletzt ausgewählt hat.
// Die letzte Durchführung wird im localStorage via `selectedCoruseSessionMap`
// gespeichert und hier geladen.
// Wenn noch keine Durchführung ausgewählt wurde, wird die erste Durchführung
// in `courseSessionForCourse` zurückgegeben.
try {
const courseSessionId = selectedCourseSessionMap.value.get(courseSlug);
if (courseSessionId) {
return allCourseSessions.value.find((cs) => {
return cs.id === courseSessionId;
});
}
} catch (e) {
log.error("Error while parsing courseSessions from localStorage", e);
}
// Keine Durchführung ausgewählt im localStorage
return undefined;
}
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: string) {
return allCourseSessions.value.find((cs) => {
return courseSessionId.toString() === cs.id.toString();
});
}
function switchCourseSessionById(courseSessionId: 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);
if (courseSession) {
return courseSession;
} else {
// return first if there is no selected courseSession
return allCourseSessions.value.find((cs) => {
return cs.course.slug === courseSlug;
});
}
}
return undefined;
}
function allCourseSessionsForCourse(courseSlug: string) {
return allCourseSessions.value.filter((cs) => {
return cs.course.slug === courseSlug;
});
}
const currentCourseSession = computed(() => {
return courseSessionForCourse(_currentCourseSlug.value);
});
const allCurrentCourseSessions = computed(() => {
return allCourseSessionsForCourse(_currentCourseSlug.value);
});
const currentCourseSessionHasCockpit = computed(() => {
if (currentCourseSession.value) {
return hasCockpit(currentCourseSession.value);
}
return false;
});
const hasCourseSessionPreview = computed(() => {
const isCourseExpert =
currentCourseSession.value && currentCourseSessionHasCockpit.value;
return Boolean(isCourseExpert && (inLearningPath() || inCompetenceProfile()));
});
function hasCockpit(courseSession: CourseSession) {
const userStore = useUserStore();
return (
userStore.course_session_experts.includes(courseSession.id) ||
userStore.is_superuser
);
}
function allDueDates() {
const allDueDatesReturn: DueDate[] = [];
allCourseSessions.value?.forEach((cs) => {
allDueDatesReturn.push(...cs.due_dates);
});
sortDueDates(allDueDatesReturn);
return allDueDatesReturn;
}
function sortDueDates(dueDates: DueDate[]) {
dueDates.sort((a, b) => {
const dateA = dayjs(a.start);
const dateB = dayjs(b.start);
if (!dateA.isValid() && !dateB.isValid()) return 0; // If both are invalid, they are equal
if (!dateA.isValid()) return 1; // If dateA is invalid, it goes after dateB
if (!dateB.isValid()) return -1; // If dateB is invalid, it goes after dateA
return dateA.diff(dateB); // sort by `start`
});
}
return {
uniqueCourseSessionsByCourse,
allCurrentCourseSessions,
courseSessionForCourse,
getCourseSessionById,
switchCourseSessionById,
hasCockpit,
hasCourseSessionPreview,
currentCourseSessionHasCockpit,
allDueDates,
// use `useCurrentCourseSession` whenever possible
currentCourseSession,
loadCourseSessionsData,
loaded,
// only used so that `router.afterEach` can switch it
_currentCourseSlug,
// only used for unit testing
allCourseSessions,
};
});