vbv/client/src/stores/courseSessions.ts

277 lines
8.1 KiB
TypeScript

import { itGetCached, itPost } from "@/fetchHelpers";
import { deleteCircleDocument } from "@/services/files";
import type {
CircleDocument,
CourseSession,
CourseSessionAssignment,
CourseSessionAttendanceCourse,
CourseSessionUser,
DueDate,
ExpertSessionUser,
} from "@/types";
import eventBus from "@/utils/eventBus";
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 { useCircleStore } from "./circle";
import { useUserStore } from "./user";
const SELECTED_COURSE_SESSIONS_KEY = "selectedCourseSessionMap";
export const useCourseSessionsStore = defineStore("courseSessions", () => {
const loaded = ref(false);
const allCourseSessions = ref<CourseSession[]>([]);
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) => {
const users = (await itGetCached(`/api/course/sessions/${cs.id}/users/`, {
reload: reload,
})) as CourseSessionUser[];
cs.users = users;
cs.due_dates.forEach((dueDate) => {
dueDate.start = dayjs(dueDate.start);
dueDate.end = dayjs(dueDate.end);
});
})
);
if (!allCourseSessions.value) {
throw `No courseSessionData found for user`;
}
}
loaded.value = true;
}
const selectedCourseSessionMap = useLocalStorage(
SELECTED_COURSE_SESSIONS_KEY,
new Map<string, number>()
);
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 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 circleExperts = computed(() => {
const circleStore = useCircleStore();
const circleTranslationKey = circleStore.circle?.translation_key;
if (currentCourseSession.value && circleTranslationKey) {
return currentCourseSession.value.users.filter((u) => {
if (u.role === "EXPERT") {
return (u as ExpertSessionUser).circles
.map((c) => c.translation_key)
.includes(circleTranslationKey);
}
return false;
}) as ExpertSessionUser[];
}
return [];
});
const canUploadCircleDocuments = computed(() => {
const userStore = useUserStore();
return (
circleExperts.value.filter((expert) => expert.user_id === userStore.id).length > 0
);
});
const circleDocuments = computed(() => {
const circleStore = useCircleStore();
return (
circleStore.circle?.learningSequences
.map((ls) => ({ id: ls.id, title: ls.title, documents: [] }))
.map((ls: { id: number; title: string; documents: CircleDocument[] }) => {
if (currentCourseSession.value === undefined) {
return ls;
}
for (const document of currentCourseSession.value.documents) {
if (document.learning_sequence === ls.id) {
ls.documents.push(document);
}
}
return ls;
})
.filter((ls) => ls.documents.length > 0) || []
);
});
function hasCockpit(couseSession: CourseSession) {
const userStore = useUserStore();
return (
userStore.course_session_experts.includes(couseSession.id) ||
userStore.is_superuser
);
}
function addDocument(document: CircleDocument) {
currentCourseSession.value?.documents.push(document);
}
function allDueDates() {
const allDueDatesReturn: DueDate[] = [];
allCourseSessions.value?.forEach((cs) => {
allDueDatesReturn.push(...cs.due_dates);
});
allDueDatesReturn.sort((a, b) => dayjs(a.end).diff(dayjs(b.end)));
return allDueDatesReturn;
}
async function startUpload() {
log.debug("loadCourseSessionsData called");
allCourseSessions.value = await itPost(`/api/core/file/start`, {
file_type: "image/png",
file_name: "test.png",
});
}
async function removeDocument(documentId: string) {
await deleteCircleDocument(documentId);
if (currentCourseSession.value === undefined) {
return;
}
currentCourseSession.value.documents = currentCourseSession.value?.documents.filter(
(d) => d.id !== documentId
);
}
function findAttendanceCourse(
contentId: number
): CourseSessionAttendanceCourse | undefined {
if (currentCourseSession.value) {
return currentCourseSession.value.attendance_courses.find(
(attendanceCourse) => attendanceCourse.learning_content_id === contentId
);
}
}
function findCourseSessionAssignment(
contentId?: number
): CourseSessionAssignment | undefined {
if (contentId && currentCourseSession.value) {
return currentCourseSession.value.assignments.find(
(a) => a.learning_content_id === contentId
);
}
}
return {
uniqueCourseSessionsByCourse,
allCurrentCourseSessions,
courseSessionForCourse,
switchCourseSession,
hasCockpit,
currentCourseSessionHasCockpit,
canUploadCircleDocuments,
circleDocuments,
circleExperts,
addDocument,
startUpload,
removeDocument,
findAttendanceCourse,
findCourseSessionAssignment,
allDueDates,
// use `useCurrentCourseSession` whenever possible
currentCourseSession,
loadCourseSessionsData,
loaded,
// only used so that `router.afterEach` can switch it
_currentCourseSlug,
// only used for unit testing
allCourseSessions,
};
});