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",
|
"next": "nächste",
|
||||||
"previous": "vorherige",
|
"previous": "vorherige",
|
||||||
"groups": "Gruppen",
|
"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",
|
"next": "next",
|
||||||
"previous": "previous",
|
"previous": "previous",
|
||||||
"groups": "Groups",
|
"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",
|
"next": "suivant",
|
||||||
"previous": "précédent",
|
"previous": "précédent",
|
||||||
"groups": "Groupes",
|
"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",
|
"next": "volgende",
|
||||||
"previous": "vorige",
|
"previous": "vorige",
|
||||||
"groups": "Groepen",
|
"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">
|
<script setup lang="ts">
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
import {ref, shallowRef} from "vue";
|
import {computed, onMounted, ref, shallowRef, watch} from "vue";
|
||||||
import {THEMESITEMS} from "@/utils/constants.ts";
|
import {THEMESITEMS} from "@/utils/constants.ts";
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t, locale} = useI18n();
|
||||||
|
|
||||||
const step = ref(1);
|
const step = ref(1);
|
||||||
|
|
||||||
const loading = ref(false)
|
const language = ref(locale.value);
|
||||||
|
|
||||||
function onClick () {
|
// If this value is set to true, the search bar will display a "loading" animation
|
||||||
loading.value = true
|
const loading = ref(false);
|
||||||
}
|
|
||||||
const searchQuery = ref("");
|
const searchQuery = ref("");
|
||||||
|
|
||||||
|
// These lists store all available and selected themes
|
||||||
|
|
||||||
// Use a list with all themes so learning-paths can be filtered by theme
|
|
||||||
const themeItems = ref(Object.keys(THEMESITEMS).slice(1));
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -28,14 +133,14 @@
|
||||||
<v-stepper class="stepper-container" alt-labels :items="[t('learning-path'), t('classes'), t('groups')]"
|
<v-stepper class="stepper-container" alt-labels :items="[t('learning-path'), t('classes'), t('groups')]"
|
||||||
v-model="step" show-actions>
|
v-model="step" show-actions>
|
||||||
<template v-slot:item.1>
|
<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-container class="step-container">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-select
|
<v-select
|
||||||
v-model="value"
|
v-model="selectedThemes"
|
||||||
:items="themeItems.map(theme => ({ title: t(`theme-options.${theme}`), value: theme }))"
|
:items="themeItems.map(theme => ({ title: t(`theme-options.${theme}`), value: theme }))"
|
||||||
variant="solo"
|
variant="solo"
|
||||||
label="Filter by themes"
|
:label="t('filter-themes')"
|
||||||
chips
|
chips
|
||||||
multiple
|
multiple
|
||||||
deletable-chips
|
deletable-chips
|
||||||
|
@ -44,17 +149,24 @@
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<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"
|
:loading="loading"
|
||||||
append-inner-icon="mdi-magnify"
|
append-inner-icon="mdi-magnify"
|
||||||
density="compact"
|
item-title="title"
|
||||||
label="Search"
|
item-value="value"
|
||||||
variant="solo"
|
:filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())"
|
||||||
hide-details
|
></v-combobox>
|
||||||
single-line
|
|
||||||
@click:append-inner="onClick"
|
|
||||||
></v-text-field>
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
@ -99,7 +211,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
padding: 1%;
|
/*padding: 1%;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.stepper-container {
|
.stepper-container {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue