Merge branch 'dev' into fix/questions-toon-enkel-groep
This commit is contained in:
		
						commit
						73ca508ff3
					
				
					 5 changed files with 118 additions and 62 deletions
				
			
		|  | @ -1,12 +1,13 @@ | ||||||
| import { getAssignmentRepository, getGroupRepository, getSubmissionRepository } from '../data/repositories.js'; | import { getSubmissionRepository } from '../data/repositories.js'; | ||||||
| import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js'; | import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js'; | ||||||
| import { NotFoundException } from '../exceptions/not-found-exception.js'; | import { NotFoundException } from '../exceptions/not-found-exception.js'; | ||||||
| import { mapToSubmission, mapToSubmissionDTO } from '../interfaces/submission.js'; | import { mapToSubmission, mapToSubmissionDTO } from '../interfaces/submission.js'; | ||||||
| import { SubmissionDTO } from '@dwengo-1/common/interfaces/submission'; | import { SubmissionDTO } from '@dwengo-1/common/interfaces/submission'; | ||||||
| import { fetchStudent } from './students.js'; | import { fetchStudent } from './students.js'; | ||||||
| import { getExistingGroupFromGroupDTO } from './groups.js'; | import { fetchGroup, getExistingGroupFromGroupDTO } from './groups.js'; | ||||||
| import { Submission } from '../entities/assignments/submission.entity.js'; | import { Submission } from '../entities/assignments/submission.entity.js'; | ||||||
| import { Language } from '@dwengo-1/common/util/language'; | import { Language } from '@dwengo-1/common/util/language'; | ||||||
|  | import { fetchAssignment } from './assignments.js'; | ||||||
| 
 | 
 | ||||||
| export async function fetchSubmission(loId: LearningObjectIdentifier, submissionNumber: number): Promise<Submission> { | export async function fetchSubmission(loId: LearningObjectIdentifier, submissionNumber: number): Promise<Submission> { | ||||||
|     const submissionRepository = getSubmissionRepository(); |     const submissionRepository = getSubmissionRepository(); | ||||||
|  | @ -64,15 +65,18 @@ export async function getSubmissionsForLearningObjectAndAssignment( | ||||||
|     groupId?: number |     groupId?: number | ||||||
| ): Promise<SubmissionDTO[]> { | ): Promise<SubmissionDTO[]> { | ||||||
|     const loId = new LearningObjectIdentifier(learningObjectHruid, language, version); |     const loId = new LearningObjectIdentifier(learningObjectHruid, language, version); | ||||||
|     const assignment = await getAssignmentRepository().findByClassIdAndAssignmentId(classId, assignmentId); |  | ||||||
| 
 | 
 | ||||||
|  |     try { | ||||||
|         let submissions: Submission[]; |         let submissions: Submission[]; | ||||||
|         if (groupId !== undefined) { |         if (groupId !== undefined) { | ||||||
|         const group = await getGroupRepository().findByAssignmentAndGroupNumber(assignment!, groupId); |             const group = await fetchGroup(classId, assignmentId, groupId); | ||||||
|         submissions = await getSubmissionRepository().findAllSubmissionsForLearningObjectAndGroup(loId, group!); |             submissions = await getSubmissionRepository().findAllSubmissionsForLearningObjectAndGroup(loId, group); | ||||||
|         } else { |         } else { | ||||||
|         submissions = await getSubmissionRepository().findAllSubmissionsForLearningObjectAndAssignment(loId, assignment!); |             const assignment = await fetchAssignment(classId, assignmentId); | ||||||
|  |             submissions = await getSubmissionRepository().findAllSubmissionsForLearningObjectAndAssignment(loId, assignment); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         return submissions.map((s) => mapToSubmissionDTO(s)); |         return submissions.map((s) => mapToSubmissionDTO(s)); | ||||||
|  |     } catch (_) { | ||||||
|  |         return []; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ | ||||||
|                 </template> |                 </template> | ||||||
|             </v-list-item> |             </v-list-item> | ||||||
|             <v-divider></v-divider> |             <v-divider></v-divider> | ||||||
|  |             <div class="nav-scroll-area"> | ||||||
|                 <v-expansion-panels v-model="expanded"> |                 <v-expansion-panels v-model="expanded"> | ||||||
|                     <using-query-result |                     <using-query-result | ||||||
|                         :query-result="allLearningPathsResult" |                         :query-result="allLearningPathsResult" | ||||||
|  | @ -48,6 +49,7 @@ | ||||||
|                     </using-query-result> |                     </using-query-result> | ||||||
|                 </v-expansion-panels> |                 </v-expansion-panels> | ||||||
|             </div> |             </div> | ||||||
|  |         </div> | ||||||
|     </v-navigation-drawer> |     </v-navigation-drawer> | ||||||
|     <div class="control-bar-above-content"> |     <div class="control-bar-above-content"> | ||||||
|         <v-btn |         <v-btn | ||||||
|  | @ -67,4 +69,10 @@ | ||||||
|         padding-top: 2%; |         padding-top: 2%; | ||||||
|         font-size: 36px; |         font-size: 36px; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     .nav-scroll-area { | ||||||
|  |         overflow-y: auto; | ||||||
|  |         flex-grow: 1; | ||||||
|  |         min-height: 0; | ||||||
|  |     } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
|     import authService from "@/services/auth/auth-service.ts"; |     import authService from "@/services/auth/auth-service.ts"; | ||||||
|     import { LearningPathNode } from "@/data-objects/learning-paths/learning-path-node.ts"; |     import { LearningPathNode } from "@/data-objects/learning-paths/learning-path-node.ts"; | ||||||
|     import LearningPathGroupSelector from "@/views/learning-paths/LearningPathGroupSelector.vue"; |     import LearningPathGroupSelector from "@/views/learning-paths/LearningPathGroupSelector.vue"; | ||||||
|     import { useQuestionsGroupQuery, useQuestionsQuery } from "@/queries/questions"; |     import { useQuestionsQuery } from "@/queries/questions"; | ||||||
|     import type { QuestionsResponse } from "@/controllers/questions"; |     import type { QuestionsResponse } from "@/controllers/questions"; | ||||||
|     import type { LearningObjectIdentifierDTO } from "@dwengo-1/common/interfaces/learning-content"; |     import type { LearningObjectIdentifierDTO } from "@dwengo-1/common/interfaces/learning-content"; | ||||||
|     import QandA from "@/components/QandA.vue"; |     import QandA from "@/components/QandA.vue"; | ||||||
|  | @ -163,11 +163,6 @@ | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const loID: LearningObjectIdentifierDTO = { |  | ||||||
|         hruid: props.learningObjectHruid as string, |  | ||||||
|         language: props.language, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const discussionLink = computed( |     const discussionLink = computed( | ||||||
|         () => |         () => | ||||||
|             "/discussion" + |             "/discussion" + | ||||||
|  | @ -237,7 +232,7 @@ | ||||||
|                     </template> |                     </template> | ||||||
|                 </v-list-item> |                 </v-list-item> | ||||||
|                 <v-divider></v-divider> |                 <v-divider></v-divider> | ||||||
|                 <div> |                 <div class="nav-scroll-area"> | ||||||
|                     <using-query-result |                     <using-query-result | ||||||
|                         :query-result="learningObjectListQueryResult" |                         :query-result="learningObjectListQueryResult" | ||||||
|                         v-slot="learningObjects: { data: LearningObject[] }" |                         v-slot="learningObjects: { data: LearningObject[] }" | ||||||
|  | @ -430,4 +425,10 @@ | ||||||
|     .discussion-link a:hover { |     .discussion-link a:hover { | ||||||
|         text-decoration: underline; |         text-decoration: underline; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     .nav-scroll-area { | ||||||
|  |         overflow-y: auto; | ||||||
|  |         flex-grow: 1; | ||||||
|  |         min-height: 0; | ||||||
|  |     } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -18,6 +18,15 @@ | ||||||
|         version: number; |         version: number; | ||||||
|         group: { forGroup: number; assignmentNo: number; classId: string }; |         group: { forGroup: number; assignmentNo: number; classId: string }; | ||||||
|     }>(); |     }>(); | ||||||
|  | 
 | ||||||
|  |     function parseContent(content: string): SubmissionData { | ||||||
|  |         if (content === "") { | ||||||
|  |             return []; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return JSON.parse(content); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     const emit = defineEmits<(e: "update:submissionData", value: SubmissionData) => void>(); |     const emit = defineEmits<(e: "update:submissionData", value: SubmissionData) => void>(); | ||||||
| 
 | 
 | ||||||
|     const submissionQuery = useSubmissionsQuery( |     const submissionQuery = useSubmissionsQuery( | ||||||
|  | @ -35,7 +44,7 @@ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function emitSubmission(submission: SubmissionDTO): void { |     function emitSubmission(submission: SubmissionDTO): void { | ||||||
|         emitSubmissionData(JSON.parse(submission.content)); |         emitSubmissionData(parseContent(submission.content)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     watch(submissionQuery.data, () => { |     watch(submissionQuery.data, () => { | ||||||
|  | @ -47,12 +56,13 @@ | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const lastSubmission = computed<SubmissionData>(() => { |     const lastSubmission = computed<SubmissionData | undefined>(() => { | ||||||
|         const submissions = submissionQuery.data.value; |         const submissions = submissionQuery.data.value; | ||||||
|         if (!submissions || submissions.length === 0) { |         if (!submissions || submissions.length === 0) { | ||||||
|             return undefined; |             return undefined; | ||||||
|         } |         } | ||||||
|         return JSON.parse(submissions[submissions.length - 1].content); | 
 | ||||||
|  |         return parseContent(submissions[submissions.length - 1].content); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const showSubmissionTable = computed(() => props.submissionData !== undefined && props.submissionData.length > 0); |     const showSubmissionTable = computed(() => props.submissionData !== undefined && props.submissionData.length > 0); | ||||||
|  |  | ||||||
|  | @ -89,6 +89,15 @@ | ||||||
|                 props.selectedLearningPath.language !== parsedLearningPath.value.language), |                 props.selectedLearningPath.language !== parsedLearningPath.value.language), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     const selectedLearningPathLink = computed(() => { | ||||||
|  |         if (!props.selectedLearningPath) { | ||||||
|  |             return undefined; | ||||||
|  |         } | ||||||
|  |         const { hruid, language } = props.selectedLearningPath; | ||||||
|  |         const startNode = props.selectedLearningPath.nodes.find((it) => it.start_node); | ||||||
|  |         return `/learningPath/${hruid}/${language}/${startNode.learningobject_hruid}`; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     function getErrorMessage(): string | null { |     function getErrorMessage(): string | null { | ||||||
|         if (postError.value) { |         if (postError.value) { | ||||||
|             return t(extractErrorMessage(postError.value)); |             return t(extractErrorMessage(postError.value)); | ||||||
|  | @ -104,23 +113,19 @@ | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <v-card :title="props.selectedLearningPath ? t('editLearningPath') : t('newLearningPath')"> |     <v-card> | ||||||
|         <template v-slot:text> |         <template v-slot:title> | ||||||
|             <json-editor-vue v-model="learningPath"></json-editor-vue> |             <div class="title-container"> | ||||||
|             <v-alert |                 <span class="title">{{ | ||||||
|                 v-if="postError || putError || deleteError || isIdModified" |                     props.selectedLearningPath ? t("editLearningPath") : t("newLearningPath") | ||||||
|                 icon="mdi mdi-alert-circle" |                 }}</span> | ||||||
|                 type="error" |                 <span class="actions"> | ||||||
|                 :title="t('error')" |  | ||||||
|                 :text="getErrorMessage()!" |  | ||||||
|             ></v-alert> |  | ||||||
|         </template> |  | ||||||
|         <template v-slot:actions> |  | ||||||
|                     <v-btn |                     <v-btn | ||||||
|                         @click="uploadLearningPath" |                         @click="uploadLearningPath" | ||||||
|                         prependIcon="mdi mdi-check" |                         prependIcon="mdi mdi-check" | ||||||
|                         :loading="isPostPending || isPutPending" |                         :loading="isPostPending || isPutPending" | ||||||
|                         :disabled="parsedLearningPath.hruid === DEFAULT_LEARNING_PATH.hruid || isIdModified" |                         :disabled="parsedLearningPath.hruid === DEFAULT_LEARNING_PATH.hruid || isIdModified" | ||||||
|  |                         variant="text" | ||||||
|                     > |                     > | ||||||
|                         {{ props.selectedLearningPath ? t("saveChanges") : t("create") }} |                         {{ props.selectedLearningPath ? t("saveChanges") : t("create") }} | ||||||
|                     </v-btn> |                     </v-btn> | ||||||
|  | @ -131,17 +136,45 @@ | ||||||
|                         color="red" |                         color="red" | ||||||
|                         prependIcon="mdi mdi-delete" |                         prependIcon="mdi mdi-delete" | ||||||
|                         :confirmQueryText="t('learningPathDeleteQuery')" |                         :confirmQueryText="t('learningPathDeleteQuery')" | ||||||
|  |                         variant="text" | ||||||
|                     /> |                     /> | ||||||
|                     <v-btn |                     <v-btn | ||||||
|                 :href="`/learningPath/${props.selectedLearningPath?.hruid}/${props.selectedLearningPath?.language}/start`" |                         :href="selectedLearningPathLink" | ||||||
|                         target="_blank" |                         target="_blank" | ||||||
|                         prepend-icon="mdi mdi-open-in-new" |                         prepend-icon="mdi mdi-open-in-new" | ||||||
|                         :disabled="!props.selectedLearningPath" |                         :disabled="!props.selectedLearningPath" | ||||||
|  |                         variant="text" | ||||||
|                     > |                     > | ||||||
|                         {{ t("open") }} |                         {{ t("open") }} | ||||||
|                     </v-btn> |                     </v-btn> | ||||||
|  |                 </span> | ||||||
|  |             </div> | ||||||
|  |         </template> | ||||||
|  |         <template v-slot:text> | ||||||
|  |             <json-editor-vue v-model="learningPath"></json-editor-vue> | ||||||
|  |             <v-alert | ||||||
|  |                 v-if="postError || putError || deleteError || isIdModified" | ||||||
|  |                 icon="mdi mdi-alert-circle" | ||||||
|  |                 type="error" | ||||||
|  |                 :title="t('error')" | ||||||
|  |                 :text="getErrorMessage()!" | ||||||
|  |             ></v-alert> | ||||||
|         </template> |         </template> | ||||||
|     </v-card> |     </v-card> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped></style> | <style scoped> | ||||||
|  |     .title-container { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: row; | ||||||
|  |         flex-wrap: wrap; | ||||||
|  |     } | ||||||
|  |     .title { | ||||||
|  |         flex: 1; | ||||||
|  |     } | ||||||
|  |     .actions { | ||||||
|  |         display: flex; | ||||||
|  |         flex-wrap: wrap; | ||||||
|  |         gap: 10px; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  |  | ||||||
		Reference in a new issue