feat(frontend): alle leerpaden en klasses worden gefetched via de controllers
This commit is contained in:
		
							parent
							
								
									23947ecd92
								
							
						
					
					
						commit
						1328771551
					
				
					 7 changed files with 113 additions and 83 deletions
				
			
		|  | @ -1,17 +1,19 @@ | ||||||
| import { BaseController } from "@/controllers/base-controller.ts"; | import {BaseController} from "@/controllers/base-controller.ts"; | ||||||
| import { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; | import {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
| import type { Language } from "@/data-objects/language.ts"; | import type {Language} from "@/data-objects/language.ts"; | ||||||
| import { single } from "@/utils/response-assertions.ts"; | import {single} from "@/utils/response-assertions.ts"; | ||||||
| import type { LearningPathDTO } from "@/data-objects/learning-paths/learning-path-dto.ts"; | import type {LearningPathDTO} from "@/data-objects/learning-paths/learning-path-dto.ts"; | ||||||
| 
 | 
 | ||||||
| export class LearningPathController extends BaseController { | export class LearningPathController extends BaseController { | ||||||
|     constructor() { |     constructor() { | ||||||
|         super("learningPath"); |         super("learningPath"); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     async search(query: string): Promise<LearningPath[]> { |     async search(query: string): Promise<LearningPath[]> { | ||||||
|         const dtos = await this.get<LearningPathDTO[]>("/", { search: query }); |         const dtos = await this.get<LearningPathDTO[]>("/", {search: query}); | ||||||
|         return dtos.map((dto) => LearningPath.fromDTO(dto)); |         return dtos.map((dto) => LearningPath.fromDTO(dto)); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     async getBy( |     async getBy( | ||||||
|         hruid: string, |         hruid: string, | ||||||
|         language: Language, |         language: Language, | ||||||
|  | @ -25,8 +27,15 @@ export class LearningPathController extends BaseController { | ||||||
|         }); |         }); | ||||||
|         return LearningPath.fromDTO(single(dtos)); |         return LearningPath.fromDTO(single(dtos)); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     async getAllByTheme(theme: string): Promise<LearningPath[]> { |     async getAllByTheme(theme: string): Promise<LearningPath[]> { | ||||||
|         const dtos = await this.get<LearningPathDTO[]>("/", { theme }); |         const dtos = await this.get<LearningPathDTO[]>("/", {theme}); | ||||||
|  |         return dtos.map((dto) => LearningPath.fromDTO(dto)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async getAllLearningPaths(language: string | null = null): Promise<LearningPath[]> { | ||||||
|  |         const query = language ? { language } : undefined; | ||||||
|  |         const dtos = await this.get<LearningPathDTO[]>("/", query); | ||||||
|         return dtos.map((dto) => LearningPath.fromDTO(dto)); |         return dtos.map((dto) => LearningPath.fromDTO(dto)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										0
									
								
								frontend/src/queries/assignments.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								frontend/src/queries/assignments.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -1,8 +1,8 @@ | ||||||
| import { type MaybeRefOrGetter, toValue } from "vue"; | import {type MaybeRefOrGetter, toValue} from "vue"; | ||||||
| import type { Language } from "@/data-objects/language.ts"; | import type {Language} from "@/data-objects/language.ts"; | ||||||
| import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; | import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query"; | ||||||
| import { getLearningPathController } from "@/controllers/controllers"; | import {getLearningPathController} from "@/controllers/controllers"; | ||||||
| import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; | import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
| 
 | 
 | ||||||
| const LEARNING_PATH_KEY = "learningPath"; | const LEARNING_PATH_KEY = "learningPath"; | ||||||
| const learningPathController = getLearningPathController(); | const learningPathController = getLearningPathController(); | ||||||
|  | @ -44,3 +44,15 @@ export function useSearchLearningPathQuery( | ||||||
|         enabled: () => Boolean(toValue(query)), |         enabled: () => Boolean(toValue(query)), | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export function useGetAllLearningPaths(language: MaybeRefOrGetter<string | undefined> | ||||||
|  | ): UseQueryReturnType<LearningPath[], Error> { | ||||||
|  |     return useQuery({ | ||||||
|  |         queryKey: [LEARNING_PATH_KEY, "getAllLearningPaths", language], | ||||||
|  |         queryFn: async () => { | ||||||
|  |             const lang = toValue(language); | ||||||
|  |             return learningPathController.getAllLearningPaths(lang); | ||||||
|  |         }, | ||||||
|  |         enabled: () => Boolean(toValue(language)) | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import { computed, toValue } from "vue"; | import { computed, toValue } from "vue"; | ||||||
| import type { MaybeRefOrGetter } from "vue"; | import type { MaybeRefOrGetter } from "vue"; | ||||||
| import { useMutation, useQuery, useQueryClient, UseMutationReturnType, UseQueryReturnType } from "@tanstack/vue-query"; | import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; | ||||||
| import { TeacherController, type TeacherResponse, type TeachersResponse } from "@/controllers/teachers.ts"; | import { TeacherController, type TeacherResponse, type TeachersResponse } from "@/controllers/teachers.ts"; | ||||||
| import type { ClassesResponse } from "@/controllers/classes.ts"; | import type { ClassesResponse } from "@/controllers/classes.ts"; | ||||||
| import type { JoinRequestResponse, JoinRequestsResponse, StudentsResponse } from "@/controllers/students.ts"; | import type { JoinRequestResponse, JoinRequestsResponse, StudentsResponse } from "@/controllers/students.ts"; | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * Submits the form data to the backend. |  * Submits the form data to the backend. | ||||||
|  * |  * | ||||||
|  | @ -16,21 +15,21 @@ import type {AssignmentDTO} from "@dwengo-1/common/interfaces/assignment"; | ||||||
| 
 | 
 | ||||||
| export const submitForm = async ( | export const submitForm = async ( | ||||||
|     assignmentTitle: string, |     assignmentTitle: string, | ||||||
|     selectedLearningPath: any, |     selectedLearningPath: string, | ||||||
|     selectedClass: string, |     selectedClass: string, | ||||||
|     groups: string[][], |     groups: string[], | ||||||
|     deadline: string, |     deadline: string, | ||||||
|     description: string, |     description: string, | ||||||
|     currentLanguage: string |     currentLanguage: string | ||||||
| ) => { | ) => { | ||||||
|     const formData: AssignmentDTO = { |     const formData: AssignmentDTO = { | ||||||
|         id: 0, |         id: 4, | ||||||
|         class: selectedClass, |         class: selectedClass, | ||||||
|         title: assignmentTitle, |         title: assignmentTitle, | ||||||
|         description: description, |         description: description, | ||||||
|         learningPath: selectedLearningPath, |         learningPath: selectedLearningPath, | ||||||
|         language: currentLanguage, |         language: currentLanguage | ||||||
|         groups: [], |         //groups: [],
 | ||||||
|         //deadline: deadline,
 |         //deadline: deadline,
 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ const teacher02: Student = {username: "id12", firstName: "John", lastName: "Hiat | ||||||
| const teacher03: Student = {username: "id13", firstName: "Aaron", lastName: "Lewis", classes: []}; | const teacher03: Student = {username: "id13", firstName: "Aaron", lastName: "Lewis", classes: []}; | ||||||
| 
 | 
 | ||||||
| const class01: Class = { | const class01: Class = { | ||||||
|     id: "34d484a1-295f-4e9f-bfdc-3e7a23d86a89", |     id: "8764b861-90a6-42e5-9732-c0d9eb2f55f9", | ||||||
|     displayName: "class 01", |     displayName: "class 01", | ||||||
|     teachers: [teacher01], |     teachers: [teacher01], | ||||||
|     students: [student01, student02], |     students: [student01, student02], | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
|     import {useI18n} from "vue-i18n"; |     import {useI18n} from "vue-i18n"; | ||||||
|     import {computed, onMounted, ref, watch} from "vue"; |     import {computed, onMounted, ref, watch} from "vue"; | ||||||
|     import GroupSelector from "@/components/GroupSelector.vue"; |     import GroupSelector from "@/components/GroupSelector.vue"; | ||||||
|     import {classes} from "@/utils/tempData.ts"; |  | ||||||
|     import { |     import { | ||||||
|         assignmentTitleRules, |         assignmentTitleRules, | ||||||
|         classRules, |         classRules, | ||||||
|  | @ -11,39 +10,80 @@ | ||||||
|         submitForm |         submitForm | ||||||
|     } from "@/utils/assignmentForm.ts"; |     } from "@/utils/assignmentForm.ts"; | ||||||
|     import DeadlineSelector from "@/components/DeadlineSelector.vue"; |     import DeadlineSelector from "@/components/DeadlineSelector.vue"; | ||||||
|  |     import auth from "@/services/auth/auth-service.ts"; | ||||||
|  |     import {useTeacherClassesQuery} from "@/queries/teachers.ts"; | ||||||
|  |     import {useRouter} from "vue-router"; | ||||||
|  |     import {useGetAllLearningPaths} from "@/queries/learning-paths.ts"; | ||||||
|  |     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
|  |     import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
|  |     import type {Language} from "@/data-objects/language.ts"; | ||||||
| 
 | 
 | ||||||
|  |     const router = useRouter(); | ||||||
|     const {t, locale} = useI18n(); |     const {t, locale} = useI18n(); | ||||||
|  |     const role = ref(auth.authState.activeRole); | ||||||
|  |     const username = ref<string | null>(null); | ||||||
|  | 
 | ||||||
|  |     onMounted(async () => { | ||||||
|  |         // Redirect student | ||||||
|  |         if (role.value === 'student') { | ||||||
|  |             await router.push('/user'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Get the user's username | ||||||
|  |         const user = await auth.loadUser(); | ||||||
|  |         username.value = user?.profile?.preferred_username ?? null; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     const language = computed(() => locale.value); | ||||||
|  |     //Fetch all learning paths | ||||||
|  |     const learningPathsQueryResults = useGetAllLearningPaths(language); | ||||||
|  | 
 | ||||||
|  |     watch(language, (newLanguage) => { | ||||||
|  |         console.log(newLanguage); | ||||||
|  |         learningPathsQueryResults.refetch(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // Fetch and store all the teacher's classes | ||||||
|  |     const { data: classes, isLoading, error, refetch } = useTeacherClassesQuery(username, true); | ||||||
|  |     const allClasses = computed(() => { | ||||||
|  |         if (isLoading.value) { | ||||||
|  |             return []; | ||||||
|  |         } | ||||||
|  |         if (error.value) { | ||||||
|  |             return []; | ||||||
|  |         } | ||||||
|  |         return classes.value?.classes || []; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     const form = ref(); |     const form = ref(); | ||||||
| 
 | 
 | ||||||
|     const language = ref(locale.value); |  | ||||||
| 
 |  | ||||||
|     const searchQuery = ref(''); |     const searchQuery = ref(''); | ||||||
| 
 | 
 | ||||||
|     const assignmentTitle = ref(''); |     const assignmentTitle = ref(''); | ||||||
|     const deadline = ref(null); |     const deadline = ref(null); | ||||||
|     const description = ref(''); |     const description = ref(''); | ||||||
|     const allLearningPaths = ref([]); |     const allLearningPaths = ref([]); | ||||||
|     const filteredLearningPaths = ref([]); |  | ||||||
|     const selectedLearningPath = ref(null); |     const selectedLearningPath = ref(null); | ||||||
|     const allClasses = ref([...classes.map(cl => ({title: cl.displayName, value: cl.id}))]); |  | ||||||
|     const selectedClass = ref(null); |     const selectedClass = ref(null); | ||||||
|     const groups = ref<string[][]>([]); |     const groups = ref<string[][]>([]); | ||||||
| 
 | 
 | ||||||
|     const availableClass = computed(() => { |     const availableClass = computed(() => { | ||||||
|         //TODO: replace by real data |         //TODO: replace by real data | ||||||
|         return classes.find(cl => selectedClass.value?.value === cl.id) || null; |         return /*classes.find(cl => selectedClass.value?.value === cl.id) ||*/ null; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const allStudents = computed(() => { |     const allStudents = computed(() => { | ||||||
|         //TODO: replace by real data |         //TODO: replace by real data | ||||||
|         if (!selectedClass.value) return []; |         /*if (!selectedClass.value) return []; | ||||||
|         const cl = classes.find(c => c.id === selectedClass.value.value); |         const cl = classes.find(c => c.id === selectedClass.value.value); | ||||||
|         return cl ? cl.students.map(st => ({ |         return cl ? cl.students.map(st => ({ | ||||||
|             title: `${st.firstName} ${st.lastName}`, |             title: `${st.firstName} ${st.lastName}`, | ||||||
|             value: st.username, |             value: st.username, | ||||||
|             classes: cl |             classes: cl | ||||||
|         })) : []; |         })) : [];*/ | ||||||
|  |         return []; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -54,48 +94,13 @@ | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     async function fetchAllLearningPaths() { |  | ||||||
|         try { |  | ||||||
|             //TODO: replace by function from controller |  | ||||||
|             const response = await fetch(`http://localhost:3000/api/learningPath?language=${language.value}`); |  | ||||||
|             if (!response.ok) throw new Error("Failed to fetch learning paths"); |  | ||||||
|             const data = await response.json(); |  | ||||||
|             allLearningPaths.value = data.map((lp: { hruid: string; title: string }) => ({ |  | ||||||
|                 hruid: lp.hruid, |  | ||||||
|                 title: lp.title |  | ||||||
|             })); |  | ||||||
|             filteredLearningPaths.value = [...allLearningPaths.value]; |  | ||||||
|         } catch (error) { |  | ||||||
|             console.error(error); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     watch( |  | ||||||
|         () => locale.value, |  | ||||||
|         (newLocale) => { |  | ||||||
|             if (!["nl", "en"].includes(newLocale)) { |  | ||||||
|                 language.value = "en"; |  | ||||||
|             } |  | ||||||
|             fetchAllLearningPaths(); |  | ||||||
|         }, |  | ||||||
|         {immediate: true} |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     watch(selectedClass, () => { |     watch(selectedClass, () => { | ||||||
|         groups.value = []; |         groups.value = []; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const searchResults = computed(() => { |  | ||||||
|         return filteredLearningPaths.value.filter((lp: { hruid: string; title: string }) => |  | ||||||
|             lp.title.toLowerCase().includes(searchQuery.value.toLowerCase()) |  | ||||||
|         ); |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     onMounted(fetchAllLearningPaths); |  | ||||||
| 
 |  | ||||||
|     const submitFormHandler = async () => { |     const submitFormHandler = async () => { | ||||||
|         const { valid } = await form.value.validate(); |         const { valid } = await form.value.validate(); | ||||||
|         // Don't submit thr form if all rules don't apply |         // Don't submit the form if all rules don't apply | ||||||
|         if (!valid) return; |         if (!valid) return; | ||||||
|         submitForm(assignmentTitle.value, selectedLearningPath.value?.hruid, selectedClass.value.value, groups.value, deadline.value, description.value, locale.value); |         submitForm(assignmentTitle.value, selectedLearningPath.value?.hruid, selectedClass.value.value, groups.value, deadline.value, description.value, locale.value); | ||||||
|     }; |     }; | ||||||
|  | @ -113,23 +118,28 @@ | ||||||
|                                       density="compact" variant="outlined" clearable required></v-text-field> |                                       density="compact" variant="outlined" clearable required></v-text-field> | ||||||
|                     </v-card-text> |                     </v-card-text> | ||||||
| 
 | 
 | ||||||
|                     <v-card-text> |                     <using-query-result | ||||||
|                         <v-combobox |                         :query-result="learningPathsQueryResults" | ||||||
|                             v-model="selectedLearningPath" |                         v-slot="{ data }: { data: LearningPath[] }" | ||||||
|                             :items="searchResults" |                     > | ||||||
|                             :label="t('choose-lp')" |                         <v-card-text> | ||||||
|                             :rules="learningPathRules" |                             <v-combobox | ||||||
|                             variant="outlined" |                                 v-model="selectedLearningPath" | ||||||
|                             clearable |                                 :items="data" | ||||||
|                             hide-details |                                 :label="t('choose-lp')" | ||||||
|                             density="compact" |                                 :rules="learningPathRules" | ||||||
|                             append-inner-icon="mdi-magnify" |                                 variant="outlined" | ||||||
|                             item-title="title" |                                 clearable | ||||||
|                             item-value="value" |                                 hide-details | ||||||
|                             required |                                 density="compact" | ||||||
|                             :filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())" |                                 append-inner-icon="mdi-magnify" | ||||||
|                         ></v-combobox> |                                 item-title="title" | ||||||
|                     </v-card-text> |                                 item-value="value" | ||||||
|  |                                 required | ||||||
|  |                                 :filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())" | ||||||
|  |                             ></v-combobox> | ||||||
|  |                         </v-card-text> | ||||||
|  |                     </using-query-result> | ||||||
| 
 | 
 | ||||||
|                     <v-card-text> |                     <v-card-text> | ||||||
|                         <v-combobox |                         <v-combobox | ||||||
|  | @ -142,8 +152,8 @@ | ||||||
|                             hide-details |                             hide-details | ||||||
|                             density="compact" |                             density="compact" | ||||||
|                             append-inner-icon="mdi-magnify" |                             append-inner-icon="mdi-magnify" | ||||||
|                             item-title="title" |                             item-title="displayName" | ||||||
|                             item-value="value" |                             item-value="id" | ||||||
|                             required |                             required | ||||||
|                         ></v-combobox> |                         ></v-combobox> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana