feat: nieuwe v-form component
This commit is contained in:
		
							parent
							
								
									9f55e62bcb
								
							
						
					
					
						commit
						35faf7a80c
					
				
					 5 changed files with 59 additions and 135 deletions
				
			
		|  | @ -46,6 +46,5 @@ | ||||||
|     "groups": "Gruppen", |     "groups": "Gruppen", | ||||||
|     "learning-path": "Lernpfad", |     "learning-path": "Lernpfad", | ||||||
|     "choose-lp": "Einen lernpfad auswählen", |     "choose-lp": "Einen lernpfad auswählen", | ||||||
|     "filter-themes": "Filter nach Themen", |     "choose-classes": "Klassen wählen" | ||||||
|     "search-lp": "Suche und Auswahl eines Lernpfads" |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -46,6 +46,5 @@ | ||||||
|     "groups": "Groups", |     "groups": "Groups", | ||||||
|     "learning-path": "Learning path", |     "learning-path": "Learning path", | ||||||
|     "choose-lp": "Select a learning path", |     "choose-lp": "Select a learning path", | ||||||
|     "filter-themes": "Filter by themes", |     "choose-classes": "Select classes" | ||||||
|     "search-lp": "Search and select a learning path" |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -46,6 +46,5 @@ | ||||||
|     "groups": "Groupes", |     "groups": "Groupes", | ||||||
|     "learning-path": "Parcours d'apprentissage", |     "learning-path": "Parcours d'apprentissage", | ||||||
|     "choose-lp": "Choisis un parcours d'apprentissage", |     "choose-lp": "Choisis un parcours d'apprentissage", | ||||||
|     "filter-themes": "Filtrer par thèmes", |     "choose-classes": "Choisis des classes" | ||||||
|     "search-lp": "Cherche et sélectionne un parcours d'apprentissage" |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -46,6 +46,5 @@ | ||||||
|     "groups": "Groepen", |     "groups": "Groepen", | ||||||
|     "learning-path": "Leerpad", |     "learning-path": "Leerpad", | ||||||
|     "choose-lp": "Kies een leerpad", |     "choose-lp": "Kies een leerpad", | ||||||
|     "filter-themes": "Filter op thema's", |     "choose-classes": "Kies klassen" | ||||||
|     "search-lp": "Zoek en selecteer een leerpad" |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,22 +1,15 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import {useI18n} from "vue-i18n"; |     import {useI18n} from "vue-i18n"; | ||||||
|     import {computed, onMounted, ref, shallowRef, watch} from "vue"; |     import {computed, onMounted, ref, watch} from "vue"; | ||||||
|     import {THEMESITEMS} from "@/utils/constants.ts"; |  | ||||||
| 
 | 
 | ||||||
|     const {t, locale} = useI18n(); |     const {t, locale} = useI18n(); | ||||||
| 
 | 
 | ||||||
|     const step = ref(1); |  | ||||||
| 
 |  | ||||||
|     const language = ref(locale.value); |     const language = ref(locale.value); | ||||||
| 
 | 
 | ||||||
|     // If this value is set to true, the search bar will display a "loading" animation |     // If this value is set to true, the search bar will display a "loading" animation | ||||||
|     const loading = ref(false); |     const loading = ref(false); | ||||||
|     const searchQuery = ref(""); |     const searchQuery = ref(""); | ||||||
| 
 | 
 | ||||||
|     // These lists store all available and selected themes |  | ||||||
|     const themeItems = ref(Object.keys(THEMESITEMS).slice(1)); |  | ||||||
|     const selectedThemes = shallowRef<string[]>([]); |  | ||||||
| 
 |  | ||||||
|     // Store all learning paths |     // Store all learning paths | ||||||
|     const allLearningPaths = ref([]); |     const allLearningPaths = ref([]); | ||||||
| 
 | 
 | ||||||
|  | @ -26,6 +19,7 @@ | ||||||
|     // The hruid and title of the currently selected learning path(TODO: use for post req) |     // The hruid and title of the currently selected learning path(TODO: use for post req) | ||||||
|     const selectedLearningPath = ref(null); |     const selectedLearningPath = ref(null); | ||||||
| 
 | 
 | ||||||
|  |     const selectedClasses = ref([]); | ||||||
| 
 | 
 | ||||||
|     // Fetch all learning paths initially |     // Fetch all learning paths initially | ||||||
|     async function fetchAllLearningPaths() { |     async function fetchAllLearningPaths() { | ||||||
|  | @ -50,6 +44,7 @@ | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Re-fetch the learning paths when the language changes | ||||||
|     watch( |     watch( | ||||||
|         () => locale.value, |         () => locale.value, | ||||||
|         (newLocale) => { |         (newLocale) => { | ||||||
|  | @ -57,102 +52,35 @@ | ||||||
|             if (!["nl", "en"].includes(newLocale)) { |             if (!["nl", "en"].includes(newLocale)) { | ||||||
|                 language.value = "en"; |                 language.value = "en"; | ||||||
|             } |             } | ||||||
|             fetchAllLearningPaths(); // Re-fetch the learning path data when language changes |             fetchAllLearningPaths(); | ||||||
|         }, |         }, | ||||||
|         { immediate: true } |         {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(() => { |     const searchResults = computed(() => { | ||||||
|         return filteredLearningPaths.value.filter(lp => |         return filteredLearningPaths.value.filter((lp: { hruid: string; title: string }) => | ||||||
|             lp.title.toLowerCase().includes(searchQuery.value.toLowerCase()) |             lp.title.toLowerCase().includes(searchQuery.value.toLowerCase()) | ||||||
|         ); |         ); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     const classes = computed(() => ["f", "r"]); | ||||||
|  | 
 | ||||||
|     // Fetch all learning paths on mount |     // Fetch all learning paths on mount | ||||||
|     onMounted(fetchAllLearningPaths); |     onMounted(fetchAllLearningPaths); | ||||||
| 
 | 
 | ||||||
|     // Watch for theme selection changes and filter lerning paths per theme |  | ||||||
|     watch(selectedThemes, filterLearningPathsByThemes); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <div class="main-container"> |     <div class="main-container"> | ||||||
|         <h1 class="title">{{ t("new-assignment") }}</h1> |         <h1 class="title">{{ t("new-assignment") }}</h1> | ||||||
|         <v-card class="form-card"> |         <v-card class="form-card"> | ||||||
|             <v-stepper class="stepper-container" alt-labels :items="[t('learning-path'), t('classes'), t('groups')]" |             <v-form class="form-container"> | ||||||
|                        v-model="step" show-actions> |  | ||||||
|                 <template v-slot:item.1> |  | ||||||
|                     <v-card :title="t('choose-lp')" flat> |  | ||||||
|                 <v-container class="step-container"> |                 <v-container class="step-container"> | ||||||
|                             <v-card-text> |  | ||||||
|                                 <v-select |  | ||||||
|                                     v-model="selectedThemes" |  | ||||||
|                                     :items="themeItems.map(theme => ({ title: t(`theme-options.${theme}`), value: theme }))" |  | ||||||
|                                     variant="solo" |  | ||||||
|                                     :label="t('filter-themes')" |  | ||||||
|                                     chips |  | ||||||
|                                     multiple |  | ||||||
|                                     deletable-chips |  | ||||||
|                                     clearable |  | ||||||
|                                 ></v-select> |  | ||||||
|                             </v-card-text> |  | ||||||
| 
 |  | ||||||
|                     <v-card-text> |                     <v-card-text> | ||||||
|                         <v-combobox |                         <v-combobox | ||||||
|                             v-model="selectedLearningPath" |                             v-model="selectedLearningPath" | ||||||
|                             :items="searchResults" |                             :items="searchResults" | ||||||
|                                     :label="t('search-lp')" |                             :label="t('choose-lp')" | ||||||
|                             variant="solo" |                             variant="solo" | ||||||
|                             clearable |                             clearable | ||||||
|                             hide-details |                             hide-details | ||||||
|  | @ -165,28 +93,25 @@ | ||||||
|                         ></v-combobox> |                         ></v-combobox> | ||||||
|                     </v-card-text> |                     </v-card-text> | ||||||
| 
 | 
 | ||||||
| 
 |                     <v-card-text> | ||||||
|  |                         <v-combobox | ||||||
|  |                             v-model="selectedClasses" | ||||||
|  |                             :items="classes" | ||||||
|  |                             :label="t('choose-classes')" | ||||||
|  |                             variant="solo" | ||||||
|  |                             clearable | ||||||
|  |                             multiple | ||||||
|  |                             hide-details | ||||||
|  |                             chips | ||||||
|  |                             append-inner-icon="mdi-magnify" | ||||||
|  |                             item-title="title" | ||||||
|  |                             item-value="value" | ||||||
|  |                         ></v-combobox> | ||||||
|  |                     </v-card-text> | ||||||
| 
 | 
 | ||||||
|                 </v-container> |                 </v-container> | ||||||
|                     </v-card> |                 <v-btn class="mt-2" type="submit" block>Submit</v-btn> | ||||||
|                 </template> |             </v-form> | ||||||
| 
 |  | ||||||
|                 <template> |  | ||||||
|                     <v-card title="Select one or more classes" flat> |  | ||||||
|                         <v-container class="step-container"> |  | ||||||
|                             <!-- Content for Step Two --> |  | ||||||
|                         </v-container> |  | ||||||
|                     </v-card> |  | ||||||
|                 </template> |  | ||||||
| 
 |  | ||||||
|                 <template> |  | ||||||
|                     <v-card title="Step Three" flat> |  | ||||||
|                         <v-container class="step-container"> |  | ||||||
|                             <!-- Content for Step Three --> |  | ||||||
|                         </v-container> |  | ||||||
|                     </v-card> |  | ||||||
|                 </template> |  | ||||||
|             </v-stepper> |  | ||||||
|         </v-card> |         </v-card> | ||||||
|     </div> |     </div> | ||||||
| </template> | </template> | ||||||
|  | @ -210,11 +135,11 @@ | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
|     align-items: center; |     align-items: center; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
|     width: 70%; |     width: 55%; | ||||||
|     /*padding: 1%;*/ |     /*padding: 1%;*/ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .stepper-container { | .form-container { | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
|  | @ -228,18 +153,21 @@ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Responsive adjustments */ | /* Responsive adjustments */ | ||||||
| @media (max-width: 650px) { | @media (max-width: 1000px) { | ||||||
|     .form-card { |     .form-card { | ||||||
|         width: 95%; |         width: 70%; | ||||||
|         padding: 1%; |         padding: 1%; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .v-stepper-header { |  | ||||||
|         display: none; /* Hides step numbers on small screens */ |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .step-container { |     .step-container { | ||||||
|         min-height: 300px; /* Gives enough space */ |         min-height: 300px; /* Gives enough space */ | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /* Responsive adjustments */ | ||||||
|  | @media (max-width: 700px) { | ||||||
|  |     .form-card { | ||||||
|  |         width: 95%; | ||||||
|  |     } | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Joyelle Ndagijimana
						Joyelle Ndagijimana