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"> | ||||
|     import {RemoteResource} from "@/services/api-client/remote-resource.ts"; | ||||
|     import {computed, type MaybeRefOrGetter} from "vue"; | ||||
|     import {computed} from "vue"; | ||||
|     import {useI18n} from "vue-i18n"; | ||||
|     import type {UseQueryReturnType} from "@tanstack/vue-query"; | ||||
| 
 | ||||
|  | @ -8,13 +7,12 @@ | |||
|         queryResult: UseQueryReturnType<T, Error> | ||||
|     }>() | ||||
| 
 | ||||
|     const { isLoading, isError, isSuccess, data, error } = props.queryResult; | ||||
| 
 | ||||
|     const { t } = useI18n(); | ||||
| 
 | ||||
|     const isLoading = computed(() => props.queryResult.isFetching); | ||||
|     const data = computed(() => props.queryResult.data); | ||||
|     const error = computed(() => props.queryResult.error); | ||||
|     const errorMessage = computed(() => { | ||||
|         let errorWithMessage = (error.value as {message: string}) || null; | ||||
|         let errorWithMessage = (error as {message: string}) || null; | ||||
|         return errorWithMessage?.message || JSON.stringify(errorWithMessage) | ||||
|     }); | ||||
| </script> | ||||
|  | @ -23,14 +21,14 @@ | |||
|     <div class="loading-div" v-if="isLoading"> | ||||
|         <v-progress-circular indeterminate></v-progress-circular> | ||||
|     </div> | ||||
|     <div v-if="error"> | ||||
|     <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="data" :data="data!"></slot> | ||||
|     <slot v-if="isSuccess && data" :data="data"></slot> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
|  |  | |||
|  | @ -1,41 +1,47 @@ | |||
| import { apiConfig } from "@/config.ts"; | ||||
| import apiClient from "@/services/api-client/api-client.ts"; | ||||
| import type {AxiosResponse, ResponseType} from "axios"; | ||||
| import {HttpErrorResponseException} from "@/exception/http-error-response-exception.ts"; | ||||
| 
 | ||||
| export abstract class BaseController { | ||||
|     protected baseUrl: string; | ||||
|     protected basePath: string; | ||||
| 
 | ||||
|     protected constructor(basePath: string) { | ||||
|         this.baseUrl = `${apiConfig.baseUrl}/${basePath}`; | ||||
|         this.basePath = basePath; | ||||
|     } | ||||
| 
 | ||||
|     private assertSuccessResponse(response: AxiosResponse<unknown, unknown>) { | ||||
|         if (response.status / 200 !== 2) { | ||||
|         if (response.status / 100 !== 2) { | ||||
|             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> { | ||||
|         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); | ||||
|         return response.data; | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|         return response.data; | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|         return response.data; | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
|         return response.data; | ||||
|     } | ||||
|  |  | |||
|  | @ -8,10 +8,10 @@ export class LearningObjectController extends BaseController { | |||
|     } | ||||
| 
 | ||||
|     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> { | ||||
|         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 type {LearningPathDTO} from "@/data-objects/learning-path.ts"; | ||||
| import type {Language} from "@/data-objects/language.ts"; | ||||
| import {single} from "@/utils/response-assertions.ts"; | ||||
| 
 | ||||
| export class LearningPathController extends BaseController { | ||||
|     constructor() { | ||||
|  | @ -18,6 +19,6 @@ export class LearningPathController extends BaseController { | |||
|             forGroup: options?.forGroup, | ||||
|             forStudent: options?.forStudent | ||||
|         }); | ||||
|         return dtos.map(dto => LearningPath.fromDTO(dto)) | ||||
|         return LearningPath.fromDTO(single(dtos)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| import type {AxiosResponse} from "axios"; | ||||
| 
 | ||||
| export class HttpErrorResponseException extends Error { | ||||
|     public statusCode: number; | ||||
|     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 = []; | ||||
|             for (let node of toValue(learningPath).nodesAsList) { | ||||
|                 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)), | ||||
|     }); | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ import UserClasses from "@/views/classes/UserClasses.vue"; | |||
| import UserAssignments from "@/views/classes/UserAssignments.vue"; | ||||
| import authState from "@/services/auth/auth-service.ts"; | ||||
| import LearningPathPage from "@/views/learning-paths/LearningPathPage.vue"; | ||||
| import path from "path"; | ||||
| import LearningPathSearchPage from "@/views/learning-paths/LearningPathSearchPage.vue"; | ||||
| import UserHomePage from "@/views/homepage/UserHomePage.vue"; | ||||
| import SingleTheme from "@/views/SingleTheme.vue"; | ||||
|  | @ -109,16 +108,15 @@ const router = createRouter({ | |||
|         }, | ||||
|         { | ||||
|             path: "/learningPath", | ||||
|             component: MenuBar, | ||||
|             children: [ | ||||
|                 { | ||||
|                     path: "/search", | ||||
|                     path: "search", | ||||
|                     name: "LearningPathSearchPage", | ||||
|                     component: LearningPathSearchPage, | ||||
|                     meta: { requiresAuth: true } | ||||
|                 }, | ||||
|                 { | ||||
|                     path: "/:hruid/:language", | ||||
|                     path: ":hruid/:language", | ||||
|                     name: "LearningPath", | ||||
|                     component: LearningPathPage, | ||||
|                     props: true, | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ import UsingQueryResult from "@/components/UsingQueryResult.vue"; | |||
| 
 | ||||
| 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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -27,10 +27,10 @@ | |||
| 
 | ||||
|     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(() => | ||||
|         (!learningPathQueryResult.isPending && !learningPathQueryResult.isError) ? learningPathQueryResult.data.value?.nodesAsList : null | ||||
|         learningPathQueryResult.isSuccess ? learningPathQueryResult.data.value?.nodesAsList : null | ||||
|     ); | ||||
| 
 | ||||
|     const currentNode = computed(() => { | ||||
|  | @ -107,7 +107,6 @@ | |||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|     <v-main> | ||||
|         <using-query-result | ||||
|             :query-result="learningPathQueryResult" | ||||
|             v-slot="learningPath: {data: LearningPath}" | ||||
|  | @ -125,7 +124,6 @@ | |||
|                     </template> | ||||
|                 </v-list-item> | ||||
|                 <v-divider></v-divider> | ||||
| 
 | ||||
|                 <div v-if="props.learningObjectHruid"> | ||||
|                     <using-query-result | ||||
|                         :query-result="learningObjectListQueryResult" | ||||
|  | @ -160,7 +158,6 @@ | |||
|                     <learning-path-search-field></learning-path-search-field> | ||||
|                 </div> | ||||
|             </div> | ||||
| 
 | ||||
|             <learning-object-view | ||||
|                 :hruid="currentNode.learningobjectHruid" | ||||
|                 :language="currentNode.language" | ||||
|  | @ -186,7 +183,6 @@ | |||
|                 </v-btn> | ||||
|             </div> | ||||
|         </using-query-result> | ||||
|     </v-main> | ||||
| </template> | ||||
| 
 | ||||
| <style scoped> | ||||
|  |  | |||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger