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,136 +52,66 @@
|
||||||
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>
|
<v-container class="step-container">
|
||||||
<template v-slot:item.1>
|
<v-card-text>
|
||||||
<v-card :title="t('choose-lp')" flat>
|
<v-combobox
|
||||||
<v-container class="step-container">
|
v-model="selectedLearningPath"
|
||||||
<v-card-text>
|
:items="searchResults"
|
||||||
<v-select
|
:label="t('choose-lp')"
|
||||||
v-model="selectedThemes"
|
variant="solo"
|
||||||
:items="themeItems.map(theme => ({ title: t(`theme-options.${theme}`), value: theme }))"
|
clearable
|
||||||
variant="solo"
|
hide-details
|
||||||
:label="t('filter-themes')"
|
density="compact"
|
||||||
chips
|
:loading="loading"
|
||||||
multiple
|
append-inner-icon="mdi-magnify"
|
||||||
deletable-chips
|
item-title="title"
|
||||||
clearable
|
item-value="value"
|
||||||
></v-select>
|
:filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())"
|
||||||
</v-card-text>
|
></v-combobox>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-combobox
|
<v-combobox
|
||||||
v-model="selectedLearningPath"
|
v-model="selectedClasses"
|
||||||
:items="searchResults"
|
:items="classes"
|
||||||
:label="t('search-lp')"
|
:label="t('choose-classes')"
|
||||||
variant="solo"
|
variant="solo"
|
||||||
clearable
|
clearable
|
||||||
hide-details
|
multiple
|
||||||
density="compact"
|
hide-details
|
||||||
:loading="loading"
|
chips
|
||||||
append-inner-icon="mdi-magnify"
|
append-inner-icon="mdi-magnify"
|
||||||
item-title="title"
|
item-title="title"
|
||||||
item-value="value"
|
item-value="value"
|
||||||
:filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())"
|
></v-combobox>
|
||||||
></v-combobox>
|
</v-card-text>
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
|
</v-container>
|
||||||
|
<v-btn class="mt-2" type="submit" block>Submit</v-btn>
|
||||||
</v-container>
|
</v-form>
|
||||||
</v-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue