Merged in feature/composable-course-session (pull request #85)
Feature/composable course session Approved-by: Elia Bieri
This commit is contained in:
commit
3acae49217
|
|
@ -30,8 +30,10 @@ onMounted(() => {
|
|||
log.debug("App mounted");
|
||||
|
||||
eventBus.on("switchedCourseSession", () => {
|
||||
// FIXME: clean up with VBV-305
|
||||
// Rerender the component tree, when the course session gets switched.
|
||||
// So that the current learning path completion data gets updated.
|
||||
componentKey.value++;
|
||||
log.info("Switched course session, re-evaluate component tree");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type { CourseSession } from "@/types";
|
||||
import log from "loglevel";
|
||||
import type { ComputedRef } from "vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
export function useCurrentCourseSession() {
|
||||
/**
|
||||
* We often need the current course session in our components.
|
||||
* With this composable we can get it easily.
|
||||
*/
|
||||
const store = useCourseSessionsStore();
|
||||
|
||||
const result: ComputedRef<CourseSession> = computed(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
() => {
|
||||
if (!store.currentCourseSession) {
|
||||
log.error(
|
||||
"currentCourseSession is only defined in pages with :courseSlug in the route"
|
||||
);
|
||||
throw new Error(
|
||||
`currentCourseSession is not defined in the store.
|
||||
It is only defined in pages with :courseSlug in the route`
|
||||
);
|
||||
}
|
||||
return store.currentCourseSession;
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { useCockpitStore } from "@/stores/cockpit";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import { useLearningPathStore } from "@/stores/learningPath";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import * as log from "loglevel";
|
||||
|
|
@ -16,25 +16,22 @@ const props = defineProps<{
|
|||
const cockpitStore = useCockpitStore();
|
||||
const competenceStore = useCompetenceStore();
|
||||
const learningPathStore = useLearningPathStore();
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
onMounted(async () => {
|
||||
log.debug("CockpitParentPage mounted", props.courseSlug);
|
||||
|
||||
try {
|
||||
const currentCourseSession = courseSessionsStore.currentCourseSession;
|
||||
if (currentCourseSession?.id) {
|
||||
await cockpitStore.loadCourseSessionUsers(currentCourseSession.id);
|
||||
cockpitStore.courseSessionUsers?.forEach((csu) => {
|
||||
competenceStore.loadCompetenceProfilePage(
|
||||
props.courseSlug + "-competence",
|
||||
csu.user_id
|
||||
);
|
||||
await cockpitStore.loadCourseSessionUsers(courseSession.value.id);
|
||||
cockpitStore.courseSessionUsers?.forEach((csu) => {
|
||||
competenceStore.loadCompetenceProfilePage(
|
||||
props.courseSlug + "-competence",
|
||||
csu.user_id
|
||||
);
|
||||
|
||||
learningPathStore.loadLearningPath(props.courseSlug + "-lp", csu.user_id);
|
||||
});
|
||||
learningPathStore.loadLearningPath(props.courseSlug + "-lp", useUserStore().id);
|
||||
}
|
||||
learningPathStore.loadLearningPath(props.courseSlug + "-lp", csu.user_id);
|
||||
});
|
||||
learningPathStore.loadLearningPath(props.courseSlug + "-lp", useUserStore().id);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ import HorizontalBarChart from "@/components/ui/HorizontalBarChart.vue";
|
|||
import OpenFeedback from "@/components/ui/OpenFeedback.vue";
|
||||
import RatingScale from "@/components/ui/RatingScale.vue";
|
||||
import VerticalBarChart from "@/components/ui/VerticalBarChart.vue";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { itGet } from "@/fetchHelpers";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import * as log from "loglevel";
|
||||
import { onMounted, reactive } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
|
@ -79,7 +79,7 @@ const props = defineProps<{
|
|||
|
||||
log.debug("FeedbackPage created", props.circleId);
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
const { t } = useI18n();
|
||||
|
||||
const orderedQuestions = [
|
||||
|
|
@ -144,7 +144,7 @@ const feedbackData = reactive<FeedbackData>({ amount: 0, questions: {} });
|
|||
onMounted(async () => {
|
||||
log.debug("FeedbackPage mounted");
|
||||
const data = await itGet(
|
||||
`/api/core/feedback/${courseSessionsStore.currentCourseSession?.course.id}/${props.circleId}`
|
||||
`/api/core/feedback/${courseSession.value.course.id}/${props.circleId}`
|
||||
);
|
||||
Object.assign(feedbackData, data);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { ASSIGNMENT_COMPLETION_QUERY } from "@/graphql/queries";
|
||||
import EvaluationContainer from "@/pages/cockpit/assignmentEvaluationPage/EvaluationContainer.vue";
|
||||
import AssignmentSubmissionResponses from "@/pages/learningPath/learningContentPage/assignment/AssignmentSubmissionResponses.vue";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type {
|
||||
Assignment,
|
||||
AssignmentCompletion,
|
||||
|
|
@ -32,14 +32,14 @@ const state: StateInterface = reactive({
|
|||
assignmentUser: undefined,
|
||||
});
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
const router = useRouter();
|
||||
|
||||
// noinspection TypeScriptValidateTypes TODO: because of IntelliJ
|
||||
const queryResult = useQuery({
|
||||
query: ASSIGNMENT_COMPLETION_QUERY,
|
||||
variables: {
|
||||
courseSessionId: courseSessionsStore.currentCourseSession!.id.toString(),
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
assignmentId: props.assignmentId,
|
||||
assignmentUserId: props.userId,
|
||||
},
|
||||
|
|
@ -48,11 +48,9 @@ const queryResult = useQuery({
|
|||
onMounted(async () => {
|
||||
log.debug("AssignmentView mounted", props.assignmentId, props.userId);
|
||||
|
||||
if (courseSessionsStore.currentCourseSession) {
|
||||
state.assignmentUser = courseSessionsStore.currentCourseSession.users.find(
|
||||
(user) => user.user_id === Number(props.userId)
|
||||
);
|
||||
}
|
||||
state.assignmentUser = courseSession.value.users.find(
|
||||
(user) => user.user_id === Number(props.userId)
|
||||
);
|
||||
});
|
||||
|
||||
function close() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type { Assignment, AssignmentCompletion, CourseSessionUser } from "@/types";
|
||||
import { useMutation } from "@urql/vue";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
|
|
@ -17,7 +17,7 @@ const emit = defineEmits(["startEvaluation"]);
|
|||
|
||||
log.debug("EvaluationIntro setup");
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const upsertAssignmentCompletionMutation = useMutation(
|
||||
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
|
||||
|
|
@ -29,7 +29,7 @@ async function startEvaluation() {
|
|||
// noinspection TypeScriptValidateTypes
|
||||
upsertAssignmentCompletionMutation.executeMutation({
|
||||
assignmentId: props.assignment.id.toString(),
|
||||
courseSessionId: (courseSessionsStore?.currentCourseSession?.id ?? 0).toString(),
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
assignmentUserId: props.assignmentUser.user_id.toString(),
|
||||
completionStatus: "EVALUATION_IN_PROGRESS",
|
||||
completionDataString: JSON.stringify({}),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
|
||||
import {
|
||||
maxAssignmentPoints,
|
||||
pointsToGrade,
|
||||
userAssignmentPoints,
|
||||
} from "@/services/assignmentService";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type {
|
||||
Assignment,
|
||||
AssignmentCompletion,
|
||||
|
|
@ -34,7 +34,7 @@ const state = reactive({
|
|||
|
||||
log.debug("EvaluationSummary setup");
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const upsertAssignmentCompletionMutation = useMutation(
|
||||
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
|
||||
|
|
@ -44,7 +44,7 @@ async function submitEvaluation() {
|
|||
// noinspection TypeScriptValidateTypes
|
||||
upsertAssignmentCompletionMutation.executeMutation({
|
||||
assignmentId: props.assignment.id.toString(),
|
||||
courseSessionId: (courseSessionsStore?.currentCourseSession?.id ?? 0).toString(),
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
assignmentUserId: props.assignmentUser.user_id.toString(),
|
||||
completionStatus: "EVALUATION_SUBMITTED",
|
||||
completionDataString: JSON.stringify({}),
|
||||
|
|
@ -87,7 +87,7 @@ const grade = computed(() => {
|
|||
|
||||
const evaluationUser = computed(() => {
|
||||
if (props.assignmentCompletion.evaluation_user) {
|
||||
return (courseSessionsStore.currentCourseSession?.users ?? []).find(
|
||||
return (courseSession.value.users ?? []).find(
|
||||
(user) => user.user_id === Number(props.assignmentCompletion.evaluation_user)
|
||||
) as CourseSessionUser;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type {
|
||||
Assignment,
|
||||
AssignmentCompletion,
|
||||
|
|
@ -24,7 +24,7 @@ const props = defineProps<{
|
|||
|
||||
log.debug("EvaluationTask setup", props.taskIndex);
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const task = computed(() => props.assignment.evaluation_tasks[props.taskIndex]);
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ async function evaluateAssignmentCompletion(completionData: AssignmentCompletion
|
|||
// noinspection TypeScriptValidateTypes
|
||||
upsertAssignmentCompletionMutation.executeMutation({
|
||||
assignmentId: props.assignment.id.toString(),
|
||||
courseSessionId: (courseSessionsStore?.currentCourseSession?.id ?? 0).toString(),
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
assignmentUserId: props.assignmentUser.user_id.toString(),
|
||||
completionStatus: "EVALUATION_IN_PROGRESS",
|
||||
completionDataString: JSON.stringify(completionData),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import AssignmentDetails from "@/pages/cockpit/assignmentsPage/AssignmentDetails.vue";
|
||||
import { calcAssignmentLearningContents } from "@/services/assignmentService";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import { useLearningPathStore } from "@/stores/learningPath";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import * as log from "loglevel";
|
||||
|
|
@ -14,31 +14,23 @@ const props = defineProps<{
|
|||
log.debug("AssignmentsPage created", props.courseSlug);
|
||||
|
||||
const learningPathStore = useLearningPathStore();
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const userStore = useUserStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
onMounted(async () => {
|
||||
log.debug("AssignmentsPage mounted");
|
||||
});
|
||||
|
||||
const assignments = computed(() => {
|
||||
// TODO: filter by selected circle
|
||||
if (!courseSessionsStore.currentCourseSession) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return calcAssignmentLearningContents(
|
||||
learningPathStore.learningPathForUser(
|
||||
courseSessionsStore.currentCourseSession.course.slug,
|
||||
userStore.id
|
||||
)
|
||||
learningPathStore.learningPathForUser(courseSession.value.course.slug, userStore.id)
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-200">
|
||||
<div v-if="courseSessionsStore.currentCourseSession" class="container-large">
|
||||
<div class="container-large">
|
||||
<nav class="py-4 pb-4">
|
||||
<router-link
|
||||
class="btn-text inline-flex items-center pl-0"
|
||||
|
|
@ -58,7 +50,7 @@ const assignments = computed(() => {
|
|||
<div v-for="assignment in assignments" :key="assignment.id">
|
||||
<div class="bg-white p-6">
|
||||
<AssignmentDetails
|
||||
:course-session="courseSessionsStore.currentCourseSession"
|
||||
:course-session="courseSession"
|
||||
:assignment="assignment"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import ItPersonRow from "@/components/ui/ItPersonRow.vue";
|
|||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import type { LearningPath } from "@/services/learningPath";
|
||||
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import AssignmentsTile from "@/pages/cockpit/cockpitPage/AssignmentsTile.vue";
|
||||
import { useCockpitStore } from "@/stores/cockpit";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import { useLearningPathStore } from "@/stores/learningPath";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import groupBy from "lodash/groupBy";
|
||||
|
|
@ -25,7 +25,7 @@ const userStore = useUserStore();
|
|||
const cockpitStore = useCockpitStore();
|
||||
const competenceStore = useCompetenceStore();
|
||||
const learningPathStore = useLearningPathStore();
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
function userCountStatusForCircle(userId: number, translationKey: string) {
|
||||
const criteria = competenceStore.flatPerformanceCriteria(
|
||||
|
|
@ -102,10 +102,7 @@ function setActiveClasses(translationKey: string) {
|
|||
</div>
|
||||
<!-- Status -->
|
||||
<div class="mb-4 grid grid-rows-2 gap-4 lg:grid-cols-2 lg:grid-rows-none">
|
||||
<AssignmentsTile
|
||||
v-if="courseSessionsStore.currentCourseSession"
|
||||
:course-session="courseSessionsStore.currentCourseSession"
|
||||
/>
|
||||
<AssignmentsTile :course-session="courseSession" />
|
||||
<div class="bg-white px-6 py-5">
|
||||
<h3 class="heading-3 mb-4 flex items-center gap-2">
|
||||
<it-icon-test-large class="h-16 w-16"></it-icon-test-large>
|
||||
|
|
@ -124,8 +121,8 @@ function setActiveClasses(translationKey: string) {
|
|||
learningPathStore.learningPathForUser(props.courseSlug, userStore.id)
|
||||
?.circles || []
|
||||
"
|
||||
:course-id="courseSessionsStore.currentCourseSession?.course.id || 0"
|
||||
:url="courseSessionsStore.currentCourseSession?.course_url || ''"
|
||||
:course-id="courseSession.course.id"
|
||||
:url="courseSession.course_url || ''"
|
||||
></FeedbackSummary>
|
||||
<div>
|
||||
<!-- progress -->
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import ItButton from "@/components/ui/ItButton.vue";
|
||||
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
|
||||
import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
|
||||
import AssignmentSubmissionResponses from "@/pages/learningPath/learningContentPage/assignment/AssignmentSubmissionResponses.vue";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
|
|
@ -24,6 +25,7 @@ const emit = defineEmits<{
|
|||
}>();
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
const { t } = useI18n();
|
||||
|
||||
const state = reactive({
|
||||
|
|
@ -57,16 +59,10 @@ const onEditTask = (task: AssignmentTask) => {
|
|||
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
const courseSessionId = courseSessionsStore.currentCourseSession?.id;
|
||||
if (!courseSessionId) {
|
||||
log.error("Invalid courseSessionId");
|
||||
return;
|
||||
}
|
||||
|
||||
// noinspection TypeScriptValidateTypes
|
||||
upsertAssignmentCompletionMutation.executeMutation({
|
||||
assignmentId: props.assignment.id.toString(),
|
||||
courseSessionId: courseSessionId.toString(),
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
completionDataString: JSON.stringify({}),
|
||||
completionStatus: "SUBMITTED",
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
|
||||
import ItTextarea from "@/components/ui/ItTextarea.vue";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type {
|
||||
AssignmentCompletion,
|
||||
AssignmentCompletionData,
|
||||
|
|
@ -23,7 +23,7 @@ const props = defineProps<{
|
|||
|
||||
const checkboxState = reactive({} as Record<string, boolean>);
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const upsertAssignmentCompletionMutation = useMutation(
|
||||
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
|
||||
|
|
@ -31,16 +31,10 @@ const upsertAssignmentCompletionMutation = useMutation(
|
|||
|
||||
async function upsertAssignmentCompletion(completion_data: AssignmentCompletionData) {
|
||||
try {
|
||||
const courseSessionId = courseSessionsStore.currentCourseSession?.id;
|
||||
if (!courseSessionId) {
|
||||
console.error("Invalid courseSessionId");
|
||||
return;
|
||||
}
|
||||
|
||||
// noinspection TypeScriptValidateTypes
|
||||
await upsertAssignmentCompletionMutation.executeMutation({
|
||||
assignmentId: props.assignmentId.toString(),
|
||||
courseSessionId: courseSessionId.toString(),
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
completionDataString: JSON.stringify(completion_data),
|
||||
completionStatus: "IN_PROGRESS",
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
|
||||
import { ASSIGNMENT_COMPLETION_QUERY } from "@/graphql/queries";
|
||||
import EvaluationSummary from "@/pages/cockpit/assignmentEvaluationPage/EvaluationSummary.vue";
|
||||
|
|
@ -6,7 +7,6 @@ import AssignmentIntroductionView from "@/pages/learningPath/learningContentPage
|
|||
import AssignmentSubmissionView from "@/pages/learningPath/learningContentPage/assignment/AssignmentSubmissionView.vue";
|
||||
import AssignmentTaskView from "@/pages/learningPath/learningContentPage/assignment/AssignmentTaskView.vue";
|
||||
import LearningContentMultiLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentMultiLayout.vue";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import type {
|
||||
Assignment,
|
||||
|
|
@ -24,7 +24,7 @@ import { computed, onMounted, reactive } from "vue";
|
|||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
const userStore = useUserStore();
|
||||
|
||||
interface State {
|
||||
|
|
@ -43,7 +43,7 @@ const props = defineProps<{
|
|||
const queryResult = useQuery({
|
||||
query: ASSIGNMENT_COMPLETION_QUERY,
|
||||
variables: {
|
||||
courseSessionId: courseSessionsStore.currentCourseSession!.id.toString(),
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
assignmentId: props.learningContent.content_assignment_id.toString(),
|
||||
},
|
||||
pause: true,
|
||||
|
|
@ -80,7 +80,7 @@ onMounted(async () => {
|
|||
// noinspection TypeScriptValidateTypes
|
||||
await upsertAssignmentCompletionMutation.executeMutation({
|
||||
assignmentId: props.learningContent.content_assignment_id.toString(),
|
||||
courseSessionId: courseSessionsStore.currentCourseSession!.id.toString(),
|
||||
courseSessionId: courseSession.value.id.toString(),
|
||||
completionDataString: JSON.stringify({}),
|
||||
completionStatus: "IN_PROGRESS",
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
|
|
@ -109,9 +109,6 @@ const showExitButton = computed(() => numPages.value === stepIndex.value + 1);
|
|||
const dueDate = computed(() =>
|
||||
dayjs(state.courseSessionAssignmentDetails?.submissionDeadlineDateTimeUtc)
|
||||
);
|
||||
const courseSessionId = computed(
|
||||
() => courseSessionsStore.currentCourseSession?.id ?? 0
|
||||
);
|
||||
const currentTask = computed(() => {
|
||||
if (stepIndex.value > 0 && stepIndex.value <= numTasks.value) {
|
||||
return assignment.value?.tasks[stepIndex.value - 1];
|
||||
|
|
@ -154,7 +151,7 @@ const getTitle = () => {
|
|||
};
|
||||
|
||||
const assignmentUser = computed(() => {
|
||||
return (courseSessionsStore.currentCourseSession?.users ?? []).find(
|
||||
return courseSession.value.users.find(
|
||||
(user) => user.user_id === Number(userStore.id)
|
||||
) as CourseSessionUser;
|
||||
});
|
||||
|
|
@ -199,7 +196,7 @@ const assignmentUser = computed(() => {
|
|||
:due-date="dueDate"
|
||||
:assignment="assignment"
|
||||
:assignment-completion="assignmentCompletion"
|
||||
:course-session-id="courseSessionId!"
|
||||
:course-session-id="courseSession.id"
|
||||
@edit-task="jumpToTask($event)"
|
||||
></AssignmentSubmissionView>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
import * as log from "loglevel";
|
||||
|
||||
import LearningPathAppointmentsMock from "@/pages/learningPath/learningPathPage/LearningPathAppointmentsMock.vue";
|
||||
import LearningPathListView from "@/pages/learningPath/learningPathPage/LearningPathListView.vue";
|
||||
import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue";
|
||||
|
|
@ -11,6 +9,7 @@ import LearningPathViewSwitch from "@/pages/learningPath/learningPathPage/Learni
|
|||
import { useLearningPathStore } from "@/stores/learningPath";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
|
||||
import * as log from "loglevel";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { useCircleStore } from "@/stores/circle";
|
|||
import type { LearningUnit } from "@/types";
|
||||
import * as log from "loglevel";
|
||||
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { COMPLETION_FAILURE, COMPLETION_SUCCESS } from "@/constants";
|
||||
import LearningContentContainer from "@/pages/learningPath/learningContentPage/LearningContentContainer.vue";
|
||||
import LearningContentMultiLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentMultiLayout.vue";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import eventBus from "@/utils/eventBus";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
import { computed, onUnmounted } from "vue";
|
||||
|
|
@ -14,7 +14,7 @@ import { computed, onUnmounted } from "vue";
|
|||
log.debug("LearningContent.vue setup");
|
||||
|
||||
const circleStore = useCircleStore();
|
||||
const courseSession = useCourseSessionsStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const questionIndex = useRouteQuery("step", "0", { transform: Number, mode: "push" });
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ onUnmounted(() => {
|
|||
<div class="mt-6 lg:mt-12">
|
||||
{{ $t("selfEvaluation.progressText") }}
|
||||
<router-link
|
||||
:to="courseSession.currentCourseSession?.competence_url || '/'"
|
||||
:to="courseSession.competence_url"
|
||||
class="text-primary-500 underline"
|
||||
>
|
||||
{{ $t("selfEvaluation.progressLink") }}
|
||||
|
|
|
|||
|
|
@ -43,3 +43,16 @@ export const expertRequired: NavigationGuard = (to: RouteLocationNormalized) =>
|
|||
return `/course/${courseSlug}/learn`;
|
||||
}
|
||||
};
|
||||
|
||||
export async function handleCourseSessions(to: RouteLocationNormalized) {
|
||||
// register after login hooks
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
if (to.params.courseSlug) {
|
||||
courseSessionsStore._currentCourseSlug = to.params.courseSlug as string;
|
||||
} else {
|
||||
courseSessionsStore._currentCourseSlug = "";
|
||||
}
|
||||
if (!courseSessionsStore.loaded) {
|
||||
await courseSessionsStore.loadCourseSessionsData();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import DashboardPage from "@/pages/DashboardPage.vue";
|
||||
import LoginPage from "@/pages/LoginPage.vue";
|
||||
import { redirectToLoginIfRequired, updateLoggedIn } from "@/router/guards";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import {
|
||||
handleCourseSessions,
|
||||
redirectToLoginIfRequired,
|
||||
updateLoggedIn,
|
||||
} from "@/router/guards";
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
|
||||
const router = createRouter({
|
||||
|
|
@ -182,18 +184,7 @@ const router = createRouter({
|
|||
router.beforeEach(updateLoggedIn);
|
||||
router.beforeEach(redirectToLoginIfRequired);
|
||||
|
||||
router.beforeEach((to) => {
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
if (to.params.courseSlug) {
|
||||
courseSessionsStore._currentCourseSlug = to.params.courseSlug as string;
|
||||
} else {
|
||||
courseSessionsStore._currentCourseSlug = "";
|
||||
}
|
||||
});
|
||||
|
||||
router.afterEach(() => {
|
||||
const appStore = useAppStore();
|
||||
appStore.routingFinished = true;
|
||||
});
|
||||
// register after login hooks
|
||||
router.beforeEach(handleCourseSessions);
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export type AppState = {
|
||||
userLoaded: boolean;
|
||||
routingFinished: boolean;
|
||||
};
|
||||
|
||||
export const useAppStore = defineStore({
|
||||
id: "app",
|
||||
state: () =>
|
||||
({
|
||||
userLoaded: false,
|
||||
routingFinished: false,
|
||||
} as AppState),
|
||||
getters: {},
|
||||
actions: {},
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { itGetCached } from "@/fetchHelpers";
|
||||
import { useCompletionStore } from "@/stores/completion";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import type {
|
||||
CircleLight,
|
||||
|
|
@ -166,11 +166,10 @@ export const useCompetenceStore = defineStore({
|
|||
if (competenceProfilePage) {
|
||||
const completionStore = useCompletionStore();
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
const courseSession = courseSessionsStore.currentCourseSession;
|
||||
const courseSession = useCurrentCourseSession();
|
||||
if (courseSession) {
|
||||
const completionData = await completionStore.loadCourseSessionCompletionData(
|
||||
courseSession.id,
|
||||
courseSession.value.id,
|
||||
userId
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,20 +17,16 @@ import { computed, ref } from "vue";
|
|||
import { useCircleStore } from "./circle";
|
||||
import { useUserStore } from "./user";
|
||||
|
||||
export type LearningSequenceCircleDocument = {
|
||||
id: number;
|
||||
title: string;
|
||||
documents: CircleDocument[];
|
||||
};
|
||||
|
||||
const SELECTED_COURSE_SESSIONS_KEY = "selectedCourseSessionMap";
|
||||
|
||||
function loadCourseSessionsData(reload = false) {
|
||||
log.debug("loadCourseSessionsData called");
|
||||
const courseSessions = ref<CourseSession[]>([]);
|
||||
export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
||||
const loaded = ref(false);
|
||||
const allCourseSessions = ref<CourseSession[]>([]);
|
||||
|
||||
async function loadAndUpdate() {
|
||||
courseSessions.value = await itGetCached(`/api/course/sessions/`, {
|
||||
async function loadCourseSessionsData(reload = false) {
|
||||
log.debug("loadCourseSessionsData called");
|
||||
|
||||
allCourseSessions.value = await itGetCached(`/api/course/sessions/`, {
|
||||
reload: reload,
|
||||
});
|
||||
|
||||
|
|
@ -38,7 +34,7 @@ function loadCourseSessionsData(reload = false) {
|
|||
if (userStore.loggedIn) {
|
||||
// TODO: refactor after implementing of Klassenkonzept
|
||||
await Promise.all(
|
||||
courseSessions.value.map(async (cs) => {
|
||||
allCourseSessions.value.map(async (cs) => {
|
||||
const users = (await itGetCached(`/api/course/sessions/${cs.id}/users/`, {
|
||||
reload: reload,
|
||||
})) as CourseSessionUser[];
|
||||
|
|
@ -46,26 +42,14 @@ function loadCourseSessionsData(reload = false) {
|
|||
})
|
||||
);
|
||||
|
||||
if (!courseSessions.value) {
|
||||
if (!allCourseSessions.value) {
|
||||
throw `No courseSessionData found for user`;
|
||||
}
|
||||
}
|
||||
|
||||
loaded.value = true;
|
||||
}
|
||||
|
||||
loadAndUpdate(); // this will be called asynchronously, but does not block
|
||||
|
||||
// returns the empty sessions array at first, then after loading populates the ref
|
||||
return { allCourseSessions: courseSessions };
|
||||
}
|
||||
|
||||
export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
||||
// using setup function seems cleaner, see https://pinia.vuejs.org/core-concepts/#setup-stores
|
||||
|
||||
// this will become a state variable (like each other `ref()`
|
||||
|
||||
// store should do own setup, we don't want to have each component initialize it
|
||||
// that's why we call the load function in here
|
||||
const { allCourseSessions } = loadCourseSessionsData();
|
||||
const selectedCourseSessionMap = useLocalStorage(
|
||||
SELECTED_COURSE_SESSIONS_KEY,
|
||||
new Map<string, number>()
|
||||
|
|
@ -106,7 +90,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
function switchCourseSession(courseSession: CourseSession) {
|
||||
log.debug("switchCourseSession", courseSession);
|
||||
selectedCourseSessionMap.value.set(courseSession.course.slug, courseSession.id);
|
||||
// FIXME: clean up with VBV-305
|
||||
// Emit event so that the App can re-render with the new courseSession
|
||||
eventBus.emit("switchedCourseSession", courseSession.id);
|
||||
}
|
||||
|
||||
|
|
@ -244,7 +228,6 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
|
||||
return {
|
||||
uniqueCourseSessionsByCourse,
|
||||
currentCourseSession,
|
||||
allCurrentCourseSessions,
|
||||
courseSessionForCourse,
|
||||
switchCourseSession,
|
||||
|
|
@ -258,6 +241,11 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
findAttendanceDay,
|
||||
findAssignmentDetails,
|
||||
|
||||
// use `useCurrentCourseSession` whenever possible
|
||||
currentCourseSession,
|
||||
|
||||
loadCourseSessionsData,
|
||||
loaded,
|
||||
// only used so that `router.afterEach` can switch it
|
||||
_currentCourseSlug,
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import log from "loglevel";
|
|||
|
||||
import { bustItGetCache, itGetCached, itPost } from "@/fetchHelpers";
|
||||
import { loadLocaleMessages, setI18nLanguage } from "@/i18n";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
const logoutRedirectUrl = import.meta.env.VITE_LOGOUT_REDIRECT || "/";
|
||||
|
|
@ -85,11 +84,9 @@ export const useUserStore = defineStore({
|
|||
});
|
||||
},
|
||||
async fetchUser() {
|
||||
const appStore = useAppStore();
|
||||
const data = await itGetCached("/api/core/me/");
|
||||
this.$state = data;
|
||||
this.loggedIn = true;
|
||||
appStore.userLoaded = true;
|
||||
await setLocale(data.language);
|
||||
},
|
||||
async setUserLanguages(language: AvailableLanguages) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import mitt from "mitt";
|
||||
|
||||
export type MittEvents = {
|
||||
// FIXME: clean up with VBV-305
|
||||
// event needed so that the App components do re-render
|
||||
// and reload the current course session
|
||||
switchedCourseSession: number;
|
||||
|
||||
finishedLearningContent: boolean;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue