Merging origin/dev into feat/assignment-page correctie
This commit is contained in:
commit
baea0051e6
249 changed files with 6754 additions and 3612 deletions
|
@ -1,9 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import ThemeCard from "@/components/ThemeCard.vue";
|
||||
import { ref, watchEffect, computed } from "vue";
|
||||
import { ref, watchEffect, computed, type Ref } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { AGE_TO_THEMES, THEMESITEMS } from "@/utils/constants.ts";
|
||||
import { useThemeQuery } from "@/queries/themes.ts";
|
||||
import type { Theme } from "@/data-objects/theme.ts";
|
||||
|
||||
const props = defineProps({
|
||||
selectedTheme: { type: String, required: true },
|
||||
|
@ -15,11 +16,11 @@
|
|||
|
||||
const { data: allThemes, isLoading, error } = useThemeQuery(language);
|
||||
|
||||
const allCards = ref([]);
|
||||
const cards = ref([]);
|
||||
const allCards: Ref<Theme[]> = ref([]);
|
||||
const cards: Ref<Theme[]> = ref([]);
|
||||
|
||||
watchEffect(() => {
|
||||
const themes = allThemes.value ?? [];
|
||||
const themes: Theme[] = allThemes.value ?? [];
|
||||
allCards.value = themes;
|
||||
|
||||
if (props.selectedTheme) {
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<main></main>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
34
frontend/src/components/LearningPathSearchField.vue
Normal file
34
frontend/src/components/LearningPathSearchField.vue
Normal file
|
@ -0,0 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { computed, ref } from "vue";
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
|
||||
const SEARCH_PATH = "/learningPath/search";
|
||||
|
||||
const query = computed({
|
||||
get: () => route.query.query as string | null,
|
||||
set: async (newValue) => router.push({ path: SEARCH_PATH, query: { query: newValue } }),
|
||||
});
|
||||
|
||||
const queryInput = ref(query.value);
|
||||
|
||||
function search(): void {
|
||||
query.value = queryInput.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-text-field
|
||||
class="search-field"
|
||||
:label="t('search')"
|
||||
append-inner-icon="mdi-magnify"
|
||||
v-model="queryInput"
|
||||
@keyup.enter="search()"
|
||||
@click:append-inner="search()"
|
||||
></v-text-field>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
62
frontend/src/components/LearningPathsGrid.vue
Normal file
62
frontend/src/components/LearningPathsGrid.vue
Normal file
|
@ -0,0 +1,62 @@
|
|||
<script setup lang="ts">
|
||||
import { convertBase64ToImageSrc } from "@/utils/base64ToImage.ts";
|
||||
import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{ learningPaths: LearningPath[] }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="results-grid"
|
||||
v-if="props.learningPaths.length > 0"
|
||||
>
|
||||
<v-card
|
||||
class="learning-path-card"
|
||||
link
|
||||
:to="`/learningPath/${learningPath.hruid}/${learningPath.language}/${learningPath.startNode.learningobjectHruid}`"
|
||||
:key="`${learningPath.hruid}/${learningPath.language}`"
|
||||
v-for="learningPath in props.learningPaths"
|
||||
>
|
||||
<v-img
|
||||
height="300px"
|
||||
:src="convertBase64ToImageSrc(learningPath.image)"
|
||||
cover
|
||||
v-if="learningPath.image"
|
||||
></v-img>
|
||||
<v-card-title class="learning-path-title">{{ learningPath.title }}</v-card-title>
|
||||
<v-card-subtitle>
|
||||
<v-icon icon="mdi-human-male-boy"></v-icon>
|
||||
<span>{{ learningPath.targetAges.min }} - {{ learningPath.targetAges.max }} {{ t("yearsAge") }}</span>
|
||||
</v-card-subtitle>
|
||||
<v-card-text>{{ learningPath.description }}</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
<div
|
||||
content="empty-state-container"
|
||||
v-else
|
||||
>
|
||||
<v-empty-state
|
||||
icon="mdi-emoticon-sad-outline"
|
||||
:title="t('noLearningPathsFound')"
|
||||
:text="t('noLearningPathsFoundDescription')"
|
||||
></v-empty-state>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.learning-path-card {
|
||||
width: 300px;
|
||||
}
|
||||
.learning-path-title {
|
||||
white-space: normal;
|
||||
}
|
||||
.results-grid {
|
||||
margin: 20px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
|
@ -26,20 +26,19 @@
|
|||
]);
|
||||
|
||||
// Logic to change the language of the website to the selected language
|
||||
const changeLanguage = (langCode: string) => {
|
||||
function changeLanguage(langCode: string): void {
|
||||
locale.value = langCode;
|
||||
localStorage.setItem("user-lang", langCode);
|
||||
};
|
||||
}
|
||||
|
||||
// Contains functionality to let the collapsed menu appear and disappear
|
||||
// When the screen size varies
|
||||
// Contains functionality to let the collapsed menu appear and disappear when the screen size varies
|
||||
const drawer = ref(false);
|
||||
|
||||
// When the user wants to logout, a popup is shown to verify this
|
||||
// If verified, the user should be logged out
|
||||
const performLogout = () => {
|
||||
auth.logout();
|
||||
};
|
||||
async function performLogout(): Promise<void> {
|
||||
await auth.logout();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
45
frontend/src/components/UsingQueryResult.vue
Normal file
45
frontend/src/components/UsingQueryResult.vue
Normal file
|
@ -0,0 +1,45 @@
|
|||
<script setup lang="ts" generic="T">
|
||||
import { computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { UseQueryReturnType } from "@tanstack/vue-query";
|
||||
|
||||
const props = defineProps<{
|
||||
queryResult: UseQueryReturnType<T, Error>;
|
||||
}>();
|
||||
|
||||
const { isLoading, isError, isSuccess, data, error } = props.queryResult;
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const errorMessage = computed(() => {
|
||||
const errorWithMessage = (error.value as { message: string }) || null;
|
||||
return errorWithMessage?.message || JSON.stringify(errorWithMessage);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="loading-div"
|
||||
v-if="isLoading"
|
||||
>
|
||||
<v-progress-circular indeterminate></v-progress-circular>
|
||||
</div>
|
||||
<div v-if="isError">
|
||||
<v-empty-state
|
||||
icon="mdi-alert-circle-outline"
|
||||
:text="errorMessage"
|
||||
:title="t('error_title')"
|
||||
></v-empty-state>
|
||||
</div>
|
||||
<slot
|
||||
v-if="isSuccess && data"
|
||||
:data="data"
|
||||
></slot>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.loading-div {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
Loading…
Add table
Add a link
Reference in a new issue