feat: leerpaden opzoeken en filteren in de create component werkt
This commit is contained in:
		
							parent
							
								
									aa33d32fa8
								
							
						
					
					
						commit
						9f55e62bcb
					
				
					 5 changed files with 150 additions and 26 deletions
				
			
		|  | @ -44,5 +44,8 @@ | |||
|     "next": "nächste", | ||||
|     "previous": "vorherige", | ||||
|     "groups": "Gruppen", | ||||
|     "learning-path": "Lernpfad" | ||||
|     "learning-path": "Lernpfad", | ||||
|     "choose-lp": "Einen lernpfad auswählen", | ||||
|     "filter-themes": "Filter nach Themen", | ||||
|     "search-lp": "Suche und Auswahl eines Lernpfads" | ||||
| } | ||||
|  |  | |||
|  | @ -44,5 +44,8 @@ | |||
|     "next": "next", | ||||
|     "previous": "previous", | ||||
|     "groups": "Groups", | ||||
|     "learning-path": "Learning path" | ||||
|     "learning-path": "Learning path", | ||||
|     "choose-lp": "Select a learning path", | ||||
|     "filter-themes": "Filter by themes", | ||||
|     "search-lp": "Search and select a learning path" | ||||
| } | ||||
|  |  | |||
|  | @ -44,5 +44,8 @@ | |||
|     "next": "suivant", | ||||
|     "previous": "précédent", | ||||
|     "groups": "Groupes", | ||||
|     "learning-path": "Parcours d'apprentissage" | ||||
|     "learning-path": "Parcours d'apprentissage", | ||||
|     "choose-lp": "Choisis un parcours d'apprentissage", | ||||
|     "filter-themes": "Filtrer par thèmes", | ||||
|     "search-lp": "Cherche et sélectionne un parcours d'apprentissage" | ||||
| } | ||||
|  |  | |||
|  | @ -44,5 +44,8 @@ | |||
|     "next": "volgende", | ||||
|     "previous": "vorige", | ||||
|     "groups": "Groepen", | ||||
|     "learning-path": "Leerpad" | ||||
|     "learning-path": "Leerpad", | ||||
|     "choose-lp": "Kies een leerpad", | ||||
|     "filter-themes": "Filter op thema's", | ||||
|     "search-lp": "Zoek en selecteer een leerpad" | ||||
| } | ||||
|  |  | |||
|  | @ -1,24 +1,129 @@ | |||
| <script setup lang="ts"> | ||||
|     import {useI18n} from "vue-i18n"; | ||||
|     import {ref, shallowRef} from "vue"; | ||||
|     import {computed, onMounted, ref, shallowRef, watch} from "vue"; | ||||
|     import {THEMESITEMS} from "@/utils/constants.ts"; | ||||
| 
 | ||||
|     const {t} = useI18n(); | ||||
|     const {t, locale} = useI18n(); | ||||
| 
 | ||||
|     const step = ref(1); | ||||
| 
 | ||||
|     const loading = ref(false) | ||||
|     const language = ref(locale.value); | ||||
| 
 | ||||
|     function onClick () { | ||||
|         loading.value = true | ||||
|     } | ||||
|     // If this value is set to true, the search bar will display a "loading" animation | ||||
|     const loading = ref(false); | ||||
|     const searchQuery = ref(""); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     // Use a list with all themes so learning-paths can be filtered by theme | ||||
|     // These lists store all available and selected themes | ||||
|     const themeItems = ref(Object.keys(THEMESITEMS).slice(1)); | ||||
|     const value = shallowRef([]); | ||||
|     const selectedThemes = shallowRef<string[]>([]); | ||||
| 
 | ||||
|     // Store all learning paths | ||||
|     const allLearningPaths = ref([]); | ||||
| 
 | ||||
|     // Filtered learning paths that will be displayed in the search bar dropdown | ||||
|     const filteredLearningPaths = ref([]); | ||||
| 
 | ||||
|     // The hruid and title of the currently selected learning path(TODO: use for post req) | ||||
|     const selectedLearningPath = ref(null); | ||||
| 
 | ||||
| 
 | ||||
|     // Fetch all learning paths initially | ||||
|     async function fetchAllLearningPaths() { | ||||
|         //TODO: replace by function from controller | ||||
|         try { | ||||
|             const response = await fetch(`http://localhost:3000/api/learningPath?language=${language.value}`); | ||||
| 
 | ||||
|             // Error | ||||
|             if (!response.ok) throw new Error("Failed to fetch learning paths"); | ||||
| 
 | ||||
|             // Collect all the learning paths and store them in a list by hruid and title | ||||
|             const data = await response.json(); | ||||
|             allLearningPaths.value = data.map((lp: { hruid: string; title: string }) => ({ | ||||
|                 hruid: lp.hruid, | ||||
|                 title: lp.title | ||||
|             })); | ||||
| 
 | ||||
|             // Get all the learning paths in the filtered list | ||||
|             filteredLearningPaths.value = [...allLearningPaths.value]; | ||||
|         } catch (error) { | ||||
|             console.error(error); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     watch( | ||||
|         () => locale.value, | ||||
|         (newLocale) => { | ||||
|             // Check if the language is valid | ||||
|             if (!["nl", "en"].includes(newLocale)) { | ||||
|                 language.value = "en"; | ||||
|             } | ||||
|             fetchAllLearningPaths(); // Re-fetch the learning path data when language changes | ||||
|         }, | ||||
|         { immediate: true } | ||||
|     ); | ||||
| 
 | ||||
|     // Filter the learning paths based on selected themes | ||||
|     async function filterLearningPathsByThemes() { | ||||
|         if (selectedThemes.value.length === 0) { | ||||
|             // Show all the learning paths if no themes are selected | ||||
|             filteredLearningPaths.value = [...allLearningPaths.value]; | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const learningPathHruids = new Set(); | ||||
| 
 | ||||
|         // Collect all themes categories that are selected | ||||
|         const themeCategories = new Set(); | ||||
|         selectedThemes.value.forEach(theme => { | ||||
|             THEMESITEMS[theme]?.forEach(category => themeCategories.add(category)); | ||||
|         }); | ||||
| 
 | ||||
|         //TODO: replace by function from controller | ||||
|         try { | ||||
|             // Fetch all theme data in parallel and wait for all to complete | ||||
|             const responses = await Promise.all( | ||||
|                 Array.from(themeCategories).map(category => | ||||
|                     fetch(`http://localhost:3000/api/theme/${category}?language=${language.value}`) | ||||
|                         .then(response => { | ||||
|                             if (!response.ok) throw new Error(`Error fetching ${category}`); | ||||
|                             return response.json(); | ||||
|                         }) | ||||
|                         .catch(error => { | ||||
|                             console.error(error); | ||||
|                             return []; // Return empty array on failure | ||||
|                         }) | ||||
|                 ) | ||||
|             ); | ||||
| 
 | ||||
|             // Combine all received hruids and add them in a set | ||||
|             responses.forEach(data => { | ||||
|                 data.forEach((lp: string ) => learningPathHruids.add(lp)); | ||||
|             }); | ||||
| 
 | ||||
| 
 | ||||
|             // Update filteredLearningPaths only after all requests complete | ||||
|             filteredLearningPaths.value = allLearningPaths.value.filter(lp => | ||||
|                 learningPathHruids.has(lp.hruid) | ||||
|             ); | ||||
| 
 | ||||
|         } catch (error) { | ||||
|             console.error("Error fetching themes:", error); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const searchResults = computed(() => { | ||||
|         return filteredLearningPaths.value.filter(lp => | ||||
|             lp.title.toLowerCase().includes(searchQuery.value.toLowerCase()) | ||||
|         ); | ||||
|     }); | ||||
| 
 | ||||
|     // Fetch all learning paths on mount | ||||
|     onMounted(fetchAllLearningPaths); | ||||
| 
 | ||||
|     // Watch for theme selection changes and filter lerning paths per theme | ||||
|     watch(selectedThemes, filterLearningPathsByThemes); | ||||
| 
 | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|  | @ -28,14 +133,14 @@ | |||
|             <v-stepper class="stepper-container" alt-labels :items="[t('learning-path'), t('classes'), t('groups')]" | ||||
|                        v-model="step" show-actions> | ||||
|                 <template v-slot:item.1> | ||||
|                     <v-card title="Select a learning path" flat> | ||||
|                     <v-card :title="t('choose-lp')" flat> | ||||
|                         <v-container class="step-container"> | ||||
|                             <v-card-text> | ||||
|                                 <v-select | ||||
|                                     v-model="value" | ||||
|                                     v-model="selectedThemes" | ||||
|                                     :items="themeItems.map(theme => ({ title: t(`theme-options.${theme}`), value: theme }))" | ||||
|                                     variant="solo" | ||||
|                                     label="Filter by themes" | ||||
|                                     :label="t('filter-themes')" | ||||
|                                     chips | ||||
|                                     multiple | ||||
|                                     deletable-chips | ||||
|  | @ -44,17 +149,24 @@ | |||
|                             </v-card-text> | ||||
| 
 | ||||
|                             <v-card-text> | ||||
|                                 <v-text-field | ||||
|                                 <v-combobox | ||||
|                                     v-model="selectedLearningPath" | ||||
|                                     :items="searchResults" | ||||
|                                     :label="t('search-lp')" | ||||
|                                     variant="solo" | ||||
|                                     clearable | ||||
|                                     hide-details | ||||
|                                     density="compact" | ||||
|                                     :loading="loading" | ||||
|                                     append-inner-icon="mdi-magnify" | ||||
|                                     density="compact" | ||||
|                                     label="Search" | ||||
|                                     variant="solo" | ||||
|                                     hide-details | ||||
|                                     single-line | ||||
|                                     @click:append-inner="onClick" | ||||
|                                 ></v-text-field> | ||||
|                                     item-title="title" | ||||
|                                     item-value="value" | ||||
|                                     :filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())" | ||||
|                                 ></v-combobox> | ||||
|                             </v-card-text> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|                         </v-container> | ||||
|                     </v-card> | ||||
|                 </template> | ||||
|  | @ -99,7 +211,7 @@ | |||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     width: 70%; | ||||
|     padding: 1%; | ||||
|     /*padding: 1%;*/ | ||||
| } | ||||
| 
 | ||||
| .stepper-container { | ||||
|  |  | |||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana