277 lines
8.1 KiB
TypeScript
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,
|
|
};
|
|
});
|