Show Diagram for users in cockpit

This commit is contained in:
Daniel Egger 2023-10-12 21:37:57 +02:00
parent 61dfdfda9d
commit a6cf4ad128
24 changed files with 184 additions and 264 deletions

View File

@ -2,29 +2,37 @@
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue"; import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils"; import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
import { computed } from "vue"; import { computed } from "vue";
import type { LearningPathType } from "@/types"; import { useLearningPathWithCompletion } from "@/composables";
import { flatCircles } from "@/composables";
export type DiagramType = "horizontal" | "horizontalSmall" | "singleSmall"; export type DiagramType = "horizontal" | "horizontalSmall" | "singleSmall";
export interface Props { export interface Props {
diagramType?: DiagramType;
learningPath: LearningPathType;
showCircleSlugs?: string[]; showCircleSlugs?: string[];
courseSlug: string;
courseSessionId: string;
userId?: string;
diagramType?: DiagramType;
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
diagramType: "horizontal", diagramType: "horizontal",
showCircleSlugs: undefined, showCircleSlugs: undefined,
userId: undefined,
}); });
const lpQueryResult = useLearningPathWithCompletion(
props.courseSlug,
props.userId,
props.courseSessionId
);
const circles = computed(() => { const circles = computed(() => {
if (props.showCircleSlugs?.length) { if (props.showCircleSlugs?.length) {
return flatCircles(props.learningPath).filter( return (lpQueryResult.circles.value ?? []).filter(
(c) => props.showCircleSlugs?.includes(c.slug) ?? true (c) => props.showCircleSlugs?.includes(c.slug) ?? true
); );
} }
return flatCircles(props.learningPath); return lpQueryResult.circles.value ?? [];
}); });
const wrapperClasses = computed(() => { const wrapperClasses = computed(() => {

View File

@ -1,27 +0,0 @@
<script setup lang="ts">
import * as log from "loglevel";
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
import { useLearningPathWithCompletion } from "@/composables";
log.debug("LearningPathDiagramSmall created");
const props = defineProps<{
courseSlug: string;
courseSessionId: string;
}>();
const lpQueryResult = useLearningPathWithCompletion(
props.courseSlug,
props.courseSessionId
);
</script>
<template>
<LearningPathDiagram
v-if="lpQueryResult.learningPath.value"
:learning-path="lpQueryResult.learningPath.value"
diagram-type="horizontalSmall"
></LearningPathDiagram>
</template>
<style scoped></style>

View File

@ -148,6 +148,18 @@ export function useLearningPath(courseSlug: string) {
resultPromise.then((result) => { resultPromise.then((result) => {
learningPath.value = result.data?.learning_path as LearningPathType; learningPath.value = result.data?.learning_path as LearningPathType;
// attach circle information to learning contents
if (learningPath.value) {
flatCircles(learningPath.value).forEach((circle) => {
circleFlatChildren(circle).forEach((lc) => {
lc.circle = {
id: circle.id,
slug: circle.slug,
title: circle.title,
};
});
});
}
}); });
const circles = computed(() => { const circles = computed(() => {
@ -163,13 +175,23 @@ export function useLearningPath(courseSlug: string) {
}); });
} }
return { resultPromise, learningPath, circles, findCircle }; function findLearningContent(learningContentId: string) {
return (circles.value ?? [])
.flatMap((c) => {
return circleFlatLearningContents(c);
})
.find((lc) => {
return lc.id === learningContentId;
});
}
return { resultPromise, learningPath, circles, findCircle, findLearningContent };
} }
export function useLearningPathWithCompletion( export function useLearningPathWithCompletion(
courseSlug?: string, courseSlug?: string,
courseSessionId?: string, userId?: string,
userId?: string courseSessionId?: string
) { ) {
if (!courseSlug) { if (!courseSlug) {
courseSlug = useCurrentCourseSession().value.course.slug; courseSlug = useCurrentCourseSession().value.course.slug;

View File

@ -18,7 +18,7 @@ const documents = {
"\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc, "\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc,
"\n query attendanceCheckQuery($courseSessionId: ID!) {\n course_session_attendance_course(id: $courseSessionId) {\n id\n attendance_user_list {\n user_id\n status\n }\n }\n }\n": types.AttendanceCheckQueryDocument, "\n query attendanceCheckQuery($courseSessionId: ID!) {\n course_session_attendance_course(id: $courseSessionId) {\n id\n attendance_user_list {\n user_id\n status\n }\n }\n }\n": types.AttendanceCheckQueryDocument,
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument, "\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument, "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
"\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument, "\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument,
"\n query learningPathQuery($slug: String!) {\n learning_path(slug: $slug) {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n competence_certificate {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.LearningPathQueryDocument, "\n query learningPathQuery($slug: String!) {\n learning_path(slug: $slug) {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n competence_certificate {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.LearningPathQueryDocument,
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument, "\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument,
@ -61,7 +61,7 @@ export function graphql(source: "\n query assignmentCompletionQuery(\n $assi
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n"]; export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"];
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,5 @@
type Query { type Query {
learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType
circle(id: ID, slug: String): CircleObjectType
learning_content_media_library: LearningContentMediaLibraryObjectType learning_content_media_library: LearningContentMediaLibraryObjectType
learning_content_assignment: LearningContentAssignmentObjectType learning_content_assignment: LearningContentAssignmentObjectType
learning_content_attendance_course: LearningContentAttendanceCourseObjectType learning_content_attendance_course: LearningContentAttendanceCourseObjectType
@ -28,7 +27,6 @@ type LearningPathObjectType implements CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
topics: [TopicObjectType!]! topics: [TopicObjectType!]!
} }
@ -41,10 +39,29 @@ interface CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
} }
type CourseObjectType {
id: ID!
title: String!
category_name: String!
slug: String!
}
type TopicObjectType implements CoursePageInterface {
is_visible: Boolean!
id: ID!
title: String!
slug: String!
content_type: String!
live: Boolean!
translation_key: String!
frontend_url: String!
course: CourseObjectType
circles: [CircleObjectType!]!
}
type CircleObjectType implements CoursePageInterface { type CircleObjectType implements CoursePageInterface {
description: String! description: String!
goals: String! goals: String!
@ -55,18 +72,10 @@ type CircleObjectType implements CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
learning_sequences: [LearningSequenceObjectType!]! learning_sequences: [LearningSequenceObjectType!]!
} }
type CourseObjectType {
id: ID!
title: String!
category_name: String!
slug: String!
}
type LearningSequenceObjectType implements CoursePageInterface { type LearningSequenceObjectType implements CoursePageInterface {
icon: String! icon: String!
id: ID! id: ID!
@ -76,7 +85,6 @@ type LearningSequenceObjectType implements CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
learning_units: [LearningUnitObjectType!]! learning_units: [LearningUnitObjectType!]!
} }
@ -90,7 +98,6 @@ type LearningUnitObjectType implements CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
learning_contents: [LearningContentInterface!]! learning_contents: [LearningContentInterface!]!
performance_criteria: [PerformanceCriteriaObjectType!]! performance_criteria: [PerformanceCriteriaObjectType!]!
@ -105,12 +112,18 @@ interface LearningContentInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
}
type CircleLightObjectType {
id: ID!
title: String!
slug: String!
} }
type PerformanceCriteriaObjectType implements CoursePageInterface { type PerformanceCriteriaObjectType implements CoursePageInterface {
@ -122,24 +135,9 @@ type PerformanceCriteriaObjectType implements CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
} }
type TopicObjectType implements CoursePageInterface {
is_visible: Boolean!
id: ID!
title: String!
slug: String!
content_type: String!
live: Boolean!
translation_key: String!
frontend_url: String!
circle: CircleObjectType
course: CourseObjectType
circles: [CircleObjectType!]!
}
type LearningContentMediaLibraryObjectType implements CoursePageInterface & LearningContentInterface { type LearningContentMediaLibraryObjectType implements CoursePageInterface & LearningContentInterface {
id: ID! id: ID!
title: String! title: String!
@ -148,12 +146,12 @@ type LearningContentMediaLibraryObjectType implements CoursePageInterface & Lear
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
} }
type LearningContentAssignmentObjectType implements CoursePageInterface & LearningContentInterface { type LearningContentAssignmentObjectType implements CoursePageInterface & LearningContentInterface {
@ -166,12 +164,12 @@ type LearningContentAssignmentObjectType implements CoursePageInterface & Learni
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
competence_certificate: CompetenceCertificateObjectType competence_certificate: CompetenceCertificateObjectType
} }
@ -202,7 +200,6 @@ type AssignmentObjectType implements CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
tasks: JSONStreamField tasks: JSONStreamField
evaluation_tasks: JSONStreamField evaluation_tasks: JSONStreamField
@ -238,7 +235,6 @@ type CompetenceCertificateObjectType implements CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
assignments: [AssignmentObjectType!]! assignments: [AssignmentObjectType!]!
} }
@ -344,12 +340,12 @@ type LearningContentAttendanceCourseObjectType implements CoursePageInterface &
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
} }
type DueDateObjectType { type DueDateObjectType {
@ -426,12 +422,12 @@ type LearningContentEdoniqTestObjectType implements CoursePageInterface & Learni
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
competence_certificate: CompetenceCertificateObjectType competence_certificate: CompetenceCertificateObjectType
} }
@ -520,12 +516,12 @@ type LearningContentFeedbackObjectType implements CoursePageInterface & Learning
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
} }
type LearningContentLearningModuleObjectType implements CoursePageInterface & LearningContentInterface { type LearningContentLearningModuleObjectType implements CoursePageInterface & LearningContentInterface {
@ -536,12 +532,12 @@ type LearningContentLearningModuleObjectType implements CoursePageInterface & Le
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
} }
type LearningContentPlaceholderObjectType implements CoursePageInterface & LearningContentInterface { type LearningContentPlaceholderObjectType implements CoursePageInterface & LearningContentInterface {
@ -552,12 +548,12 @@ type LearningContentPlaceholderObjectType implements CoursePageInterface & Learn
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
} }
type LearningContentRichTextObjectType implements CoursePageInterface & LearningContentInterface { type LearningContentRichTextObjectType implements CoursePageInterface & LearningContentInterface {
@ -568,12 +564,12 @@ type LearningContentRichTextObjectType implements CoursePageInterface & Learning
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
} }
type LearningContentVideoObjectType implements CoursePageInterface & LearningContentInterface { type LearningContentVideoObjectType implements CoursePageInterface & LearningContentInterface {
@ -584,12 +580,12 @@ type LearningContentVideoObjectType implements CoursePageInterface & LearningCon
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
} }
type LearningContentDocumentListObjectType implements CoursePageInterface & LearningContentInterface { type LearningContentDocumentListObjectType implements CoursePageInterface & LearningContentInterface {
@ -600,12 +596,12 @@ type LearningContentDocumentListObjectType implements CoursePageInterface & Lear
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType!
course: CourseObjectType course: CourseObjectType
minutes: Int minutes: Int
description: String! description: String!
content_url: String! content_url: String!
can_user_self_toggle_course_completion: Boolean! can_user_self_toggle_course_completion: Boolean!
circle: CircleLightObjectType
} }
type CompetenceCertificateListObjectType implements CoursePageInterface { type CompetenceCertificateListObjectType implements CoursePageInterface {
@ -616,7 +612,6 @@ type CompetenceCertificateListObjectType implements CoursePageInterface {
live: Boolean! live: Boolean!
translation_key: String! translation_key: String!
frontend_url: String! frontend_url: String!
circle: CircleObjectType
course: CourseObjectType course: CourseObjectType
competence_certificates: [CompetenceCertificateObjectType!]! competence_certificates: [CompetenceCertificateObjectType!]!
} }

View File

@ -9,6 +9,7 @@ export const AttendanceUserInputType = "AttendanceUserInputType";
export const AttendanceUserObjectType = "AttendanceUserObjectType"; export const AttendanceUserObjectType = "AttendanceUserObjectType";
export const AttendanceUserStatus = "AttendanceUserStatus"; export const AttendanceUserStatus = "AttendanceUserStatus";
export const Boolean = "Boolean"; export const Boolean = "Boolean";
export const CircleLightObjectType = "CircleLightObjectType";
export const CircleObjectType = "CircleObjectType"; export const CircleObjectType = "CircleObjectType";
export const CompetenceCertificateListObjectType = "CompetenceCertificateListObjectType"; export const CompetenceCertificateListObjectType = "CompetenceCertificateListObjectType";
export const CompetenceCertificateObjectType = "CompetenceCertificateObjectType"; export const CompetenceCertificateObjectType = "CompetenceCertificateObjectType";

View File

@ -97,7 +97,9 @@ export const COMPETENCE_NAVI_CERTIFICATE_QUERY = graphql(`
learning_content { learning_content {
...CoursePageFields ...CoursePageFields
circle { circle {
...CoursePageFields id
title
slug
} }
} }
} }

View File

@ -1,17 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref, watch } from "vue"; import { computed, onMounted, ref, watch } from "vue";
import { useCourseSessionsStore } from "@/stores/courseSessions"; import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useLearningPathStore } from "@/stores/learningPath";
import { useTranslation } from "i18next-vue"; import { useTranslation } from "i18next-vue";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue"; import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import type { DueDate } from "@/types"; import type { DueDate } from "@/types";
import DueDatesList from "@/components/dueDates/DueDatesList.vue"; import DueDatesList from "@/components/dueDates/DueDatesList.vue";
import { useLearningPath } from "@/composables";
const { t } = useTranslation(); const { t } = useTranslation();
const UNFILTERED = Number.MAX_SAFE_INTEGER.toString(); const UNFILTERED = Number.MAX_SAFE_INTEGER.toString();
const courseSessionsStore = useCourseSessionsStore(); const courseSessionsStore = useCourseSessionsStore();
const learningPathStore = useLearningPathStore();
type Item = { type Item = {
id: string; id: string;
@ -61,16 +60,15 @@ const circles = ref<Item[]>([initialItemCircle]);
const selectedCircle = ref<Item>(circles.value[0]); const selectedCircle = ref<Item>(circles.value[0]);
async function loadCircleValues() { async function loadCircleValues() {
const data = await learningPathStore.loadLearningPath( if (selectedCourse.value) {
`${selectedCourse.value.slug}-lp`, const learningPathQuery = useLearningPath(selectedCourse.value.slug);
undefined, await learningPathQuery.resultPromise;
false,
false
);
if (data) {
circles.value = [ circles.value = [
initialItemCircle, initialItemCircle,
...data.circles.map((circle) => ({ id: circle.id, name: circle.title })), ...(learningPathQuery.circles.value ?? []).map((circle) => ({
id: circle.id,
name: circle.title,
})),
]; ];
} else { } else {
circles.value = [initialItemCircle]; circles.value = [initialItemCircle];

View File

@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import DueDatesList from "@/components/dueDates/DueDatesList.vue"; import DueDatesList from "@/components/dueDates/DueDatesList.vue";
import LearningPathDiagramSmall from "@/components/learningPath/LearningPathDiagramSmall.vue";
import { useCourseSessionsStore } from "@/stores/courseSessions"; import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useUserStore } from "@/stores/user"; import { useUserStore } from "@/stores/user";
import type { CourseSession } from "@/types"; import type { CourseSession } from "@/types";
import log from "loglevel"; import log from "loglevel";
import { computed, onMounted } from "vue"; import { computed, onMounted } from "vue";
import { getCockpitUrl, getLearningPathUrl } from "@/utils/utils"; import { getCockpitUrl, getLearningPathUrl } from "@/utils/utils";
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
log.debug("DashboardPage created"); log.debug("DashboardPage created");
@ -47,11 +47,12 @@ const getNextStepLink = (courseSession: CourseSession) => {
<div class="bg-white p-6 md:h-full"> <div class="bg-white p-6 md:h-full">
<h3 class="mb-4">{{ courseSession.course.title }}</h3> <h3 class="mb-4">{{ courseSession.course.title }}</h3>
<div> <div>
<LearningPathDiagramSmall <LearningPathDiagram
class="mb-4" class="mb-4"
:course-slug="courseSession.course.slug" :course-slug="courseSession.course.slug"
:course-session-id="courseSession.id" :course-session-id="courseSession.id"
></LearningPathDiagramSmall> diagram-type="horizontalSmall"
></LearningPathDiagram>
</div> </div>
<div> <div>
<router-link <router-link

View File

@ -2,8 +2,6 @@
import { useCourseSessionDetailQuery } from "@/composables"; import { useCourseSessionDetailQuery } from "@/composables";
import { useCockpitStore } from "@/stores/cockpit"; import { useCockpitStore } from "@/stores/cockpit";
import { useCompetenceStore } from "@/stores/competence"; import { useCompetenceStore } from "@/stores/competence";
import { useLearningPathStore } from "@/stores/learningPath";
import { useUserStore } from "@/stores/user";
import * as log from "loglevel"; import * as log from "loglevel";
import { onMounted } from "vue"; import { onMounted } from "vue";
@ -15,8 +13,6 @@ const props = defineProps<{
const cockpitStore = useCockpitStore(); const cockpitStore = useCockpitStore();
const competenceStore = useCompetenceStore(); const competenceStore = useCompetenceStore();
const learningPathStore = useLearningPathStore();
const courseSessionDetailResult = useCourseSessionDetailQuery(); const courseSessionDetailResult = useCourseSessionDetailQuery();
onMounted(async () => { onMounted(async () => {
@ -30,13 +26,7 @@ onMounted(async () => {
props.courseSlug + "-competencenavi-competences", props.courseSlug + "-competencenavi-competences",
csu.user_id csu.user_id
); );
learningPathStore.loadLearningPath(props.courseSlug + "-lp", csu.user_id);
}); });
await learningPathStore.loadLearningPath(
props.courseSlug + "-lp",
useUserStore().id
);
await cockpitStore.loadCircles( await cockpitStore.loadCircles(
props.courseSlug, props.courseSlug,
courseSessionDetailResult.findCurrentUser() courseSessionDetailResult.findCurrentUser()

View File

@ -1,12 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { useCompetenceStore } from "@/stores/competence"; import { useCompetenceStore } from "@/stores/competence";
import { useLearningPathStore } from "@/stores/learningPath";
import * as log from "loglevel"; import * as log from "loglevel";
import { computed, onMounted } from "vue"; import { computed, onMounted } from "vue";
import CompetenceDetail from "@/pages/competence/ActionCompetenceDetail.vue"; import CompetenceDetail from "@/pages/competence/ActionCompetenceDetail.vue";
import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue"; import LearningPathPathView from "@/pages/learningPath/learningPathPage/LearningPathPathView.vue";
import { useCourseSessionDetailQuery } from "@/composables"; import {
useCourseSessionDetailQuery,
useLearningPathWithCompletion,
} from "@/composables";
const props = defineProps<{ const props = defineProps<{
userId: string; userId: string;
@ -16,14 +18,15 @@ const props = defineProps<{
log.debug("CockpitUserProfilePage created", props.userId); log.debug("CockpitUserProfilePage created", props.userId);
const competenceStore = useCompetenceStore(); const competenceStore = useCompetenceStore();
const learningPathStore = useLearningPathStore();
onMounted(async () => { onMounted(async () => {
log.debug("CockpitUserProfilePage mounted"); log.debug("CockpitUserProfilePage mounted");
}); });
const lpQueryResult = useLearningPathWithCompletion(props.courseSlug, props.userId);
const learningPath = computed(() => { const learningPath = computed(() => {
return learningPathStore.learningPathForUser(props.courseSlug, props.userId); return lpQueryResult.learningPath.value;
}); });
const { findUser } = useCourseSessionDetailQuery(); const { findUser } = useCourseSessionDetailQuery();

View File

@ -13,16 +13,14 @@ import { computed, onMounted, reactive } from "vue";
import AssignmentSubmissionProgress from "@/pages/cockpit/cockpitPage/AssignmentSubmissionProgress.vue"; import AssignmentSubmissionProgress from "@/pages/cockpit/cockpitPage/AssignmentSubmissionProgress.vue";
import { useCourseSessionDetailQuery } from "@/composables"; import { useCourseSessionDetailQuery } from "@/composables";
import { formatDueDate } from "../../../components/dueDates/dueDatesUtils"; import { formatDueDate } from "../../../components/dueDates/dueDatesUtils";
import { stringifyParse } from "@/utils/utils";
const props = defineProps<{ const props = defineProps<{
courseSession: CourseSession; courseSession: CourseSession;
learningContentAssignment: LearningContentAssignment; learningContentAssignment: LearningContentAssignment;
}>(); }>();
log.debug( log.debug("AssignmentDetails created", stringifyParse(props));
"AssignmentDetails created",
props.learningContentAssignment.content_assignment_id
);
const courseSessionDetailResult = useCourseSessionDetailQuery(); const courseSessionDetailResult = useCourseSessionDetailQuery();
@ -39,7 +37,7 @@ const assignmentDetail = computed(() => {
onMounted(async () => { onMounted(async () => {
const { gradedUsers, assignmentSubmittedUsers } = const { gradedUsers, assignmentSubmittedUsers } =
await loadAssignmentCompletionStatusData( await loadAssignmentCompletionStatusData(
props.learningContentAssignment.content_assignment_id, props.learningContentAssignment.content_assignment.id,
props.courseSession.id, props.courseSession.id,
props.learningContentAssignment.id props.learningContentAssignment.id
); );
@ -54,17 +52,17 @@ onMounted(async () => {
{{ learningContentAssignment.title }} {{ learningContentAssignment.title }}
</h2> </h2>
<div class="pt-1 underline"> <div class="pt-1 underline">
{{ $t("a.Circle") }} «{{ learningContentAssignment.parentCircle.title }}» {{ $t("a.Circle") }} «{{ learningContentAssignment.circle?.title }}»
</div> </div>
<div v-if="assignmentDetail"> <div v-if="assignmentDetail">
<span v-if="assignmentDetail.submission_deadline?.start"> <span v-if="assignmentDetail.submission_deadline?.start">
{{ $t("Abgabetermin Ergebnisse:") }} {{ $t("Abgabetermin Ergebnisse:") }}
{{ formatDueDate(assignmentDetail.submission_deadline.start) }} {{ formatDueDate(assignmentDetail.submission_deadline?.start) }}
</span> </span>
<template v-if="assignmentDetail.evaluation_deadline?.start"> <template v-if="assignmentDetail.evaluation_deadline?.start">
<br /> <br />
{{ $t("Freigabetermin Bewertungen:") }} {{ $t("Freigabetermin Bewertungen:") }}
{{ formatDueDate(assignmentDetail.evaluation_deadline.start) }} {{ formatDueDate(assignmentDetail.evaluation_deadline?.start) }}
</template> </template>
</div> </div>
<div v-else> <div v-else>
@ -131,7 +129,7 @@ onMounted(async () => {
<template #link> <template #link>
<router-link <router-link
v-if="state.assignmentSubmittedUsers.includes(csu)" v-if="state.assignmentSubmittedUsers.includes(csu)"
:to="`/course/${props.courseSession.course.slug}/cockpit/assignment/${learningContentAssignment.content_assignment_id}/${csu.user_id}`" :to="`/course/${props.courseSession.course.slug}/cockpit/assignment/${learningContentAssignment.content_assignment.id}/${csu.user_id}`"
class="link lg:w-full lg:text-right" class="link lg:w-full lg:text-right"
data-cy="show-results" data-cy="show-results"
> >

View File

@ -1,11 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { useCurrentCourseSession } from "@/composables"; import { useCurrentCourseSession, useLearningPath } from "@/composables";
import AssignmentDetails from "@/pages/cockpit/assignmentsPage/AssignmentDetails.vue"; import AssignmentDetails from "@/pages/cockpit/assignmentsPage/AssignmentDetails.vue";
import * as log from "loglevel"; import * as log from "loglevel";
import { computed, onMounted } from "vue"; import { computed, onMounted } from "vue";
import { useUserStore } from "@/stores/user"; import type { LearningContentAssignment } from "@/types";
import { useLearningPathStore } from "@/stores/learningPath";
import { calcLearningContentAssignments } from "@/services/assignmentService";
const props = defineProps<{ const props = defineProps<{
courseSlug: string; courseSlug: string;
@ -15,17 +13,15 @@ const props = defineProps<{
log.debug("AssignmentsPage created", props.courseSlug); log.debug("AssignmentsPage created", props.courseSlug);
const courseSession = useCurrentCourseSession(); const courseSession = useCurrentCourseSession();
const userStore = useUserStore();
const learningPathStore = useLearningPathStore();
onMounted(async () => { onMounted(async () => {
log.debug("AssignmentsPage mounted"); log.debug("AssignmentsPage mounted");
}); });
const lpQueryResult = useLearningPath(props.courseSlug);
const learningContentAssignment = computed(() => { const learningContentAssignment = computed(() => {
return calcLearningContentAssignments( return lpQueryResult.findLearningContent(props.assignmentId);
learningPathStore.learningPathForUser(courseSession.value.course.slug, userStore.id)
).filter((lc) => lc.id === props.assignmentId)[0];
}); });
</script> </script>
@ -46,7 +42,7 @@ const learningContentAssignment = computed(() => {
<AssignmentDetails <AssignmentDetails
v-if="learningContentAssignment" v-if="learningContentAssignment"
:course-session="courseSession" :course-session="courseSession"
:learning-content-assignment="learningContentAssignment" :learning-content-assignment="learningContentAssignment as LearningContentAssignment"
/> />
</div> </div>
</main> </main>

View File

@ -32,7 +32,7 @@ const presenceCoursesDropdownOptions = computed(() => {
({ ({
id: attendanceCourse.id, id: attendanceCourse.id,
name: `${t("Präsenzkurs")} ${ name: `${t("Präsenzkurs")} ${
attendanceCourse.learning_content.circle.title attendanceCourse.learning_content.circle?.title
} ${dayjs(attendanceCourse.due_date?.start).format("DD.MM.YYYY")}`, } ${dayjs(attendanceCourse.due_date?.start).format("DD.MM.YYYY")}`,
} as DropdownSelectable) } as DropdownSelectable)
); );

View File

@ -9,6 +9,7 @@ import type {
} from "@/types"; } from "@/types";
import log from "loglevel"; import log from "loglevel";
import { onMounted, reactive } from "vue"; import { onMounted, reactive } from "vue";
import { stringifyParse } from "@/utils/utils";
const props = defineProps<{ const props = defineProps<{
courseSession: CourseSession; courseSession: CourseSession;
@ -16,10 +17,7 @@ const props = defineProps<{
showTitle: boolean; showTitle: boolean;
}>(); }>();
log.debug( log.debug("AssignmentSubmissionProgress created", stringifyParse(props));
"AssignmentSubmissionProgress created",
props.learningContentAssignment.content_assignment_id
);
const state = reactive({ const state = reactive({
statusByUser: [] as { statusByUser: [] as {
@ -34,7 +32,7 @@ const state = reactive({
onMounted(async () => { onMounted(async () => {
const { assignmentSubmittedUsers, gradedUsers, total } = const { assignmentSubmittedUsers, gradedUsers, total } =
await loadAssignmentCompletionStatusData( await loadAssignmentCompletionStatusData(
props.learningContentAssignment.content_assignment_id, props.learningContentAssignment.content_assignment.id,
props.courseSession.id, props.courseSession.id,
props.learningContentAssignment.id props.learningContentAssignment.id
); );

View File

@ -1,13 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue"; import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
import ItPersonRow from "@/components/ui/ItPersonRow.vue"; import ItPersonRow from "@/components/ui/ItPersonRow.vue";
import type { LearningPath } from "@/services/learningPath";
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables"; import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
import SubmissionsOverview from "@/pages/cockpit/cockpitPage/SubmissionsOverview.vue"; import SubmissionsOverview from "@/pages/cockpit/cockpitPage/SubmissionsOverview.vue";
import { useCockpitStore } from "@/stores/cockpit"; import { useCockpitStore } from "@/stores/cockpit";
import { useCompetenceStore } from "@/stores/competence"; import { useCompetenceStore } from "@/stores/competence";
import { useLearningPathStore } from "@/stores/learningPath";
import log from "loglevel"; import log from "loglevel";
import CockpitDates from "@/pages/cockpit/cockpitPage/CockpitDates.vue"; import CockpitDates from "@/pages/cockpit/cockpitPage/CockpitDates.vue";
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue"; import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
@ -20,7 +18,6 @@ log.debug("CockpitIndexPage created", props.courseSlug);
const cockpitStore = useCockpitStore(); const cockpitStore = useCockpitStore();
const competenceStore = useCompetenceStore(); const competenceStore = useCompetenceStore();
const learningPathStore = useLearningPathStore();
const courseSession = useCurrentCourseSession(); const courseSession = useCurrentCourseSession();
const courseSessionDetailResult = useCourseSessionDetailQuery(); const courseSessionDetailResult = useCourseSessionDetailQuery();
@ -143,18 +140,9 @@ function userCountStatusForCircle(userId: string) {
class="mt-2 flex w-full flex-col items-center justify-between lg:mt-0 lg:flex-row" class="mt-2 flex w-full flex-col items-center justify-between lg:mt-0 lg:flex-row"
> >
<LearningPathDiagram <LearningPathDiagram
v-if=" :course-session-id="courseSession.id"
learningPathStore.learningPathForUser( :course-slug="props.courseSlug"
props.courseSlug, :user-id="csu.user_id"
csu.user_id
)
"
:learning-path="
learningPathStore.learningPathForUser(
props.courseSlug,
csu.user_id
) as LearningPath
"
:show-circle-slugs="[cockpitStore.currentCircle.slug]" :show-circle-slugs="[cockpitStore.currentCircle.slug]"
diagram-type="singleSmall" diagram-type="singleSmall"
class="mr-4" class="mr-4"

View File

@ -1,8 +1,6 @@
2 2
<script setup lang="ts"> <script setup lang="ts">
import AssignmentSubmissionProgress from "@/pages/cockpit/cockpitPage/AssignmentSubmissionProgress.vue"; import AssignmentSubmissionProgress from "@/pages/cockpit/cockpitPage/AssignmentSubmissionProgress.vue";
import { useLearningPathStore } from "@/stores/learningPath";
import { useUserStore } from "@/stores/user";
import type { import type {
CourseSession, CourseSession,
LearningContent, LearningContent,
@ -13,11 +11,16 @@ import { computed } from "vue";
import { useTranslation } from "i18next-vue"; import { useTranslation } from "i18next-vue";
import FeedbackSubmissionProgress from "@/pages/cockpit/cockpitPage/FeedbackSubmissionProgress.vue"; import FeedbackSubmissionProgress from "@/pages/cockpit/cockpitPage/FeedbackSubmissionProgress.vue";
import { learningContentTypeData } from "@/utils/typeMaps"; import { learningContentTypeData } from "@/utils/typeMaps";
import { useCourseSessionDetailQuery } from "@/composables"; import {
useCourseSessionDetailQuery,
useLearningPathWithCompletion,
} from "@/composables";
import { circleFlatLearningContents } from "@/services/circle";
interface Submittable { interface Submittable {
id: string; id: string;
circleName: string; circleName: string;
circleId: string;
frontendUrl: string; frontendUrl: string;
title: string; title: string;
showDetailsText: string; showDetailsText: string;
@ -32,24 +35,20 @@ const props = defineProps<{
log.debug("SubmissionsOverview created", props.courseSession.id); log.debug("SubmissionsOverview created", props.courseSession.id);
const userStore = useUserStore();
const learningPathStore = useLearningPathStore();
const courseSessionDetailResult = useCourseSessionDetailQuery(); const courseSessionDetailResult = useCourseSessionDetailQuery();
const { t } = useTranslation(); const { t } = useTranslation();
const lpQueryResult = useLearningPathWithCompletion();
const submittables = computed(() => { const submittables = computed(() => {
const learningPath = learningPathStore.learningPathForUser( if (!lpQueryResult.circles.value?.length) {
props.courseSession.course.slug,
userStore.id
);
if (!learningPath) {
return []; return [];
} }
return learningPath.circles return lpQueryResult.circles.value
.filter((circle) => props.selectedCircle == circle.id) .filter((circle) => props.selectedCircle == circle.id)
.flatMap((circle) => { .flatMap((circle) => {
const learningContents = circle.flatLearningContents.filter( const learningContents = circleFlatLearningContents(circle).filter(
(lc) => (lc) =>
lc.content_type === "learnpath.LearningContentAssignment" || lc.content_type === "learnpath.LearningContentAssignment" ||
lc.content_type === "learnpath.LearningContentFeedback" lc.content_type === "learnpath.LearningContentFeedback"
@ -59,10 +58,11 @@ const submittables = computed(() => {
return { return {
id: lc.id, id: lc.id,
circleName: circle.title, circleName: circle.title,
circleId: circle.id,
frontendUrl: lc.frontend_url, frontendUrl: lc.frontend_url,
title: getLearningContentType(lc), title: getLearningContentType(lc),
showDetailsText: getShowDetailsText(lc), showDetailsText: getShowDetailsText(lc),
detailsLink: getDetailsLink(lc), detailsLink: getDetailsLink(lc, circle.id),
content: lc, content: lc,
}; };
}); });
@ -103,9 +103,9 @@ const getShowDetailsText = (lc: LearningContent) => {
return t("Feedback anschauen"); return t("Feedback anschauen");
}; };
const getDetailsLink = (lc: LearningContent) => { const getDetailsLink = (lc: LearningContent, circleId: string) => {
if (isFeedback(lc)) { if (isFeedback(lc)) {
return `cockpit/feedback/${lc.parentCircle.id}`; return `cockpit/feedback/${circleId}`;
} }
return `cockpit/assignment/${lc.id}`; return `cockpit/assignment/${lc.id}`;
}; };
@ -162,7 +162,7 @@ const getIconName = (lc: LearningContent) => {
<FeedbackSubmissionProgress <FeedbackSubmissionProgress
v-if="isFeedback(submittable.content)" v-if="isFeedback(submittable.content)"
:course-session="props.courseSession" :course-session="props.courseSession"
:circle-id="submittable.content.parentCircle.id" :circle-id="submittable.circleId"
class="grow pr-8" class="grow pr-8"
></FeedbackSubmissionProgress> ></FeedbackSubmissionProgress>
<div class="flex items-center lg:w-1/4 lg:justify-end"> <div class="flex items-center lg:w-1/4 lg:justify-end">

View File

@ -32,7 +32,10 @@ const props = withDefaults(defineProps<Props>(), {
log.debug("CirclePage created", stringifyParse(props)); log.debug("CirclePage created", stringifyParse(props));
const lpQueryResult = useLearningPathWithCompletion(props.courseSlug); const lpQueryResult = useLearningPathWithCompletion(
props.courseSlug,
props.profileUser?.user_id
);
const circle = computed(() => { const circle = computed(() => {
return lpQueryResult.findCircle(props.circleSlug); return lpQueryResult.findCircle(props.circleSlug);

View File

@ -1,11 +1,9 @@
import { useCourseSessionDetailQuery } from "@/composables"; import { useCourseSessionDetailQuery } from "@/composables";
import { itGet } from "@/fetchHelpers"; import { itGet } from "@/fetchHelpers";
import type { LearningPath } from "@/services/learningPath";
import type { import type {
Assignment, Assignment,
AssignmentCompletion, AssignmentCompletion,
CourseSessionUser, CourseSessionUser,
LearningContentAssignment,
UserAssignmentCompletionStatus, UserAssignmentCompletionStatus,
} from "@/types"; } from "@/types";
import { sum } from "d3"; import { sum } from "d3";
@ -16,17 +14,6 @@ export interface GradedUser {
points: number; points: number;
} }
export function calcLearningContentAssignments(learningPath?: LearningPath) {
// TODO: filter by circle
if (!learningPath) return [];
return learningPath.circles.flatMap((circle) => {
return circle.flatLearningContents.filter(
(lc) => lc.content_type === "learnpath.LearningContentAssignment"
) as LearningContentAssignment[];
});
}
export async function loadAssignmentCompletionStatusData( export async function loadAssignmentCompletionStatusData(
assignmentId: string, assignmentId: string,
courseSessionId: string, courseSessionId: string,

View File

@ -149,6 +149,7 @@ export type LearningUnitPerformanceCriteria = Omit<
> & > &
Completable & { Completable & {
readonly content_type: "competence.PerformanceCriteria"; readonly content_type: "competence.PerformanceCriteria";
circle?: CircleLight;
}; };
export interface CourseCompletion { export interface CourseCompletion {

View File

@ -12,7 +12,6 @@ class CoursePageInterface(graphene.Interface):
live = graphene.Boolean(required=True) live = graphene.Boolean(required=True)
translation_key = graphene.String(required=True) translation_key = graphene.String(required=True)
frontend_url = graphene.String(required=True) frontend_url = graphene.String(required=True)
circle = graphene.Field("vbv_lernwelt.learnpath.graphql.types.CircleObjectType")
course = graphene.Field("vbv_lernwelt.course.graphql.types.CourseObjectType") course = graphene.Field("vbv_lernwelt.course.graphql.types.CourseObjectType")
def resolve_frontend_url(self, info): def resolve_frontend_url(self, info):
@ -21,11 +20,5 @@ class CoursePageInterface(graphene.Interface):
def resolve_content_type(self, info): def resolve_content_type(self, info):
return get_django_content_type(self) return get_django_content_type(self)
def resolve_circle(self, info):
circle = self.get_ancestors().exact_type(Circle).first()
if circle:
return circle.specific
return None
def resolve_course(self, info): def resolve_course(self, info):
return self.get_course() return self.get_course()

View File

@ -2,7 +2,6 @@ import graphene
from vbv_lernwelt.course.graphql.types import resolve_course_page from vbv_lernwelt.course.graphql.types import resolve_course_page
from vbv_lernwelt.learnpath.graphql.types import ( from vbv_lernwelt.learnpath.graphql.types import (
CircleObjectType,
LearningContentAssignmentObjectType, LearningContentAssignmentObjectType,
LearningContentAttendanceCourseObjectType, LearningContentAttendanceCourseObjectType,
LearningContentDocumentListObjectType, LearningContentDocumentListObjectType,
@ -15,7 +14,7 @@ from vbv_lernwelt.learnpath.graphql.types import (
LearningContentVideoObjectType, LearningContentVideoObjectType,
LearningPathObjectType, LearningPathObjectType,
) )
from vbv_lernwelt.learnpath.models import Circle, LearningPath from vbv_lernwelt.learnpath.models import LearningPath
class LearningPathQuery: class LearningPathQuery:
@ -26,10 +25,6 @@ class LearningPathQuery:
course_id=graphene.ID(), course_id=graphene.ID(),
course_slug=graphene.String(), course_slug=graphene.String(),
) )
circle = graphene.Field(CircleObjectType, id=graphene.ID(), slug=graphene.String())
def resolve_circle(root, info, id=None, slug=None):
return resolve_course_page(Circle, root, info, id=id, slug=slug)
def resolve_learning_path( def resolve_learning_path(
root, info, id=None, slug=None, course_id=None, course_slug=None root, info, id=None, slug=None, course_id=None, course_slug=None

View File

@ -25,14 +25,22 @@ from vbv_lernwelt.learnpath.models import (
logger = structlog.get_logger(__name__) logger = structlog.get_logger(__name__)
class CircleLightObjectType(graphene.ObjectType):
id = graphene.ID(required=True)
title = graphene.String(required=True)
slug = graphene.String(required=True)
class LearningContentInterface(CoursePageInterface): class LearningContentInterface(CoursePageInterface):
minutes = graphene.Int() minutes = graphene.Int()
description = graphene.String(required=True) description = graphene.String(required=True)
content_url = graphene.String(required=True) content_url = graphene.String(required=True)
can_user_self_toggle_course_completion = graphene.Boolean(required=True) can_user_self_toggle_course_completion = graphene.Boolean(required=True)
circle = graphene.Field( circle = graphene.Field(CircleLightObjectType)
"vbv_lernwelt.learnpath.graphql.types.CircleObjectType", required=True
) @staticmethod
def resolve_circle(root, info):
return root.get_parent_circle()
@classmethod @classmethod
def resolve_type(cls, instance, info): def resolve_type(cls, instance, info):