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([]); 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() ); 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, }; });