style: fix linting issues met Prettier

This commit is contained in:
Lint Action 2025-04-01 15:09:28 +00:00
parent ed8b5c919d
commit ea5cf7abf9
26 changed files with 467 additions and 422 deletions

View file

@ -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);

View file

@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import ThemeCard from "@/components/ThemeCard.vue"; import ThemeCard from "@/components/ThemeCard.vue";
import {ref, watchEffect, computed, type Ref} from "vue"; import { ref, watchEffect, computed, type Ref } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { AGE_TO_THEMES, THEMESITEMS } from "@/utils/constants.ts"; import { AGE_TO_THEMES, THEMESITEMS } from "@/utils/constants.ts";
import { useThemeQuery } from "@/queries/themes.ts"; import { useThemeQuery } from "@/queries/themes.ts";
import type {Theme} from "@/data-objects/theme.ts"; import type { Theme } from "@/data-objects/theme.ts";
const props = defineProps({ const props = defineProps({
selectedTheme: { type: String, required: true }, selectedTheme: { type: String, required: true },
@ -17,7 +17,7 @@
const { data: allThemes, isLoading, error } = useThemeQuery(language); const { data: allThemes, isLoading, error } = useThemeQuery(language);
const allCards: Ref<Theme[]> = ref([]); const allCards: Ref<Theme[]> = ref([]);
const cards: Ref<Theme[]> = ref([]); const cards: Ref<Theme[]> = ref([]);
watchEffect(() => { watchEffect(() => {
const themes: Theme[] = allThemes.value ?? []; const themes: Theme[] = allThemes.value ?? [];

View file

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import {useI18n} from "vue-i18n"; import { useI18n } from "vue-i18n";
import {useRoute, useRouter} from "vue-router"; import { useRoute, useRouter } from "vue-router";
import {computed, ref} from "vue"; import { computed, ref } from "vue";
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
@ -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>

View file

@ -1,16 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { convertBase64ToImageSrc } from "@/utils/base64ToImage.ts";
import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts";
import { useI18n } from "vue-i18n";
import {convertBase64ToImageSrc} from "@/utils/base64ToImage.ts"; const { t } = useI18n();
import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; const props = defineProps<{ learningPaths: LearningPath[] }>();
import {useI18n} from "vue-i18n";
const { t } = useI18n();
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>{{ learningPath.title }}</v-card-title> <v-card-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')"

View file

@ -1,24 +1,27 @@
<script setup lang="ts" generic="T"> <script setup lang="ts" generic="T">
import {computed} from "vue"; import { computed } from "vue";
import {useI18n} from "vue-i18n"; import { useI18n } from "vue-i18n";
import type {UseQueryReturnType} from "@tanstack/vue-query"; import type { UseQueryReturnType } from "@tanstack/vue-query";
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;
const { t } = useI18n(); const { t } = useI18n();
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,12 +31,15 @@
: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>
.loading-div { .loading-div {
padding: 20px; padding: 20px;
text-align: center; text-align: center;
} }
</style> </style>

View file

@ -1,6 +1,6 @@
import apiClient from "@/services/api-client/api-client.ts"; import apiClient from "@/services/api-client/api-client.ts";
import type {AxiosResponse, ResponseType} from "axios"; import type { AxiosResponse, ResponseType } from "axios";
import {HttpErrorResponseException} from "@/exception/http-error-response-exception.ts"; import { HttpErrorResponseException } from "@/exception/http-error-response-exception.ts";
export abstract class BaseController { export abstract class BaseController {
protected basePath: string; protected basePath: string;
@ -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;
} }

View file

@ -1,6 +1,6 @@
import { ThemeController } from "@/controllers/themes.ts"; import { ThemeController } from "@/controllers/themes.ts";
import {LearningObjectController} from "@/controllers/learning-objects.ts"; import { LearningObjectController } from "@/controllers/learning-objects.ts";
import {LearningPathController} from "@/controllers/learning-paths.ts"; import { LearningPathController } from "@/controllers/learning-paths.ts";
export function controllerGetter<T>(factory: new () => T): () => T { export function controllerGetter<T>(factory: new () => T): () => T {
let instance: T | undefined; let instance: T | undefined;

View file

@ -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-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() {
@ -8,10 +8,10 @@ export class LearningObjectController extends BaseController {
} }
async getMetadata(hruid: string, language: Language, version: number): Promise<LearningObject> { async getMetadata(hruid: string, language: Language, version: number): Promise<LearningObject> {
return this.get<LearningObject>(`/${hruid}`, {language, version}); return this.get<LearningObject>(`/${hruid}`, { language, version });
} }
async getHTML(hruid: string, language: Language, version: number): Promise<Document> { async getHTML(hruid: string, language: Language, version: number): Promise<Document> {
return this.get<Document>(`/${hruid}/html`, {language, version}, "document"); return this.get<Document>(`/${hruid}/html`, { language, version }, "document");
} }
} }

View file

@ -1,28 +1,32 @@
import {BaseController} from "@/controllers/base-controller.ts"; import { BaseController } from "@/controllers/base-controller.ts";
import {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; import { LearningPath } from "@/data-objects/learning-paths/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"; 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): 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));
} }
} }

View file

@ -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",
} }

View file

@ -1,6 +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"; import type { ReturnValue } from "@/data-objects/learning-objects/return-value.ts";
import type {EducationalGoal} from "@/data-objects/learning-objects/educational-goal.ts"; import type { EducationalGoal } from "@/data-objects/learning-objects/educational-goal.ts";
export interface LearningObject { export interface LearningObject {
key: string; key: string;

View file

@ -1,4 +1,4 @@
import type {LearningPathNodeDTO} from "@/data-objects/learning-paths/learning-path.ts"; import type { LearningPathNodeDTO } from "@/data-objects/learning-paths/learning-path.ts";
export interface LearningPathDTO { export interface LearningPathDTO {
language: string; language: string;

View file

@ -1,23 +1,23 @@
import type {Language} from "@/data-objects/language.ts"; import type { Language } from "@/data-objects/language.ts";
import type {LearningPathNodeDTO} from "@/data-objects/learning-paths/learning-path.ts"; import type { LearningPathNodeDTO } from "@/data-objects/learning-paths/learning-path.ts";
export class LearningPathNode { 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,
}) });
} }
} }

View file

@ -1,6 +1,6 @@
import type {Language} from "@/data-objects/language.ts"; import type { Language } from "@/data-objects/language.ts";
import {LearningPathNode} from "@/data-objects/learning-paths/learning-path-node.ts"; import { LearningPathNode } from "@/data-objects/learning-paths/learning-path-node.ts";
import type {LearningPathDTO} from "@/data-objects/learning-paths/learning-path-dto.ts"; import type { LearningPathDTO } from "@/data-objects/learning-paths/learning-path-dto.ts";
export interface LearningPathNodeDTO { export interface LearningPathNodeDTO {
_id: string; _id: string;
@ -33,21 +33,21 @@ export class LearningPath {
public readonly amountOfNodes: number; public readonly amountOfNodes: number;
public readonly amountOfNodesLeft: number; public readonly amountOfNodesLeft: number;
public readonly keywords: string[]; public readonly keywords: string[];
public readonly targetAges: {min: number; max: number}; public readonly targetAges: { min: number; max: number };
public readonly startNode: LearningPathNode; public readonly startNode: LearningPathNode;
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,
}); });
} }
} }

View file

@ -1,9 +1,9 @@
import type {AxiosResponse} from "axios"; import type { AxiosResponse } from "axios";
export class HttpErrorResponseException extends Error { export class HttpErrorResponseException extends Error {
public statusCode: number; public statusCode: number;
constructor(public response: AxiosResponse<unknown, unknown>) { constructor(public response: AxiosResponse<unknown, unknown>) {
super((response.data as {message: string})?.message || JSON.stringify(response.data)); super((response.data as { message: string })?.message || JSON.stringify(response.data));
this.statusCode = response.status; this.statusCode = response.status;
} }
} }

View file

@ -10,8 +10,8 @@ import i18n from "./i18n/i18n.ts";
// Components // Components
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({

View file

@ -1,9 +1,9 @@
import {type MaybeRefOrGetter, toValue} from "vue"; 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-objects/learning-object.ts"; import type { LearningObject } from "@/data-objects/learning-objects/learning-object.ts";
import type {LearningPath} from "@/data-objects/learning-paths/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();
@ -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);

View file

@ -1,8 +1,8 @@
import {type MaybeRefOrGetter, toValue} from "vue"; 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-paths/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();
@ -10,31 +10,31 @@ 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],
queryFn: async () => { queryFn: async () => {
@ -42,5 +42,5 @@ export function useSearchLearningPathQuery(
return learningPathController.search(queryVal); return learningPathController.search(queryVal);
}, },
enabled: () => Boolean(toValue(query)), enabled: () => Boolean(toValue(query)),
}) });
} }

View file

@ -1,7 +1,7 @@
import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query";
import { getThemeController } from "@/controllers/controllers"; import { getThemeController } from "@/controllers/controllers";
import { type MaybeRefOrGetter, toValue } from "vue"; import { type MaybeRefOrGetter, toValue } from "vue";
import type {Theme} from "@/data-objects/theme.ts"; import type { Theme } from "@/data-objects/theme.ts";
const themeController = getThemeController(); const themeController = getThemeController();

View file

@ -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(.*)",

View file

@ -1,5 +1,5 @@
import {NotFoundException} from "@/exception/not-found-exception.ts"; import { NotFoundException } from "@/exception/not-found-exception.ts";
import {InvalidResponseException} from "@/exception/invalid-response-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) {
@ -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.`,
);
} }
} }

View file

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import {onMounted, ref, type Ref} 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();

View file

@ -1,33 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
import type {LearningPath} from "@/data-objects/learning-paths/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";
import {computed, ref} from "vue"; import { computed, ref } from "vue";
import {useI18n} from "vue-i18n"; import { useI18n } from "vue-i18n";
import {useThemeQuery} from "@/queries/themes.ts"; import { useThemeQuery } from "@/queries/themes.ts";
const props = defineProps<{theme: string}>(); const props = defineProps<{ theme: string }>();
const { locale } = useI18n(); const { locale } = useI18n();
const language = computed(() => locale.value); 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);
const { t } = useI18n(); const { t } = useI18n();
const searchFilter = ref(""); const searchFilter = ref("");
function filterLearningPaths(learningPaths: LearningPath[]): LearningPath[] {
return learningPaths.filter(it =>
it.title.toLowerCase().includes(searchFilter.value.toLowerCase())
|| it.description.toLowerCase().includes(searchFilter.value.toLowerCase())
);
}
function filterLearningPaths(learningPaths: LearningPath[]): LearningPath[] {
return learningPaths.filter(
(it) =>
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>

View file

@ -1,18 +1,27 @@
<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";
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>

View file

@ -1,68 +1,61 @@
<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} from "@/data-objects/learning-paths/learning-path.ts"; import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts";
import {computed, type ComputedRef, ref} from "vue"; import { computed, type ComputedRef, ref } from "vue";
import type {LearningObject} from "@/data-objects/learning-objects/learning-object.ts"; import type { LearningObject } from "@/data-objects/learning-objects/learning-object.ts";
import {useRoute} from "vue-router"; import { useRoute } from "vue-router";
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";
import {useGetLearningPathQuery} from "@/queries/learning-paths.ts"; import { useGetLearningPathQuery } from "@/queries/learning-paths.ts";
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"; import { LearningPathNode } from "@/data-objects/learning-paths/learning-path-node.ts";
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) {
@ -100,15 +96,14 @@
} else if (isLearningObjectCompleted(learningObject)) { } else if (isLearningObjectCompleted(learningObject)) {
return "completed"; return "completed";
} }
return "notCompleted"; return "notCompleted";
} }
</script> </script>
<template> <template>
<using-query-result <using-query-result
:query-result="learningPathQueryResult" :query-result="learningPathQueryResult"
v-slot="learningPath: {data: LearningPath}" v-slot="learningPath: { data: LearningPath }"
> >
<v-navigation-drawer v-model="navigationDrawerShown"> <v-navigation-drawer v-model="navigationDrawerShown">
<v-list-item <v-list-item
@ -117,21 +112,39 @@
></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>
<div v-if="props.learningObjectHruid"> <div v-if="props.learningObjectHruid">
<using-query-result <using-query-result
:query-result="learningObjectListQueryResult" :query-result="learningObjectListQueryResult"
v-slot="learningObjects: {data: LearningObject[]}" v-slot="learningObjects: { data: LearningObject[] }"
> >
<template v-for="node in learningObjects.data"> <template v-for="node in learningObjects.data">
<v-list-item <v-list-item
link link
: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" :key="node.key"
@ -140,11 +153,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>
@ -155,7 +167,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>
@ -171,7 +184,7 @@
prepend-icon="mdi-chevron-left" prepend-icon="mdi-chevron-left"
variant="text" variant="text"
:disabled="!previousNode" :disabled="!previousNode"
:to="previousNode ? {path: previousNode.learningobjectHruid, query: route.query} : undefined" :to="previousNode ? { path: previousNode.learningobjectHruid, query: route.query } : undefined"
> >
{{ t("previous") }} {{ t("previous") }}
</v-btn> </v-btn>
@ -179,7 +192,7 @@
append-icon="mdi-chevron-right" append-icon="mdi-chevron-right"
variant="text" variant="text"
:disabled="!nextNode" :disabled="!nextNode"
:to="nextNode ? {path: nextNode.learningobjectHruid, query: route.query} : undefined" :to="nextNode ? { path: nextNode.learningobjectHruid, query: route.query } : undefined"
> >
{{ t("next") }} {{ t("next") }}
</v-btn> </v-btn>

View file

@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts"; import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts";
import {useRoute} 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";
import {useSearchLearningPathQuery} from "@/queries/learning-paths.ts"; import { useSearchLearningPathQuery } from "@/queries/learning-paths.ts";
import UsingQueryResult from "@/components/UsingQueryResult.vue"; import UsingQueryResult from "@/components/UsingQueryResult.vue";
import LearningPathsGrid from "@/components/LearningPathsGrid.vue"; import LearningPathsGrid from "@/components/LearningPathsGrid.vue";
@ -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">