fix(frontend): Linting errors/warnings opgelost
This commit is contained in:
		
							parent
							
								
									b2e6b33716
								
							
						
					
					
						commit
						4d98be78c1
					
				
					 26 changed files with 272 additions and 258 deletions
				
			
		|  | @ -10,12 +10,12 @@ | ||||||
| 
 | 
 | ||||||
|     const query = computed({ |     const query = computed({ | ||||||
|         get: () => route.query.query as string | null, |         get: () => route.query.query as string | null, | ||||||
|         set: (newValue) => router.push({path: SEARCH_PATH, query: {query: newValue}}) |         set: async (newValue) => router.push({path: SEARCH_PATH, query: {query: newValue}}) | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const queryInput = ref(query.value); |     const queryInput = ref(query.value); | ||||||
| 
 | 
 | ||||||
|     function search() { |     function search(): void { | ||||||
|         query.value = queryInput.value; |         query.value = queryInput.value; | ||||||
|     } |     } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| 
 | 
 | ||||||
| import {convertBase64ToImageSrc} from "@/utils/base64ToImage.ts"; | import {convertBase64ToImageSrc} from "@/utils/base64ToImage.ts"; | ||||||
| import type {LearningPath} from "@/data-objects/learning-path.ts"; | import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
| import {useI18n} from "vue-i18n"; | import {useI18n} from "vue-i18n"; | ||||||
| 
 | 
 | ||||||
| const { t } = useI18n(); | const { t } = useI18n(); | ||||||
|  | @ -15,6 +15,7 @@ const props = defineProps<{learningPaths: LearningPath[]}>(); | ||||||
|             class="learning-path-card" |             class="learning-path-card" | ||||||
|             link |             link | ||||||
|             :to="`/learningPath/${learningPath.hruid}/${learningPath.language}/${learningPath.startNode.learningobjectHruid}`" |             :to="`/learningPath/${learningPath.hruid}/${learningPath.language}/${learningPath.startNode.learningobjectHruid}`" | ||||||
|  |             :key="[learningPath.hruid, learningPath.language]" | ||||||
|             v-for="learningPath in props.learningPaths" |             v-for="learningPath in props.learningPaths" | ||||||
|         > |         > | ||||||
|             <v-img |             <v-img | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|     const errorMessage = computed(() => { |     const errorMessage = computed(() => { | ||||||
|         let errorWithMessage = (error.value as {message: string}) || null; |         const errorWithMessage = (error.value as {message: string}) || null; | ||||||
|         return errorWithMessage?.message || JSON.stringify(errorWithMessage) |         return errorWithMessage?.message || JSON.stringify(errorWithMessage) | ||||||
|     }); |     }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -9,40 +9,42 @@ export abstract class BaseController { | ||||||
|         this.basePath = basePath; |         this.basePath = basePath; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private assertSuccessResponse(response: AxiosResponse<unknown, unknown>) { |     private static assertSuccessResponse(response: AxiosResponse<unknown, unknown>): void { | ||||||
|         if (response.status / 100 !== 2) { |         if (response.status / 100 !== 2) { | ||||||
|             throw new HttpErrorResponseException(response); |             throw new HttpErrorResponseException(response); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private absolutePathFor(path: string) { |     protected async get<T>(path: string, queryParams?: QueryParams, responseType?: ResponseType): Promise<T> { | ||||||
|         return "/" + this.basePath + path; |         const response = await apiClient.get<T>( | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected async get<T>(path: string, queryParams?: Record<string, any>, responseType?: ResponseType): Promise<T> { |  | ||||||
|         let response = await apiClient.get<T>( |  | ||||||
|             this.absolutePathFor(path), |             this.absolutePathFor(path), | ||||||
|             {params: queryParams, responseType} |             {params: queryParams, responseType} | ||||||
|         ); |         ); | ||||||
|         this.assertSuccessResponse(response); |         BaseController.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>(this.absolutePathFor(path), body); |         const response = await apiClient.post<T>(this.absolutePathFor(path), body); | ||||||
|         this.assertSuccessResponse(response); |         BaseController.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>(this.absolutePathFor(path)) |         const response = await apiClient.delete<T>(this.absolutePathFor(path)) | ||||||
|         this.assertSuccessResponse(response); |         BaseController.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>(this.absolutePathFor(path), body); |         const response = await apiClient.put<T>(this.absolutePathFor(path), body); | ||||||
|         this.assertSuccessResponse(response); |         BaseController.assertSuccessResponse(response); | ||||||
|         return response.data; |         return response.data; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     private absolutePathFor(path: string): string { | ||||||
|  |         return "/" + this.basePath + path; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | type QueryParams = Record<string, string | number | boolean | undefined>; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import {BaseController} from "@/controllers/base-controller.ts"; | import {BaseController} from "@/controllers/base-controller.ts"; | ||||||
| import type {Language} from "@/data-objects/language.ts"; | import type {Language} from "@/data-objects/language.ts"; | ||||||
| import type {LearningObject} from "@/data-objects/learning-object.ts"; | import type {LearningObject} from "@/data-objects/learning-objects/learning-object.ts"; | ||||||
| 
 | 
 | ||||||
| export class LearningObjectController extends BaseController { | export class LearningObjectController extends BaseController { | ||||||
|     constructor() { |     constructor() { | ||||||
|  |  | ||||||
|  | @ -1,19 +1,19 @@ | ||||||
| import {BaseController} from "@/controllers/base-controller.ts"; | import {BaseController} from "@/controllers/base-controller.ts"; | ||||||
| import {LearningPath} from "@/data-objects/learning-path.ts"; | import {LearningPath} from "@/data-objects/learning-paths/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"; | import {single} from "@/utils/response-assertions.ts"; | ||||||
|  | import type {LearningPathDTO} from "@/data-objects/learning-paths/learning-path-dto.ts"; | ||||||
| 
 | 
 | ||||||
| export class LearningPathController extends BaseController { | export class LearningPathController extends BaseController { | ||||||
|     constructor() { |     constructor() { | ||||||
|         super("learningPath"); |         super("learningPath"); | ||||||
|     } |     } | ||||||
|     async search(query: string) { |     async search(query: string): Promise<LearningPath[]> { | ||||||
|         let dtos = await this.get<LearningPathDTO[]>("/", {search: query}); |         const dtos = await this.get<LearningPathDTO[]>("/", {search: query}); | ||||||
|         return dtos.map(dto => LearningPath.fromDTO(dto)); |         return dtos.map(dto => LearningPath.fromDTO(dto)); | ||||||
|     } |     } | ||||||
|     async getBy(hruid: string, language: Language, options?: {forGroup?: string, forStudent?: string}) { |     async getBy(hruid: string, language: Language, options?: {forGroup?: string, forStudent?: string}): Promise<LearningPath> { | ||||||
|         let dtos = await this.get<LearningPathDTO[]>("/", { |         const dtos = await this.get<LearningPathDTO[]>("/", { | ||||||
|             hruid, |             hruid, | ||||||
|             language, |             language, | ||||||
|             forGroup: options?.forGroup, |             forGroup: options?.forGroup, | ||||||
|  | @ -21,8 +21,8 @@ export class LearningPathController extends BaseController { | ||||||
|         }); |         }); | ||||||
|         return LearningPath.fromDTO(single(dtos)); |         return LearningPath.fromDTO(single(dtos)); | ||||||
|     } |     } | ||||||
|     async getAllByTheme(theme: string) { |     async getAllByTheme(theme: string): Promise<LearningPath[]> { | ||||||
|         let dtos = await this.get<LearningPathDTO[]>("/", {theme}); |         const dtos = await this.get<LearningPathDTO[]>("/", {theme}); | ||||||
|         return dtos.map(dto => LearningPath.fromDTO(dto)); |         return dtos.map(dto => LearningPath.fromDTO(dto)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | export interface EducationalGoal { | ||||||
|  |     source: string; | ||||||
|  |     id: string; | ||||||
|  | } | ||||||
|  | @ -1,14 +1,6 @@ | ||||||
| import type {Language} from "@/data-objects/language.ts"; | import type {Language} from "@/data-objects/language.ts"; | ||||||
| 
 | import type {ReturnValue} from "@/data-objects/learning-objects/return-value.ts"; | ||||||
| export interface EducationalGoal { | import type {EducationalGoal} from "@/data-objects/learning-objects/educational-goal.ts"; | ||||||
|     source: string; |  | ||||||
|     id: string; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export interface ReturnValue { |  | ||||||
|     callback_url: string; |  | ||||||
|     callback_schema: Record<string, any>; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export interface LearningObject { | export interface LearningObject { | ||||||
|     key: string; |     key: string; | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | export interface ReturnValue { | ||||||
|  |     callback_url: string; | ||||||
|  |     callback_schema: Record<string, unknown>; | ||||||
|  | } | ||||||
|  | @ -1,128 +0,0 @@ | ||||||
| import type {Language} from "@/data-objects/language.ts"; |  | ||||||
| 
 |  | ||||||
| export interface LearningPathDTO { |  | ||||||
|     language: string; |  | ||||||
|     hruid: string; |  | ||||||
|     title: string; |  | ||||||
|     description: string; |  | ||||||
|     image?: string; // Image might be missing, so it's optional
 |  | ||||||
|     num_nodes: number; |  | ||||||
|     num_nodes_left: number; |  | ||||||
|     nodes: LearningPathNodeDTO[]; |  | ||||||
|     keywords: string; |  | ||||||
|     target_ages: number[]; |  | ||||||
|     min_age: number; |  | ||||||
|     max_age: number; |  | ||||||
|     __order: number; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface LearningPathNodeDTO { |  | ||||||
|     _id: string; |  | ||||||
|     learningobject_hruid: string; |  | ||||||
|     version: number; |  | ||||||
|     language: Language; |  | ||||||
|     start_node?: boolean; |  | ||||||
|     transitions: LearningPathTransitionDTO[]; |  | ||||||
|     created_at: string; |  | ||||||
|     updatedAt: string; |  | ||||||
|     done?: boolean; // True if a submission exists for this node by the user for whom the learning path is customized.
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| interface LearningPathTransitionDTO { |  | ||||||
|     default: boolean; |  | ||||||
|     _id: string; |  | ||||||
|     next: { |  | ||||||
|         _id: string; |  | ||||||
|         hruid: string; |  | ||||||
|         version: number; |  | ||||||
|         language: string; |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class LearningPathNode { |  | ||||||
| 
 |  | ||||||
|     constructor( |  | ||||||
|         public readonly learningobjectHruid: string, |  | ||||||
|         public readonly version: number, |  | ||||||
|         public readonly language: Language, |  | ||||||
|         public readonly transitions: {next: LearningPathNode, default: boolean}[], |  | ||||||
|         public readonly createdAt: Date, |  | ||||||
|         public readonly updatedAt: Date, |  | ||||||
|         public readonly done: boolean = false |  | ||||||
|     ) { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     static fromDTOAndOtherNodes(dto: LearningPathNodeDTO, otherNodes: LearningPathNodeDTO[]): LearningPathNode { |  | ||||||
|         return new LearningPathNode( |  | ||||||
|             dto.learningobject_hruid, |  | ||||||
|             dto.version, |  | ||||||
|             dto.language, |  | ||||||
|             dto.transitions.map(transDto => { |  | ||||||
|                 let nextNodeDto = otherNodes.filter(it => |  | ||||||
|                     it.learningobject_hruid === transDto.next.hruid |  | ||||||
|                     && it.language === transDto.next.language |  | ||||||
|                     && it.version === transDto.next.version |  | ||||||
|                 ); |  | ||||||
|                 if (nextNodeDto.length !== 1) { |  | ||||||
|                     throw new Error(`Invalid learning path! There is a transition to node` |  | ||||||
|                         + `${transDto.next.hruid}/${transDto.next.language}/${transDto.next.version}, but there are` |  | ||||||
|                         + `${nextNodeDto.length} such nodes.`); |  | ||||||
|                 } |  | ||||||
|                 return { |  | ||||||
|                     next: LearningPathNode.fromDTOAndOtherNodes(nextNodeDto[0], otherNodes), |  | ||||||
|                     default: transDto.default |  | ||||||
|                 } |  | ||||||
|             }), |  | ||||||
|             new Date(dto.created_at), |  | ||||||
|             new Date(dto.updatedAt), |  | ||||||
|             dto.done |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class LearningPath { |  | ||||||
|     constructor( |  | ||||||
|         public readonly language: string, |  | ||||||
|         public readonly hruid: string, |  | ||||||
|         public readonly title: string, |  | ||||||
|         public readonly description: string, |  | ||||||
|         public readonly amountOfNodes: number, |  | ||||||
|         public readonly amountOfNodesLeft: number, |  | ||||||
|         public readonly keywords: string[], |  | ||||||
|         public readonly targetAges: {min: number; max: number}, |  | ||||||
|         public readonly startNode: LearningPathNode, |  | ||||||
|         public readonly image?: string // Image might be missing, so it's optional
 |  | ||||||
|     ) { |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public get nodesAsList(): LearningPathNode[] { |  | ||||||
|         let list: LearningPathNode[] = []; |  | ||||||
|         let currentNode = this.startNode; |  | ||||||
|         while (currentNode) { |  | ||||||
|             list.push(currentNode); |  | ||||||
|             currentNode = currentNode.transitions.filter(it => it.default)[0]?.next |  | ||||||
|                             || currentNode.transitions[0]?.next; |  | ||||||
|         } |  | ||||||
|         return list; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     static fromDTO(dto: LearningPathDTO): LearningPath { |  | ||||||
|         let startNodeDto = dto.nodes.filter(it => it.start_node === true); |  | ||||||
|         if (startNodeDto.length !== 1) { |  | ||||||
|             throw new Error(`Invalid learning path: ${dto.hruid}/${dto.language}!
 |  | ||||||
|                                 Expected precisely one start node, but there were ${startNodeDto.length}.`);
 |  | ||||||
|         } |  | ||||||
|         return new LearningPath( |  | ||||||
|             dto.language, |  | ||||||
|             dto.hruid, |  | ||||||
|             dto.title, |  | ||||||
|             dto.description, |  | ||||||
|             dto.num_nodes, |  | ||||||
|             dto.num_nodes_left, |  | ||||||
|             dto.keywords.split(' '), |  | ||||||
|             {min: dto.min_age, max: dto.max_age}, |  | ||||||
|             LearningPathNode.fromDTOAndOtherNodes(startNodeDto[0], dto.nodes), |  | ||||||
|             dto.image |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,17 @@ | ||||||
|  | import type {LearningPathNodeDTO} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
|  | 
 | ||||||
|  | export interface LearningPathDTO { | ||||||
|  |     language: string; | ||||||
|  |     hruid: string; | ||||||
|  |     title: string; | ||||||
|  |     description: string; | ||||||
|  |     image?: string; // Image might be missing, so it's optional
 | ||||||
|  |     num_nodes: number; | ||||||
|  |     num_nodes_left: number; | ||||||
|  |     nodes: LearningPathNodeDTO[]; | ||||||
|  |     keywords: string; | ||||||
|  |     target_ages: number[]; | ||||||
|  |     min_age: number; | ||||||
|  |     max_age: number; | ||||||
|  |     __order: number; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,57 @@ | ||||||
|  | import type {Language} from "@/data-objects/language.ts"; | ||||||
|  | import type {LearningPathNodeDTO} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
|  | 
 | ||||||
|  | export class LearningPathNode { | ||||||
|  |     public readonly learningobjectHruid: string; | ||||||
|  |     public readonly version: number; | ||||||
|  |     public readonly language: Language; | ||||||
|  |     public readonly transitions: { next: LearningPathNode, default: boolean }[]; | ||||||
|  |     public readonly createdAt: Date; | ||||||
|  |     public readonly updatedAt: Date; | ||||||
|  |     public readonly done: boolean; | ||||||
|  | 
 | ||||||
|  |     constructor(options: { | ||||||
|  |         learningobjectHruid: string, | ||||||
|  |         version: number, | ||||||
|  |         language: Language, | ||||||
|  |         transitions: { next: LearningPathNode, default: boolean }[], | ||||||
|  |         createdAt: Date, | ||||||
|  |         updatedAt: Date, | ||||||
|  |         done?: boolean | ||||||
|  |     }) { | ||||||
|  |         this.learningobjectHruid = options.learningobjectHruid; | ||||||
|  |         this.version = options.version; | ||||||
|  |         this.language = options.language; | ||||||
|  |         this.transitions = options.transitions; | ||||||
|  |         this.createdAt = options.createdAt; | ||||||
|  |         this.updatedAt = options.updatedAt; | ||||||
|  |         this.done = options.done || false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static fromDTOAndOtherNodes(dto: LearningPathNodeDTO, otherNodes: LearningPathNodeDTO[]): LearningPathNode { | ||||||
|  |         return new LearningPathNode({ | ||||||
|  |             learningobjectHruid: dto.learningobject_hruid, | ||||||
|  |             version: dto.version, | ||||||
|  |             language: dto.language, | ||||||
|  |             transitions: dto.transitions.map(transDto => { | ||||||
|  |                 const nextNodeDto = otherNodes.filter(it => | ||||||
|  |                     it.learningobject_hruid === transDto.next.hruid | ||||||
|  |                     && it.language === transDto.next.language | ||||||
|  |                     && it.version === transDto.next.version | ||||||
|  |                 ); | ||||||
|  |                 if (nextNodeDto.length !== 1) { | ||||||
|  |                     throw new Error(`Invalid learning path! There is a transition to node` | ||||||
|  |                         + `${transDto.next.hruid}/${transDto.next.language}/${transDto.next.version}, but there are` | ||||||
|  |                         + `${nextNodeDto.length} such nodes.`); | ||||||
|  |                 } | ||||||
|  |                 return { | ||||||
|  |                     next: LearningPathNode.fromDTOAndOtherNodes(nextNodeDto[0], otherNodes), | ||||||
|  |                     default: transDto.default | ||||||
|  |                 } | ||||||
|  |             }), | ||||||
|  |             createdAt: new Date(dto.created_at), | ||||||
|  |             updatedAt: new Date(dto.updatedAt), | ||||||
|  |             done: dto.done | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								frontend/src/data-objects/learning-paths/learning-path.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								frontend/src/data-objects/learning-paths/learning-path.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | ||||||
|  | import type {Language} from "@/data-objects/language.ts"; | ||||||
|  | import {LearningPathNode} from "@/data-objects/learning-paths/learning-path-node.ts"; | ||||||
|  | import type {LearningPathDTO} from "@/data-objects/learning-paths/learning-path-dto.ts"; | ||||||
|  | 
 | ||||||
|  | export interface LearningPathNodeDTO { | ||||||
|  |     _id: string; | ||||||
|  |     learningobject_hruid: string; | ||||||
|  |     version: number; | ||||||
|  |     language: Language; | ||||||
|  |     start_node?: boolean; | ||||||
|  |     transitions: LearningPathTransitionDTO[]; | ||||||
|  |     created_at: string; | ||||||
|  |     updatedAt: string; | ||||||
|  |     done?: boolean; // True if a submission exists for this node by the user for whom the learning path is customized.
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface LearningPathTransitionDTO { | ||||||
|  |     default: boolean; | ||||||
|  |     _id: string; | ||||||
|  |     next: { | ||||||
|  |         _id: string; | ||||||
|  |         hruid: string; | ||||||
|  |         version: number; | ||||||
|  |         language: string; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class LearningPath { | ||||||
|  |     public readonly language: string; | ||||||
|  |     public readonly hruid: string; | ||||||
|  |     public readonly title: string; | ||||||
|  |     public readonly description: string; | ||||||
|  |     public readonly amountOfNodes: number; | ||||||
|  |     public readonly amountOfNodesLeft: number; | ||||||
|  |     public readonly keywords: string[]; | ||||||
|  |     public readonly targetAges: {min: number; max: number}; | ||||||
|  |     public readonly startNode: LearningPathNode; | ||||||
|  |     public readonly image?: string; // Image might be missing, so it's optional
 | ||||||
|  | 
 | ||||||
|  |     constructor(options: { | ||||||
|  |         language: string, | ||||||
|  |         hruid: string, | ||||||
|  |         title: string, | ||||||
|  |         description: string, | ||||||
|  |         amountOfNodes: number, | ||||||
|  |         amountOfNodesLeft: number, | ||||||
|  |         keywords: string[], | ||||||
|  |         targetAges: {min: number; max: number}, | ||||||
|  |         startNode: LearningPathNode, | ||||||
|  |         image?: string // Image might be missing, so it's optional
 | ||||||
|  |     }) { | ||||||
|  |         this.language = options.language; | ||||||
|  |         this.hruid = options.hruid; | ||||||
|  |         this.title = options.title; | ||||||
|  |         this.description = options.description; | ||||||
|  |         this.amountOfNodes = options.amountOfNodes; | ||||||
|  |         this.amountOfNodesLeft = options.amountOfNodesLeft; | ||||||
|  |         this.keywords = options.keywords; | ||||||
|  |         this.targetAges = options.targetAges; | ||||||
|  |         this.startNode = options.startNode; | ||||||
|  |         this.image = options.image; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public get nodesAsList(): LearningPathNode[] { | ||||||
|  |         const list: LearningPathNode[] = []; | ||||||
|  |         let currentNode = this.startNode; | ||||||
|  |         while (currentNode) { | ||||||
|  |             list.push(currentNode); | ||||||
|  |             currentNode = currentNode.transitions.find(it => it.default)?.next | ||||||
|  |                             || currentNode.transitions[0]?.next; | ||||||
|  |         } | ||||||
|  |         return list; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static fromDTO(dto: LearningPathDTO): LearningPath { | ||||||
|  |         const startNodeDto = dto.nodes.filter(it => it.start_node === true); | ||||||
|  |         if (startNodeDto.length !== 1) { | ||||||
|  |             throw new Error(`Invalid learning path: ${dto.hruid}/${dto.language}!
 | ||||||
|  |                                 Expected precisely one start node, but there were ${startNodeDto.length}.`);
 | ||||||
|  |         } | ||||||
|  |         return new LearningPath({ | ||||||
|  |             language: dto.language, | ||||||
|  |             hruid: dto.hruid, | ||||||
|  |             title: dto.title, | ||||||
|  |             description: dto.description, | ||||||
|  |             amountOfNodes: dto.num_nodes, | ||||||
|  |             amountOfNodesLeft: dto.num_nodes_left, | ||||||
|  |             keywords: dto.keywords.split(' '), | ||||||
|  |             targetAges: {min: dto.min_age, max: dto.max_age}, | ||||||
|  |             startNode: LearningPathNode.fromDTOAndOtherNodes(startNodeDto[0], dto.nodes), | ||||||
|  |             image: dto.image | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								frontend/src/exception/invalid-response-exception.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								frontend/src/exception/invalid-response-exception.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | export class InvalidResponseException extends Error { | ||||||
|  |     constructor(message: string) { | ||||||
|  |         super(message); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								frontend/src/exception/not-found-exception.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								frontend/src/exception/not-found-exception.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | export class NotFoundException extends Error { | ||||||
|  |     constructor(message: string) { | ||||||
|  |         super(message); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -2,8 +2,8 @@ import {type MaybeRefOrGetter, toValue} from "vue"; | ||||||
| import type {Language} from "@/data-objects/language.ts"; | import type {Language} from "@/data-objects/language.ts"; | ||||||
| import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query"; | import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query"; | ||||||
| import {getLearningObjectController} from "@/controllers/controllers.ts"; | import {getLearningObjectController} from "@/controllers/controllers.ts"; | ||||||
| import type {LearningObject} from "@/data-objects/learning-object.ts"; | import type {LearningObject} from "@/data-objects/learning-objects/learning-object.ts"; | ||||||
| import type {LearningPath} from "@/data-objects/learning-path.ts"; | import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
| 
 | 
 | ||||||
| const LEARNING_OBJECT_KEY = "learningObject"; | const LEARNING_OBJECT_KEY = "learningObject"; | ||||||
| const learningObjectController = getLearningObjectController(); | const learningObjectController = getLearningObjectController(); | ||||||
|  | @ -15,7 +15,7 @@ export function useLearningObjectMetadataQuery( | ||||||
| ): UseQueryReturnType<LearningObject, Error> { | ): UseQueryReturnType<LearningObject, Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_OBJECT_KEY, "metadata", hruid, language, version], |         queryKey: [LEARNING_OBJECT_KEY, "metadata", hruid, language, version], | ||||||
|         queryFn: () => { |         queryFn: async () => { | ||||||
|             const [hruidVal, languageVal, versionVal] = [toValue(hruid), toValue(language), toValue(version)]; |             const [hruidVal, languageVal, versionVal] = [toValue(hruid), toValue(language), toValue(version)]; | ||||||
|             return learningObjectController.getMetadata(hruidVal, languageVal, versionVal) |             return learningObjectController.getMetadata(hruidVal, languageVal, versionVal) | ||||||
|         }, |         }, | ||||||
|  | @ -30,7 +30,7 @@ export function useLearningObjectHTMLQuery( | ||||||
| ): UseQueryReturnType<Document, Error> { | ): UseQueryReturnType<Document, Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_OBJECT_KEY, "html", hruid, language, version], |         queryKey: [LEARNING_OBJECT_KEY, "html", hruid, language, version], | ||||||
|         queryFn: () => { |         queryFn: async () => { | ||||||
|             const [hruidVal, languageVal, versionVal] = [toValue(hruid), toValue(language), toValue(version)]; |             const [hruidVal, languageVal, versionVal] = [toValue(hruid), toValue(language), toValue(version)]; | ||||||
|             return learningObjectController.getHTML(hruidVal, languageVal, versionVal) |             return learningObjectController.getHTML(hruidVal, languageVal, versionVal) | ||||||
|         }, |         }, | ||||||
|  | @ -43,9 +43,9 @@ export function useLearningObjectListForPathQuery( | ||||||
| ): UseQueryReturnType<LearningObject, Error> { | ): UseQueryReturnType<LearningObject, Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_OBJECT_KEY, "onPath", learningPath], |         queryKey: [LEARNING_OBJECT_KEY, "onPath", learningPath], | ||||||
|         queryFn: () => { |         queryFn: async () => { | ||||||
|             let learningObjects = []; |             const learningObjects = []; | ||||||
|             for (let node of toValue(learningPath).nodesAsList) { |             for (const node of toValue(learningPath).nodesAsList) { | ||||||
|                 learningObjects.push( |                 learningObjects.push( | ||||||
|                     learningObjectController.getMetadata(node.learningobjectHruid, node.language, node.version) |                     learningObjectController.getMetadata(node.learningobjectHruid, node.language, node.version) | ||||||
|                 ); |                 ); | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ import {type MaybeRefOrGetter, toValue} from "vue"; | ||||||
| import type {Language} from "@/data-objects/language.ts"; | import type {Language} from "@/data-objects/language.ts"; | ||||||
| import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query"; | import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query"; | ||||||
| import { getLearningPathController } from "@/controllers/controllers"; | import { getLearningPathController } from "@/controllers/controllers"; | ||||||
| import type {LearningPath} from "@/data-objects/learning-path.ts"; | import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
| 
 | 
 | ||||||
| const LEARNING_PATH_KEY = "learningPath"; | const LEARNING_PATH_KEY = "learningPath"; | ||||||
| const learningPathController = getLearningPathController(); | const learningPathController = getLearningPathController(); | ||||||
|  | @ -14,7 +14,7 @@ export function useGetLearningPathQuery( | ||||||
| ): UseQueryReturnType<LearningPath, Error> { | ): UseQueryReturnType<LearningPath, Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_PATH_KEY, "get", hruid, language, options], |         queryKey: [LEARNING_PATH_KEY, "get", hruid, language, options], | ||||||
|         queryFn: () => { |         queryFn: async () => { | ||||||
|             const [hruidVal, languageVal, optionsVal] = [toValue(hruid), toValue(language), toValue(options)]; |             const [hruidVal, languageVal, optionsVal] = [toValue(hruid), toValue(language), toValue(options)]; | ||||||
|             return learningPathController.getBy(hruidVal, languageVal, optionsVal) |             return learningPathController.getBy(hruidVal, languageVal, optionsVal) | ||||||
|         }, |         }, | ||||||
|  | @ -27,9 +27,7 @@ export function useGetAllLearningPathsByThemeQuery( | ||||||
| ): UseQueryReturnType<LearningPath[], Error> { | ): UseQueryReturnType<LearningPath[], Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_PATH_KEY, "getAllByTheme", theme], |         queryKey: [LEARNING_PATH_KEY, "getAllByTheme", theme], | ||||||
|         queryFn: () => { |         queryFn: async () => learningPathController.getAllByTheme(toValue(theme)), | ||||||
|             return learningPathController.getAllByTheme(toValue(theme)) |  | ||||||
|         }, |  | ||||||
|         enabled: () => Boolean(toValue(theme)), |         enabled: () => Boolean(toValue(theme)), | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  | @ -39,7 +37,7 @@ export function useSearchLearningPathQuery( | ||||||
| ): UseQueryReturnType<LearningPath[], Error>  { | ): UseQueryReturnType<LearningPath[], Error>  { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_PATH_KEY, "search", query], |         queryKey: [LEARNING_PATH_KEY, "search", query], | ||||||
|         queryFn: () => { |         queryFn: async () => { | ||||||
|             const queryVal = toValue(query); |             const queryVal = toValue(query); | ||||||
|             return learningPathController.search(queryVal); |             return learningPathController.search(queryVal); | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ import LearningPathPage from "@/views/learning-paths/LearningPathPage.vue"; | ||||||
| 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"; | ||||||
|  | import LearningObjectView from "@/views/learning-paths/LearningObjectView.vue"; | ||||||
| 
 | 
 | ||||||
| const router = createRouter({ | const router = createRouter({ | ||||||
|     history: createWebHistory(import.meta.env.BASE_URL), |     history: createWebHistory(import.meta.env.BASE_URL), | ||||||
|  | @ -117,22 +118,21 @@ const router = createRouter({ | ||||||
|                     meta: { requiresAuth: true } |                     meta: { requiresAuth: true } | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                     path: ":hruid/:language", |                     path: ":hruid/:language/:learningObjectHruid", | ||||||
|                     name: "LearningPath", |                     name: "LearningPath", | ||||||
|                     component: LearningPathPage, |                     component: LearningPathPage, | ||||||
|                     props: true, |                     props: true, | ||||||
|                     meta: { requiresAuth: true }, |                     meta: { requiresAuth: true }, | ||||||
|                     children: [ |  | ||||||
|                         { |  | ||||||
|                             path: ":learningObjectHruid", |  | ||||||
|                             component: LearningPathPage, |  | ||||||
|                             props: true, |  | ||||||
|                             meta: { requiresAuth: true } |  | ||||||
|                         } |  | ||||||
|                     ] |  | ||||||
|                 }, |                 }, | ||||||
|             ] |             ] | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             path: "/learningObject/:hruid/:language/:version/raw", | ||||||
|  |             name: "LearningObjectView", | ||||||
|  |             component: LearningObjectView, | ||||||
|  |             props: true, | ||||||
|  |             meta: { requiresAuth: true } | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|             path: "/:catchAll(.*)", |             path: "/:catchAll(.*)", | ||||||
|             name: "NotFound", |             name: "NotFound", | ||||||
|  |  | ||||||
|  | @ -1,22 +0,0 @@ | ||||||
| import type {AxiosResponse} from "axios"; |  | ||||||
| 
 |  | ||||||
| export class HttpErrorStatusException extends Error { |  | ||||||
|     public readonly statusCode: number; |  | ||||||
| 
 |  | ||||||
|     constructor(response: AxiosResponse<any, any>) { |  | ||||||
|         super(`${response.statusText} (${response.status})`); |  | ||||||
|         this.statusCode = response.status; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class NotFoundException extends Error { |  | ||||||
|     constructor(message: string) { |  | ||||||
|         super(message); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class InvalidResponseException extends Error { |  | ||||||
|     constructor(message: string) { |  | ||||||
|         super(message); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| import {InvalidResponseException, NotFoundException} from "@/services/api-client/api-exceptions.ts"; | import {NotFoundException} from "@/exception/not-found-exception.ts"; | ||||||
|  | import {InvalidResponseException} from "@/exception/invalid-response-exception.ts"; | ||||||
| 
 | 
 | ||||||
| export function single<T>(list: T[]): T { | export function single<T>(list: T[]): T { | ||||||
|     if (list.length === 1) { |     if (list.length === 1) { | ||||||
|  |  | ||||||
|  | @ -1,22 +1,25 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import { useRouter } from "vue-router"; |     import { useRouter } from "vue-router"; | ||||||
|     import { onMounted } from "vue"; |     import {onMounted, ref, type Ref} from "vue"; | ||||||
|     import auth from "../services/auth/auth-service.ts"; |     import auth from "../services/auth/auth-service.ts"; | ||||||
| 
 | 
 | ||||||
|     const router = useRouter(); |     const router = useRouter(); | ||||||
| 
 | 
 | ||||||
|  |     const errorMessage: Ref<string | null> = ref(null); | ||||||
|  | 
 | ||||||
|     onMounted(async () => { |     onMounted(async () => { | ||||||
|         try { |         try { | ||||||
|             await auth.handleLoginCallback(); |             await auth.handleLoginCallback(); | ||||||
|             await router.replace("/user"); // Redirect to theme page |             await router.replace("/user"); // Redirect to theme page | ||||||
|         } catch (_error) { |         } catch (error) { | ||||||
|             // FIXME console.error("OIDC callback error:", error); |             errorMessage.value = `OIDC callback error: ${error}`; | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <p>Logging you in...</p> |     <p v-if="!errorMessage">Logging you in...</p> | ||||||
|  |     <p v-else>{{ errorMessage }}</p> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped></style> | <style scoped></style> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,4 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import auth from "@/services/auth/auth-service.ts"; |  | ||||||
|     import apiClient from "@/services/api-client/api-client.ts"; |  | ||||||
|     import { ref } from "vue"; |     import { ref } from "vue"; | ||||||
|     import dwengoLogo from "../../../assets/img/dwengo-groen-zwart.svg"; |     import dwengoLogo from "../../../assets/img/dwengo-groen-zwart.svg"; | ||||||
|     import { useI18n } from "vue-i18n"; |     import { useI18n } from "vue-i18n"; | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import type {LearningPath} from "@/data-objects/learning-path.ts"; | import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
| import LearningPathsGrid from "@/components/LearningPathsGrid.vue"; | import LearningPathsGrid from "@/components/LearningPathsGrid.vue"; | ||||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
| import {useGetAllLearningPathsByThemeQuery} from "@/queries/learning-paths.ts"; | import {useGetAllLearningPathsByThemeQuery} from "@/queries/learning-paths.ts"; | ||||||
|  | @ -23,7 +23,7 @@ const learningPathsForThemeQueryResult = useGetAllLearningPathsByThemeQuery(() = | ||||||
| const { t } = useI18n(); | const { t } = useI18n(); | ||||||
| const searchFilter = ref(""); | const searchFilter = ref(""); | ||||||
| 
 | 
 | ||||||
| function filterLearningPaths(learningPaths: LearningPath[]) { | function filterLearningPaths(learningPaths: LearningPath[]): LearningPath[] { | ||||||
|     return learningPaths.filter(it => |     return learningPaths.filter(it => | ||||||
|         it.title.toLowerCase().includes(searchFilter.value.toLowerCase()) |         it.title.toLowerCase().includes(searchFilter.value.toLowerCase()) | ||||||
|         || it.description.toLowerCase().includes(searchFilter.value.toLowerCase) |         || it.description.toLowerCase().includes(searchFilter.value.toLowerCase) | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import {Language} from "@/data-objects/language.ts"; | import {Language} from "@/data-objects/language.ts"; | ||||||
| import {type UseQueryReturnType} from "@tanstack/vue-query"; | import type {UseQueryReturnType} from "@tanstack/vue-query"; | ||||||
| import {useLearningObjectHTMLQuery} from "@/queries/learning-objects.ts"; | import {useLearningObjectHTMLQuery} from "@/queries/learning-objects.ts"; | ||||||
| import UsingQueryResult from "@/components/UsingQueryResult.vue"; | import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,10 +1,9 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import {Language} from "@/data-objects/language.ts"; |     import {Language} from "@/data-objects/language.ts"; | ||||||
|     import {type LearningPath, LearningPathNode} from "@/data-objects/learning-path.ts"; |     import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
|     import {computed, type ComputedRef, ref, watch} from "vue"; |     import {computed, type ComputedRef, ref} from "vue"; | ||||||
|     import type {LearningObject} from "@/data-objects/learning-object.ts"; |     import type {LearningObject} from "@/data-objects/learning-objects/learning-object.ts"; | ||||||
|     import {useRoute, useRouter} from "vue-router"; |     import {useRoute} from "vue-router"; | ||||||
|     import {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"; |     import {useI18n} from "vue-i18n"; | ||||||
|     import LearningPathSearchField from "@/components/LearningPathSearchField.vue"; |     import LearningPathSearchField from "@/components/LearningPathSearchField.vue"; | ||||||
|  | @ -12,8 +11,8 @@ | ||||||
|     import {useLearningObjectListForPathQuery} from "@/queries/learning-objects.ts"; |     import {useLearningObjectListForPathQuery} from "@/queries/learning-objects.ts"; | ||||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; |     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
|     import authService from "@/services/auth/auth-service.ts"; |     import authService from "@/services/auth/auth-service.ts"; | ||||||
|  |     import {LearningPathNode} from "@/data-objects/learning-paths/learning-path-node.ts"; | ||||||
| 
 | 
 | ||||||
|     const router = useRouter(); |  | ||||||
|     const route = useRoute(); |     const route = useRoute(); | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|  | @ -30,11 +29,11 @@ | ||||||
|                 forStudent: route.query.forStudent, |                 forStudent: route.query.forStudent, | ||||||
|                 forGroup: route.query.forGroup |                 forGroup: route.query.forGroup | ||||||
|             } as Personalization |             } as Personalization | ||||||
|         } else { |         } | ||||||
|             return { |             return { | ||||||
|                 forStudent: authService.authState.user?.profile?.preferred_username |                 forStudent: authService.authState.user?.profile?.preferred_username | ||||||
|             } as Personalization |             } as Personalization | ||||||
|         } | 
 | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const learningPathQueryResult = useGetLearningPathQuery( |     const learningPathQueryResult = useGetLearningPathQuery( | ||||||
|  | @ -51,61 +50,45 @@ | ||||||
| 
 | 
 | ||||||
|     const currentNode = computed(() => { |     const currentNode = computed(() => { | ||||||
|         const currentHruid = props.learningObjectHruid; |         const currentHruid = props.learningObjectHruid; | ||||||
|         if (nodesList.value) { |         return nodesList.value?.find(it => it.learningobjectHruid === currentHruid); | ||||||
|             return nodesList.value.filter(it => it.learningobjectHruid === currentHruid)[0] |  | ||||||
|         } |  | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const nextNode = computed(() => { |     const nextNode = computed(() => { | ||||||
|         if (!currentNode.value || !nodesList.value) |         if (!currentNode.value || !nodesList.value) | ||||||
|             return; |             return undefined; | ||||||
|         const currentIndex = nodesList.value?.indexOf(currentNode.value); |         const currentIndex = nodesList.value?.indexOf(currentNode.value); | ||||||
|         if (currentIndex < nodesList.value?.length) { |         return currentIndex < nodesList.value?.length ? nodesList.value?.[currentIndex + 1] : undefined; | ||||||
|             return nodesList.value?.[currentIndex + 1]; |  | ||||||
|         } |  | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const previousNode = computed(() => { |     const previousNode = computed(() => { | ||||||
|         if (!currentNode.value || !nodesList.value) |         if (!currentNode.value || !nodesList.value) | ||||||
|             return; |             return undefined; | ||||||
|         const currentIndex = nodesList.value?.indexOf(currentNode.value); |         const currentIndex = nodesList.value?.indexOf(currentNode.value); | ||||||
|         if (currentIndex < nodesList.value?.length) { |         return currentIndex < nodesList.value?.length ? nodesList.value?.[currentIndex - 1] : undefined; | ||||||
|             return nodesList.value?.[currentIndex - 1]; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     watch(() => learningPathQueryResult, (newValue) => { |  | ||||||
|         if (learningPathQueryResult.isSuccess && false) { |  | ||||||
|             router.push({ |  | ||||||
|                 path: router.currentRoute.value.path + "/" + (newValue as SuccessState<LearningPath>).data.startNode.learningobjectHruid, |  | ||||||
|                 query: route.query, |  | ||||||
|             }); |  | ||||||
|         } |  | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const navigationDrawerShown = ref(true); |     const navigationDrawerShown = ref(true); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     function isLearningObjectCompleted(learningObject: LearningObject): boolean { |     function isLearningObjectCompleted(learningObject: LearningObject): boolean { | ||||||
|         if (learningObjectListQueryResult.isSuccess) { |         if (learningObjectListQueryResult.isSuccess) { | ||||||
|             return learningPathQueryResult.data.value.nodesAsList.filter(it => |             return learningPathQueryResult.data.value.nodesAsList.find(it => | ||||||
|                 it.learningobjectHruid === learningObject.key |                 it.learningobjectHruid === learningObject.key | ||||||
|                 && it.version === learningObject.version |                 && it.version === learningObject.version | ||||||
|                 && it.language == learningObject.language |                 && it.language === learningObject.language | ||||||
|             )[0].done; |             ).done; | ||||||
|         } |         } | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     type NavItemState = "teacherExclusive" | "completed" | "notCompleted"; |     type NavItemState = "teacherExclusive" | "completed" | "notCompleted"; | ||||||
| 
 | 
 | ||||||
|     const ICONS: {[key: NavItemState]: string} = { |     const ICONS: Record<NavItemState, string> = { | ||||||
|         teacherExclusive: "mdi-information", |         teacherExclusive: "mdi-information", | ||||||
|         completed: "mdi-checkbox-marked-circle-outline", |         completed: "mdi-checkbox-marked-circle-outline", | ||||||
|         notCompleted: "mdi-checkbox-blank-circle-outline" |         notCompleted: "mdi-checkbox-blank-circle-outline" | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const COLORS: {[key: NavItemState]: string | undefined}  = { |     const COLORS: Record<NavItemState, string | undefined>  = { | ||||||
|         teacherExclusive: "info", |         teacherExclusive: "info", | ||||||
|         completed: "success", |         completed: "success", | ||||||
|         notCompleted: undefined |         notCompleted: undefined | ||||||
|  | @ -116,9 +99,9 @@ | ||||||
|             return "teacherExclusive"; |             return "teacherExclusive"; | ||||||
|         } else if (isLearningObjectCompleted(learningObject)) { |         } else if (isLearningObjectCompleted(learningObject)) { | ||||||
|             return "completed"; |             return "completed"; | ||||||
|         } else { |  | ||||||
|             return "notCompleted"; |  | ||||||
|         } |         } | ||||||
|  |             return "notCompleted"; | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -151,6 +134,7 @@ | ||||||
|                             :to="{path: node.key, query: route.query}" |                             :to="{path: node.key, query: route.query}" | ||||||
|                             :title="node.title" |                             :title="node.title" | ||||||
|                             :active="node.key === props.learningObjectHruid" |                             :active="node.key === props.learningObjectHruid" | ||||||
|  |                             :key="node.key" | ||||||
|                             v-if="!node.teacherExclusive || authService.authState.activeRole === 'teacher'" |                             v-if="!node.teacherExclusive || authService.authState.activeRole === 'teacher'" | ||||||
|                         > |                         > | ||||||
|                             <template v-slot:prepend> |                             <template v-slot:prepend> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import type {LearningPath} from "@/data-objects/learning-path.ts"; |     import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; | ||||||
|     import {useRoute, useRouter} from "vue-router"; |     import {useRoute} from "vue-router"; | ||||||
|     import {computed} from "vue"; |     import {computed} from "vue"; | ||||||
|     import {useI18n} from "vue-i18n"; |     import {useI18n} from "vue-i18n"; | ||||||
|     import LearningPathSearchField from "@/components/LearningPathSearchField.vue"; |     import LearningPathSearchField from "@/components/LearningPathSearchField.vue"; | ||||||
|  | @ -9,7 +9,6 @@ | ||||||
|     import LearningPathsGrid from "@/components/LearningPathsGrid.vue"; |     import LearningPathsGrid from "@/components/LearningPathsGrid.vue"; | ||||||
| 
 | 
 | ||||||
|     const route = useRoute(); |     const route = useRoute(); | ||||||
|     const router = useRouter(); |  | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|     const query = computed(() => route.query.query as string | null); |     const query = computed(() => route.query.query as string | null); | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger