fix(backend): Diverse bugfixes omtrent LearningPathPage
This commit is contained in:
parent
27b9cdf833
commit
12b9f31f5f
9 changed files with 34 additions and 33 deletions
|
@ -1,6 +1,5 @@
|
||||||
<script setup lang="ts" generic="T">
|
<script setup lang="ts" generic="T">
|
||||||
import {RemoteResource} from "@/services/api-client/remote-resource.ts";
|
import {computed} from "vue";
|
||||||
import {computed, type MaybeRefOrGetter} from "vue";
|
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
import type {UseQueryReturnType} from "@tanstack/vue-query";
|
import type {UseQueryReturnType} from "@tanstack/vue-query";
|
||||||
|
|
||||||
|
@ -8,13 +7,12 @@
|
||||||
queryResult: UseQueryReturnType<T, Error>
|
queryResult: UseQueryReturnType<T, Error>
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { isLoading, isError, isSuccess, data, error } = props.queryResult;
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const isLoading = computed(() => props.queryResult.isFetching);
|
|
||||||
const data = computed(() => props.queryResult.data);
|
|
||||||
const error = computed(() => props.queryResult.error);
|
|
||||||
const errorMessage = computed(() => {
|
const errorMessage = computed(() => {
|
||||||
let errorWithMessage = (error.value as {message: string}) || null;
|
let errorWithMessage = (error as {message: string}) || null;
|
||||||
return errorWithMessage?.message || JSON.stringify(errorWithMessage)
|
return errorWithMessage?.message || JSON.stringify(errorWithMessage)
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -23,14 +21,14 @@
|
||||||
<div class="loading-div" v-if="isLoading">
|
<div class="loading-div" v-if="isLoading">
|
||||||
<v-progress-circular indeterminate></v-progress-circular>
|
<v-progress-circular indeterminate></v-progress-circular>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="error">
|
<div v-if="isError">
|
||||||
<v-empty-state
|
<v-empty-state
|
||||||
icon="mdi-alert-circle-outline"
|
icon="mdi-alert-circle-outline"
|
||||||
:text="errorMessage"
|
:text="errorMessage"
|
||||||
:title="t('error_title')"
|
:title="t('error_title')"
|
||||||
></v-empty-state>
|
></v-empty-state>
|
||||||
</div>
|
</div>
|
||||||
<slot v-if="data" :data="data!"></slot>
|
<slot v-if="isSuccess && data" :data="data"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -1,41 +1,47 @@
|
||||||
import { apiConfig } from "@/config.ts";
|
|
||||||
import apiClient from "@/services/api-client/api-client.ts";
|
import apiClient from "@/services/api-client/api-client.ts";
|
||||||
import type {AxiosResponse, ResponseType} from "axios";
|
import type {AxiosResponse, ResponseType} from "axios";
|
||||||
import {HttpErrorResponseException} from "@/exception/http-error-response-exception.ts";
|
import {HttpErrorResponseException} from "@/exception/http-error-response-exception.ts";
|
||||||
|
|
||||||
export abstract class BaseController {
|
export abstract class BaseController {
|
||||||
protected baseUrl: string;
|
protected basePath: string;
|
||||||
|
|
||||||
protected constructor(basePath: string) {
|
protected constructor(basePath: string) {
|
||||||
this.baseUrl = `${apiConfig.baseUrl}/${basePath}`;
|
this.basePath = basePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertSuccessResponse(response: AxiosResponse<unknown, unknown>) {
|
private assertSuccessResponse(response: AxiosResponse<unknown, unknown>) {
|
||||||
if (response.status / 200 !== 2) {
|
if (response.status / 100 !== 2) {
|
||||||
throw new HttpErrorResponseException(response);
|
throw new HttpErrorResponseException(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private absolutePathFor(path: string) {
|
||||||
|
return "/" + this.basePath + path;
|
||||||
|
}
|
||||||
|
|
||||||
protected async get<T>(path: string, queryParams?: Record<string, any>, responseType?: ResponseType): Promise<T> {
|
protected async get<T>(path: string, queryParams?: Record<string, any>, responseType?: ResponseType): Promise<T> {
|
||||||
let response = await apiClient.get<T>(path, {params: queryParams, responseType});
|
let response = await apiClient.get<T>(
|
||||||
|
this.absolutePathFor(path),
|
||||||
|
{params: queryParams, responseType}
|
||||||
|
);
|
||||||
this.assertSuccessResponse(response);
|
this.assertSuccessResponse(response);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async post<T>(path: string, body: unknown): Promise<T> {
|
protected async post<T>(path: string, body: unknown): Promise<T> {
|
||||||
let response = await apiClient.post<T>(path, body);
|
let response = await apiClient.post<T>(this.absolutePathFor(path), body);
|
||||||
this.assertSuccessResponse(response);
|
this.assertSuccessResponse(response);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async delete<T>(path: string): Promise<T> {
|
protected async delete<T>(path: string): Promise<T> {
|
||||||
let response = await apiClient.delete<T>(path)
|
let response = await apiClient.delete<T>(this.absolutePathFor(path))
|
||||||
this.assertSuccessResponse(response);
|
this.assertSuccessResponse(response);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async put<T>(path: string, body: unknown): Promise<T> {
|
protected async put<T>(path: string, body: unknown): Promise<T> {
|
||||||
let response = await apiClient.put<T>(path, body);
|
let response = await apiClient.put<T>(this.absolutePathFor(path), body);
|
||||||
this.assertSuccessResponse(response);
|
this.assertSuccessResponse(response);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,10 @@ export class LearningObjectController extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMetadata(hruid: string, language: Language, version: number): Promise<LearningObject> {
|
async getMetadata(hruid: string, language: Language, version: number): Promise<LearningObject> {
|
||||||
return this.get<LearningObject>(`/learningObject/${hruid}`, {language, version});
|
return this.get<LearningObject>(`/${hruid}`, {language, version});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getHTML(hruid: string, language: Language, version: number): Promise<Document> {
|
async getHTML(hruid: string, language: Language, version: number): Promise<Document> {
|
||||||
return this.get<Document>(`/learningObject/${hruid}/html`, {language, version}, "document");
|
return this.get<Document>(`/${hruid}/html`, {language, version}, "document");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {BaseController} from "@/controllers/base-controller.ts";
|
||||||
import {LearningPath} from "@/data-objects/learning-path.ts";
|
import {LearningPath} from "@/data-objects/learning-path.ts";
|
||||||
import type {LearningPathDTO} from "@/data-objects/learning-path.ts";
|
import type {LearningPathDTO} from "@/data-objects/learning-path.ts";
|
||||||
import type {Language} from "@/data-objects/language.ts";
|
import type {Language} from "@/data-objects/language.ts";
|
||||||
|
import {single} from "@/utils/response-assertions.ts";
|
||||||
|
|
||||||
export class LearningPathController extends BaseController {
|
export class LearningPathController extends BaseController {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -18,6 +19,6 @@ export class LearningPathController extends BaseController {
|
||||||
forGroup: options?.forGroup,
|
forGroup: options?.forGroup,
|
||||||
forStudent: options?.forStudent
|
forStudent: options?.forStudent
|
||||||
});
|
});
|
||||||
return dtos.map(dto => LearningPath.fromDTO(dto))
|
return LearningPath.fromDTO(single(dtos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import type {AxiosResponse} from "axios";
|
import type {AxiosResponse} from "axios";
|
||||||
|
|
||||||
export class HttpErrorResponseException extends Error {
|
export class HttpErrorResponseException extends Error {
|
||||||
|
public statusCode: number;
|
||||||
constructor(public response: AxiosResponse<unknown, unknown>) {
|
constructor(public response: AxiosResponse<unknown, unknown>) {
|
||||||
super(response.statusText);
|
super((response.data as {message: string})?.message || JSON.stringify(response.data));
|
||||||
|
this.statusCode = response.status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,10 +47,10 @@ export function useLearningObjectListForPathQuery(
|
||||||
let learningObjects = [];
|
let learningObjects = [];
|
||||||
for (let node of toValue(learningPath).nodesAsList) {
|
for (let node of toValue(learningPath).nodesAsList) {
|
||||||
learningObjects.push(
|
learningObjects.push(
|
||||||
learningObjectController.getHTML(node.learningobjectHruid, node.language, node.version)
|
learningObjectController.getMetadata(node.learningobjectHruid, node.language, node.version)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return learningObjects;
|
return Promise.all(learningObjects);
|
||||||
},
|
},
|
||||||
enabled: () => Boolean(toValue(learningPath)),
|
enabled: () => Boolean(toValue(learningPath)),
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,6 @@ import UserClasses from "@/views/classes/UserClasses.vue";
|
||||||
import UserAssignments from "@/views/classes/UserAssignments.vue";
|
import UserAssignments from "@/views/classes/UserAssignments.vue";
|
||||||
import authState from "@/services/auth/auth-service.ts";
|
import authState from "@/services/auth/auth-service.ts";
|
||||||
import LearningPathPage from "@/views/learning-paths/LearningPathPage.vue";
|
import LearningPathPage from "@/views/learning-paths/LearningPathPage.vue";
|
||||||
import path from "path";
|
|
||||||
import LearningPathSearchPage from "@/views/learning-paths/LearningPathSearchPage.vue";
|
import LearningPathSearchPage from "@/views/learning-paths/LearningPathSearchPage.vue";
|
||||||
import UserHomePage from "@/views/homepage/UserHomePage.vue";
|
import UserHomePage from "@/views/homepage/UserHomePage.vue";
|
||||||
import SingleTheme from "@/views/SingleTheme.vue";
|
import SingleTheme from "@/views/SingleTheme.vue";
|
||||||
|
@ -109,16 +108,15 @@ const router = createRouter({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/learningPath",
|
path: "/learningPath",
|
||||||
component: MenuBar,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/search",
|
path: "search",
|
||||||
name: "LearningPathSearchPage",
|
name: "LearningPathSearchPage",
|
||||||
component: LearningPathSearchPage,
|
component: LearningPathSearchPage,
|
||||||
meta: { requiresAuth: true }
|
meta: { requiresAuth: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/:hruid/:language",
|
path: ":hruid/:language",
|
||||||
name: "LearningPath",
|
name: "LearningPath",
|
||||||
component: LearningPathPage,
|
component: LearningPathPage,
|
||||||
props: true,
|
props: true,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import UsingQueryResult from "@/components/UsingQueryResult.vue";
|
||||||
|
|
||||||
const props = defineProps<{hruid: string, language: Language, version: number}>()
|
const props = defineProps<{hruid: string, language: Language, version: number}>()
|
||||||
|
|
||||||
const learningObjectHtmlQueryResult: UseQueryReturnType<Document, Error> = useLearningObjectHTMLQuery(props.hruid, props.language, props.version);
|
const learningObjectHtmlQueryResult: UseQueryReturnType<Document, Error> = useLearningObjectHTMLQuery(() => props.hruid, () => props.language, () => props.version);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,10 @@
|
||||||
|
|
||||||
const learningPathQueryResult = useGetLearningPathQuery(props.hruid, props.language, typedQuery.value);
|
const learningPathQueryResult = useGetLearningPathQuery(props.hruid, props.language, typedQuery.value);
|
||||||
|
|
||||||
const learningObjectListQueryResult = useLearningObjectListForPathQuery(learningPathQueryResult.data.value);
|
const learningObjectListQueryResult = useLearningObjectListForPathQuery(learningPathQueryResult.data);
|
||||||
|
|
||||||
const nodesList: ComputedRef<LearningPathNode[] | null> = computed(() =>
|
const nodesList: ComputedRef<LearningPathNode[] | null> = computed(() =>
|
||||||
(!learningPathQueryResult.isPending && !learningPathQueryResult.isError) ? learningPathQueryResult.data.value?.nodesAsList : null
|
learningPathQueryResult.isSuccess ? learningPathQueryResult.data.value?.nodesAsList : null
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentNode = computed(() => {
|
const currentNode = computed(() => {
|
||||||
|
@ -107,7 +107,6 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-main>
|
|
||||||
<using-query-result
|
<using-query-result
|
||||||
:query-result="learningPathQueryResult"
|
:query-result="learningPathQueryResult"
|
||||||
v-slot="learningPath: {data: LearningPath}"
|
v-slot="learningPath: {data: LearningPath}"
|
||||||
|
@ -125,7 +124,6 @@
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
|
|
||||||
<div v-if="props.learningObjectHruid">
|
<div v-if="props.learningObjectHruid">
|
||||||
<using-query-result
|
<using-query-result
|
||||||
:query-result="learningObjectListQueryResult"
|
:query-result="learningObjectListQueryResult"
|
||||||
|
@ -160,7 +158,6 @@
|
||||||
<learning-path-search-field></learning-path-search-field>
|
<learning-path-search-field></learning-path-search-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<learning-object-view
|
<learning-object-view
|
||||||
:hruid="currentNode.learningobjectHruid"
|
:hruid="currentNode.learningobjectHruid"
|
||||||
:language="currentNode.language"
|
:language="currentNode.language"
|
||||||
|
@ -186,7 +183,6 @@
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
</using-query-result>
|
</using-query-result>
|
||||||
</v-main>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue