feat(frontend): "Volgende" en "vorige"-knop toegevoegd aan leerpadpagina.

This commit is contained in:
Gerald Schmittinger 2025-03-24 22:58:44 +01:00
parent 728b04c9d8
commit 4356a1ccd2
8 changed files with 112 additions and 22 deletions

View file

@ -35,6 +35,7 @@
<style scoped>
.loading-div {
padding: 20px;
text-align: center;
}
</style>

View file

@ -1,4 +1,6 @@
{
"welcome": "Willkommen",
"error_title": "Fehler"
"error_title": "Fehler",
"previous": "Zurück",
"next": "Weiter"
}

View file

@ -6,5 +6,7 @@
"classes": "classes",
"discussions": "discussions",
"logout": "log out",
"error_title": "Error"
"error_title": "Error",
"previous": "Previous",
"next": "Next"
}

View file

@ -1,4 +1,6 @@
{
"welcome": "Bienvenue",
"error_title": "Erreur"
"error_title": "Erreur",
"previous": "Précédente",
"next": "Suivante"
}

View file

@ -6,5 +6,7 @@
"classes": "klassen",
"discussions": "discussies",
"logout": "log uit",
"error_title": "Fout"
"error_title": "Fout",
"previous": "Vorige",
"next": "Volgende"
}

View file

@ -3,16 +3,21 @@ import {LearningPath, type LearningPathDTO} from "@/services/learning-content/le
import type {Language} from "@/services/learning-content/language.ts";
import {single} from "@/utils/response-assertions.ts";
const learningPathEndpoint = new GetEndpoint<{}, {search?: string, hruid?: string, language?: Language}, LearningPathDTO[]>(
"/learningPath"
);
const learningPathEndpoint = new GetEndpoint<
{},
{search?: string, hruid?: string, language?: Language, forGroup?: string, forStudent?: string},
LearningPathDTO[]
>("/learningPath");
export async function searchLearningPaths(query: string): Promise<LearningPath[]> {
let dtos = await learningPathEndpoint.get({}, {search: query})
return dtos.map(dto => LearningPath.fromDTO(dto));
}
export async function getLearningPath(hruid: string, language: Language): Promise<LearningPath> {
let dtos = await learningPathEndpoint.get({}, {hruid, language});
export async function getLearningPath(hruid: string, language: Language, options?: {forGroup?: string, forStudent?: string}): Promise<LearningPath> {
let dtos = await learningPathEndpoint.get(
{},
{hruid, language, forGroup: options?.forGroup, forStudent: options?.forStudent}
);
return LearningPath.fromDTO(single(dtos));
}

View file

@ -49,7 +49,8 @@ export class LearningPathNode {
public readonly language: Language,
public readonly transitions: {next: LearningPathNode, default: boolean}[],
public readonly createdAt: Date,
public readonly updatedAt: Date
public readonly updatedAt: Date,
public readonly done: boolean = false
) {
}
@ -80,6 +81,7 @@ export class LearningPathNode {
}),
new Date(dto.created_at),
new Date(dto.updatedAt),
dto.done
)
}
}

View file

@ -2,20 +2,36 @@
import {Language} from "@/services/learning-content/language.ts";
import {getLearningPath} from "@/services/learning-content/learning-path-service.ts";
import UsingRemoteResource from "@/components/UsingRemoteResource.vue";
import {type LearningPath} from "@/services/learning-content/learning-path.ts";
import {computed, watch, watchEffect} from "vue";
import {type LearningPath, LearningPathNode} from "@/services/learning-content/learning-path.ts";
import {computed, type ComputedRef, watch} from "vue";
import type {LearningObject} from "@/services/learning-content/learning-object.ts";
import {useRouter} from "vue-router";
import {useRoute, useRouter} from "vue-router";
import {loadResource, remoteResource, type SuccessState} from "@/services/api-client/remote-resource.ts";
import LearningObjectView from "@/views/learning-paths/LearningObjectView.vue";
import {useI18n} from "vue-i18n";
const router = useRouter();
const route = useRoute();
const { t } = useI18n();
const props = defineProps<{hruid: string, language: Language, learningObjectHruid?: string}>()
interface QueryParams {
forStudent?: string,
forGroup?: string
}
const learningPathResource = remoteResource<LearningPath>();
watchEffect(() => {
loadResource(learningPathResource, getLearningPath(props.hruid, props.language));
});
watch([() => props.hruid, () => props.language, () => route.query.forStudent, () => route.query.forGroup], () => {
loadResource(
learningPathResource,
getLearningPath(
props.hruid,
props.language,
route.query as QueryParams
)
)
}, {immediate: true});
const learningObjectListResource = remoteResource<LearningObject[]>();
watch(learningPathResource, () => {
@ -24,12 +40,36 @@
}
}, {immediate: true});
const currentNode = computed(() => {
let currentHruid = props.learningObjectHruid;
const nodesList: ComputedRef<LearningPathNode[] | null> = computed(() => {
if (learningPathResource.state.type === "success") {
return learningPathResource.state.data.nodesAsList.filter(it => it.learningobjectHruid === currentHruid)[0]
return learningPathResource.state.data.nodesAsList;
} else {
return undefined;
return null;
}
})
const currentNode = computed(() => {
const currentHruid = props.learningObjectHruid;
if (nodesList.value) {
return nodesList.value.filter(it => it.learningobjectHruid === currentHruid)[0]
}
});
const nextNode = computed(() => {
if (!currentNode.value || !nodesList.value)
return;
const currentIndex = nodesList.value?.indexOf(currentNode.value);
if (currentIndex < nodesList.value?.length) {
return nodesList.value?.[currentIndex + 1];
}
});
const previousNode = computed(() => {
if (!currentNode.value || !nodesList.value)
return;
const currentIndex = nodesList.value?.indexOf(currentNode.value);
if (currentIndex < nodesList.value?.length) {
return nodesList.value?.[currentIndex - 1];
}
});
@ -41,6 +81,17 @@
}
});
}
function isLearningObjectCompleted(learningObject: LearningObject): boolean {
if (learningPathResource.state.type === "success") {
return learningPathResource.state.data.nodesAsList.filter(it =>
it.learningobjectHruid === learningObject.key
&& it.version === learningObject.version
&& it.language == learningObject.language
)[0].done;
}
return false;
}
</script>
<template>
@ -62,9 +113,10 @@
>
<v-list-item
link
:to="node.key"
:to="{path: node.key, query: route.query}"
:title="node.title"
:active="node.key === props.learningObjectHruid"
:prepend-icon="isLearningObjectCompleted(node) ? 'mdi-checkbox-marked-circle-outline' : 'mdi-checkbox-blank-circle-outline'"
v-for="node in learningObjects.data"
>
<template v-slot:append>
@ -80,9 +132,31 @@
:version="currentNode.version"
v-if="currentNode"
></learning-object-view>
<div class="navigation-buttons-container">
<v-btn
prepend-icon="mdi-chevron-left"
variant="text"
:disabled="!previousNode"
:to="previousNode ? {path: previousNode.learningobjectHruid, query: route.query} : undefined"
>
{{ t("previous") }}
</v-btn>
<v-btn
append-icon="mdi-chevron-right"
variant="text"
:disabled="!nextNode"
:to="nextNode ? {path: nextNode.learningobjectHruid, query: route.query} : undefined"
>
{{ t("next") }}
</v-btn>
</div>
</using-remote-resource>
</template>
<style scoped>
.navigation-buttons-container {
padding: 20px;
display: flex;
justify-content: space-between;
}
</style>