style: fix linting issues met Prettier
This commit is contained in:
		
							parent
							
								
									af8c783a26
								
							
						
					
					
						commit
						5168ceaee0
					
				
					 56 changed files with 680 additions and 741 deletions
				
			
		|  | @ -15,14 +15,14 @@ export class LearningPathController extends BaseController { | |||
|     async getBy( | ||||
|         hruid: string, | ||||
|         language: Language, | ||||
|         forGroup?: { forGroup: number, assignmentNo: number, classId: string }, | ||||
|         forGroup?: { forGroup: number; assignmentNo: number; classId: string }, | ||||
|     ): Promise<LearningPath> { | ||||
|         const dtos = await this.get<LearningPathDTO[]>("/", { | ||||
|             hruid, | ||||
|             language, | ||||
|             forGroup: forGroup?.forGroup, | ||||
|             assignmentNo: forGroup?.assignmentNo, | ||||
|             classId: forGroup?.classId | ||||
|             classId: forGroup?.classId, | ||||
|         }); | ||||
|         return LearningPath.fromDTO(single(dtos)); | ||||
|     } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { BaseController } from "./base-controller"; | ||||
| import type { SubmissionDTO, SubmissionDTOId } from "@dwengo-1/common/interfaces/submission"; | ||||
| import type {Language} from "@dwengo-1/common/util/language"; | ||||
| import type { Language } from "@dwengo-1/common/util/language"; | ||||
| 
 | ||||
| export interface SubmissionsResponse { | ||||
|     submissions: SubmissionDTO[] | SubmissionDTOId[]; | ||||
|  | @ -11,18 +11,19 @@ export interface SubmissionResponse { | |||
| } | ||||
| 
 | ||||
| export class SubmissionController extends BaseController { | ||||
| 
 | ||||
|     constructor(hruid: string) { | ||||
|         super(`learningObject/${hruid}/submissions`); | ||||
|     } | ||||
| 
 | ||||
|     async getAll( | ||||
|         language: Language, version: number, classId: string, assignmentId: number, groupId?: number, full = true | ||||
|         language: Language, | ||||
|         version: number, | ||||
|         classId: string, | ||||
|         assignmentId: number, | ||||
|         groupId?: number, | ||||
|         full = true, | ||||
|     ): Promise<SubmissionsResponse> { | ||||
|         return this.get<SubmissionsResponse>( | ||||
|             `/`, | ||||
|             { language, version, classId, assignmentId, groupId, full } | ||||
|         ); | ||||
|         return this.get<SubmissionsResponse>(`/`, { language, version, classId, assignmentId, groupId, full }); | ||||
|     } | ||||
| 
 | ||||
|     async getByNumber( | ||||
|  | @ -31,12 +32,15 @@ export class SubmissionController extends BaseController { | |||
|         classId: string, | ||||
|         assignmentId: number, | ||||
|         groupId: number, | ||||
|         submissionNumber: number | ||||
|         submissionNumber: number, | ||||
|     ): Promise<SubmissionResponse> { | ||||
|         return this.get<SubmissionResponse>( | ||||
|             `/${submissionNumber}`, | ||||
|             { language, version, classId, assignmentId, groupId }, | ||||
|         ); | ||||
|         return this.get<SubmissionResponse>(`/${submissionNumber}`, { | ||||
|             language, | ||||
|             version, | ||||
|             classId, | ||||
|             assignmentId, | ||||
|             groupId, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     async createSubmission(data: SubmissionDTO): Promise<SubmissionResponse> { | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ const learningPathController = getLearningPathController(); | |||
| export function useGetLearningPathQuery( | ||||
|     hruid: MaybeRefOrGetter<string>, | ||||
|     language: MaybeRefOrGetter<Language>, | ||||
|     forGroup?: MaybeRefOrGetter<{forGroup: number, assignmentNo: number, classId: string} | undefined>, | ||||
|     forGroup?: MaybeRefOrGetter<{ forGroup: number; assignmentNo: number; classId: string } | undefined>, | ||||
| ): UseQueryReturnType<LearningPath, Error> { | ||||
|     return useQuery({ | ||||
|         queryKey: [LEARNING_PATH_KEY, "get", hruid, language, forGroup], | ||||
|  | @ -34,7 +34,7 @@ export function useGetAllLearningPathsByThemeQuery( | |||
| 
 | ||||
| export function useSearchLearningPathQuery( | ||||
|     query: MaybeRefOrGetter<string | undefined>, | ||||
|     language: MaybeRefOrGetter<string | undefined> | ||||
|     language: MaybeRefOrGetter<string | undefined>, | ||||
| ): UseQueryReturnType<LearningPath[], Error> { | ||||
|     return useQuery({ | ||||
|         queryKey: [LEARNING_PATH_KEY, "search", query, language], | ||||
|  |  | |||
|  | @ -9,9 +9,9 @@ import { | |||
|     type UseQueryReturnType, | ||||
| } from "@tanstack/vue-query"; | ||||
| import { computed, toValue, type MaybeRefOrGetter } from "vue"; | ||||
| import {LEARNING_PATH_KEY} from "@/queries/learning-paths.ts"; | ||||
| import {LEARNING_OBJECT_KEY} from "@/queries/learning-objects.ts"; | ||||
| import type {Language} from "@dwengo-1/common/util/language"; | ||||
| import { LEARNING_PATH_KEY } from "@/queries/learning-paths.ts"; | ||||
| import { LEARNING_OBJECT_KEY } from "@/queries/learning-objects.ts"; | ||||
| import type { Language } from "@dwengo-1/common/util/language"; | ||||
| 
 | ||||
| export const SUBMISSION_KEY = "submissions"; | ||||
| 
 | ||||
|  | @ -22,7 +22,7 @@ function submissionQueryKey( | |||
|     classid: string, | ||||
|     assignmentNumber: number, | ||||
|     groupNumber: number, | ||||
|     submissionNumber: number | ||||
|     submissionNumber: number, | ||||
| ) { | ||||
|     return ["submission", hruid, language, version, classid, assignmentNumber, groupNumber, submissionNumber]; | ||||
| } | ||||
|  | @ -41,29 +41,37 @@ export async function invalidateAllSubmissionKeys( | |||
| 
 | ||||
|     for (const key of keys) { | ||||
|         const queryKey = [ | ||||
|             key, hruid, language, version, classid, assignmentNumber, groupNumber, submissionNumber | ||||
|         ].filter( | ||||
|             (arg) => arg !== undefined, | ||||
|         ); | ||||
|             key, | ||||
|             hruid, | ||||
|             language, | ||||
|             version, | ||||
|             classid, | ||||
|             assignmentNumber, | ||||
|             groupNumber, | ||||
|             submissionNumber, | ||||
|         ].filter((arg) => arg !== undefined); | ||||
|         await queryClient.invalidateQueries({ queryKey: queryKey }); | ||||
|     } | ||||
| 
 | ||||
|     await queryClient.invalidateQueries({ | ||||
|         queryKey: ["submissions", hruid, language, version, classid, assignmentNumber, groupNumber] | ||||
|             .filter((arg) => arg !== undefined), | ||||
|         queryKey: ["submissions", hruid, language, version, classid, assignmentNumber, groupNumber].filter( | ||||
|             (arg) => arg !== undefined, | ||||
|         ), | ||||
|     }); | ||||
|     await queryClient.invalidateQueries({ | ||||
|         queryKey: ["group-submissions", hruid, language, version, classid, assignmentNumber, groupNumber] | ||||
|             .filter((arg) => arg !== undefined), | ||||
|         queryKey: ["group-submissions", hruid, language, version, classid, assignmentNumber, groupNumber].filter( | ||||
|             (arg) => arg !== undefined, | ||||
|         ), | ||||
|     }); | ||||
|     await queryClient.invalidateQueries({ | ||||
|         queryKey: ["assignment-submissions", hruid, language, version,classid, assignmentNumber] | ||||
|             .filter((arg) => arg !== undefined), | ||||
|         queryKey: ["assignment-submissions", hruid, language, version, classid, assignmentNumber].filter( | ||||
|             (arg) => arg !== undefined, | ||||
|         ), | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function checkEnabled(properties: MaybeRefOrGetter<unknown>[]): boolean { | ||||
|     return properties.every(prop => Boolean(toValue(prop))); | ||||
|     return properties.every((prop) => Boolean(toValue(prop))); | ||||
| } | ||||
| 
 | ||||
| export function useSubmissionsQuery( | ||||
|  | @ -87,9 +95,14 @@ export function useSubmissionsQuery( | |||
|             const fullVal = toValue(full); | ||||
| 
 | ||||
|             const response = await new SubmissionController(hruidVal!).getAll( | ||||
|                 languageVal, versionVal!, classIdVal!, assignmentNumberVal!, groupNumberVal, fullVal | ||||
|                 languageVal, | ||||
|                 versionVal!, | ||||
|                 classIdVal!, | ||||
|                 assignmentNumberVal!, | ||||
|                 groupNumberVal, | ||||
|                 fullVal, | ||||
|             ); | ||||
|             return response ? response.submissions as SubmissionDTO[] : undefined; | ||||
|             return response ? (response.submissions as SubmissionDTO[]) : undefined; | ||||
|         }, | ||||
|         enabled: () => checkEnabled([hruid, language, version, classid, assignmentNumber]), | ||||
|     }); | ||||
|  | @ -113,13 +126,33 @@ export function useSubmissionQuery( | |||
|     const submissionNumberVal = toValue(submissionNumber); | ||||
| 
 | ||||
|     return useQuery({ | ||||
|         queryKey: computed(() => submissionQueryKey( | ||||
|             hruidVal!, languageVal, versionVal!, classIdVal!, assignmentNumberVal!, groupNumberVal!, submissionNumberVal! | ||||
|         )), | ||||
|         queryFn: async () => new SubmissionController(hruidVal!).getByNumber( | ||||
|             languageVal, versionVal!, classIdVal!, assignmentNumberVal!, groupNumberVal!, submissionNumberVal! | ||||
|         queryKey: computed(() => | ||||
|             submissionQueryKey( | ||||
|                 hruidVal!, | ||||
|                 languageVal, | ||||
|                 versionVal!, | ||||
|                 classIdVal!, | ||||
|                 assignmentNumberVal!, | ||||
|                 groupNumberVal!, | ||||
|                 submissionNumberVal!, | ||||
|             ), | ||||
|         ), | ||||
|         enabled: () => Boolean(hruidVal) && Boolean(languageVal) && Boolean(versionVal) && Boolean(classIdVal) && Boolean(assignmentNumberVal) && Boolean(submissionNumber), | ||||
|         queryFn: async () => | ||||
|             new SubmissionController(hruidVal!).getByNumber( | ||||
|                 languageVal, | ||||
|                 versionVal!, | ||||
|                 classIdVal!, | ||||
|                 assignmentNumberVal!, | ||||
|                 groupNumberVal!, | ||||
|                 submissionNumberVal!, | ||||
|             ), | ||||
|         enabled: () => | ||||
|             Boolean(hruidVal) && | ||||
|             Boolean(languageVal) && | ||||
|             Boolean(versionVal) && | ||||
|             Boolean(classIdVal) && | ||||
|             Boolean(assignmentNumberVal) && | ||||
|             Boolean(submissionNumber), | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
|  | @ -132,7 +165,8 @@ export function useCreateSubmissionMutation(): UseMutationReturnType< | |||
|     const queryClient = useQueryClient(); | ||||
| 
 | ||||
|     return useMutation({ | ||||
|         mutationFn: async ({ data }) => new SubmissionController(data.learningObjectIdentifier.hruid).createSubmission(data), | ||||
|         mutationFn: async ({ data }) => | ||||
|             new SubmissionController(data.learningObjectIdentifier.hruid).createSubmission(data), | ||||
|         onSuccess: async (response) => { | ||||
|             if (!response.submission.group) { | ||||
|                 await invalidateAllSubmissionKeys(queryClient); | ||||
|  | @ -144,13 +178,13 @@ export function useCreateSubmissionMutation(): UseMutationReturnType< | |||
|                 const an = typeof assignment === "number" ? assignment : assignment.id; | ||||
|                 const gn = response.submission.group.groupNumber; | ||||
| 
 | ||||
|                 const {hruid, language, version} = response.submission.learningObjectIdentifier; | ||||
|                 const { hruid, language, version } = response.submission.learningObjectIdentifier; | ||||
|                 await invalidateAllSubmissionKeys(queryClient, hruid, language, version, cid, an, gn); | ||||
| 
 | ||||
|                 await queryClient.invalidateQueries({queryKey: [LEARNING_PATH_KEY, "get"]}); | ||||
|                 await queryClient.invalidateQueries({ queryKey: [LEARNING_PATH_KEY, "get"] }); | ||||
| 
 | ||||
|                 await queryClient.invalidateQueries({ | ||||
|                     queryKey: [LEARNING_OBJECT_KEY, "metadata", hruid, language, version] | ||||
|                     queryKey: [LEARNING_OBJECT_KEY, "metadata", hruid, language, version], | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|  | @ -178,15 +212,9 @@ export function useDeleteSubmissionMutation(): UseMutationReturnType< | |||
|                 const an = typeof assignment === "number" ? assignment : assignment.id; | ||||
|                 const gn = response.submission.group.groupNumber; | ||||
| 
 | ||||
|                 const {hruid, language, version} = response.submission.learningObjectIdentifier; | ||||
|                 const { hruid, language, version } = response.submission.learningObjectIdentifier; | ||||
| 
 | ||||
|                 await invalidateAllSubmissionKeys( | ||||
|                     queryClient, | ||||
|                     hruid, | ||||
|                     language, | ||||
|                     version, | ||||
|                     cid, an, gn | ||||
|                 ); | ||||
|                 await invalidateAllSubmissionKeys(queryClient, hruid, language, version, cid, an, gn); | ||||
|             } | ||||
|         }, | ||||
|     }); | ||||
|  |  | |||
|  | @ -1,20 +1,29 @@ | |||
| export function deepEquals<T>(a: T, b: T): boolean { | ||||
|     if (a === b) {return true;} | ||||
|     if (a === b) { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if (typeof a !== 'object' || typeof b !== 'object' || a == null || b == null) | ||||
|         {return false;} | ||||
|     if (typeof a !== "object" || typeof b !== "object" || a == null || b == null) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (Array.isArray(a) !== Array.isArray(b)) {return false;} | ||||
|     if (Array.isArray(a) !== Array.isArray(b)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (Array.isArray(a) && Array.isArray(b)) { | ||||
|         if (a.length !== b.length) {return false;} | ||||
|         if (a.length !== b.length) { | ||||
|             return false; | ||||
|         } | ||||
|         return a.every((val, i) => deepEquals(val, b[i])); | ||||
|     } | ||||
| 
 | ||||
|     const keysA = Object.keys(a) as (keyof T)[]; | ||||
|     const keysB = Object.keys(b) as (keyof T)[]; | ||||
| 
 | ||||
|     if (keysA.length !== keysB.length) {return false;} | ||||
|     if (keysA.length !== keysB.length) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return keysA.every(key => deepEquals(a[key], b[key])); | ||||
|     return keysA.every((key) => deepEquals(a[key], b[key])); | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <script setup lang="ts"> | ||||
|     import {useRoute} from "vue-router"; | ||||
|     import { useRoute } from "vue-router"; | ||||
| 
 | ||||
|     const route = useRoute(); | ||||
| </script> | ||||
|  |  | |||
|  | @ -1,24 +1,24 @@ | |||
| <script setup lang="ts"> | ||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
|     import {useGroupsQuery} from "@/queries/groups.ts"; | ||||
|     import type {GroupsResponse} from "@/controllers/groups.ts"; | ||||
|     import {useI18n} from "vue-i18n"; | ||||
|     import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; | ||||
|     import { useGroupsQuery } from "@/queries/groups.ts"; | ||||
|     import type { GroupsResponse } from "@/controllers/groups.ts"; | ||||
|     import { useI18n } from "vue-i18n"; | ||||
|     import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; | ||||
| 
 | ||||
|     const { t } = useI18n(); | ||||
| 
 | ||||
|     const props = defineProps<{ | ||||
|         classId: string, | ||||
|         assignmentNumber: number | ||||
|         classId: string; | ||||
|         assignmentNumber: number; | ||||
|     }>(); | ||||
| 
 | ||||
|     const model = defineModel<number | undefined>({default: undefined}); | ||||
|     const model = defineModel<number | undefined>({ default: undefined }); | ||||
| 
 | ||||
|     const groupsQuery = useGroupsQuery(props.classId, props.assignmentNumber, true); | ||||
| 
 | ||||
|     interface GroupSelectorOption { | ||||
|         groupNumber: number | undefined, | ||||
|         label: string | ||||
|         groupNumber: number | undefined; | ||||
|         label: string; | ||||
|     } | ||||
| 
 | ||||
|     function groupOptions(groups: GroupDTO[]): GroupSelectorOption[] { | ||||
|  | @ -26,7 +26,7 @@ | |||
|             .sort((a, b) => a.groupNumber - b.groupNumber) | ||||
|             .map((group, index) => ({ | ||||
|                 groupNumber: group.groupNumber, | ||||
|                 label: `${index + 1}` | ||||
|                 label: `${index + 1}`, | ||||
|             })); | ||||
|     } | ||||
| </script> | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
|     import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; | ||||
|     import { computed, type ComputedRef, ref } from "vue"; | ||||
|     import type { LearningObject } from "@/data-objects/learning-objects/learning-object.ts"; | ||||
|     import {useRoute, useRouter} from "vue-router"; | ||||
|     import { useRoute, useRouter } from "vue-router"; | ||||
|     import LearningObjectView from "@/views/learning-paths/learning-object/LearningObjectView.vue"; | ||||
|     import { useI18n } from "vue-i18n"; | ||||
|     import LearningPathSearchField from "@/components/LearningPathSearchField.vue"; | ||||
|  | @ -21,7 +21,7 @@ | |||
|     const props = defineProps<{ | ||||
|         hruid: string; | ||||
|         language: Language; | ||||
|         learningObjectHruid?: string, | ||||
|         learningObjectHruid?: string; | ||||
|     }>(); | ||||
| 
 | ||||
|     interface LearningPathPageQuery { | ||||
|  | @ -37,7 +37,7 @@ | |||
|             return { | ||||
|                 forGroup: parseInt(query.value.forGroup), | ||||
|                 assignmentNo: parseInt(query.value.assignmentNo), | ||||
|                 classId: query.value.classId | ||||
|                 classId: query.value.classId, | ||||
|             }; | ||||
|         } | ||||
|     }); | ||||
|  | @ -112,7 +112,7 @@ | |||
|             let query = structuredClone(route.query); | ||||
|             query.forGroup = value; | ||||
|             router.push({ query }); | ||||
|         } | ||||
|         }, | ||||
|     }); | ||||
| 
 | ||||
|     function assign() { | ||||
|  | @ -120,8 +120,8 @@ | |||
|             path: "/assignment/create", | ||||
|             query: { | ||||
|                 hruid: props.hruid, | ||||
|                 language: props.language | ||||
|             } | ||||
|                 language: props.language, | ||||
|             }, | ||||
|         }); | ||||
|     } | ||||
| </script> | ||||
|  | @ -136,80 +136,86 @@ | |||
|             :width="350" | ||||
|         > | ||||
|             <div class="d-flex flex-column h-100"> | ||||
|             <v-list-item> | ||||
|                 <template v-slot:title> | ||||
|                     <div class="learning-path-title">{{ learningPath.data.title }}</div> | ||||
|                 </template> | ||||
|                 <template v-slot:subtitle> | ||||
|                     <div>{{ learningPath.data.description }}</div> | ||||
|                 </template> | ||||
|             </v-list-item> | ||||
|             <v-list-item> | ||||
|                 <template v-slot:subtitle> | ||||
|                     <p> | ||||
|                         <v-icon | ||||
|                             :color="COLORS.notCompleted" | ||||
|                             :icon="ICONS.notCompleted" | ||||
|                         ></v-icon> | ||||
|                         {{ t("legendNotCompletedYet") }} | ||||
|                     </p> | ||||
|                     <p> | ||||
|                         <v-icon | ||||
|                             :color="COLORS.completed" | ||||
|                             :icon="ICONS.completed" | ||||
|                         ></v-icon> | ||||
|                         {{ t("legendCompleted") }} | ||||
|                     </p> | ||||
|                     <p> | ||||
|                         <v-icon | ||||
|                             :color="COLORS.teacherExclusive" | ||||
|                             :icon="ICONS.teacherExclusive" | ||||
|                         ></v-icon> | ||||
|                         {{ t("legendTeacherExclusive") }} | ||||
|                     </p> | ||||
|                 </template> | ||||
|             </v-list-item> | ||||
|             <v-list-item v-if="query.classId && query.assignmentNo && authService.authState.activeRole === 'teacher'"> | ||||
|                 <template v-slot:default> | ||||
|                     <learning-path-group-selector | ||||
|                         :class-id="query.classId" | ||||
|                         :assignment-number="parseInt(query.assignmentNo)" | ||||
|                         v-model="forGroupQueryParam" | ||||
|                     /> | ||||
|                 </template> | ||||
|             </v-list-item> | ||||
|             <v-divider></v-divider> | ||||
|             <div v-if="props.learningObjectHruid"> | ||||
|                 <using-query-result | ||||
|                     :query-result="learningObjectListQueryResult" | ||||
|                     v-slot="learningObjects: { data: LearningObject[] }" | ||||
|                 > | ||||
|                     <template v-for="node in learningObjects.data"> | ||||
|                         <v-list-item | ||||
|                             link | ||||
|                             :to="{ path: node.key, query: route.query }" | ||||
|                             :title="node.title" | ||||
|                             :active="node.key === props.learningObjectHruid" | ||||
|                             :key="node.key" | ||||
|                             v-if="!node.teacherExclusive || authService.authState.activeRole === 'teacher'" | ||||
|                         > | ||||
|                             <template v-slot:prepend> | ||||
|                                 <v-icon | ||||
|                                     :color="COLORS[getNavItemState(node)]" | ||||
|                                     :icon="ICONS[getNavItemState(node)]" | ||||
|                                 ></v-icon> | ||||
|                             </template> | ||||
|                             <template v-slot:append> {{ node.estimatedTime }}' </template> | ||||
|                         </v-list-item> | ||||
|                 <v-list-item> | ||||
|                     <template v-slot:title> | ||||
|                         <div class="learning-path-title">{{ learningPath.data.title }}</div> | ||||
|                     </template> | ||||
|                 </using-query-result> | ||||
|             </div> | ||||
|             <v-spacer></v-spacer> | ||||
|             <v-list-item v-if="authService.authState.activeRole === 'teacher'"> | ||||
|                 <template v-slot:default> | ||||
|                     <v-btn class="button-in-nav" @click="assign()">{{ t("assignLearningPath") }}</v-btn> | ||||
|                 </template> | ||||
|             </v-list-item> | ||||
|                     <template v-slot:subtitle> | ||||
|                         <div>{{ learningPath.data.description }}</div> | ||||
|                     </template> | ||||
|                 </v-list-item> | ||||
|                 <v-list-item> | ||||
|                     <template v-slot:subtitle> | ||||
|                         <p> | ||||
|                             <v-icon | ||||
|                                 :color="COLORS.notCompleted" | ||||
|                                 :icon="ICONS.notCompleted" | ||||
|                             ></v-icon> | ||||
|                             {{ t("legendNotCompletedYet") }} | ||||
|                         </p> | ||||
|                         <p> | ||||
|                             <v-icon | ||||
|                                 :color="COLORS.completed" | ||||
|                                 :icon="ICONS.completed" | ||||
|                             ></v-icon> | ||||
|                             {{ t("legendCompleted") }} | ||||
|                         </p> | ||||
|                         <p> | ||||
|                             <v-icon | ||||
|                                 :color="COLORS.teacherExclusive" | ||||
|                                 :icon="ICONS.teacherExclusive" | ||||
|                             ></v-icon> | ||||
|                             {{ t("legendTeacherExclusive") }} | ||||
|                         </p> | ||||
|                     </template> | ||||
|                 </v-list-item> | ||||
|                 <v-list-item | ||||
|                     v-if="query.classId && query.assignmentNo && authService.authState.activeRole === 'teacher'" | ||||
|                 > | ||||
|                     <template v-slot:default> | ||||
|                         <learning-path-group-selector | ||||
|                             :class-id="query.classId" | ||||
|                             :assignment-number="parseInt(query.assignmentNo)" | ||||
|                             v-model="forGroupQueryParam" | ||||
|                         /> | ||||
|                     </template> | ||||
|                 </v-list-item> | ||||
|                 <v-divider></v-divider> | ||||
|                 <div v-if="props.learningObjectHruid"> | ||||
|                     <using-query-result | ||||
|                         :query-result="learningObjectListQueryResult" | ||||
|                         v-slot="learningObjects: { data: LearningObject[] }" | ||||
|                     > | ||||
|                         <template v-for="node in learningObjects.data"> | ||||
|                             <v-list-item | ||||
|                                 link | ||||
|                                 :to="{ path: node.key, query: route.query }" | ||||
|                                 :title="node.title" | ||||
|                                 :active="node.key === props.learningObjectHruid" | ||||
|                                 :key="node.key" | ||||
|                                 v-if="!node.teacherExclusive || authService.authState.activeRole === 'teacher'" | ||||
|                             > | ||||
|                                 <template v-slot:prepend> | ||||
|                                     <v-icon | ||||
|                                         :color="COLORS[getNavItemState(node)]" | ||||
|                                         :icon="ICONS[getNavItemState(node)]" | ||||
|                                     ></v-icon> | ||||
|                                 </template> | ||||
|                                 <template v-slot:append> {{ node.estimatedTime }}' </template> | ||||
|                             </v-list-item> | ||||
|                         </template> | ||||
|                     </using-query-result> | ||||
|                 </div> | ||||
|                 <v-spacer></v-spacer> | ||||
|                 <v-list-item v-if="authService.authState.activeRole === 'teacher'"> | ||||
|                     <template v-slot:default> | ||||
|                         <v-btn | ||||
|                             class="button-in-nav" | ||||
|                             @click="assign()" | ||||
|                             >{{ t("assignLearningPath") }}</v-btn | ||||
|                         > | ||||
|                     </template> | ||||
|                 </v-list-item> | ||||
|             </div> | ||||
|         </v-navigation-drawer> | ||||
|         <div class="control-bar-above-content"> | ||||
|  |  | |||
|  | @ -1,13 +1,18 @@ | |||
| export const essayQuestionAdapter: GiftAdapter = { | ||||
|     questionType: "Essay", | ||||
| 
 | ||||
|     installListener(questionElement: Element, answerUpdateCallback: (newAnswer: string | number | object) => void): void { | ||||
|         const textArea = questionElement.querySelector('textarea')!; | ||||
|         textArea.addEventListener('input', () => { answerUpdateCallback(textArea.value); }); | ||||
|     installListener( | ||||
|         questionElement: Element, | ||||
|         answerUpdateCallback: (newAnswer: string | number | object) => void, | ||||
|     ): void { | ||||
|         const textArea = questionElement.querySelector("textarea")!; | ||||
|         textArea.addEventListener("input", () => { | ||||
|             answerUpdateCallback(textArea.value); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     setAnswer(questionElement: Element, answer: string | number | object): void { | ||||
|         const textArea = questionElement.querySelector('textarea')!; | ||||
|         const textArea = questionElement.querySelector("textarea")!; | ||||
|         textArea.value = String(answer); | ||||
|     } | ||||
| } | ||||
|     }, | ||||
| }; | ||||
|  |  | |||
|  | @ -1,5 +1,8 @@ | |||
| interface GiftAdapter { | ||||
|     questionType: string; | ||||
|     installListener(questionElement: Element, answerUpdateCallback: (newAnswer: string | number | object) => void): void; | ||||
|     installListener( | ||||
|         questionElement: Element, | ||||
|         answerUpdateCallback: (newAnswer: string | number | object) => void, | ||||
|     ): void; | ||||
|     setAnswer(questionElement: Element, answer: string | number | object): void; | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| import {multipleChoiceQuestionAdapter} from "@/views/learning-paths/gift-adapters/multiple-choice-question-adapter.ts"; | ||||
| import {essayQuestionAdapter} from "@/views/learning-paths/gift-adapters/essay-question-adapter.ts"; | ||||
| import { multipleChoiceQuestionAdapter } from "@/views/learning-paths/gift-adapters/multiple-choice-question-adapter.ts"; | ||||
| import { essayQuestionAdapter } from "@/views/learning-paths/gift-adapters/essay-question-adapter.ts"; | ||||
| 
 | ||||
| export const giftAdapters = [multipleChoiceQuestionAdapter, essayQuestionAdapter]; | ||||
| 
 | ||||
| export function getGiftAdapterForType(questionType: string): GiftAdapter | undefined { | ||||
|     return giftAdapters.find(it => it.questionType === questionType); | ||||
|     return giftAdapters.find((it) => it.questionType === questionType); | ||||
| } | ||||
|  |  | |||
|  | @ -1,11 +1,14 @@ | |||
| export const multipleChoiceQuestionAdapter: GiftAdapter = { | ||||
|     questionType: "MC", | ||||
| 
 | ||||
|     installListener(questionElement: Element, answerUpdateCallback: (newAnswer: string | number | object) => void): void { | ||||
|         questionElement.querySelectorAll('input[type=radio]').forEach(element => { | ||||
|     installListener( | ||||
|         questionElement: Element, | ||||
|         answerUpdateCallback: (newAnswer: string | number | object) => void, | ||||
|     ): void { | ||||
|         questionElement.querySelectorAll("input[type=radio]").forEach((element) => { | ||||
|             const input = element as HTMLInputElement; | ||||
| 
 | ||||
|             input.addEventListener('change', () => { | ||||
|             input.addEventListener("change", () => { | ||||
|                 answerUpdateCallback(parseInt(input.value)); | ||||
|             }); | ||||
|             // Optional: initialize value if already selected
 | ||||
|  | @ -16,9 +19,9 @@ export const multipleChoiceQuestionAdapter: GiftAdapter = { | |||
|     }, | ||||
| 
 | ||||
|     setAnswer(questionElement: Element, answer: string | number | object): void { | ||||
|         questionElement.querySelectorAll('input[type=radio]').forEach(element => { | ||||
|         questionElement.querySelectorAll("input[type=radio]").forEach((element) => { | ||||
|             const input = element as HTMLInputElement; | ||||
|             input.checked = String(answer) === String(input.value); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|     }, | ||||
| }; | ||||
|  |  | |||
|  | @ -3,9 +3,9 @@ | |||
|     import type { UseQueryReturnType } from "@tanstack/vue-query"; | ||||
|     import { useLearningObjectHTMLQuery } from "@/queries/learning-objects.ts"; | ||||
|     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 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 LearningObjectSubmissionsView from "@/views/learning-paths/learning-object/submissions/LearningObjectSubmissionsView.vue"; | ||||
| 
 | ||||
|  | @ -14,8 +14,8 @@ | |||
|     const props = defineProps<{ | ||||
|         hruid: string; | ||||
|         language: Language; | ||||
|         version: number, | ||||
|         group?: {forGroup: number, assignmentNo: number, classId: string} | ||||
|         version: number; | ||||
|         group?: { forGroup: number; assignmentNo: number; classId: string }; | ||||
|     }>(); | ||||
| 
 | ||||
|     const learningObjectHtmlQueryResult: UseQueryReturnType<Document, Error> = useLearningObjectHTMLQuery( | ||||
|  | @ -35,7 +35,7 @@ | |||
|             :learning-object-content="learningPathHtml.data" | ||||
|             v-model:submission-data="currentSubmission" | ||||
|         /> | ||||
|         <div class="content-submissions-spacer"/> | ||||
|         <div class="content-submissions-spacer" /> | ||||
|         <learning-object-submissions-view | ||||
|             v-if="props.group" | ||||
|             :group="props.group" | ||||
|  | @ -48,26 +48,26 @@ | |||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| :deep(hr) { | ||||
|     margin-top: 10px; | ||||
|     margin-bottom: 10px; | ||||
| } | ||||
| :deep(li) { | ||||
|     margin-left: 30px; | ||||
|     margin-top: 5px; | ||||
|     margin-bottom: 5px; | ||||
| } | ||||
| :deep(img) { | ||||
|     max-width: 80%; | ||||
| } | ||||
| :deep(h2), | ||||
| :deep(h3), | ||||
| :deep(h4), | ||||
| :deep(h5), | ||||
| :deep(h6) { | ||||
|     margin-top: 10px; | ||||
| } | ||||
| .content-submissions-spacer { | ||||
|     height: 20px; | ||||
| } | ||||
|     :deep(hr) { | ||||
|         margin-top: 10px; | ||||
|         margin-bottom: 10px; | ||||
|     } | ||||
|     :deep(li) { | ||||
|         margin-left: 30px; | ||||
|         margin-top: 5px; | ||||
|         margin-bottom: 5px; | ||||
|     } | ||||
|     :deep(img) { | ||||
|         max-width: 80%; | ||||
|     } | ||||
|     :deep(h2), | ||||
|     :deep(h3), | ||||
|     :deep(h4), | ||||
|     :deep(h5), | ||||
|     :deep(h6) { | ||||
|         margin-top: 10px; | ||||
|     } | ||||
|     .content-submissions-spacer { | ||||
|         height: 20px; | ||||
|     } | ||||
| </style> | ||||
|  |  | |||
|  | @ -1,32 +1,32 @@ | |||
| <script setup lang="ts"> | ||||
| 
 | ||||
|     import type {SubmissionData} from "@/views/learning-paths/learning-object/submission-data"; | ||||
|     import {getGiftAdapterForType} from "@/views/learning-paths/gift-adapters/gift-adapters.ts"; | ||||
|     import {computed, nextTick, onMounted, watch} from "vue"; | ||||
|     import {copyArrayWith} from "@/utils/array-utils.ts"; | ||||
|     import type { SubmissionData } from "@/views/learning-paths/learning-object/submission-data"; | ||||
|     import { getGiftAdapterForType } from "@/views/learning-paths/gift-adapters/gift-adapters.ts"; | ||||
|     import { computed, nextTick, onMounted, watch } from "vue"; | ||||
|     import { copyArrayWith } from "@/utils/array-utils.ts"; | ||||
| 
 | ||||
|     const props = defineProps<{ | ||||
|         learningObjectContent: Document | ||||
|         submissionData?: SubmissionData | ||||
|         learningObjectContent: Document; | ||||
|         submissionData?: SubmissionData; | ||||
|     }>(); | ||||
| 
 | ||||
|     const emit = defineEmits<{ | ||||
|         (e: "update:submissionData", value: SubmissionData): void | ||||
|         (e: "update:submissionData", value: SubmissionData): void; | ||||
|     }>(); | ||||
| 
 | ||||
|     const submissionData = computed<SubmissionData | undefined>({ | ||||
|         get: () => props.submissionData, | ||||
|         set: (v?: SubmissionData) => v ? emit('update:submissionData', v) : undefined, | ||||
|         set: (v?: SubmissionData) => (v ? emit("update:submissionData", v) : undefined), | ||||
|     }); | ||||
| 
 | ||||
|     function forEachQuestion( | ||||
|         doAction: (questionIndex: number, questionName: string, questionType: string, questionElement: Element) => void | ||||
|         doAction: (questionIndex: number, questionName: string, questionType: string, questionElement: Element) => void, | ||||
|     ) { | ||||
|         const questions = document.querySelectorAll(".gift-question"); | ||||
|         questions.forEach(question => { | ||||
|             const name = question.id.match(/gift-q(\d+)/)?.[1] | ||||
|             const questionType = question.className.split(" ") | ||||
|                 .find(it => it.startsWith("gift-question-type")) | ||||
|         questions.forEach((question) => { | ||||
|             const name = question.id.match(/gift-q(\d+)/)?.[1]; | ||||
|             const questionType = question.className | ||||
|                 .split(" ") | ||||
|                 .find((it) => it.startsWith("gift-question-type")) | ||||
|                 ?.match(/gift-question-type-([^ ]*)/)?.[1]; | ||||
| 
 | ||||
|             if (!name || isNaN(parseInt(name)) || !questionType) return; | ||||
|  | @ -39,12 +39,9 @@ | |||
| 
 | ||||
|     function attachQuestionListeners(): void { | ||||
|         forEachQuestion((index, _name, type, element) => { | ||||
|             getGiftAdapterForType(type)?.installListener( | ||||
|                 element, | ||||
|                 (newAnswer) => { | ||||
|                     submissionData.value = copyArrayWith(index, newAnswer, submissionData.value ?? []) | ||||
|                 } | ||||
|             ); | ||||
|             getGiftAdapterForType(type)?.installListener(element, (newAnswer) => { | ||||
|                 submissionData.value = copyArrayWith(index, newAnswer, submissionData.value ?? []); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|  | @ -62,19 +59,25 @@ | |||
| 
 | ||||
|     onMounted(() => | ||||
|         nextTick(() => { | ||||
|             attachQuestionListeners() | ||||
|             attachQuestionListeners(); | ||||
|             setAnswers(props.submissionData ?? []); | ||||
|         }) | ||||
|         }), | ||||
|     ); | ||||
| 
 | ||||
|     watch(() => props.learningObjectContent, async () => { | ||||
|         await nextTick(); | ||||
|         attachQuestionListeners(); | ||||
|     }); | ||||
|     watch(() => props.submissionData, async () => { | ||||
|         await nextTick(); | ||||
|         setAnswers(props.submissionData ?? []); | ||||
|     }); | ||||
|     watch( | ||||
|         () => props.learningObjectContent, | ||||
|         async () => { | ||||
|             await nextTick(); | ||||
|             attachQuestionListeners(); | ||||
|         }, | ||||
|     ); | ||||
|     watch( | ||||
|         () => props.submissionData, | ||||
|         async () => { | ||||
|             await nextTick(); | ||||
|             setAnswers(props.submissionData ?? []); | ||||
|         }, | ||||
|     ); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|  | @ -84,5 +87,4 @@ | |||
|     ></div> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| </style> | ||||
| <style scoped></style> | ||||
|  |  | |||
|  | @ -1,36 +1,37 @@ | |||
| <script setup lang="ts"> | ||||
|     import type {SubmissionDTO} from "@dwengo-1/common/interfaces/submission"; | ||||
|     import {computed} from "vue"; | ||||
|     import {useI18n} from "vue-i18n"; | ||||
|     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[] | ||||
|         allSubmissions: SubmissionDTO[]; | ||||
|     }>(); | ||||
|     const emit = defineEmits<{ | ||||
|         (e: "submission-selected", submission: SubmissionDTO): void | ||||
|         (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: t("timestamp"), value: "timestamp" }, | ||||
|         { title: "", key: "action", width: "70px", sortable: false }, | ||||
|     ]); | ||||
| 
 | ||||
|     const data = computed(() => [...props.allSubmissions] | ||||
|         .sort((a, b) => (a.submissionNumber ?? 0) - (b.submissionNumber ?? 0)) | ||||
|         .map((submission, index) => ({ | ||||
|             submissionNo: index + 1, | ||||
|             submittedBy: `${submission.submitter.firstName} ${submission.submitter.lastName}`, | ||||
|             timestamp: submission.time ? new Date(submission.time).toLocaleString(): "-", | ||||
|             dto: submission | ||||
|         }) | ||||
|     )); | ||||
|     const data = computed(() => | ||||
|         [...props.allSubmissions] | ||||
|             .sort((a, b) => (a.submissionNumber ?? 0) - (b.submissionNumber ?? 0)) | ||||
|             .map((submission, index) => ({ | ||||
|                 submissionNo: index + 1, | ||||
|                 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); | ||||
|         emit("submission-selected", submission); | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
|  | @ -38,14 +39,19 @@ | |||
|     <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 | ||||
|                           :no-data-text="t('noSubmissionsYet')" | ||||
|             <v-data-table | ||||
|                 :headers="headers" | ||||
|                 :items="data" | ||||
|                 density="compact" | ||||
|                 hide-default-footer | ||||
|                 :no-data-text="t('noSubmissionsYet')" | ||||
|             > | ||||
|                 <template v-slot:item.action="{ item }"> | ||||
|                     <v-btn density="compact" variant="plain" @click="selectSubmission(item.dto)"> | ||||
|                     <v-btn | ||||
|                         density="compact" | ||||
|                         variant="plain" | ||||
|                         @click="selectSubmission(item.dto)" | ||||
|                     > | ||||
|                         {{ t("loadSubmission") }} | ||||
|                     </v-btn> | ||||
|                 </template> | ||||
|  | @ -54,6 +60,4 @@ | |||
|     </v-card> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| 
 | ||||
| </style> | ||||
| <style scoped></style> | ||||
|  |  | |||
|  | @ -1,26 +1,25 @@ | |||
| <script setup lang="ts"> | ||||
|     import type {SubmissionData} from "@/views/learning-paths/learning-object/submission-data"; | ||||
|     import type {SubmissionDTO} from "@dwengo-1/common/interfaces/submission"; | ||||
|     import {Language} from "@/data-objects/language.ts"; | ||||
|     import {useSubmissionsQuery} from "@/queries/submissions.ts"; | ||||
|     import type { SubmissionData } from "@/views/learning-paths/learning-object/submission-data"; | ||||
|     import type { SubmissionDTO } from "@dwengo-1/common/interfaces/submission"; | ||||
|     import { Language } from "@/data-objects/language.ts"; | ||||
|     import { useSubmissionsQuery } from "@/queries/submissions.ts"; | ||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||
|     import SubmitButton from "@/views/learning-paths/learning-object/submissions/SubmitButton.vue"; | ||||
|     import {computed, watch} from "vue"; | ||||
|     import LearningObjectSubmissionsTable | ||||
|         from "@/views/learning-paths/learning-object/submissions/LearningObjectSubmissionsTable.vue"; | ||||
|     import {useI18n} from "vue-i18n"; | ||||
|     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<{ | ||||
|         submissionData?: SubmissionData, | ||||
|         submissionData?: SubmissionData; | ||||
|         hruid: string; | ||||
|         language: Language; | ||||
|         version: number, | ||||
|         group: {forGroup: number, assignmentNo: number, classId: string} | ||||
|         version: number; | ||||
|         group: { forGroup: number; assignmentNo: number; classId: string }; | ||||
|     }>(); | ||||
|     const emit = defineEmits<{ | ||||
|         (e: "update:submissionData", value: SubmissionData): void | ||||
|         (e: "update:submissionData", value: SubmissionData): void; | ||||
|     }>(); | ||||
| 
 | ||||
|     const submissionQuery = useSubmissionsQuery( | ||||
|  | @ -30,7 +29,7 @@ | |||
|         () => props.group.classId, | ||||
|         () => props.group.assignmentNo, | ||||
|         () => props.group.forGroup, | ||||
|         () => true | ||||
|         () => true, | ||||
|     ); | ||||
| 
 | ||||
|     function emitSubmissionData(submissionData: SubmissionData) { | ||||
|  | @ -58,17 +57,16 @@ | |||
|         return JSON.parse(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); | ||||
| 
 | ||||
|     const showIsDoneMessage = computed(() => | ||||
|         lastSubmission.value !== undefined && lastSubmission.value.length === 0 | ||||
|     ); | ||||
|     const showIsDoneMessage = computed(() => lastSubmission.value !== undefined && lastSubmission.value.length === 0); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <using-query-result :query-result="submissionQuery" v-slot="submissions: { data: SubmissionDTO[] }"> | ||||
|     <using-query-result | ||||
|         :query-result="submissionQuery" | ||||
|         v-slot="submissions: { data: SubmissionDTO[] }" | ||||
|     > | ||||
|         <submit-button | ||||
|             :hruid="props.hruid" | ||||
|             :language="props.language" | ||||
|  | @ -78,12 +76,13 @@ | |||
|             :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 | ||||
|             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" | ||||
|  | @ -94,7 +93,7 @@ | |||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| .submit-submissions-spacer { | ||||
|     height: 20px; | ||||
| } | ||||
|     .submit-submissions-spacer { | ||||
|         height: 20px; | ||||
|     } | ||||
| </style> | ||||
|  |  | |||
|  | @ -1,26 +1,26 @@ | |||
| <script setup lang="ts"> | ||||
|     import {computed} from "vue"; | ||||
|     import { computed } from "vue"; | ||||
|     import authService from "@/services/auth/auth-service.ts"; | ||||
|     import type {SubmissionData} from "@/views/learning-paths/learning-object/submission-data"; | ||||
|     import {Language} from "@/data-objects/language.ts"; | ||||
|     import type {SubmissionDTO} from "@dwengo-1/common/interfaces/submission"; | ||||
|     import {useCreateSubmissionMutation} from "@/queries/submissions.ts"; | ||||
|     import {deepEquals} from "@/utils/deep-equals.ts"; | ||||
|     import type {UserProfile} from "oidc-client-ts"; | ||||
|     import type {LearningObjectIdentifierDTO} from "@dwengo-1/common/interfaces/learning-content"; | ||||
|     import type {StudentDTO} from "@dwengo-1/common/interfaces/student"; | ||||
|     import type {GroupDTO} from "@dwengo-1/common/interfaces/group"; | ||||
|     import {useI18n} from "vue-i18n"; | ||||
|     import type { SubmissionData } from "@/views/learning-paths/learning-object/submission-data"; | ||||
|     import { Language } from "@/data-objects/language.ts"; | ||||
|     import type { SubmissionDTO } from "@dwengo-1/common/interfaces/submission"; | ||||
|     import { useCreateSubmissionMutation } from "@/queries/submissions.ts"; | ||||
|     import { deepEquals } from "@/utils/deep-equals.ts"; | ||||
|     import type { UserProfile } from "oidc-client-ts"; | ||||
|     import type { LearningObjectIdentifierDTO } from "@dwengo-1/common/interfaces/learning-content"; | ||||
|     import type { StudentDTO } from "@dwengo-1/common/interfaces/student"; | ||||
|     import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; | ||||
|     import { useI18n } from "vue-i18n"; | ||||
| 
 | ||||
|     const { t } = useI18n(); | ||||
| 
 | ||||
|     const props = defineProps<{ | ||||
|         submissionData?: SubmissionData, | ||||
|         submissions: SubmissionDTO[], | ||||
|         submissionData?: SubmissionData; | ||||
|         submissions: SubmissionDTO[]; | ||||
|         hruid: string; | ||||
|         language: Language; | ||||
|         version: number, | ||||
|         group: {forGroup: number, assignmentNo: number, classId: string} | ||||
|         version: number; | ||||
|         group: { forGroup: number; assignmentNo: number; classId: string }; | ||||
|     }>(); | ||||
| 
 | ||||
|     const { | ||||
|  | @ -28,7 +28,7 @@ | |||
|         isError: submissionFailed, | ||||
|         error: submissionError, | ||||
|         isSuccess: submissionSuccess, | ||||
|         mutate: submitSolution | ||||
|         mutate: submitSolution, | ||||
|     } = useCreateSubmissionMutation(); | ||||
| 
 | ||||
|     const isStudent = computed(() => authService.authState.activeRole === "student"); | ||||
|  | @ -37,16 +37,13 @@ | |||
|         if (!props.submissionData || props.submissions === undefined) { | ||||
|             return true; | ||||
|         } | ||||
|         if (props.submissionData.some(answer => answer === null)) { | ||||
|         if (props.submissionData.some((answer) => answer === null)) { | ||||
|             return false; | ||||
|         } | ||||
|         if (props.submissions.length === 0) { | ||||
|             return false; | ||||
|         } | ||||
|         return deepEquals( | ||||
|             JSON.parse(props.submissions[props.submissions.length - 1].content), | ||||
|             props.submissionData | ||||
|         ); | ||||
|         return deepEquals(JSON.parse(props.submissions[props.submissions.length - 1].content), props.submissionData); | ||||
|     }); | ||||
| 
 | ||||
|     function submitCurrentAnswer(): void { | ||||
|  | @ -55,25 +52,25 @@ | |||
|         const learningObjectIdentifier: LearningObjectIdentifierDTO = { | ||||
|             hruid: props.hruid, | ||||
|             language: props.language as Language, | ||||
|             version: props.version | ||||
|             version: props.version, | ||||
|         }; | ||||
|         const submitter: StudentDTO = { | ||||
|             id: currentUser.preferred_username!, | ||||
|             username: currentUser.preferred_username!, | ||||
|             firstName: currentUser.given_name!, | ||||
|             lastName: currentUser.family_name! | ||||
|             lastName: currentUser.family_name!, | ||||
|         }; | ||||
|         const group: GroupDTO = { | ||||
|             class: classId, | ||||
|             assignment: assignmentNo, | ||||
|             groupNumber: forGroup | ||||
|         } | ||||
|             groupNumber: forGroup, | ||||
|         }; | ||||
|         const submission: SubmissionDTO = { | ||||
|             learningObjectIdentifier, | ||||
|             submitter, | ||||
|             group, | ||||
|             content: JSON.stringify(props.submissionData) | ||||
|         } | ||||
|             content: JSON.stringify(props.submissionData), | ||||
|         }; | ||||
|         submitSolution({ data: submission }); | ||||
|     } | ||||
| 
 | ||||
|  | @ -86,17 +83,16 @@ | |||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <v-btn v-if="isStudent && !isSubmitDisabled" | ||||
|            prepend-icon="mdi-check" | ||||
|            variant="elevated" | ||||
|            :loading="submissionIsPending" | ||||
|            :disabled="isSubmitDisabled" | ||||
|            @click="submitCurrentAnswer()" | ||||
|     <v-btn | ||||
|         v-if="isStudent && !isSubmitDisabled" | ||||
|         prepend-icon="mdi-check" | ||||
|         variant="elevated" | ||||
|         :loading="submissionIsPending" | ||||
|         :disabled="isSubmitDisabled" | ||||
|         @click="submitCurrentAnswer()" | ||||
|     > | ||||
|         {{ buttonText }} | ||||
|     </v-btn> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
| 
 | ||||
| </style> | ||||
| <style scoped></style> | ||||
|  |  | |||
		Reference in a new issue
	
	 Lint Action
						Lint Action