diff --git a/frontend/src/components/LearningPathSearchField.vue b/frontend/src/components/LearningPathSearchField.vue
index d9e9f85d..2d89c192 100644
--- a/frontend/src/components/LearningPathSearchField.vue
+++ b/frontend/src/components/LearningPathSearchField.vue
@@ -10,12 +10,12 @@
const query = computed({
get: () => route.query.query as string | null,
- set: (newValue) => router.push({path: SEARCH_PATH, query: {query: newValue}})
+ set: async (newValue) => router.push({path: SEARCH_PATH, query: {query: newValue}})
});
const queryInput = ref(query.value);
- function search() {
+ function search(): void {
query.value = queryInput.value;
}
diff --git a/frontend/src/components/LearningPathsGrid.vue b/frontend/src/components/LearningPathsGrid.vue
index aeaf5003..11aab575 100644
--- a/frontend/src/components/LearningPathsGrid.vue
+++ b/frontend/src/components/LearningPathsGrid.vue
@@ -1,7 +1,7 @@
diff --git a/frontend/src/controllers/base-controller.ts b/frontend/src/controllers/base-controller.ts
index d05f579e..ab2b3af6 100644
--- a/frontend/src/controllers/base-controller.ts
+++ b/frontend/src/controllers/base-controller.ts
@@ -9,40 +9,42 @@ export abstract class BaseController {
this.basePath = basePath;
}
- private assertSuccessResponse(response: AxiosResponse) {
+ private static assertSuccessResponse(response: AxiosResponse): void {
if (response.status / 100 !== 2) {
throw new HttpErrorResponseException(response);
}
}
- private absolutePathFor(path: string) {
- return "/" + this.basePath + path;
- }
-
- protected async get(path: string, queryParams?: Record, responseType?: ResponseType): Promise {
- let response = await apiClient.get(
+ protected async get(path: string, queryParams?: QueryParams, responseType?: ResponseType): Promise {
+ const response = await apiClient.get(
this.absolutePathFor(path),
{params: queryParams, responseType}
);
- this.assertSuccessResponse(response);
+ BaseController.assertSuccessResponse(response);
return response.data;
}
protected async post(path: string, body: unknown): Promise {
- let response = await apiClient.post(this.absolutePathFor(path), body);
- this.assertSuccessResponse(response);
+ const response = await apiClient.post(this.absolutePathFor(path), body);
+ BaseController.assertSuccessResponse(response);
return response.data;
}
protected async delete(path: string): Promise {
- let response = await apiClient.delete(this.absolutePathFor(path))
- this.assertSuccessResponse(response);
+ const response = await apiClient.delete(this.absolutePathFor(path))
+ BaseController.assertSuccessResponse(response);
return response.data;
}
protected async put(path: string, body: unknown): Promise {
- let response = await apiClient.put(this.absolutePathFor(path), body);
- this.assertSuccessResponse(response);
+ const response = await apiClient.put(this.absolutePathFor(path), body);
+ BaseController.assertSuccessResponse(response);
return response.data;
}
+
+ private absolutePathFor(path: string): string {
+ return "/" + this.basePath + path;
+ }
}
+
+type QueryParams = Record;
diff --git a/frontend/src/controllers/learning-objects.ts b/frontend/src/controllers/learning-objects.ts
index df70326b..35a4f755 100644
--- a/frontend/src/controllers/learning-objects.ts
+++ b/frontend/src/controllers/learning-objects.ts
@@ -1,6 +1,6 @@
import {BaseController} from "@/controllers/base-controller.ts";
import type {Language} from "@/data-objects/language.ts";
-import type {LearningObject} from "@/data-objects/learning-object.ts";
+import type {LearningObject} from "@/data-objects/learning-objects/learning-object.ts";
export class LearningObjectController extends BaseController {
constructor() {
diff --git a/frontend/src/controllers/learning-paths.ts b/frontend/src/controllers/learning-paths.ts
index c729c7e9..3add1175 100644
--- a/frontend/src/controllers/learning-paths.ts
+++ b/frontend/src/controllers/learning-paths.ts
@@ -1,19 +1,19 @@
import {BaseController} from "@/controllers/base-controller.ts";
-import {LearningPath} from "@/data-objects/learning-path.ts";
-import type {LearningPathDTO} from "@/data-objects/learning-path.ts";
+import {LearningPath} from "@/data-objects/learning-paths/learning-path.ts";
import type {Language} from "@/data-objects/language.ts";
import {single} from "@/utils/response-assertions.ts";
+import type {LearningPathDTO} from "@/data-objects/learning-paths/learning-path-dto.ts";
export class LearningPathController extends BaseController {
constructor() {
super("learningPath");
}
- async search(query: string) {
- let dtos = await this.get("/", {search: query});
+ async search(query: string): Promise {
+ const dtos = await this.get("/", {search: query});
return dtos.map(dto => LearningPath.fromDTO(dto));
}
- async getBy(hruid: string, language: Language, options?: {forGroup?: string, forStudent?: string}) {
- let dtos = await this.get("/", {
+ async getBy(hruid: string, language: Language, options?: {forGroup?: string, forStudent?: string}): Promise {
+ const dtos = await this.get("/", {
hruid,
language,
forGroup: options?.forGroup,
@@ -21,8 +21,8 @@ export class LearningPathController extends BaseController {
});
return LearningPath.fromDTO(single(dtos));
}
- async getAllByTheme(theme: string) {
- let dtos = await this.get("/", {theme});
+ async getAllByTheme(theme: string): Promise {
+ const dtos = await this.get("/", {theme});
return dtos.map(dto => LearningPath.fromDTO(dto));
}
}
diff --git a/frontend/src/data-objects/learning-objects/educational-goal.ts b/frontend/src/data-objects/learning-objects/educational-goal.ts
new file mode 100644
index 00000000..05c7a786
--- /dev/null
+++ b/frontend/src/data-objects/learning-objects/educational-goal.ts
@@ -0,0 +1,4 @@
+export interface EducationalGoal {
+ source: string;
+ id: string;
+}
diff --git a/frontend/src/data-objects/learning-object.ts b/frontend/src/data-objects/learning-objects/learning-object.ts
similarity index 76%
rename from frontend/src/data-objects/learning-object.ts
rename to frontend/src/data-objects/learning-objects/learning-object.ts
index 9599d44f..803e83ae 100644
--- a/frontend/src/data-objects/learning-object.ts
+++ b/frontend/src/data-objects/learning-objects/learning-object.ts
@@ -1,14 +1,6 @@
import type {Language} from "@/data-objects/language.ts";
-
-export interface EducationalGoal {
- source: string;
- id: string;
-}
-
-export interface ReturnValue {
- callback_url: string;
- callback_schema: Record;
-}
+import type {ReturnValue} from "@/data-objects/learning-objects/return-value.ts";
+import type {EducationalGoal} from "@/data-objects/learning-objects/educational-goal.ts";
export interface LearningObject {
key: string;
diff --git a/frontend/src/data-objects/learning-objects/return-value.ts b/frontend/src/data-objects/learning-objects/return-value.ts
new file mode 100644
index 00000000..56b89380
--- /dev/null
+++ b/frontend/src/data-objects/learning-objects/return-value.ts
@@ -0,0 +1,4 @@
+export interface ReturnValue {
+ callback_url: string;
+ callback_schema: Record;
+}
diff --git a/frontend/src/data-objects/learning-path.ts b/frontend/src/data-objects/learning-path.ts
deleted file mode 100644
index ddafb41f..00000000
--- a/frontend/src/data-objects/learning-path.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import type {Language} from "@/data-objects/language.ts";
-
-export interface LearningPathDTO {
- language: string;
- hruid: string;
- title: string;
- description: string;
- image?: string; // Image might be missing, so it's optional
- num_nodes: number;
- num_nodes_left: number;
- nodes: LearningPathNodeDTO[];
- keywords: string;
- target_ages: number[];
- min_age: number;
- max_age: number;
- __order: number;
-}
-
-interface LearningPathNodeDTO {
- _id: string;
- learningobject_hruid: string;
- version: number;
- language: Language;
- start_node?: boolean;
- transitions: LearningPathTransitionDTO[];
- created_at: string;
- updatedAt: string;
- done?: boolean; // True if a submission exists for this node by the user for whom the learning path is customized.
-}
-
-interface LearningPathTransitionDTO {
- default: boolean;
- _id: string;
- next: {
- _id: string;
- hruid: string;
- version: number;
- language: string;
- };
-}
-
-export class LearningPathNode {
-
- constructor(
- public readonly learningobjectHruid: string,
- public readonly version: number,
- public readonly language: Language,
- public readonly transitions: {next: LearningPathNode, default: boolean}[],
- public readonly createdAt: Date,
- public readonly updatedAt: Date,
- public readonly done: boolean = false
- ) {
- }
-
- static fromDTOAndOtherNodes(dto: LearningPathNodeDTO, otherNodes: LearningPathNodeDTO[]): LearningPathNode {
- return new LearningPathNode(
- dto.learningobject_hruid,
- dto.version,
- dto.language,
- dto.transitions.map(transDto => {
- let nextNodeDto = otherNodes.filter(it =>
- it.learningobject_hruid === transDto.next.hruid
- && it.language === transDto.next.language
- && it.version === transDto.next.version
- );
- if (nextNodeDto.length !== 1) {
- throw new Error(`Invalid learning path! There is a transition to node`
- + `${transDto.next.hruid}/${transDto.next.language}/${transDto.next.version}, but there are`
- + `${nextNodeDto.length} such nodes.`);
- }
- return {
- next: LearningPathNode.fromDTOAndOtherNodes(nextNodeDto[0], otherNodes),
- default: transDto.default
- }
- }),
- new Date(dto.created_at),
- new Date(dto.updatedAt),
- dto.done
- )
- }
-}
-
-export class LearningPath {
- constructor(
- public readonly language: string,
- public readonly hruid: string,
- public readonly title: string,
- public readonly description: string,
- public readonly amountOfNodes: number,
- public readonly amountOfNodesLeft: number,
- public readonly keywords: string[],
- public readonly targetAges: {min: number; max: number},
- public readonly startNode: LearningPathNode,
- public readonly image?: string // Image might be missing, so it's optional
- ) {
- }
-
- public get nodesAsList(): LearningPathNode[] {
- let list: LearningPathNode[] = [];
- let currentNode = this.startNode;
- while (currentNode) {
- list.push(currentNode);
- currentNode = currentNode.transitions.filter(it => it.default)[0]?.next
- || currentNode.transitions[0]?.next;
- }
- return list;
- }
-
- static fromDTO(dto: LearningPathDTO): LearningPath {
- let startNodeDto = dto.nodes.filter(it => it.start_node === true);
- if (startNodeDto.length !== 1) {
- throw new Error(`Invalid learning path: ${dto.hruid}/${dto.language}!
- Expected precisely one start node, but there were ${startNodeDto.length}.`);
- }
- return new LearningPath(
- dto.language,
- dto.hruid,
- dto.title,
- dto.description,
- dto.num_nodes,
- dto.num_nodes_left,
- dto.keywords.split(' '),
- {min: dto.min_age, max: dto.max_age},
- LearningPathNode.fromDTOAndOtherNodes(startNodeDto[0], dto.nodes),
- dto.image
- )
- }
-}
diff --git a/frontend/src/data-objects/learning-paths/learning-path-dto.ts b/frontend/src/data-objects/learning-paths/learning-path-dto.ts
new file mode 100644
index 00000000..5e0f8746
--- /dev/null
+++ b/frontend/src/data-objects/learning-paths/learning-path-dto.ts
@@ -0,0 +1,17 @@
+import type {LearningPathNodeDTO} from "@/data-objects/learning-paths/learning-path.ts";
+
+export interface LearningPathDTO {
+ language: string;
+ hruid: string;
+ title: string;
+ description: string;
+ image?: string; // Image might be missing, so it's optional
+ num_nodes: number;
+ num_nodes_left: number;
+ nodes: LearningPathNodeDTO[];
+ keywords: string;
+ target_ages: number[];
+ min_age: number;
+ max_age: number;
+ __order: number;
+}
diff --git a/frontend/src/data-objects/learning-paths/learning-path-node.ts b/frontend/src/data-objects/learning-paths/learning-path-node.ts
new file mode 100644
index 00000000..d423f6bc
--- /dev/null
+++ b/frontend/src/data-objects/learning-paths/learning-path-node.ts
@@ -0,0 +1,57 @@
+import type {Language} from "@/data-objects/language.ts";
+import type {LearningPathNodeDTO} from "@/data-objects/learning-paths/learning-path.ts";
+
+export class LearningPathNode {
+ public readonly learningobjectHruid: string;
+ public readonly version: number;
+ public readonly language: Language;
+ public readonly transitions: { next: LearningPathNode, default: boolean }[];
+ public readonly createdAt: Date;
+ public readonly updatedAt: Date;
+ public readonly done: boolean;
+
+ constructor(options: {
+ learningobjectHruid: string,
+ version: number,
+ language: Language,
+ transitions: { next: LearningPathNode, default: boolean }[],
+ createdAt: Date,
+ updatedAt: Date,
+ done?: boolean
+ }) {
+ this.learningobjectHruid = options.learningobjectHruid;
+ this.version = options.version;
+ this.language = options.language;
+ this.transitions = options.transitions;
+ this.createdAt = options.createdAt;
+ this.updatedAt = options.updatedAt;
+ this.done = options.done || false;
+ }
+
+ static fromDTOAndOtherNodes(dto: LearningPathNodeDTO, otherNodes: LearningPathNodeDTO[]): LearningPathNode {
+ return new LearningPathNode({
+ learningobjectHruid: dto.learningobject_hruid,
+ version: dto.version,
+ language: dto.language,
+ transitions: dto.transitions.map(transDto => {
+ const nextNodeDto = otherNodes.filter(it =>
+ it.learningobject_hruid === transDto.next.hruid
+ && it.language === transDto.next.language
+ && it.version === transDto.next.version
+ );
+ if (nextNodeDto.length !== 1) {
+ throw new Error(`Invalid learning path! There is a transition to node`
+ + `${transDto.next.hruid}/${transDto.next.language}/${transDto.next.version}, but there are`
+ + `${nextNodeDto.length} such nodes.`);
+ }
+ return {
+ next: LearningPathNode.fromDTOAndOtherNodes(nextNodeDto[0], otherNodes),
+ default: transDto.default
+ }
+ }),
+ createdAt: new Date(dto.created_at),
+ updatedAt: new Date(dto.updatedAt),
+ done: dto.done
+ })
+ }
+}
diff --git a/frontend/src/data-objects/learning-paths/learning-path.ts b/frontend/src/data-objects/learning-paths/learning-path.ts
new file mode 100644
index 00000000..00a33522
--- /dev/null
+++ b/frontend/src/data-objects/learning-paths/learning-path.ts
@@ -0,0 +1,94 @@
+import type {Language} from "@/data-objects/language.ts";
+import {LearningPathNode} from "@/data-objects/learning-paths/learning-path-node.ts";
+import type {LearningPathDTO} from "@/data-objects/learning-paths/learning-path-dto.ts";
+
+export interface LearningPathNodeDTO {
+ _id: string;
+ learningobject_hruid: string;
+ version: number;
+ language: Language;
+ start_node?: boolean;
+ transitions: LearningPathTransitionDTO[];
+ created_at: string;
+ updatedAt: string;
+ done?: boolean; // True if a submission exists for this node by the user for whom the learning path is customized.
+}
+
+export interface LearningPathTransitionDTO {
+ default: boolean;
+ _id: string;
+ next: {
+ _id: string;
+ hruid: string;
+ version: number;
+ language: string;
+ };
+}
+
+export class LearningPath {
+ public readonly language: string;
+ public readonly hruid: string;
+ public readonly title: string;
+ public readonly description: string;
+ public readonly amountOfNodes: number;
+ public readonly amountOfNodesLeft: number;
+ public readonly keywords: string[];
+ public readonly targetAges: {min: number; max: number};
+ public readonly startNode: LearningPathNode;
+ public readonly image?: string; // Image might be missing, so it's optional
+
+ constructor(options: {
+ language: string,
+ hruid: string,
+ title: string,
+ description: string,
+ amountOfNodes: number,
+ amountOfNodesLeft: number,
+ keywords: string[],
+ targetAges: {min: number; max: number},
+ startNode: LearningPathNode,
+ image?: string // Image might be missing, so it's optional
+ }) {
+ this.language = options.language;
+ this.hruid = options.hruid;
+ this.title = options.title;
+ this.description = options.description;
+ this.amountOfNodes = options.amountOfNodes;
+ this.amountOfNodesLeft = options.amountOfNodesLeft;
+ this.keywords = options.keywords;
+ this.targetAges = options.targetAges;
+ this.startNode = options.startNode;
+ this.image = options.image;
+ }
+
+ public get nodesAsList(): LearningPathNode[] {
+ const list: LearningPathNode[] = [];
+ let currentNode = this.startNode;
+ while (currentNode) {
+ list.push(currentNode);
+ currentNode = currentNode.transitions.find(it => it.default)?.next
+ || currentNode.transitions[0]?.next;
+ }
+ return list;
+ }
+
+ static fromDTO(dto: LearningPathDTO): LearningPath {
+ const startNodeDto = dto.nodes.filter(it => it.start_node === true);
+ if (startNodeDto.length !== 1) {
+ throw new Error(`Invalid learning path: ${dto.hruid}/${dto.language}!
+ Expected precisely one start node, but there were ${startNodeDto.length}.`);
+ }
+ return new LearningPath({
+ language: dto.language,
+ hruid: dto.hruid,
+ title: dto.title,
+ description: dto.description,
+ amountOfNodes: dto.num_nodes,
+ amountOfNodesLeft: dto.num_nodes_left,
+ keywords: dto.keywords.split(' '),
+ targetAges: {min: dto.min_age, max: dto.max_age},
+ startNode: LearningPathNode.fromDTOAndOtherNodes(startNodeDto[0], dto.nodes),
+ image: dto.image
+ });
+ }
+}
diff --git a/frontend/src/exception/invalid-response-exception.ts b/frontend/src/exception/invalid-response-exception.ts
new file mode 100644
index 00000000..5cbd35b3
--- /dev/null
+++ b/frontend/src/exception/invalid-response-exception.ts
@@ -0,0 +1,5 @@
+export class InvalidResponseException extends Error {
+ constructor(message: string) {
+ super(message);
+ }
+}
diff --git a/frontend/src/exception/not-found-exception.ts b/frontend/src/exception/not-found-exception.ts
new file mode 100644
index 00000000..fd5b29a6
--- /dev/null
+++ b/frontend/src/exception/not-found-exception.ts
@@ -0,0 +1,5 @@
+export class NotFoundException extends Error {
+ constructor(message: string) {
+ super(message);
+ }
+}
diff --git a/frontend/src/queries/learning-objects.ts b/frontend/src/queries/learning-objects.ts
index 93821873..df858a36 100644
--- a/frontend/src/queries/learning-objects.ts
+++ b/frontend/src/queries/learning-objects.ts
@@ -2,8 +2,8 @@ import {type MaybeRefOrGetter, toValue} from "vue";
import type {Language} from "@/data-objects/language.ts";
import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query";
import {getLearningObjectController} from "@/controllers/controllers.ts";
-import type {LearningObject} from "@/data-objects/learning-object.ts";
-import type {LearningPath} from "@/data-objects/learning-path.ts";
+import type {LearningObject} from "@/data-objects/learning-objects/learning-object.ts";
+import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts";
const LEARNING_OBJECT_KEY = "learningObject";
const learningObjectController = getLearningObjectController();
@@ -15,7 +15,7 @@ export function useLearningObjectMetadataQuery(
): UseQueryReturnType {
return useQuery({
queryKey: [LEARNING_OBJECT_KEY, "metadata", hruid, language, version],
- queryFn: () => {
+ queryFn: async () => {
const [hruidVal, languageVal, versionVal] = [toValue(hruid), toValue(language), toValue(version)];
return learningObjectController.getMetadata(hruidVal, languageVal, versionVal)
},
@@ -30,7 +30,7 @@ export function useLearningObjectHTMLQuery(
): UseQueryReturnType {
return useQuery({
queryKey: [LEARNING_OBJECT_KEY, "html", hruid, language, version],
- queryFn: () => {
+ queryFn: async () => {
const [hruidVal, languageVal, versionVal] = [toValue(hruid), toValue(language), toValue(version)];
return learningObjectController.getHTML(hruidVal, languageVal, versionVal)
},
@@ -43,9 +43,9 @@ export function useLearningObjectListForPathQuery(
): UseQueryReturnType {
return useQuery({
queryKey: [LEARNING_OBJECT_KEY, "onPath", learningPath],
- queryFn: () => {
- let learningObjects = [];
- for (let node of toValue(learningPath).nodesAsList) {
+ queryFn: async () => {
+ const learningObjects = [];
+ for (const node of toValue(learningPath).nodesAsList) {
learningObjects.push(
learningObjectController.getMetadata(node.learningobjectHruid, node.language, node.version)
);
diff --git a/frontend/src/queries/learning-paths.ts b/frontend/src/queries/learning-paths.ts
index ca0dac10..032aa493 100644
--- a/frontend/src/queries/learning-paths.ts
+++ b/frontend/src/queries/learning-paths.ts
@@ -2,7 +2,7 @@ import {type MaybeRefOrGetter, toValue} from "vue";
import type {Language} from "@/data-objects/language.ts";
import {useQuery, type UseQueryReturnType} from "@tanstack/vue-query";
import { getLearningPathController } from "@/controllers/controllers";
-import type {LearningPath} from "@/data-objects/learning-path.ts";
+import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts";
const LEARNING_PATH_KEY = "learningPath";
const learningPathController = getLearningPathController();
@@ -14,7 +14,7 @@ export function useGetLearningPathQuery(
): UseQueryReturnType {
return useQuery({
queryKey: [LEARNING_PATH_KEY, "get", hruid, language, options],
- queryFn: () => {
+ queryFn: async () => {
const [hruidVal, languageVal, optionsVal] = [toValue(hruid), toValue(language), toValue(options)];
return learningPathController.getBy(hruidVal, languageVal, optionsVal)
},
@@ -27,9 +27,7 @@ export function useGetAllLearningPathsByThemeQuery(
): UseQueryReturnType {
return useQuery({
queryKey: [LEARNING_PATH_KEY, "getAllByTheme", theme],
- queryFn: () => {
- return learningPathController.getAllByTheme(toValue(theme))
- },
+ queryFn: async () => learningPathController.getAllByTheme(toValue(theme)),
enabled: () => Boolean(toValue(theme)),
})
}
@@ -39,7 +37,7 @@ export function useSearchLearningPathQuery(
): UseQueryReturnType {
return useQuery({
queryKey: [LEARNING_PATH_KEY, "search", query],
- queryFn: () => {
+ queryFn: async () => {
const queryVal = toValue(query);
return learningPathController.search(queryVal);
},
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index cc32bb35..64c49d25 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -15,6 +15,7 @@ import LearningPathPage from "@/views/learning-paths/LearningPathPage.vue";
import LearningPathSearchPage from "@/views/learning-paths/LearningPathSearchPage.vue";
import UserHomePage from "@/views/homepage/UserHomePage.vue";
import SingleTheme from "@/views/SingleTheme.vue";
+import LearningObjectView from "@/views/learning-paths/LearningObjectView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@@ -117,22 +118,21 @@ const router = createRouter({
meta: { requiresAuth: true }
},
{
- path: ":hruid/:language",
+ path: ":hruid/:language/:learningObjectHruid",
name: "LearningPath",
component: LearningPathPage,
props: true,
meta: { requiresAuth: true },
- children: [
- {
- path: ":learningObjectHruid",
- component: LearningPathPage,
- props: true,
- meta: { requiresAuth: true }
- }
- ]
},
]
},
+ {
+ path: "/learningObject/:hruid/:language/:version/raw",
+ name: "LearningObjectView",
+ component: LearningObjectView,
+ props: true,
+ meta: { requiresAuth: true }
+ },
{
path: "/:catchAll(.*)",
name: "NotFound",
diff --git a/frontend/src/services/api-client/api-exceptions.ts b/frontend/src/services/api-client/api-exceptions.ts
deleted file mode 100644
index d2fa2ac2..00000000
--- a/frontend/src/services/api-client/api-exceptions.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import type {AxiosResponse} from "axios";
-
-export class HttpErrorStatusException extends Error {
- public readonly statusCode: number;
-
- constructor(response: AxiosResponse) {
- super(`${response.statusText} (${response.status})`);
- this.statusCode = response.status;
- }
-}
-
-export class NotFoundException extends Error {
- constructor(message: string) {
- super(message);
- }
-}
-
-export class InvalidResponseException extends Error {
- constructor(message: string) {
- super(message);
- }
-}
diff --git a/frontend/src/utils/response-assertions.ts b/frontend/src/utils/response-assertions.ts
index 5be421ff..ebc6f9c0 100644
--- a/frontend/src/utils/response-assertions.ts
+++ b/frontend/src/utils/response-assertions.ts
@@ -1,4 +1,5 @@
-import {InvalidResponseException, NotFoundException} from "@/services/api-client/api-exceptions.ts";
+import {NotFoundException} from "@/exception/not-found-exception.ts";
+import {InvalidResponseException} from "@/exception/invalid-response-exception.ts";
export function single(list: T[]): T {
if (list.length === 1) {
diff --git a/frontend/src/views/CallbackPage.vue b/frontend/src/views/CallbackPage.vue
index 45a0134f..7d792d96 100644
--- a/frontend/src/views/CallbackPage.vue
+++ b/frontend/src/views/CallbackPage.vue
@@ -1,22 +1,25 @@
- Logging you in...
+ Logging you in...
+ {{ errorMessage }}
diff --git a/frontend/src/views/HomePage.vue b/frontend/src/views/HomePage.vue
index 1c4d716a..08ce44c1 100644
--- a/frontend/src/views/HomePage.vue
+++ b/frontend/src/views/HomePage.vue
@@ -1,6 +1,4 @@
@@ -151,6 +134,7 @@
:to="{path: node.key, query: route.query}"
:title="node.title"
:active="node.key === props.learningObjectHruid"
+ :key="node.key"
v-if="!node.teacherExclusive || authService.authState.activeRole === 'teacher'"
>
diff --git a/frontend/src/views/learning-paths/LearningPathSearchPage.vue b/frontend/src/views/learning-paths/LearningPathSearchPage.vue
index baafb0cc..4220457d 100644
--- a/frontend/src/views/learning-paths/LearningPathSearchPage.vue
+++ b/frontend/src/views/learning-paths/LearningPathSearchPage.vue
@@ -1,6 +1,6 @@