feat(frontend): "Volgende" en "vorige"-knop toegevoegd aan leerpadpagina.
This commit is contained in:
parent
728b04c9d8
commit
4356a1ccd2
8 changed files with 112 additions and 22 deletions
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.loading-div {
|
.loading-div {
|
||||||
|
padding: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{
|
{
|
||||||
"welcome": "Willkommen",
|
"welcome": "Willkommen",
|
||||||
"error_title": "Fehler"
|
"error_title": "Fehler",
|
||||||
|
"previous": "Zurück",
|
||||||
|
"next": "Weiter"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,7 @@
|
||||||
"classes": "classes",
|
"classes": "classes",
|
||||||
"discussions": "discussions",
|
"discussions": "discussions",
|
||||||
"logout": "log out",
|
"logout": "log out",
|
||||||
"error_title": "Error"
|
"error_title": "Error",
|
||||||
|
"previous": "Previous",
|
||||||
|
"next": "Next"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{
|
{
|
||||||
"welcome": "Bienvenue",
|
"welcome": "Bienvenue",
|
||||||
"error_title": "Erreur"
|
"error_title": "Erreur",
|
||||||
|
"previous": "Précédente",
|
||||||
|
"next": "Suivante"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,7 @@
|
||||||
"classes": "klassen",
|
"classes": "klassen",
|
||||||
"discussions": "discussies",
|
"discussions": "discussies",
|
||||||
"logout": "log uit",
|
"logout": "log uit",
|
||||||
"error_title": "Fout"
|
"error_title": "Fout",
|
||||||
|
"previous": "Vorige",
|
||||||
|
"next": "Volgende"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,21 @@ import {LearningPath, type LearningPathDTO} from "@/services/learning-content/le
|
||||||
import type {Language} from "@/services/learning-content/language.ts";
|
import type {Language} from "@/services/learning-content/language.ts";
|
||||||
import {single} from "@/utils/response-assertions.ts";
|
import {single} from "@/utils/response-assertions.ts";
|
||||||
|
|
||||||
const learningPathEndpoint = new GetEndpoint<{}, {search?: string, hruid?: string, language?: Language}, LearningPathDTO[]>(
|
const learningPathEndpoint = new GetEndpoint<
|
||||||
"/learningPath"
|
{},
|
||||||
);
|
{search?: string, hruid?: string, language?: Language, forGroup?: string, forStudent?: string},
|
||||||
|
LearningPathDTO[]
|
||||||
|
>("/learningPath");
|
||||||
|
|
||||||
export async function searchLearningPaths(query: string): Promise<LearningPath[]> {
|
export async function searchLearningPaths(query: string): Promise<LearningPath[]> {
|
||||||
let dtos = await learningPathEndpoint.get({}, {search: query})
|
let dtos = await learningPathEndpoint.get({}, {search: query})
|
||||||
return dtos.map(dto => LearningPath.fromDTO(dto));
|
return dtos.map(dto => LearningPath.fromDTO(dto));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLearningPath(hruid: string, language: Language): Promise<LearningPath> {
|
export async function getLearningPath(hruid: string, language: Language, options?: {forGroup?: string, forStudent?: string}): Promise<LearningPath> {
|
||||||
let dtos = await learningPathEndpoint.get({}, {hruid, language});
|
let dtos = await learningPathEndpoint.get(
|
||||||
|
{},
|
||||||
|
{hruid, language, forGroup: options?.forGroup, forStudent: options?.forStudent}
|
||||||
|
);
|
||||||
return LearningPath.fromDTO(single(dtos));
|
return LearningPath.fromDTO(single(dtos));
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,8 @@ export class LearningPathNode {
|
||||||
public readonly language: Language,
|
public readonly language: Language,
|
||||||
public readonly transitions: {next: LearningPathNode, default: boolean}[],
|
public readonly transitions: {next: LearningPathNode, default: boolean}[],
|
||||||
public readonly createdAt: Date,
|
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.created_at),
|
||||||
new Date(dto.updatedAt),
|
new Date(dto.updatedAt),
|
||||||
|
dto.done
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,36 @@
|
||||||
import {Language} from "@/services/learning-content/language.ts";
|
import {Language} from "@/services/learning-content/language.ts";
|
||||||
import {getLearningPath} from "@/services/learning-content/learning-path-service.ts";
|
import {getLearningPath} from "@/services/learning-content/learning-path-service.ts";
|
||||||
import UsingRemoteResource from "@/components/UsingRemoteResource.vue";
|
import UsingRemoteResource from "@/components/UsingRemoteResource.vue";
|
||||||
import {type LearningPath} from "@/services/learning-content/learning-path.ts";
|
import {type LearningPath, LearningPathNode} from "@/services/learning-content/learning-path.ts";
|
||||||
import {computed, watch, watchEffect} from "vue";
|
import {computed, type ComputedRef, watch} from "vue";
|
||||||
import type {LearningObject} from "@/services/learning-content/learning-object.ts";
|
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 {loadResource, remoteResource, type SuccessState} from "@/services/api-client/remote-resource.ts";
|
||||||
import LearningObjectView from "@/views/learning-paths/LearningObjectView.vue";
|
import LearningObjectView from "@/views/learning-paths/LearningObjectView.vue";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps<{hruid: string, language: Language, learningObjectHruid?: string}>()
|
const props = defineProps<{hruid: string, language: Language, learningObjectHruid?: string}>()
|
||||||
|
|
||||||
|
interface QueryParams {
|
||||||
|
forStudent?: string,
|
||||||
|
forGroup?: string
|
||||||
|
}
|
||||||
|
|
||||||
const learningPathResource = remoteResource<LearningPath>();
|
const learningPathResource = remoteResource<LearningPath>();
|
||||||
watchEffect(() => {
|
watch([() => props.hruid, () => props.language, () => route.query.forStudent, () => route.query.forGroup], () => {
|
||||||
loadResource(learningPathResource, getLearningPath(props.hruid, props.language));
|
loadResource(
|
||||||
});
|
learningPathResource,
|
||||||
|
getLearningPath(
|
||||||
|
props.hruid,
|
||||||
|
props.language,
|
||||||
|
route.query as QueryParams
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}, {immediate: true});
|
||||||
|
|
||||||
const learningObjectListResource = remoteResource<LearningObject[]>();
|
const learningObjectListResource = remoteResource<LearningObject[]>();
|
||||||
watch(learningPathResource, () => {
|
watch(learningPathResource, () => {
|
||||||
|
@ -24,12 +40,36 @@
|
||||||
}
|
}
|
||||||
}, {immediate: true});
|
}, {immediate: true});
|
||||||
|
|
||||||
const currentNode = computed(() => {
|
const nodesList: ComputedRef<LearningPathNode[] | null> = computed(() => {
|
||||||
let currentHruid = props.learningObjectHruid;
|
|
||||||
if (learningPathResource.state.type === "success") {
|
if (learningPathResource.state.type === "success") {
|
||||||
return learningPathResource.state.data.nodesAsList.filter(it => it.learningobjectHruid === currentHruid)[0]
|
return learningPathResource.state.data.nodesAsList;
|
||||||
} else {
|
} 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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -62,9 +113,10 @@
|
||||||
>
|
>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
link
|
link
|
||||||
:to="node.key"
|
:to="{path: node.key, query: route.query}"
|
||||||
:title="node.title"
|
:title="node.title"
|
||||||
:active="node.key === props.learningObjectHruid"
|
: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"
|
v-for="node in learningObjects.data"
|
||||||
>
|
>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
|
@ -80,9 +132,31 @@
|
||||||
:version="currentNode.version"
|
:version="currentNode.version"
|
||||||
v-if="currentNode"
|
v-if="currentNode"
|
||||||
></learning-object-view>
|
></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>
|
</using-remote-resource>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.navigation-buttons-container {
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue