Merge remote-tracking branch 'origin/feat/pagina-om-leerpaden-te-bekijken-#41' into feat/pagina-om-leerpaden-te-bekijken-#41
This commit is contained in:
		
						commit
						5bee1cd6be
					
				
					 26 changed files with 467 additions and 422 deletions
				
			
		|  | @ -49,7 +49,7 @@ export async function getLearningObject(req: Request, res: Response): Promise<vo | ||||||
|     const learningObject = await learningObjectService.getLearningObjectById(learningObjectId); |     const learningObject = await learningObjectService.getLearningObjectById(learningObjectId); | ||||||
| 
 | 
 | ||||||
|     if (!learningObject) { |     if (!learningObject) { | ||||||
|         throw new NotFoundException("Learning object not found"); |         throw new NotFoundException('Learning object not found'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     res.json(learningObject); |     res.json(learningObject); | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ | ||||||
| 
 | 
 | ||||||
|     const query = computed({ |     const query = computed({ | ||||||
|         get: () => route.query.query as string | null, |         get: () => route.query.query as string | null, | ||||||
|         set: async (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); | ||||||
|  | @ -31,6 +31,4 @@ | ||||||
|     ></v-text-field> |     ></v-text-field> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped></style> | ||||||
| 
 |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -1,16 +1,17 @@ | ||||||
| <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-paths/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(); | ||||||
|     const props = defineProps<{ learningPaths: LearningPath[] }>(); |     const props = defineProps<{ learningPaths: LearningPath[] }>(); | ||||||
| 
 |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <div class="results-grid" v-if="props.learningPaths.length > 0"> |     <div | ||||||
|  |         class="results-grid" | ||||||
|  |         v-if="props.learningPaths.length > 0" | ||||||
|  |     > | ||||||
|         <v-card |         <v-card | ||||||
|             class="learning-path-card" |             class="learning-path-card" | ||||||
|             link |             link | ||||||
|  | @ -27,12 +28,15 @@ const props = defineProps<{learningPaths: LearningPath[]}>(); | ||||||
|             <v-card-title class="learning-path-title">{{ learningPath.title }}</v-card-title> |             <v-card-title class="learning-path-title">{{ learningPath.title }}</v-card-title> | ||||||
|             <v-card-subtitle> |             <v-card-subtitle> | ||||||
|                 <v-icon icon="mdi-human-male-boy"></v-icon> |                 <v-icon icon="mdi-human-male-boy"></v-icon> | ||||||
|                 <span>{{ learningPath.targetAges.min }} - {{ learningPath.targetAges.max }} {{ t('yearsAge') }}</span> |                 <span>{{ learningPath.targetAges.min }} - {{ learningPath.targetAges.max }} {{ t("yearsAge") }}</span> | ||||||
|             </v-card-subtitle> |             </v-card-subtitle> | ||||||
|             <v-card-text>{{ learningPath.description }}</v-card-text> |             <v-card-text>{{ learningPath.description }}</v-card-text> | ||||||
|         </v-card> |         </v-card> | ||||||
|     </div> |     </div> | ||||||
|     <div content="empty-state-container" v-else> |     <div | ||||||
|  |         content="empty-state-container" | ||||||
|  |         v-else | ||||||
|  |     > | ||||||
|         <v-empty-state |         <v-empty-state | ||||||
|             icon="mdi-emoticon-sad-outline" |             icon="mdi-emoticon-sad-outline" | ||||||
|             :title="t('noLearningPathsFound')" |             :title="t('noLearningPathsFound')" | ||||||
|  |  | ||||||
|  | @ -4,8 +4,8 @@ | ||||||
|     import type { UseQueryReturnType } from "@tanstack/vue-query"; |     import type { UseQueryReturnType } from "@tanstack/vue-query"; | ||||||
| 
 | 
 | ||||||
|     const props = defineProps<{ |     const props = defineProps<{ | ||||||
|         queryResult: UseQueryReturnType<T, Error> |         queryResult: UseQueryReturnType<T, Error>; | ||||||
|     }>() |     }>(); | ||||||
| 
 | 
 | ||||||
|     const { isLoading, isError, isSuccess, data, error } = props.queryResult; |     const { isLoading, isError, isSuccess, data, error } = props.queryResult; | ||||||
| 
 | 
 | ||||||
|  | @ -13,12 +13,15 @@ | ||||||
| 
 | 
 | ||||||
|     const errorMessage = computed(() => { |     const errorMessage = computed(() => { | ||||||
|         const 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> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <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="isError"> |     <div v-if="isError"> | ||||||
|  | @ -28,7 +31,10 @@ | ||||||
|             :title="t('error_title')" |             :title="t('error_title')" | ||||||
|         ></v-empty-state> |         ></v-empty-state> | ||||||
|     </div> |     </div> | ||||||
|     <slot v-if="isSuccess && data" :data="data"></slot> |     <slot | ||||||
|  |         v-if="isSuccess && data" | ||||||
|  |         :data="data" | ||||||
|  |     ></slot> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
|  |  | ||||||
|  | @ -16,10 +16,7 @@ export abstract class BaseController { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected async get<T>(path: string, queryParams?: QueryParams, responseType?: ResponseType): Promise<T> { |     protected async get<T>(path: string, queryParams?: QueryParams, responseType?: ResponseType): Promise<T> { | ||||||
|         const response = await apiClient.get<T>( |         const response = await apiClient.get<T>(this.absolutePathFor(path), { params: queryParams, responseType }); | ||||||
|             this.absolutePathFor(path), |  | ||||||
|             {params: queryParams, responseType} |  | ||||||
|         ); |  | ||||||
|         BaseController.assertSuccessResponse(response); |         BaseController.assertSuccessResponse(response); | ||||||
|         return response.data; |         return response.data; | ||||||
|     } |     } | ||||||
|  | @ -31,7 +28,7 @@ export abstract class BaseController { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected async delete<T>(path: string): Promise<T> { |     protected async delete<T>(path: string): Promise<T> { | ||||||
|         const response = await apiClient.delete<T>(this.absolutePathFor(path)) |         const response = await apiClient.delete<T>(this.absolutePathFor(path)); | ||||||
|         BaseController.assertSuccessResponse(response); |         BaseController.assertSuccessResponse(response); | ||||||
|         return response.data; |         return response.data; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -10,19 +10,23 @@ export class LearningPathController extends BaseController { | ||||||
|     } |     } | ||||||
|     async search(query: string): Promise<LearningPath[]> { |     async search(query: string): Promise<LearningPath[]> { | ||||||
|         const 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}): Promise<LearningPath> { |     async getBy( | ||||||
|  |         hruid: string, | ||||||
|  |         language: Language, | ||||||
|  |         options?: { forGroup?: string; forStudent?: string }, | ||||||
|  |     ): Promise<LearningPath> { | ||||||
|         const dtos = await this.get<LearningPathDTO[]>("/", { |         const dtos = await this.get<LearningPathDTO[]>("/", { | ||||||
|             hruid, |             hruid, | ||||||
|             language, |             language, | ||||||
|             forGroup: options?.forGroup, |             forGroup: options?.forGroup, | ||||||
|             forStudent: options?.forStudent |             forStudent: options?.forStudent, | ||||||
|         }); |         }); | ||||||
|         return LearningPath.fromDTO(single(dtos)); |         return LearningPath.fromDTO(single(dtos)); | ||||||
|     } |     } | ||||||
|     async getAllByTheme(theme: string): Promise<LearningPath[]> { |     async getAllByTheme(theme: string): Promise<LearningPath[]> { | ||||||
|         const 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)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,186 +1,186 @@ | ||||||
| export enum Language { | export enum Language { | ||||||
|     Afar = 'aa', |     Afar = "aa", | ||||||
|     Abkhazian = 'ab', |     Abkhazian = "ab", | ||||||
|     Afrikaans = 'af', |     Afrikaans = "af", | ||||||
|     Akan = 'ak', |     Akan = "ak", | ||||||
|     Albanian = 'sq', |     Albanian = "sq", | ||||||
|     Amharic = 'am', |     Amharic = "am", | ||||||
|     Arabic = 'ar', |     Arabic = "ar", | ||||||
|     Aragonese = 'an', |     Aragonese = "an", | ||||||
|     Armenian = 'hy', |     Armenian = "hy", | ||||||
|     Assamese = 'as', |     Assamese = "as", | ||||||
|     Avaric = 'av', |     Avaric = "av", | ||||||
|     Avestan = 'ae', |     Avestan = "ae", | ||||||
|     Aymara = 'ay', |     Aymara = "ay", | ||||||
|     Azerbaijani = 'az', |     Azerbaijani = "az", | ||||||
|     Bashkir = 'ba', |     Bashkir = "ba", | ||||||
|     Bambara = 'bm', |     Bambara = "bm", | ||||||
|     Basque = 'eu', |     Basque = "eu", | ||||||
|     Belarusian = 'be', |     Belarusian = "be", | ||||||
|     Bengali = 'bn', |     Bengali = "bn", | ||||||
|     Bihari = 'bh', |     Bihari = "bh", | ||||||
|     Bislama = 'bi', |     Bislama = "bi", | ||||||
|     Bosnian = 'bs', |     Bosnian = "bs", | ||||||
|     Breton = 'br', |     Breton = "br", | ||||||
|     Bulgarian = 'bg', |     Bulgarian = "bg", | ||||||
|     Burmese = 'my', |     Burmese = "my", | ||||||
|     Catalan = 'ca', |     Catalan = "ca", | ||||||
|     Chamorro = 'ch', |     Chamorro = "ch", | ||||||
|     Chechen = 'ce', |     Chechen = "ce", | ||||||
|     Chinese = 'zh', |     Chinese = "zh", | ||||||
|     ChurchSlavic = 'cu', |     ChurchSlavic = "cu", | ||||||
|     Chuvash = 'cv', |     Chuvash = "cv", | ||||||
|     Cornish = 'kw', |     Cornish = "kw", | ||||||
|     Corsican = 'co', |     Corsican = "co", | ||||||
|     Cree = 'cr', |     Cree = "cr", | ||||||
|     Czech = 'cs', |     Czech = "cs", | ||||||
|     Danish = 'da', |     Danish = "da", | ||||||
|     Divehi = 'dv', |     Divehi = "dv", | ||||||
|     Dutch = 'nl', |     Dutch = "nl", | ||||||
|     Dzongkha = 'dz', |     Dzongkha = "dz", | ||||||
|     English = 'en', |     English = "en", | ||||||
|     Esperanto = 'eo', |     Esperanto = "eo", | ||||||
|     Estonian = 'et', |     Estonian = "et", | ||||||
|     Ewe = 'ee', |     Ewe = "ee", | ||||||
|     Faroese = 'fo', |     Faroese = "fo", | ||||||
|     Fijian = 'fj', |     Fijian = "fj", | ||||||
|     Finnish = 'fi', |     Finnish = "fi", | ||||||
|     French = 'fr', |     French = "fr", | ||||||
|     Frisian = 'fy', |     Frisian = "fy", | ||||||
|     Fulah = 'ff', |     Fulah = "ff", | ||||||
|     Georgian = 'ka', |     Georgian = "ka", | ||||||
|     German = 'de', |     German = "de", | ||||||
|     Gaelic = 'gd', |     Gaelic = "gd", | ||||||
|     Irish = 'ga', |     Irish = "ga", | ||||||
|     Galician = 'gl', |     Galician = "gl", | ||||||
|     Manx = 'gv', |     Manx = "gv", | ||||||
|     Greek = 'el', |     Greek = "el", | ||||||
|     Guarani = 'gn', |     Guarani = "gn", | ||||||
|     Gujarati = 'gu', |     Gujarati = "gu", | ||||||
|     Haitian = 'ht', |     Haitian = "ht", | ||||||
|     Hausa = 'ha', |     Hausa = "ha", | ||||||
|     Hebrew = 'he', |     Hebrew = "he", | ||||||
|     Herero = 'hz', |     Herero = "hz", | ||||||
|     Hindi = 'hi', |     Hindi = "hi", | ||||||
|     HiriMotu = 'ho', |     HiriMotu = "ho", | ||||||
|     Croatian = 'hr', |     Croatian = "hr", | ||||||
|     Hungarian = 'hu', |     Hungarian = "hu", | ||||||
|     Igbo = 'ig', |     Igbo = "ig", | ||||||
|     Icelandic = 'is', |     Icelandic = "is", | ||||||
|     Ido = 'io', |     Ido = "io", | ||||||
|     SichuanYi = 'ii', |     SichuanYi = "ii", | ||||||
|     Inuktitut = 'iu', |     Inuktitut = "iu", | ||||||
|     Interlingue = 'ie', |     Interlingue = "ie", | ||||||
|     Interlingua = 'ia', |     Interlingua = "ia", | ||||||
|     Indonesian = 'id', |     Indonesian = "id", | ||||||
|     Inupiaq = 'ik', |     Inupiaq = "ik", | ||||||
|     Italian = 'it', |     Italian = "it", | ||||||
|     Javanese = 'jv', |     Javanese = "jv", | ||||||
|     Japanese = 'ja', |     Japanese = "ja", | ||||||
|     Kalaallisut = 'kl', |     Kalaallisut = "kl", | ||||||
|     Kannada = 'kn', |     Kannada = "kn", | ||||||
|     Kashmiri = 'ks', |     Kashmiri = "ks", | ||||||
|     Kanuri = 'kr', |     Kanuri = "kr", | ||||||
|     Kazakh = 'kk', |     Kazakh = "kk", | ||||||
|     Khmer = 'km', |     Khmer = "km", | ||||||
|     Kikuyu = 'ki', |     Kikuyu = "ki", | ||||||
|     Kinyarwanda = 'rw', |     Kinyarwanda = "rw", | ||||||
|     Kirghiz = 'ky', |     Kirghiz = "ky", | ||||||
|     Komi = 'kv', |     Komi = "kv", | ||||||
|     Kongo = 'kg', |     Kongo = "kg", | ||||||
|     Korean = 'ko', |     Korean = "ko", | ||||||
|     Kuanyama = 'kj', |     Kuanyama = "kj", | ||||||
|     Kurdish = 'ku', |     Kurdish = "ku", | ||||||
|     Lao = 'lo', |     Lao = "lo", | ||||||
|     Latin = 'la', |     Latin = "la", | ||||||
|     Latvian = 'lv', |     Latvian = "lv", | ||||||
|     Limburgan = 'li', |     Limburgan = "li", | ||||||
|     Lingala = 'ln', |     Lingala = "ln", | ||||||
|     Lithuanian = 'lt', |     Lithuanian = "lt", | ||||||
|     Luxembourgish = 'lb', |     Luxembourgish = "lb", | ||||||
|     LubaKatanga = 'lu', |     LubaKatanga = "lu", | ||||||
|     Ganda = 'lg', |     Ganda = "lg", | ||||||
|     Macedonian = 'mk', |     Macedonian = "mk", | ||||||
|     Marshallese = 'mh', |     Marshallese = "mh", | ||||||
|     Malayalam = 'ml', |     Malayalam = "ml", | ||||||
|     Maori = 'mi', |     Maori = "mi", | ||||||
|     Marathi = 'mr', |     Marathi = "mr", | ||||||
|     Malay = 'ms', |     Malay = "ms", | ||||||
|     Malagasy = 'mg', |     Malagasy = "mg", | ||||||
|     Maltese = 'mt', |     Maltese = "mt", | ||||||
|     Mongolian = 'mn', |     Mongolian = "mn", | ||||||
|     Nauru = 'na', |     Nauru = "na", | ||||||
|     Navajo = 'nv', |     Navajo = "nv", | ||||||
|     SouthNdebele = 'nr', |     SouthNdebele = "nr", | ||||||
|     NorthNdebele = 'nd', |     NorthNdebele = "nd", | ||||||
|     Ndonga = 'ng', |     Ndonga = "ng", | ||||||
|     Nepali = 'ne', |     Nepali = "ne", | ||||||
|     NorwegianNynorsk = 'nn', |     NorwegianNynorsk = "nn", | ||||||
|     NorwegianBokmal = 'nb', |     NorwegianBokmal = "nb", | ||||||
|     Norwegian = 'no', |     Norwegian = "no", | ||||||
|     Chichewa = 'ny', |     Chichewa = "ny", | ||||||
|     Occitan = 'oc', |     Occitan = "oc", | ||||||
|     Ojibwa = 'oj', |     Ojibwa = "oj", | ||||||
|     Oriya = 'or', |     Oriya = "or", | ||||||
|     Oromo = 'om', |     Oromo = "om", | ||||||
|     Ossetian = 'os', |     Ossetian = "os", | ||||||
|     Punjabi = 'pa', |     Punjabi = "pa", | ||||||
|     Persian = 'fa', |     Persian = "fa", | ||||||
|     Pali = 'pi', |     Pali = "pi", | ||||||
|     Polish = 'pl', |     Polish = "pl", | ||||||
|     Portuguese = 'pt', |     Portuguese = "pt", | ||||||
|     Pashto = 'ps', |     Pashto = "ps", | ||||||
|     Quechua = 'qu', |     Quechua = "qu", | ||||||
|     Romansh = 'rm', |     Romansh = "rm", | ||||||
|     Romanian = 'ro', |     Romanian = "ro", | ||||||
|     Rundi = 'rn', |     Rundi = "rn", | ||||||
|     Russian = 'ru', |     Russian = "ru", | ||||||
|     Sango = 'sg', |     Sango = "sg", | ||||||
|     Sanskrit = 'sa', |     Sanskrit = "sa", | ||||||
|     Sinhala = 'si', |     Sinhala = "si", | ||||||
|     Slovak = 'sk', |     Slovak = "sk", | ||||||
|     Slovenian = 'sl', |     Slovenian = "sl", | ||||||
|     NorthernSami = 'se', |     NorthernSami = "se", | ||||||
|     Samoan = 'sm', |     Samoan = "sm", | ||||||
|     Shona = 'sn', |     Shona = "sn", | ||||||
|     Sindhi = 'sd', |     Sindhi = "sd", | ||||||
|     Somali = 'so', |     Somali = "so", | ||||||
|     Sotho = 'st', |     Sotho = "st", | ||||||
|     Spanish = 'es', |     Spanish = "es", | ||||||
|     Sardinian = 'sc', |     Sardinian = "sc", | ||||||
|     Serbian = 'sr', |     Serbian = "sr", | ||||||
|     Swati = 'ss', |     Swati = "ss", | ||||||
|     Sundanese = 'su', |     Sundanese = "su", | ||||||
|     Swahili = 'sw', |     Swahili = "sw", | ||||||
|     Swedish = 'sv', |     Swedish = "sv", | ||||||
|     Tahitian = 'ty', |     Tahitian = "ty", | ||||||
|     Tamil = 'ta', |     Tamil = "ta", | ||||||
|     Tatar = 'tt', |     Tatar = "tt", | ||||||
|     Telugu = 'te', |     Telugu = "te", | ||||||
|     Tajik = 'tg', |     Tajik = "tg", | ||||||
|     Tagalog = 'tl', |     Tagalog = "tl", | ||||||
|     Thai = 'th', |     Thai = "th", | ||||||
|     Tibetan = 'bo', |     Tibetan = "bo", | ||||||
|     Tigrinya = 'ti', |     Tigrinya = "ti", | ||||||
|     Tonga = 'to', |     Tonga = "to", | ||||||
|     Tswana = 'tn', |     Tswana = "tn", | ||||||
|     Tsonga = 'ts', |     Tsonga = "ts", | ||||||
|     Turkmen = 'tk', |     Turkmen = "tk", | ||||||
|     Turkish = 'tr', |     Turkish = "tr", | ||||||
|     Twi = 'tw', |     Twi = "tw", | ||||||
|     Uighur = 'ug', |     Uighur = "ug", | ||||||
|     Ukrainian = 'uk', |     Ukrainian = "uk", | ||||||
|     Urdu = 'ur', |     Urdu = "ur", | ||||||
|     Uzbek = 'uz', |     Uzbek = "uz", | ||||||
|     Venda = 've', |     Venda = "ve", | ||||||
|     Vietnamese = 'vi', |     Vietnamese = "vi", | ||||||
|     Volapuk = 'vo', |     Volapuk = "vo", | ||||||
|     Welsh = 'cy', |     Welsh = "cy", | ||||||
|     Walloon = 'wa', |     Walloon = "wa", | ||||||
|     Wolof = 'wo', |     Wolof = "wo", | ||||||
|     Xhosa = 'xh', |     Xhosa = "xh", | ||||||
|     Yiddish = 'yi', |     Yiddish = "yi", | ||||||
|     Yoruba = 'yo', |     Yoruba = "yo", | ||||||
|     Zhuang = 'za', |     Zhuang = "za", | ||||||
|     Zulu = 'zu', |     Zulu = "zu", | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,19 +5,19 @@ export class LearningPathNode { | ||||||
|     public readonly learningobjectHruid: string; |     public readonly learningobjectHruid: string; | ||||||
|     public readonly version: number; |     public readonly version: number; | ||||||
|     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; |     public readonly done: boolean; | ||||||
| 
 | 
 | ||||||
|     constructor(options: { |     constructor(options: { | ||||||
|         learningobjectHruid: string, |         learningobjectHruid: string; | ||||||
|         version: number, |         version: number; | ||||||
|         language: Language, |         language: Language; | ||||||
|         transitions: { next: LearningPathNode, default: boolean }[], |         transitions: { next: LearningPathNode; default: boolean }[]; | ||||||
|         createdAt: Date, |         createdAt: Date; | ||||||
|         updatedAt: Date, |         updatedAt: Date; | ||||||
|         done?: boolean |         done?: boolean; | ||||||
|     }) { |     }) { | ||||||
|         this.learningobjectHruid = options.learningobjectHruid; |         this.learningobjectHruid = options.learningobjectHruid; | ||||||
|         this.version = options.version; |         this.version = options.version; | ||||||
|  | @ -33,25 +33,28 @@ export class LearningPathNode { | ||||||
|             learningobjectHruid: dto.learningobject_hruid, |             learningobjectHruid: dto.learningobject_hruid, | ||||||
|             version: dto.version, |             version: dto.version, | ||||||
|             language: dto.language, |             language: dto.language, | ||||||
|             transitions: dto.transitions.map(transDto => { |             transitions: dto.transitions.map((transDto) => { | ||||||
|                 const nextNodeDto = otherNodes.filter(it => |                 const nextNodeDto = otherNodes.filter( | ||||||
|                     it.learningobject_hruid === transDto.next.hruid |                     (it) => | ||||||
|                     && it.language === transDto.next.language |                         it.learningobject_hruid === transDto.next.hruid && | ||||||
|                     && it.version === transDto.next.version |                         it.language === transDto.next.language && | ||||||
|  |                         it.version === transDto.next.version, | ||||||
|                 ); |                 ); | ||||||
|                 if (nextNodeDto.length !== 1) { |                 if (nextNodeDto.length !== 1) { | ||||||
|                     throw new Error(`Invalid learning path! There is a transition to node` |                     throw new Error( | ||||||
|                         + `${transDto.next.hruid}/${transDto.next.language}/${transDto.next.version}, but there are` |                         `Invalid learning path! There is a transition to node` + | ||||||
|                         + `${nextNodeDto.length} such nodes.`); |                             `${transDto.next.hruid}/${transDto.next.language}/${transDto.next.version}, but there are` + | ||||||
|  |                             `${nextNodeDto.length} such nodes.`, | ||||||
|  |                     ); | ||||||
|                 } |                 } | ||||||
|                 return { |                 return { | ||||||
|                     next: LearningPathNode.fromDTOAndOtherNodes(nextNodeDto[0], otherNodes), |                     next: LearningPathNode.fromDTOAndOtherNodes(nextNodeDto[0], otherNodes), | ||||||
|                     default: transDto.default |                     default: transDto.default, | ||||||
|                 } |                 }; | ||||||
|             }), |             }), | ||||||
|             createdAt: new Date(dto.created_at), |             createdAt: new Date(dto.created_at), | ||||||
|             updatedAt: new Date(dto.updatedAt), |             updatedAt: new Date(dto.updatedAt), | ||||||
|             done: dto.done |             done: dto.done, | ||||||
|         }) |         }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -38,16 +38,16 @@ export class LearningPath { | ||||||
|     public readonly image?: string; // Image might be missing, so it's optional
 |     public readonly image?: string; // Image might be missing, so it's optional
 | ||||||
| 
 | 
 | ||||||
|     constructor(options: { |     constructor(options: { | ||||||
|         language: string, |         language: string; | ||||||
|         hruid: string, |         hruid: string; | ||||||
|         title: string, |         title: string; | ||||||
|         description: string, |         description: string; | ||||||
|         amountOfNodes: number, |         amountOfNodes: number; | ||||||
|         amountOfNodesLeft: number, |         amountOfNodesLeft: number; | ||||||
|         keywords: string[], |         keywords: string[]; | ||||||
|         targetAges: {min: number; max: number}, |         targetAges: { min: number; max: number }; | ||||||
|         startNode: LearningPathNode, |         startNode: LearningPathNode; | ||||||
|         image?: string // Image might be missing, so it's optional
 |         image?: string; // Image might be missing, so it's optional
 | ||||||
|     }) { |     }) { | ||||||
|         this.language = options.language; |         this.language = options.language; | ||||||
|         this.hruid = options.hruid; |         this.hruid = options.hruid; | ||||||
|  | @ -66,14 +66,13 @@ export class LearningPath { | ||||||
|         let currentNode = this.startNode; |         let currentNode = this.startNode; | ||||||
|         while (currentNode) { |         while (currentNode) { | ||||||
|             list.push(currentNode); |             list.push(currentNode); | ||||||
|             currentNode = currentNode.transitions.find(it => it.default)?.next |             currentNode = currentNode.transitions.find((it) => it.default)?.next || currentNode.transitions[0]?.next; | ||||||
|                             || currentNode.transitions[0]?.next; |  | ||||||
|         } |         } | ||||||
|         return list; |         return list; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static fromDTO(dto: LearningPathDTO): LearningPath { |     static fromDTO(dto: LearningPathDTO): LearningPath { | ||||||
|         const startNodeDto = dto.nodes.filter(it => it.start_node === true); |         const startNodeDto = dto.nodes.filter((it) => it.start_node === true); | ||||||
|         if (startNodeDto.length !== 1) { |         if (startNodeDto.length !== 1) { | ||||||
|             throw new Error(`Invalid learning path: ${dto.hruid}/${dto.language}!
 |             throw new Error(`Invalid learning path: ${dto.hruid}/${dto.language}!
 | ||||||
|                                 Expected precisely one start node, but there were ${startNodeDto.length}.`);
 |                                 Expected precisely one start node, but there were ${startNodeDto.length}.`);
 | ||||||
|  | @ -85,10 +84,10 @@ export class LearningPath { | ||||||
|             description: dto.description, |             description: dto.description, | ||||||
|             amountOfNodes: dto.num_nodes, |             amountOfNodes: dto.num_nodes, | ||||||
|             amountOfNodesLeft: dto.num_nodes_left, |             amountOfNodesLeft: dto.num_nodes_left, | ||||||
|             keywords: dto.keywords.split(' '), |             keywords: dto.keywords.split(" "), | ||||||
|             targetAges: { min: dto.min_age, max: dto.max_age }, |             targetAges: { min: dto.min_age, max: dto.max_age }, | ||||||
|             startNode: LearningPathNode.fromDTOAndOtherNodes(startNodeDto[0], dto.nodes), |             startNode: LearningPathNode.fromDTOAndOtherNodes(startNodeDto[0], dto.nodes), | ||||||
|             image: dto.image |             image: dto.image, | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ import i18n from "./i18n/i18n.ts"; | ||||||
| import App from "./App.vue"; | import App from "./App.vue"; | ||||||
| import router from "./router"; | import router from "./router"; | ||||||
| import { aliases, mdi } from "vuetify/iconsets/mdi"; | import { aliases, mdi } from "vuetify/iconsets/mdi"; | ||||||
| import { VueQueryPlugin, QueryClient } from '@tanstack/vue-query'; | import { VueQueryPlugin, QueryClient } from "@tanstack/vue-query"; | ||||||
| 
 | 
 | ||||||
| const app = createApp(App); | const app = createApp(App); | ||||||
| 
 | 
 | ||||||
|  | @ -29,9 +29,9 @@ const vuetify = createVuetify({ | ||||||
|         defaultSet: "mdi", |         defaultSet: "mdi", | ||||||
|         aliases, |         aliases, | ||||||
|         sets: { |         sets: { | ||||||
|             mdi |             mdi, | ||||||
|         } |         }, | ||||||
|     } |     }, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const queryClient = new QueryClient({ | const queryClient = new QueryClient({ | ||||||
|  |  | ||||||
|  | @ -11,13 +11,13 @@ const learningObjectController = getLearningObjectController(); | ||||||
| export function useLearningObjectMetadataQuery( | export function useLearningObjectMetadataQuery( | ||||||
|     hruid: MaybeRefOrGetter<string>, |     hruid: MaybeRefOrGetter<string>, | ||||||
|     language: MaybeRefOrGetter<Language>, |     language: MaybeRefOrGetter<Language>, | ||||||
|     version: MaybeRefOrGetter<number> |     version: MaybeRefOrGetter<number>, | ||||||
| ): 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: async () => { |         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); | ||||||
|         }, |         }, | ||||||
|         enabled: () => Boolean(toValue(hruid)) && Boolean(toValue(language)) && Boolean(toValue(version)), |         enabled: () => Boolean(toValue(hruid)) && Boolean(toValue(language)) && Boolean(toValue(version)), | ||||||
|     }); |     }); | ||||||
|  | @ -26,20 +26,20 @@ export function useLearningObjectMetadataQuery( | ||||||
| export function useLearningObjectHTMLQuery( | export function useLearningObjectHTMLQuery( | ||||||
|     hruid: MaybeRefOrGetter<string>, |     hruid: MaybeRefOrGetter<string>, | ||||||
|     language: MaybeRefOrGetter<Language>, |     language: MaybeRefOrGetter<Language>, | ||||||
|     version: MaybeRefOrGetter<number> |     version: MaybeRefOrGetter<number>, | ||||||
| ): 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: async () => { |         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); | ||||||
|         }, |         }, | ||||||
|         enabled: () => Boolean(toValue(hruid)) && Boolean(toValue(language)) && Boolean(toValue(version)), |         enabled: () => Boolean(toValue(hruid)) && Boolean(toValue(language)) && Boolean(toValue(version)), | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useLearningObjectListForPathQuery( | export function useLearningObjectListForPathQuery( | ||||||
|     learningPath: MaybeRefOrGetter<LearningPath | undefined> |     learningPath: MaybeRefOrGetter<LearningPath | undefined>, | ||||||
| ): UseQueryReturnType<LearningObject[], Error> { | ): UseQueryReturnType<LearningObject[], Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_OBJECT_KEY, "onPath", learningPath], |         queryKey: [LEARNING_OBJECT_KEY, "onPath", learningPath], | ||||||
|  | @ -47,7 +47,7 @@ export function useLearningObjectListForPathQuery( | ||||||
|             const learningObjects: Promise<LearningObject>[] = []; |             const learningObjects: Promise<LearningObject>[] = []; | ||||||
|             for (const 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), | ||||||
|                 ); |                 ); | ||||||
|             } |             } | ||||||
|             return Promise.all(learningObjects); |             return Promise.all(learningObjects); | ||||||
|  |  | ||||||
|  | @ -10,30 +10,30 @@ const learningPathController = getLearningPathController(); | ||||||
| export function useGetLearningPathQuery( | export function useGetLearningPathQuery( | ||||||
|     hruid: MaybeRefOrGetter<string>, |     hruid: MaybeRefOrGetter<string>, | ||||||
|     language: MaybeRefOrGetter<Language>, |     language: MaybeRefOrGetter<Language>, | ||||||
|     options?: MaybeRefOrGetter<{forGroup?: string, forStudent?: string}> |     options?: MaybeRefOrGetter<{ forGroup?: string; forStudent?: string }>, | ||||||
| ): 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: async () => { |         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); | ||||||
|         }, |         }, | ||||||
|         enabled: () => Boolean(toValue(hruid)) && Boolean(toValue(language)), |         enabled: () => Boolean(toValue(hruid)) && Boolean(toValue(language)), | ||||||
|     }) |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useGetAllLearningPathsByThemeQuery( | export function useGetAllLearningPathsByThemeQuery( | ||||||
|     theme: MaybeRefOrGetter<string> |     theme: MaybeRefOrGetter<string>, | ||||||
| ): UseQueryReturnType<LearningPath[], Error> { | ): UseQueryReturnType<LearningPath[], Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_PATH_KEY, "getAllByTheme", theme], |         queryKey: [LEARNING_PATH_KEY, "getAllByTheme", theme], | ||||||
|         queryFn: async () => learningPathController.getAllByTheme(toValue(theme)), |         queryFn: async () => learningPathController.getAllByTheme(toValue(theme)), | ||||||
|         enabled: () => Boolean(toValue(theme)), |         enabled: () => Boolean(toValue(theme)), | ||||||
|     }) |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useSearchLearningPathQuery( | export function useSearchLearningPathQuery( | ||||||
|     query: MaybeRefOrGetter<string | undefined> |     query: MaybeRefOrGetter<string | undefined>, | ||||||
| ): UseQueryReturnType<LearningPath[], Error> { | ): UseQueryReturnType<LearningPath[], Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_PATH_KEY, "search", query], |         queryKey: [LEARNING_PATH_KEY, "search", query], | ||||||
|  | @ -42,5 +42,5 @@ export function useSearchLearningPathQuery( | ||||||
|             return learningPathController.search(queryVal); |             return learningPathController.search(queryVal); | ||||||
|         }, |         }, | ||||||
|         enabled: () => Boolean(toValue(query)), |         enabled: () => Boolean(toValue(query)), | ||||||
|     }) |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -115,7 +115,7 @@ const router = createRouter({ | ||||||
|                     path: "search", |                     path: "search", | ||||||
|                     name: "LearningPathSearchPage", |                     name: "LearningPathSearchPage", | ||||||
|                     component: LearningPathSearchPage, |                     component: LearningPathSearchPage, | ||||||
|                     meta: { requiresAuth: true } |                     meta: { requiresAuth: true }, | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                     path: ":hruid/:language/:learningObjectHruid", |                     path: ":hruid/:language/:learningObjectHruid", | ||||||
|  | @ -124,14 +124,14 @@ const router = createRouter({ | ||||||
|                     props: true, |                     props: true, | ||||||
|                     meta: { requiresAuth: true }, |                     meta: { requiresAuth: true }, | ||||||
|                 }, |                 }, | ||||||
|             ] |             ], | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             path: "/learningObject/:hruid/:language/:version/raw", |             path: "/learningObject/:hruid/:language/:version/raw", | ||||||
|             name: "LearningObjectView", |             name: "LearningObjectView", | ||||||
|             component: LearningObjectView, |             component: LearningObjectView, | ||||||
|             props: true, |             props: true, | ||||||
|             meta: { requiresAuth: true } |             meta: { requiresAuth: true }, | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             path: "/:catchAll(.*)", |             path: "/:catchAll(.*)", | ||||||
|  |  | ||||||
|  | @ -7,6 +7,8 @@ export function single<T>(list: T[]): T { | ||||||
|     } else if (list.length === 0) { |     } else if (list.length === 0) { | ||||||
|         throw new NotFoundException("Expected list with exactly one element, but got an empty list."); |         throw new NotFoundException("Expected list with exactly one element, but got an empty list."); | ||||||
|     } else { |     } else { | ||||||
|         throw new InvalidResponseException(`Expected list with exactly one element, but got one with ${list.length} elements.`); |         throw new InvalidResponseException( | ||||||
|  |             `Expected list with exactly one element, but got one with ${list.length} elements.`, | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ const language = computed(() => locale.value); | ||||||
| 
 | 
 | ||||||
|     const themeQueryResult = useThemeQuery(language); |     const themeQueryResult = useThemeQuery(language); | ||||||
| 
 | 
 | ||||||
| const currentThemeInfo = computed(() => themeQueryResult.data.value?.find(it => it.key === props.theme)); |     const currentThemeInfo = computed(() => themeQueryResult.data.value?.find((it) => it.key === props.theme)); | ||||||
| 
 | 
 | ||||||
|     const learningPathsForThemeQueryResult = useGetAllLearningPathsByThemeQuery(() => props.theme); |     const learningPathsForThemeQueryResult = useGetAllLearningPathsByThemeQuery(() => props.theme); | ||||||
| 
 | 
 | ||||||
|  | @ -22,12 +22,12 @@ const { t } = useI18n(); | ||||||
|     const searchFilter = ref(""); |     const searchFilter = ref(""); | ||||||
| 
 | 
 | ||||||
|     function filterLearningPaths(learningPaths: LearningPath[]): LearningPath[] { |     function filterLearningPaths(learningPaths: LearningPath[]): LearningPath[] { | ||||||
|     return learningPaths.filter(it => |         return learningPaths.filter( | ||||||
|         it.title.toLowerCase().includes(searchFilter.value.toLowerCase()) |             (it) => | ||||||
|         || it.description.toLowerCase().includes(searchFilter.value.toLowerCase()) |                 it.title.toLowerCase().includes(searchFilter.value.toLowerCase()) || | ||||||
|  |                 it.description.toLowerCase().includes(searchFilter.value.toLowerCase()), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -44,7 +44,10 @@ function filterLearningPaths(learningPaths: LearningPath[]): LearningPath[] { | ||||||
|                 ></v-text-field> |                 ></v-text-field> | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|             <using-query-result :query-result="learningPathsForThemeQueryResult" v-slot="{ data }: {data: LearningPath[]}"> |             <using-query-result | ||||||
|  |                 :query-result="learningPathsForThemeQueryResult" | ||||||
|  |                 v-slot="{ data }: { data: LearningPath[] }" | ||||||
|  |             > | ||||||
|                 <learning-paths-grid :learning-paths="filterLearningPaths(data)"></learning-paths-grid> |                 <learning-paths-grid :learning-paths="filterLearningPaths(data)"></learning-paths-grid> | ||||||
|             </using-query-result> |             </using-query-result> | ||||||
|         </using-query-result> |         </using-query-result> | ||||||
|  |  | ||||||
|  | @ -4,15 +4,24 @@ 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"; | ||||||
| 
 | 
 | ||||||
| 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> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <using-query-result :query-result="learningObjectHtmlQueryResult as UseQueryReturnType<Document, Error>" v-slot="learningPathHtml : {data: Document}"> |     <using-query-result | ||||||
|         <div class="learning-object-container" v-html="learningPathHtml.data.body.innerHTML"></div> |         :query-result="learningObjectHtmlQueryResult as UseQueryReturnType<Document, Error>" | ||||||
|  |         v-slot="learningPathHtml: { data: Document }" | ||||||
|  |     > | ||||||
|  |         <div | ||||||
|  |             class="learning-object-container" | ||||||
|  |             v-html="learningPathHtml.data.body.innerHTML" | ||||||
|  |         ></div> | ||||||
|     </using-query-result> |     </using-query-result> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
|  | @ -32,7 +41,11 @@ const learningObjectHtmlQueryResult: UseQueryReturnType<Document, Error> = useLe | ||||||
|     :deep(img) { |     :deep(img) { | ||||||
|         max-width: 80%; |         max-width: 80%; | ||||||
|     } |     } | ||||||
|     :deep(h2), :deep(h3), :deep(h4), :deep(h5), :deep(h6) { |     :deep(h2), | ||||||
|  |     :deep(h3), | ||||||
|  |     :deep(h4), | ||||||
|  |     :deep(h5), | ||||||
|  |     :deep(h6) { | ||||||
|         margin-top: 10px; |         margin-top: 10px; | ||||||
|     } |     } | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -16,53 +16,46 @@ | ||||||
|     const route = useRoute(); |     const route = useRoute(); | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|     const props = defineProps<{hruid: string, language: Language, learningObjectHruid?: string}>() |     const props = defineProps<{ hruid: string; language: Language; learningObjectHruid?: string }>(); | ||||||
| 
 | 
 | ||||||
|     interface Personalization { |     interface Personalization { | ||||||
|         forStudent?: string, |         forStudent?: string; | ||||||
|         forGroup?: string |         forGroup?: string; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const personalization = computed(() => { |     const personalization = computed(() => { | ||||||
|         if (route.query.forStudent || route.query.forGroup) { |         if (route.query.forStudent || route.query.forGroup) { | ||||||
|             return { |             return { | ||||||
|                 forStudent: route.query.forStudent, |                 forStudent: route.query.forStudent, | ||||||
|                 forGroup: route.query.forGroup |                 forGroup: route.query.forGroup, | ||||||
|             } as Personalization |             } as Personalization; | ||||||
|         } |         } | ||||||
|         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(props.hruid, props.language, personalization); | ||||||
|         props.hruid, |  | ||||||
|         props.language, |  | ||||||
|         personalization |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     const learningObjectListQueryResult = useLearningObjectListForPathQuery(learningPathQueryResult.data); |     const learningObjectListQueryResult = useLearningObjectListForPathQuery(learningPathQueryResult.data); | ||||||
| 
 | 
 | ||||||
|     const nodesList: ComputedRef<LearningPathNode[] | null> = computed(() => |     const nodesList: ComputedRef<LearningPathNode[] | null> = computed( | ||||||
|         learningPathQueryResult.data.value?.nodesAsList ?? null |         () => learningPathQueryResult.data.value?.nodesAsList ?? null, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     const currentNode = computed(() => { |     const currentNode = computed(() => { | ||||||
|         const currentHruid = props.learningObjectHruid; |         const currentHruid = props.learningObjectHruid; | ||||||
|         return nodesList.value?.find(it => it.learningobjectHruid === currentHruid); |         return nodesList.value?.find((it) => it.learningobjectHruid === currentHruid); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const nextNode = computed(() => { |     const nextNode = computed(() => { | ||||||
|         if (!currentNode.value || !nodesList.value) |         if (!currentNode.value || !nodesList.value) return undefined; | ||||||
|             return undefined; |  | ||||||
|         const currentIndex = nodesList.value?.indexOf(currentNode.value); |         const currentIndex = nodesList.value?.indexOf(currentNode.value); | ||||||
|         return currentIndex < nodesList.value?.length ? nodesList.value?.[currentIndex + 1] : undefined; |         return currentIndex < nodesList.value?.length ? nodesList.value?.[currentIndex + 1] : undefined; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const previousNode = computed(() => { |     const previousNode = computed(() => { | ||||||
|         if (!currentNode.value || !nodesList.value) |         if (!currentNode.value || !nodesList.value) return undefined; | ||||||
|             return undefined; |  | ||||||
|         const currentIndex = nodesList.value?.indexOf(currentNode.value); |         const currentIndex = nodesList.value?.indexOf(currentNode.value); | ||||||
|         return currentIndex < nodesList.value?.length ? nodesList.value?.[currentIndex - 1] : undefined; |         return currentIndex < nodesList.value?.length ? nodesList.value?.[currentIndex - 1] : undefined; | ||||||
|     }); |     }); | ||||||
|  | @ -71,11 +64,14 @@ | ||||||
| 
 | 
 | ||||||
|     function isLearningObjectCompleted(learningObject: LearningObject): boolean { |     function isLearningObjectCompleted(learningObject: LearningObject): boolean { | ||||||
|         if (learningObjectListQueryResult.isSuccess) { |         if (learningObjectListQueryResult.isSuccess) { | ||||||
|             return learningPathQueryResult.data.value?.nodesAsList?.find(it => |             return ( | ||||||
|                 it.learningobjectHruid === learningObject.key |                 learningPathQueryResult.data.value?.nodesAsList?.find( | ||||||
|                 && it.version === learningObject.version |                     (it) => | ||||||
|                 && it.language === learningObject.language |                         it.learningobjectHruid === learningObject.key && | ||||||
|             )?.done ?? false; |                         it.version === learningObject.version && | ||||||
|  |                         it.language === learningObject.language, | ||||||
|  |                 )?.done ?? false | ||||||
|  |             ); | ||||||
|         } |         } | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  | @ -85,14 +81,14 @@ | ||||||
|     const ICONS: Record<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: Record<NavItemState, string | undefined> = { |     const COLORS: Record<NavItemState, string | undefined> = { | ||||||
|         teacherExclusive: "info", |         teacherExclusive: "info", | ||||||
|         completed: "success", |         completed: "success", | ||||||
|         notCompleted: undefined |         notCompleted: undefined, | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     function getNavItemState(learningObject: LearningObject): NavItemState { |     function getNavItemState(learningObject: LearningObject): NavItemState { | ||||||
|         if (learningObject.teacherExclusive) { |         if (learningObject.teacherExclusive) { | ||||||
|  | @ -101,7 +97,6 @@ | ||||||
|             return "completed"; |             return "completed"; | ||||||
|         } |         } | ||||||
|         return "notCompleted"; |         return "notCompleted"; | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -121,9 +116,27 @@ | ||||||
|             </v-list-item> |             </v-list-item> | ||||||
|             <v-list-item> |             <v-list-item> | ||||||
|                 <template v-slot:subtitle> |                 <template v-slot:subtitle> | ||||||
|                     <p><v-icon :color="COLORS.notCompleted" :icon="ICONS.notCompleted"></v-icon> {{ t("legendNotCompletedYet") }}</p> |                     <p> | ||||||
|                     <p><v-icon :color="COLORS.completed" :icon="ICONS.completed"></v-icon> {{ t("legendCompleted") }}</p> |                         <v-icon | ||||||
|                     <p><v-icon :color="COLORS.teacherExclusive" :icon="ICONS.teacherExclusive"></v-icon> {{ t("legendTeacherExclusive") }}</p> |                             :color="COLORS.notCompleted" | ||||||
|  |                             :icon="ICONS.notCompleted" | ||||||
|  |                         ></v-icon> | ||||||
|  |                         {{ t("legendNotCompletedYet") }} | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                         <v-icon | ||||||
|  |                             :color="COLORS.completed" | ||||||
|  |                             :icon="ICONS.completed" | ||||||
|  |                         ></v-icon> | ||||||
|  |                         {{ t("legendCompleted") }} | ||||||
|  |                     </p> | ||||||
|  |                     <p> | ||||||
|  |                         <v-icon | ||||||
|  |                             :color="COLORS.teacherExclusive" | ||||||
|  |                             :icon="ICONS.teacherExclusive" | ||||||
|  |                         ></v-icon> | ||||||
|  |                         {{ t("legendTeacherExclusive") }} | ||||||
|  |                     </p> | ||||||
|                 </template> |                 </template> | ||||||
|             </v-list-item> |             </v-list-item> | ||||||
|             <v-divider></v-divider> |             <v-divider></v-divider> | ||||||
|  | @ -144,11 +157,10 @@ | ||||||
|                             <template v-slot:prepend> |                             <template v-slot:prepend> | ||||||
|                                 <v-icon |                                 <v-icon | ||||||
|                                     :color="COLORS[getNavItemState(node)]" |                                     :color="COLORS[getNavItemState(node)]" | ||||||
|                                     :icon="ICONS[getNavItemState(node)]"></v-icon> |                                     :icon="ICONS[getNavItemState(node)]" | ||||||
|                             </template> |                                 ></v-icon> | ||||||
|                             <template v-slot:append> |  | ||||||
|                                 {{ node.estimatedTime }}' |  | ||||||
|                             </template> |                             </template> | ||||||
|  |                             <template v-slot:append> {{ node.estimatedTime }}' </template> | ||||||
|                         </v-list-item> |                         </v-list-item> | ||||||
|                     </template> |                     </template> | ||||||
|                 </using-query-result> |                 </using-query-result> | ||||||
|  | @ -159,7 +171,8 @@ | ||||||
|                 :icon="navigationDrawerShown ? 'mdi-menu-open' : 'mdi-menu'" |                 :icon="navigationDrawerShown ? 'mdi-menu-open' : 'mdi-menu'" | ||||||
|                 class="navigation-drawer-toggle-button" |                 class="navigation-drawer-toggle-button" | ||||||
|                 variant="plain" |                 variant="plain" | ||||||
|                 @click="navigationDrawerShown = !navigationDrawerShown"></v-btn> |                 @click="navigationDrawerShown = !navigationDrawerShown" | ||||||
|  |             ></v-btn> | ||||||
|             <div class="search-field-container"> |             <div class="search-field-container"> | ||||||
|                 <learning-path-search-field></learning-path-search-field> |                 <learning-path-search-field></learning-path-search-field> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|  | @ -21,7 +21,10 @@ | ||||||
|         <learning-path-search-field class="search-field"></learning-path-search-field> |         <learning-path-search-field class="search-field"></learning-path-search-field> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <using-query-result :query-result="searchQueryResults" v-slot="{ data }: {data: LearningPath[]}"> |     <using-query-result | ||||||
|  |         :query-result="searchQueryResults" | ||||||
|  |         v-slot="{ data }: { data: LearningPath[] }" | ||||||
|  |     > | ||||||
|         <learning-paths-grid :learning-paths="data"></learning-paths-grid> |         <learning-paths-grid :learning-paths="data"></learning-paths-grid> | ||||||
|     </using-query-result> |     </using-query-result> | ||||||
|     <div content="empty-state-container"> |     <div content="empty-state-container"> | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger