From 6edb5be0930d588c57a85553e53420c32331ba61 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Tue, 2 Apr 2024 13:03:29 +0200 Subject: [PATCH] WIP: Add own components --- .../dashboard/CompetenceSummary.vue | 36 +++++++++++++ .../src/components/dashboard/CoursePanel.vue | 50 +++++++++++++++++++ client/src/gql/gql.ts | 5 ++ client/src/gql/graphql.ts | 28 +++++++++-- client/src/gql/schema.graphql | 14 +++++- client/src/gql/typenames.ts | 1 + client/src/graphql/queries.ts | 11 ++++ client/src/pages/dashboard/DashboardPage.vue | 7 +++ client/src/router/guards.ts | 2 + client/src/router/index.ts | 21 +++++++- client/src/services/dashboard.ts | 19 +++++++ .../vbv_lernwelt/dashboard/graphql/queries.py | 50 ++++++++++++++++++- .../dashboard/graphql/types/dashboard.py | 18 ++++++- 13 files changed, 252 insertions(+), 10 deletions(-) create mode 100644 client/src/components/dashboard/CompetenceSummary.vue create mode 100644 client/src/components/dashboard/CoursePanel.vue diff --git a/client/src/components/dashboard/CompetenceSummary.vue b/client/src/components/dashboard/CompetenceSummary.vue new file mode 100644 index 00000000..ba701185 --- /dev/null +++ b/client/src/components/dashboard/CompetenceSummary.vue @@ -0,0 +1,36 @@ + + + diff --git a/client/src/components/dashboard/CoursePanel.vue b/client/src/components/dashboard/CoursePanel.vue new file mode 100644 index 00000000..19cadce3 --- /dev/null +++ b/client/src/components/dashboard/CoursePanel.vue @@ -0,0 +1,50 @@ + + + diff --git a/client/src/gql/gql.ts b/client/src/gql/gql.ts index eb6023e4..66a37920 100644 --- a/client/src/gql/gql.ts +++ b/client/src/gql/gql.ts @@ -23,6 +23,7 @@ const documents = { "\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\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 content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.CourseQueryDocument, "\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n }\n": types.DashboardConfigDocument, "\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n": types.DashboardProgressDocument, + "\n query dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n widgets\n }\n }\n": types.DashboardCourseDataDocument, "\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n": types.CourseStatisticsDocument, "\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $learningContentType: String!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n learning_content_type: $learningContentType\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, }; @@ -81,6 +82,10 @@ export function graphql(source: "\n query dashboardConfig {\n dashboard_conf * 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 dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n"): (typeof documents)["\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n"]; +/** + * 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 dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n widgets\n }\n }\n"): (typeof documents)["\n query dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n widgets\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/client/src/gql/graphql.ts b/client/src/gql/graphql.ts index e6086ccb..37d161ab 100644 --- a/client/src/gql/graphql.ts +++ b/client/src/gql/graphql.ts @@ -361,10 +361,11 @@ export type CoursePageInterface = { export type CourseProgressType = { __typename?: 'CourseProgressType'; _id: Scalars['ID']['output']; - assignment: ProgressDashboardAssignmentType; - competence: ProgressDashboardCompetenceType; + assignment?: Maybe; + competence?: Maybe; course_id: Scalars['ID']['output']; session_to_continue_id?: Maybe; + widgets: Array; }; export type CourseSessionAssignmentObjectType = { @@ -907,6 +908,7 @@ export type Query = { course_session_attendance_course?: Maybe; course_statistics?: Maybe; dashboard_config: Array; + dashboard_data?: Maybe; learning_content_assignment?: Maybe; learning_content_attendance_course?: Maybe; learning_content_document_list?: Maybe; @@ -978,6 +980,11 @@ export type QueryCourseStatisticsArgs = { }; +export type QueryDashboardDataArgs = { + course_id: Scalars['ID']['input']; +}; + + export type QueryLearningPathArgs = { course_id?: InputMaybe; course_slug?: InputMaybe; @@ -1046,6 +1053,13 @@ export type UserObjectType = { username: Scalars['String']['output']; }; +export type WidgetType = + | 'COMPETENCE_CERTIFICATE_WIDGET' + | 'COMPETENCE_WIDGET' + | 'MENTEE_WIDGET' + | 'MENTOR_TASKS_WIDGET' + | 'PROGRESS_WIDGET'; + export type AttendanceCheckMutationMutationVariables = Exact<{ attendanceCourseId: Scalars['ID']['input']; attendanceUserList: Array> | InputMaybe; @@ -1278,7 +1292,14 @@ export type DashboardProgressQueryVariables = Exact<{ }>; -export type DashboardProgressQuery = { __typename?: 'Query', course_progress?: { __typename?: 'CourseProgressType', _id: string, course_id: string, session_to_continue_id?: string | null, competence: { __typename?: 'ProgressDashboardCompetenceType', _id: string, total_count: number, success_count: number, fail_count: number }, assignment: { __typename?: 'ProgressDashboardAssignmentType', _id: string, total_count: number, points_max_count: number, points_achieved_count: number } } | null }; +export type DashboardProgressQuery = { __typename?: 'Query', course_progress?: { __typename?: 'CourseProgressType', _id: string, course_id: string, session_to_continue_id?: string | null, competence?: { __typename?: 'ProgressDashboardCompetenceType', _id: string, total_count: number, success_count: number, fail_count: number } | null, assignment?: { __typename?: 'ProgressDashboardAssignmentType', _id: string, total_count: number, points_max_count: number, points_achieved_count: number } | null } | null }; + +export type DashboardCourseDataQueryVariables = Exact<{ + courseId: Scalars['ID']['input']; +}>; + + +export type DashboardCourseDataQuery = { __typename?: 'Query', course_progress?: { __typename?: 'CourseProgressType', _id: string, course_id: string, session_to_continue_id?: string | null, widgets: Array } | null }; export type CourseStatisticsQueryVariables = Exact<{ courseId: Scalars['ID']['input']; @@ -1308,5 +1329,6 @@ export const CourseSessionDetailDocument = {"kind":"Document","definitions":[{"k export const CourseQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"category_name"}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"enable_circle_documents"}},{"kind":"Field","name":{"kind":"Name","value":"enable_learning_mentor"}},{"kind":"Field","name":{"kind":"Name","value":"enable_competence_certificates"}}]}},{"kind":"Field","name":{"kind":"Name","value":"action_competences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"performance_criteria"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_id"}},{"kind":"Field","name":{"kind":"Name","value":"learning_unit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"evaluate_url"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_path"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"topics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"is_visible"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"goals"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"learning_sequences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"learning_units"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"evaluate_url"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"performance_criteria"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_contents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"can_user_self_toggle_course_completion"}},{"kind":"Field","name":{"kind":"Name","value":"content_url"}},{"kind":"Field","name":{"kind":"Name","value":"minutes"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentAssignmentObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"content_assignment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentEdoniqTestObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"checkbox_text"}},{"kind":"Field","name":{"kind":"Name","value":"has_extended_time_test"}},{"kind":"Field","name":{"kind":"Name","value":"content_assignment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentRichTextObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}}]}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode; export const DashboardConfigDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"dashboardConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dashboard_config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dashboard_type"}},{"kind":"Field","name":{"kind":"Name","value":"course_configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"enable_circle_documents"}},{"kind":"Field","name":{"kind":"Name","value":"enable_learning_mentor"}},{"kind":"Field","name":{"kind":"Name","value":"enable_competence_certificates"}}]}}]}}]}}]} as unknown as DocumentNode; export const DashboardProgressDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"dashboardProgress"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_progress"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_id"}},{"kind":"Field","name":{"kind":"Name","value":"session_to_continue_id"}},{"kind":"Field","name":{"kind":"Name","value":"competence"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"total_count"}},{"kind":"Field","name":{"kind":"Name","value":"success_count"}},{"kind":"Field","name":{"kind":"Name","value":"fail_count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"total_count"}},{"kind":"Field","name":{"kind":"Name","value":"points_max_count"}},{"kind":"Field","name":{"kind":"Name","value":"points_achieved_count"}}]}}]}}]}}]} as unknown as DocumentNode; +export const DashboardCourseDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"dashboardCourseData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_progress"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_id"}},{"kind":"Field","name":{"kind":"Name","value":"session_to_continue_id"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"}}]}}]}}]} as unknown as DocumentNode; export const CourseStatisticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseStatistics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_statistics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_title"}},{"kind":"Field","name":{"kind":"Name","value":"course_slug"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_properties"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"sessions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"generations"}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"course_session_selection_ids"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_selection_metrics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"session_count"}},{"kind":"Field","name":{"kind":"Name","value":"participant_count"}},{"kind":"Field","name":{"kind":"Name","value":"expert_count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"attendance_day_presences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"records"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_id"}},{"kind":"Field","name":{"kind":"Name","value":"generation"}},{"kind":"Field","name":{"kind":"Name","value":"circle_id"}},{"kind":"Field","name":{"kind":"Name","value":"due_date"}},{"kind":"Field","name":{"kind":"Name","value":"participants_present"}},{"kind":"Field","name":{"kind":"Name","value":"participants_total"}},{"kind":"Field","name":{"kind":"Name","value":"details_url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"days_completed"}},{"kind":"Field","name":{"kind":"Name","value":"participants_present"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"feedback_responses"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"records"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_id"}},{"kind":"Field","name":{"kind":"Name","value":"generation"}},{"kind":"Field","name":{"kind":"Name","value":"circle_id"}},{"kind":"Field","name":{"kind":"Name","value":"experts"}},{"kind":"Field","name":{"kind":"Name","value":"satisfaction_average"}},{"kind":"Field","name":{"kind":"Name","value":"satisfaction_max"}},{"kind":"Field","name":{"kind":"Name","value":"details_url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"satisfaction_average"}},{"kind":"Field","name":{"kind":"Name","value":"satisfaction_max"}},{"kind":"Field","name":{"kind":"Name","value":"total_responses"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"completed_count"}},{"kind":"Field","name":{"kind":"Name","value":"average_passed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"records"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_assignment_id"}},{"kind":"Field","name":{"kind":"Name","value":"circle_id"}},{"kind":"Field","name":{"kind":"Name","value":"generation"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_title"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type_translation_key"}},{"kind":"Field","name":{"kind":"Name","value":"details_url"}},{"kind":"Field","name":{"kind":"Name","value":"deadline"}},{"kind":"Field","name":{"kind":"Name","value":"metrics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"passed_count"}},{"kind":"Field","name":{"kind":"Name","value":"failed_count"}},{"kind":"Field","name":{"kind":"Name","value":"unranked_count"}},{"kind":"Field","name":{"kind":"Name","value":"ranking_completed"}},{"kind":"Field","name":{"kind":"Name","value":"average_passed"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"competences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"success_total"}},{"kind":"Field","name":{"kind":"Name","value":"fail_total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"records"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_id"}},{"kind":"Field","name":{"kind":"Name","value":"generation"}},{"kind":"Field","name":{"kind":"Name","value":"circle_id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"success_count"}},{"kind":"Field","name":{"kind":"Name","value":"fail_count"}},{"kind":"Field","name":{"kind":"Name","value":"details_url"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const SendFeedbackMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendFeedbackMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GenericScalar"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"send_feedback"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_type"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentType"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"submitted"},"value":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feedback_response"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"submitted"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"messages"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/client/src/gql/schema.graphql b/client/src/gql/schema.graphql index 4c421b9b..7bda685a 100644 --- a/client/src/gql/schema.graphql +++ b/client/src/gql/schema.graphql @@ -2,6 +2,7 @@ type Query { course_statistics(course_id: ID!): CourseStatisticsType course_progress(course_id: ID!): CourseProgressType dashboard_config: [DashboardConfigType!]! + dashboard_data(course_id: ID!): DashboardConfigType learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType course_session_attendance_course(id: ID!, assignment_user_id: ID): CourseSessionAttendanceCourseObjectType course(id: ID, slug: String): CourseObjectType @@ -177,8 +178,9 @@ type CourseProgressType { _id: ID! course_id: ID! session_to_continue_id: ID - competence: ProgressDashboardCompetenceType! - assignment: ProgressDashboardAssignmentType! + competence: ProgressDashboardCompetenceType + assignment: ProgressDashboardAssignmentType + widgets: [WidgetType!]! } type ProgressDashboardCompetenceType { @@ -195,6 +197,14 @@ type ProgressDashboardAssignmentType { points_achieved_count: Int! } +enum WidgetType { + PROGRESS_WIDGET + COMPETENCE_WIDGET + MENTEE_WIDGET + MENTOR_TASKS_WIDGET + COMPETENCE_CERTIFICATE_WIDGET +} + type DashboardConfigType { id: ID! name: String! diff --git a/client/src/gql/typenames.ts b/client/src/gql/typenames.ts index 5fb963dc..732cb885 100644 --- a/client/src/gql/typenames.ts +++ b/client/src/gql/typenames.ts @@ -84,3 +84,4 @@ export const String = "String"; export const TopicObjectType = "TopicObjectType"; export const UUID = "UUID"; export const UserObjectType = "UserObjectType"; +export const WidgetType = "WidgetType"; diff --git a/client/src/graphql/queries.ts b/client/src/graphql/queries.ts index df6f5259..15dc2766 100644 --- a/client/src/graphql/queries.ts +++ b/client/src/graphql/queries.ts @@ -331,6 +331,17 @@ export const DASHBOARD_COURSE_SESSION_PROGRESS = graphql(` } `); +export const DASHBOARD_COURSE_DATA = graphql(` + query dashboardCourseData($courseId: ID!) { + course_progress(course_id: $courseId) { + _id + course_id + session_to_continue_id + widgets + } + } +`); + export const DASHBOARD_COURSE_STATISTICS = graphql(` query courseStatistics($courseId: ID!) { course_statistics(course_id: $courseId) { diff --git a/client/src/pages/dashboard/DashboardPage.vue b/client/src/pages/dashboard/DashboardPage.vue index a882818e..cbe936fd 100644 --- a/client/src/pages/dashboard/DashboardPage.vue +++ b/client/src/pages/dashboard/DashboardPage.vue @@ -12,6 +12,7 @@ import LoadingSpinner from "@/components/ui/LoadingSpinner.vue"; import CourseDetailDates from "@/components/dashboard/CourseDetailDates.vue"; import NoCourseSession from "@/components/dashboard/NoCourseSession.vue"; import MentorPage from "@/pages/dashboard/MentorPage.vue"; +import CoursePanel from "@/components/dashboard/CoursePanel.vue"; const dashboardStore = useDashboardStore(); @@ -50,6 +51,12 @@ onMounted(dashboardStore.loadDashboardDetails); > +
    +
  • +

    {{ config }}

    + +
  • +
diff --git a/client/src/router/guards.ts b/client/src/router/guards.ts index 089ac20a..8ee4825a 100644 --- a/client/src/router/guards.ts +++ b/client/src/router/guards.ts @@ -62,6 +62,7 @@ export async function handleCurrentCourseSession(to: RouteLocationNormalized) { courseSessionsStore._currentCourseSlug = ""; } if (!courseSessionsStore.loaded) { + console.log("handleCurrentCourseSession: loadCourseSessionsData"); await courseSessionsStore.loadCourseSessionsData(); } } @@ -77,6 +78,7 @@ export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalize if (userStore.loggedIn) { const courseSessionsStore = useCourseSessionsStore(); if (!courseSessionsStore.loaded) { + console.log("handleCourseSessionAsQueryParam: loadCourseSessionsData"); await courseSessionsStore.loadCourseSessionsData(); } diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 52a39878..14c9eb43 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -387,8 +387,25 @@ router.beforeEach(updateLoggedIn); router.beforeEach(redirectToLoginIfRequired); // register after login hooks -router.beforeEach(handleCurrentCourseSession); -router.beforeEach(handleCourseSessionAsQueryParam); +router.beforeEach( + async (to, from) => + await ignoreGuardsForHomeRoute(to, from, handleCurrentCourseSession) +); + +router.beforeEach( + async (to, from) => + await ignoreGuardsForHomeRoute(to, from, handleCourseSessionAsQueryParam) +); + +async function ignoreGuardsForHomeRoute( + to: RouteLocationNormalized, + from: RouteLocationNormalized, + guard: NavigationGuardNext +) { + if (to.name !== "home") { + return await guard(to, from); + } +} router.beforeEach(addToHistory); diff --git a/client/src/services/dashboard.ts b/client/src/services/dashboard.ts index a320774f..b4232d64 100644 --- a/client/src/services/dashboard.ts +++ b/client/src/services/dashboard.ts @@ -1,6 +1,7 @@ import { graphqlClient } from "@/graphql/client"; import { DASHBOARD_CONFIG, + DASHBOARD_COURSE_DATA, DASHBOARD_COURSE_SESSION_PROGRESS, DASHBOARD_COURSE_STATISTICS, } from "@/graphql/queries"; @@ -61,3 +62,21 @@ export const fetchDashboardConfig = async (): Promise => { + try { + const res = await graphqlClient.query(DASHBOARD_COURSE_DATA, { + courseId, + }); + + if (res.error) { + console.error("Error fetching data for course ID:", courseId, res.error); + } + return res.data?.course_progress || null; + } catch (error) { + console.error(`Error fetching data for course ID: ${courseId}`, error); + return null; + } +}; diff --git a/server/vbv_lernwelt/dashboard/graphql/queries.py b/server/vbv_lernwelt/dashboard/graphql/queries.py index 38c3c71f..72ee4234 100644 --- a/server/vbv_lernwelt/dashboard/graphql/queries.py +++ b/server/vbv_lernwelt/dashboard/graphql/queries.py @@ -17,6 +17,7 @@ from vbv_lernwelt.dashboard.graphql.types.dashboard import ( DashboardType, ProgressDashboardAssignmentType, ProgressDashboardCompetenceType, + WidgetType, ) from vbv_lernwelt.iam.permissions import ( can_view_course_session, @@ -39,6 +40,10 @@ class DashboardQuery(graphene.ObjectType): graphene.NonNull(DashboardConfigType), required=True ) + dashboard_data = graphene.Field( + DashboardConfigType, course_id=graphene.ID(required=True) + ) + def resolve_course_statistics(root, info, course_id: str): # noqa user = info.context.user course = Course.objects.get(id=course_id) @@ -164,8 +169,12 @@ class DashboardQuery(graphene.ObjectType): points_max_count=int(points_max_count), # noqa points_achieved_count=int(points_achieved_count), # noqa ), + widgets=get_widget_for_course(course_id, user), # noqa ) + def resolve_dashboard_data(root, info, course_id: str): + return get_widget_for_course(course_id, info.context.user) + def get_user_statistics_dashboards(user: User) -> Tuple[List[Dict[str, str]], Set[int]]: course_ids = set() @@ -222,7 +231,6 @@ def get_user_course_session_dashboards( sessions of a course, but with varying permissions? -> We just show the simple list dashboard for now. """ - dashboards = [] course_ids = set() @@ -246,6 +254,7 @@ def get_user_course_session_dashboards( resolved_dashboard_type = DashboardType.SIMPLE_DASHBOARD elif course_role == CourseSessionUser.Role.MEMBER: resolved_dashboard_type = DashboardType.PROGRESS_DASHBOARD + else: # fallback: just go with simple list dashboard resolved_dashboard_type = DashboardType.SIMPLE_DASHBOARD @@ -263,3 +272,42 @@ def get_user_course_session_dashboards( ) return dashboards, course_ids + + +def get_widget_for_course(course_id: str, user: User) -> List[WidgetType]: + widgets = [] + + course_sessions = CourseSession.objects.filter(course__id=course_id).prefetch_related( + "course", + "course__configuration", + ) + roles_by_course: Dict[Course, Set[DashboardType]] = {} + + learning_mentors = LearningMentor.objects.filter(mentor=user).values_list( + "course_session__course__id", "id" + ) + mentor_course_ids = set([mentor[0] for mentor in learning_mentors]) + + # duplicate code + for course_session in course_sessions: + if can_view_course_session(user=user, course_session=course_session): + role = CourseSessionUser.objects.get( + course_session=course_session, user=user + ).role + roles_by_course.setdefault(course_session.course, set()) + roles_by_course[course_session.course].add(role) + + for course, roles in roles_by_course.items(): + if len(roles) == 1: + course_role = roles.pop() + + # test widgets + if course_role == CourseSessionUser.Role.MEMBER: + widgets.append(WidgetType.PROGRESS_WIDGET) + widgets.append(WidgetType.COMPETENCE_WIDGET) + if course.configuration.enable_competence_certificates: + widgets.append(WidgetType.COMPETENCE_CERTIFICATE_WIDGET) + if course.id in mentor_course_ids: + widgets.append(WidgetType.MENTOR_TASKS_WIDGET) + # add KN if has KN + return widgets diff --git a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py index 2ad4d5de..35648f76 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py @@ -56,6 +56,14 @@ class DashboardType(Enum): MENTOR_DASHBOARD = "MentorDashboard" +class WidgetType(Enum): + PROGRESS_WIDGET = "ProgressWidget" + COMPETENCE_WIDGET = "CompetenceWidget" + MENTEE_WIDGET = "MenteeWidget" + MENTOR_TASKS_WIDGET = "MentorTasksWidget" + COMPETENCE_CERTIFICATE_WIDGET = "CompetenceCertificateWidget" + + class DashboardConfigType(graphene.ObjectType): id = graphene.ID(required=True) name = graphene.String(required=True) @@ -64,6 +72,11 @@ class DashboardConfigType(graphene.ObjectType): course_configuration = graphene.Field(CourseConfigurationObjectType, required=True) +class DashboardDataType(graphene.ObjectType): + id = graphene.ID(required=True) + widgets = graphene.List(graphene.NonNull(WidgetType), required=True) + + class ProgressDashboardCompetenceType(graphene.ObjectType): _id = graphene.ID(required=True) total_count = graphene.Int(required=True) @@ -82,8 +95,9 @@ class CourseProgressType(graphene.ObjectType): _id = graphene.ID(required=True) course_id = graphene.ID(required=True) session_to_continue_id = graphene.ID(required=False) - competence = graphene.Field(ProgressDashboardCompetenceType, required=True) - assignment = graphene.Field(ProgressDashboardAssignmentType, required=True) + competence = graphene.Field(ProgressDashboardCompetenceType, required=False) + assignment = graphene.Field(ProgressDashboardAssignmentType, required=False) + widgets = graphene.List(graphene.NonNull(WidgetType), required=True) class CourseStatisticsType(graphene.ObjectType):