feat(backend): Overzicht over submissions toegevoegd.
This commit is contained in:
		
							parent
							
								
									cc92727a09
								
							
						
					
					
						commit
						4dc880c2f7
					
				
					 9 changed files with 165 additions and 39 deletions
				
			
		|  | @ -76,6 +76,12 @@ | ||||||
|     "failed": "fehlgeschlagen", |     "failed": "fehlgeschlagen", | ||||||
|     "wrong": "etwas ist schief gelaufen", |     "wrong": "etwas ist schief gelaufen", | ||||||
|     "created": "erstellt", |     "created": "erstellt", | ||||||
|     "submit": "Einreichen", |     "submitSolution": "Lösung einreichen", | ||||||
|     "markAsDone": "Als fertig markieren" |     "submitNewSolution": "Neue Lösung einreichen", | ||||||
|  |     "markAsDone": "Als fertig markieren", | ||||||
|  |     "groupSubmissions": "Einreichungen dieser Gruppe", | ||||||
|  |     "taskCompleted": "Aufgabe erledigt.", | ||||||
|  |     "submittedBy": "Eingereicht von", | ||||||
|  |     "timestamp": "Zeitpunkt", | ||||||
|  |     "loadSubmission": "Einladen" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -76,6 +76,12 @@ | ||||||
|     "failed": "failed", |     "failed": "failed", | ||||||
|     "wrong": "something went wrong", |     "wrong": "something went wrong", | ||||||
|     "created": "created", |     "created": "created", | ||||||
|     "submit": "Submit", |     "submitSolution": "Submit solution", | ||||||
|     "markAsDone": "Mark as done" |     "submitNewSolution": "Submit new solution", | ||||||
|  |     "markAsDone": "Mark as completed", | ||||||
|  |     "groupSubmissions": "This group's submissions", | ||||||
|  |     "taskCompleted": "Task completed.", | ||||||
|  |     "submittedBy": "Submitted by", | ||||||
|  |     "timestamp": "Timestamp", | ||||||
|  |     "loadSubmission": "Load" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -75,5 +75,13 @@ | ||||||
|     "sent": "envoyé", |     "sent": "envoyé", | ||||||
|     "failed": "échoué", |     "failed": "échoué", | ||||||
|     "wrong": "quelque chose n'a pas fonctionné", |     "wrong": "quelque chose n'a pas fonctionné", | ||||||
|     "created": "créé" |     "created": "créé", | ||||||
|  |     "submitSolution": "Soumettre la solution", | ||||||
|  |     "submitNewSolution": "Soumettre une nouvelle solution", | ||||||
|  |     "markAsDone": "Marquer comme terminé", | ||||||
|  |     "groupSubmissions": "Soumissions de ce groupe", | ||||||
|  |     "taskCompleted": "Tâche terminée.", | ||||||
|  |     "submittedBy": "Soumis par", | ||||||
|  |     "timestamp": "Horodatage", | ||||||
|  |     "loadSubmission": "Charger" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -76,6 +76,12 @@ | ||||||
|     "failed": "mislukt", |     "failed": "mislukt", | ||||||
|     "wrong": "er ging iets verkeerd", |     "wrong": "er ging iets verkeerd", | ||||||
|     "created": "gecreëerd", |     "created": "gecreëerd", | ||||||
|     "submit": "Indienen", |     "submitSolution": "Oplossing indienen", | ||||||
|     "markAsDone": "Markeren als afgewerkt" |     "submitNewSolution": "Nieuwe oplossing indienen", | ||||||
|  |     "markAsDone": "Markeren als afgewerkt", | ||||||
|  |     "groupSubmissions": "Indieningen van deze groep", | ||||||
|  |     "taskCompleted": "Taak afgewerkt.", | ||||||
|  |     "submittedBy": "Ingediend door", | ||||||
|  |     "timestamp": "Tijdstip", | ||||||
|  |     "loadSubmission": "Inladen" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,29 +1,29 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { Language } from "@/data-objects/language.ts"; |     import { Language } from "@/data-objects/language.ts"; | ||||||
| import type { UseQueryReturnType } from "@tanstack/vue-query"; |     import type { UseQueryReturnType } from "@tanstack/vue-query"; | ||||||
| import { useLearningObjectHTMLQuery } from "@/queries/learning-objects.ts"; |     import { useLearningObjectHTMLQuery } from "@/queries/learning-objects.ts"; | ||||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; |     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
| import {computed, ref} from "vue"; |     import {computed, ref} from "vue"; | ||||||
| import authService from "@/services/auth/auth-service.ts"; |     import authService from "@/services/auth/auth-service.ts"; | ||||||
| import type {SubmissionData} from "@/views/learning-paths/learning-object/submission-data"; |     import type {SubmissionData} from "@/views/learning-paths/learning-object/submission-data"; | ||||||
| import LearningObjectContentView from "@/views/learning-paths/learning-object/content/LearningObjectContentView.vue"; |     import LearningObjectContentView from "@/views/learning-paths/learning-object/content/LearningObjectContentView.vue"; | ||||||
| import LearningObjectSubmissionsView from "@/views/learning-paths/learning-object/submissions/LearningObjectSubmissionsView.vue"; |     import LearningObjectSubmissionsView from "@/views/learning-paths/learning-object/submissions/LearningObjectSubmissionsView.vue"; | ||||||
| 
 | 
 | ||||||
| const isStudent = computed(() => authService.authState.activeRole === "student"); |     const isStudent = computed(() => authService.authState.activeRole === "student"); | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ |     const props = defineProps<{ | ||||||
|         hruid: string; |         hruid: string; | ||||||
|         language: Language; |         language: Language; | ||||||
|         version: number, |         version: number, | ||||||
|         group?: {forGroup: number, assignmentNo: number, classId: string} |         group?: {forGroup: number, assignmentNo: number, classId: string} | ||||||
| }>(); |     }>(); | ||||||
| 
 | 
 | ||||||
| const learningObjectHtmlQueryResult: UseQueryReturnType<Document, Error> = useLearningObjectHTMLQuery( |     const learningObjectHtmlQueryResult: UseQueryReturnType<Document, Error> = useLearningObjectHTMLQuery( | ||||||
|         () => props.hruid, |         () => props.hruid, | ||||||
|         () => props.language, |         () => props.language, | ||||||
|         () => props.version, |         () => props.version, | ||||||
| ); |     ); | ||||||
| const currentSubmission = ref<SubmissionData>([]); |     const currentSubmission = ref<SubmissionData>([]); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  |  | ||||||
|  | @ -38,7 +38,6 @@ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function attachQuestionListeners(): void { |     function attachQuestionListeners(): void { | ||||||
|         let counter = 0; |  | ||||||
|         forEachQuestion((index, _name, type, element) => { |         forEachQuestion((index, _name, type, element) => { | ||||||
|             getGiftAdapterForType(type)?.installListener( |             getGiftAdapterForType(type)?.installListener( | ||||||
|                 element, |                 element, | ||||||
|  | @ -46,7 +45,6 @@ | ||||||
|                     submissionData.value = copyArrayWith(index, newAnswer, submissionData.value ?? []) |                     submissionData.value = copyArrayWith(index, newAnswer, submissionData.value ?? []) | ||||||
|                 } |                 } | ||||||
|             ); |             ); | ||||||
|             counter++; |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -62,7 +60,12 @@ | ||||||
|         submissionData.value = answers; |         submissionData.value = answers; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     onMounted(() => nextTick(() => attachQuestionListeners())); |     onMounted(() => | ||||||
|  |         nextTick(() => { | ||||||
|  |             attachQuestionListeners() | ||||||
|  |             setAnswers(props.submissionData ?? []); | ||||||
|  |         }) | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     watch(() => props.learningObjectContent, async () => { |     watch(() => props.learningObjectContent, async () => { | ||||||
|         await nextTick(); |         await nextTick(); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,55 @@ | ||||||
|  | <script setup lang="ts"> | ||||||
|  |     import type {SubmissionDTO} from "@dwengo-1/common/interfaces/submission"; | ||||||
|  |     import {computed} from "vue"; | ||||||
|  |     import {useI18n} from "vue-i18n"; | ||||||
|  | 
 | ||||||
|  |     const { t } = useI18n(); | ||||||
|  | 
 | ||||||
|  |     const props = defineProps<{ | ||||||
|  |         allSubmissions: SubmissionDTO[] | ||||||
|  |     }>(); | ||||||
|  |     const emit = defineEmits<{ | ||||||
|  |         (e: "submission-selected", submission: SubmissionDTO): void | ||||||
|  |     }>(); | ||||||
|  | 
 | ||||||
|  |     const headers = computed(() => [ | ||||||
|  |         { title: "#", value: "submissionNo", width: "50px" }, | ||||||
|  |         { title: t("submittedBy"), value: "submittedBy" }, | ||||||
|  |         { title: t("timestamp"), value: "timestamp"}, | ||||||
|  |         { title: "", key: "action", width: "70px", sortable: false }, | ||||||
|  |     ]); | ||||||
|  | 
 | ||||||
|  |     const data = computed(() => props.allSubmissions.map(submission => ({ | ||||||
|  |         submissionNo: submission.submissionNumber, | ||||||
|  |         submittedBy: `${submission.submitter.firstName} ${submission.submitter.lastName}`, | ||||||
|  |         timestamp: submission.time ? new Date(submission.time).toLocaleString(): "-", | ||||||
|  |         dto: submission | ||||||
|  |     }))); | ||||||
|  | 
 | ||||||
|  |     function selectSubmission(submission: SubmissionDTO) { | ||||||
|  |         emit('submission-selected', submission); | ||||||
|  |     } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |     <v-card> | ||||||
|  |         <v-card-title>{{ t("groupSubmissions") }}</v-card-title> | ||||||
|  |         <v-card-text> | ||||||
|  |             <v-data-table :headers="headers" | ||||||
|  |                           :items="data" | ||||||
|  |                           density="compact" | ||||||
|  |                           hide-default-footer | ||||||
|  |             > | ||||||
|  |                 <template v-slot:item.action="{ item }"> | ||||||
|  |                     <v-btn density="compact" variant="plain" @click="selectSubmission(item.dto)"> | ||||||
|  |                         {{ t("loadSubmission") }} | ||||||
|  |                     </v-btn> | ||||||
|  |                 </template> | ||||||
|  |             </v-data-table> | ||||||
|  |         </v-card-text> | ||||||
|  |     </v-card> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <style scoped> | ||||||
|  | 
 | ||||||
|  | </style> | ||||||
|  | @ -5,7 +5,12 @@ | ||||||
|     import {useSubmissionsQuery} from "@/queries/submissions.ts"; |     import {useSubmissionsQuery} from "@/queries/submissions.ts"; | ||||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; |     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
|     import SubmitButton from "@/views/learning-paths/learning-object/submissions/SubmitButton.vue"; |     import SubmitButton from "@/views/learning-paths/learning-object/submissions/SubmitButton.vue"; | ||||||
|     import {watch} from "vue"; |     import {computed, watch} from "vue"; | ||||||
|  |     import LearningObjectSubmissionsTable | ||||||
|  |         from "@/views/learning-paths/learning-object/submissions/LearningObjectSubmissionsTable.vue"; | ||||||
|  |     import {useI18n} from "vue-i18n"; | ||||||
|  | 
 | ||||||
|  |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|     const props = defineProps<{ |     const props = defineProps<{ | ||||||
|         submissionData?: SubmissionData, |         submissionData?: SubmissionData, | ||||||
|  | @ -28,17 +33,38 @@ | ||||||
|         () => true |         () => true | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     function loadSubmission(submission: SubmissionDTO) { |     function emitSubmissionData(submissionData: SubmissionData) { | ||||||
|         emit("update:submissionData", JSON.parse(submission.content)); |         emit("update:submissionData", submissionData); | ||||||
|         console.log(`emitted: ${JSON.parse(submission.content)}`); |     } | ||||||
|  | 
 | ||||||
|  |     function emitSubmission(submission: SubmissionDTO) { | ||||||
|  |         emitSubmissionData(JSON.parse(submission.content)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     watch(submissionQuery.data, () => { |     watch(submissionQuery.data, () => { | ||||||
|         const submissions = submissionQuery.data.value; |         const submissions = submissionQuery.data.value; | ||||||
|         if (submissions && submissions.length > 0) { |         if (submissions && submissions.length > 0) { | ||||||
|             loadSubmission(submissions[submissions.length - 1]); |             emitSubmission(submissions[submissions.length - 1]); | ||||||
|  |         } else { | ||||||
|  |             emitSubmissionData([]); | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     const lastSubmission = computed<SubmissionData>(() => { | ||||||
|  |         const submissions = submissionQuery.data.value; | ||||||
|  |         if (!submissions || submissions.length === 0) { | ||||||
|  |             return undefined; | ||||||
|  |         } | ||||||
|  |         return JSON.parse(submissions[submissions.length - 1].content); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const showSubmissionTable = computed(() => | ||||||
|  |         props.submissionData !== undefined && props.submissionData.length > 0 | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     const showIsDoneMessage = computed(() => | ||||||
|  |         lastSubmission.value !== undefined && lastSubmission.value.length === 0 | ||||||
|  |     ); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -51,8 +77,24 @@ | ||||||
|             :submission-data="props.submissionData" |             :submission-data="props.submissionData" | ||||||
|             :submissions="submissions.data" |             :submissions="submissions.data" | ||||||
|         /> |         /> | ||||||
|  |         <div class="submit-submissions-spacer"></div> | ||||||
|  |         <v-alert icon="mdi-check" | ||||||
|  |                  :text="t('taskCompleted')" | ||||||
|  |                  type="success" | ||||||
|  |                  variant="tonal" | ||||||
|  |                  density="compact" | ||||||
|  |                  v-if="showIsDoneMessage" | ||||||
|  |         ></v-alert> | ||||||
|  |         <learning-object-submissions-table | ||||||
|  |             v-if="submissionQuery.data && showSubmissionTable" | ||||||
|  |             :all-submissions="submissions.data" | ||||||
|  |             @submission-selected="emitSubmission" | ||||||
|  |         /> | ||||||
|     </using-query-result> |     </using-query-result> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
|  | .submit-submissions-spacer { | ||||||
|  |     height: 20px; | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -81,12 +81,12 @@ | ||||||
|         if (props.submissionData && props.submissionData.length === 0) { |         if (props.submissionData && props.submissionData.length === 0) { | ||||||
|             return t("markAsDone"); |             return t("markAsDone"); | ||||||
|         } |         } | ||||||
|         return t("submit"); |         return t(props.submissions.length > 0 ? "submitNewSolution" : "submitSolution"); | ||||||
|     }); |     }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <v-btn v-if="isStudent" |     <v-btn v-if="isStudent && !isSubmitDisabled" | ||||||
|            prepend-icon="mdi-check" |            prepend-icon="mdi-check" | ||||||
|            variant="elevated" |            variant="elevated" | ||||||
|            :loading="submissionIsPending" |            :loading="submissionIsPending" | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger