204 lines
6.2 KiB
TypeScript
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,
|
|
};
|
|
});
|