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 { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; | ||||
| import type { Language } from "@/data-objects/language.ts"; | ||||
| import { single } from "@/utils/response-assertions.ts"; | ||||
| import type { LearningPathDTO } from "@/data-objects/learning-paths/learning-path-dto.ts"; | ||||
| import {BaseController} from "@/controllers/base-controller.ts"; | ||||
| import {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||
| import type {Language} from "@/data-objects/language.ts"; | ||||
| import {single} from "@/utils/response-assertions.ts"; | ||||
| import type {LearningPathDTO} from "@/data-objects/learning-paths/learning-path-dto.ts"; | ||||
| 
 | ||||
| export class LearningPathController extends BaseController { | ||||
|     constructor() { | ||||
|         super("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)); | ||||
|     } | ||||
| 
 | ||||
|     async getBy( | ||||
|         hruid: string, | ||||
|         language: Language, | ||||
|  | @ -25,8 +27,15 @@ export class LearningPathController extends BaseController { | |||
|         }); | ||||
|         return LearningPath.fromDTO(single(dtos)); | ||||
|     } | ||||
| 
 | ||||
|     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)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										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 { Language } from "@/data-objects/language.ts"; | ||||
| import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; | ||||
| import { getLearningPathController } from "@/controllers/controllers"; | ||||
| import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; | ||||
| import {type MaybeRefOrGetter, toValue} from "vue"; | ||||
| import type {Language} from "@/data-objects/language.ts"; | ||||
| import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query"; | ||||
| import {getLearningPathController} from "@/controllers/controllers"; | ||||
| import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||
| 
 | ||||
| const LEARNING_PATH_KEY = "learningPath"; | ||||
| const learningPathController = getLearningPathController(); | ||||
|  | @ -44,3 +44,15 @@ export function useSearchLearningPathQuery( | |||
|         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 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 type { ClassesResponse } from "@/controllers/classes.ts"; | ||||
| import type { JoinRequestResponse, JoinRequestsResponse, StudentsResponse } from "@/controllers/students.ts"; | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| 
 | ||||
| /** | ||||
|  * Submits the form data to the backend. | ||||
|  * | ||||
|  | @ -16,21 +15,21 @@ import type {AssignmentDTO} from "@dwengo-1/common/interfaces/assignment"; | |||
| 
 | ||||
| export const submitForm = async ( | ||||
|     assignmentTitle: string, | ||||
|     selectedLearningPath: any, | ||||
|     selectedLearningPath: string, | ||||
|     selectedClass: string, | ||||
|     groups: string[][], | ||||
|     groups: string[], | ||||
|     deadline: string, | ||||
|     description: string, | ||||
|     currentLanguage: string | ||||
| ) => { | ||||
|     const formData: AssignmentDTO = { | ||||
|         id: 0, | ||||
|         id: 4, | ||||
|         class: selectedClass, | ||||
|         title: assignmentTitle, | ||||
|         description: description, | ||||
|         learningPath: selectedLearningPath, | ||||
|         language: currentLanguage, | ||||
|         groups: [], | ||||
|         language: currentLanguage | ||||
|         //groups: [],
 | ||||
|         //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 class01: Class = { | ||||
|     id: "34d484a1-295f-4e9f-bfdc-3e7a23d86a89", | ||||
|     id: "8764b861-90a6-42e5-9732-c0d9eb2f55f9", | ||||
|     displayName: "class 01", | ||||
|     teachers: [teacher01], | ||||
|     students: [student01, student02], | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
|     import {useI18n} from "vue-i18n"; | ||||
|     import {computed, onMounted, ref, watch} from "vue"; | ||||
|     import GroupSelector from "@/components/GroupSelector.vue"; | ||||
|     import {classes} from "@/utils/tempData.ts"; | ||||
|     import { | ||||
|         assignmentTitleRules, | ||||
|         classRules, | ||||
|  | @ -11,39 +10,80 @@ | |||
|         submitForm | ||||
|     } from "@/utils/assignmentForm.ts"; | ||||
|     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 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 language = ref(locale.value); | ||||
| 
 | ||||
|     const searchQuery = ref(''); | ||||
| 
 | ||||
|     const assignmentTitle = ref(''); | ||||
|     const deadline = ref(null); | ||||
|     const description = ref(''); | ||||
|     const allLearningPaths = ref([]); | ||||
|     const filteredLearningPaths = ref([]); | ||||
|     const selectedLearningPath = ref(null); | ||||
|     const allClasses = ref([...classes.map(cl => ({title: cl.displayName, value: cl.id}))]); | ||||
|     const selectedClass = ref(null); | ||||
|     const groups = ref<string[][]>([]); | ||||
| 
 | ||||
|     const availableClass = computed(() => { | ||||
|         //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(() => { | ||||
|         //TODO: replace by real data | ||||
|         if (!selectedClass.value) return []; | ||||
|         /*if (!selectedClass.value) return []; | ||||
|         const cl = classes.find(c => c.id === selectedClass.value.value); | ||||
|         return cl ? cl.students.map(st => ({ | ||||
|             title: `${st.firstName} ${st.lastName}`, | ||||
|             value: st.username, | ||||
|             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, () => { | ||||
|         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 { 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; | ||||
|         submitForm(assignmentTitle.value, selectedLearningPath.value?.hruid, selectedClass.value.value, groups.value, deadline.value, description.value, locale.value); | ||||
|     }; | ||||
|  | @ -113,10 +118,14 @@ | |||
|                                       density="compact" variant="outlined" clearable required></v-text-field> | ||||
|                     </v-card-text> | ||||
| 
 | ||||
|                     <using-query-result | ||||
|                         :query-result="learningPathsQueryResults" | ||||
|                         v-slot="{ data }: { data: LearningPath[] }" | ||||
|                     > | ||||
|                         <v-card-text> | ||||
|                             <v-combobox | ||||
|                                 v-model="selectedLearningPath" | ||||
|                             :items="searchResults" | ||||
|                                 :items="data" | ||||
|                                 :label="t('choose-lp')" | ||||
|                                 :rules="learningPathRules" | ||||
|                                 variant="outlined" | ||||
|  | @ -130,6 +139,7 @@ | |||
|                                 :filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())" | ||||
|                             ></v-combobox> | ||||
|                         </v-card-text> | ||||
|                     </using-query-result> | ||||
| 
 | ||||
|                     <v-card-text> | ||||
|                         <v-combobox | ||||
|  | @ -142,8 +152,8 @@ | |||
|                             hide-details | ||||
|                             density="compact" | ||||
|                             append-inner-icon="mdi-magnify" | ||||
|                             item-title="title" | ||||
|                             item-value="value" | ||||
|                             item-title="displayName" | ||||
|                             item-value="id" | ||||
|                             required | ||||
|                         ></v-combobox> | ||||
| 
 | ||||
|  |  | |||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana