style: fix linting issues met Prettier
This commit is contained in:
		
							parent
							
								
									a65e95ac46
								
							
						
					
					
						commit
						1203f12ff4
					
				
					 33 changed files with 359 additions and 307 deletions
				
			
		|  | @ -7,8 +7,8 @@ import { BadRequestException } from '../exceptions/bad-request-exception.js'; | ||||||
| import { NotFoundException } from '../exceptions/not-found-exception.js'; | import { NotFoundException } from '../exceptions/not-found-exception.js'; | ||||||
| import { envVars, getEnvVar } from '../util/envVars.js'; | import { envVars, getEnvVar } from '../util/envVars.js'; | ||||||
| import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||||
| import {UploadedFile} from "express-fileupload"; | import { UploadedFile } from 'express-fileupload'; | ||||||
| import {AuthenticatedRequest} from "../middleware/auth/authenticated-request"; | import { AuthenticatedRequest } from '../middleware/auth/authenticated-request'; | ||||||
| 
 | 
 | ||||||
| function getLearningObjectIdentifierFromRequest(req: Request): LearningObjectIdentifierDTO { | function getLearningObjectIdentifierFromRequest(req: Request): LearningObjectIdentifierDTO { | ||||||
|     if (!req.params.hruid) { |     if (!req.params.hruid) { | ||||||
|  | @ -32,12 +32,13 @@ function getLearningPathIdentifierFromRequest(req: Request): LearningPathIdentif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function getAllLearningObjects(req: Request, res: Response): Promise<void> { | export async function getAllLearningObjects(req: Request, res: Response): Promise<void> { | ||||||
|     if (req.query.admin) { // If the admin query parameter is present, the user wants to have all learning objects with this admin.
 |     if (req.query.admin) { | ||||||
|         const learningObjects = |         // If the admin query parameter is present, the user wants to have all learning objects with this admin.
 | ||||||
|             await learningObjectService.getLearningObjectsAdministratedBy(req.query.admin as string); |         const learningObjects = await learningObjectService.getLearningObjectsAdministratedBy(req.query.admin as string); | ||||||
| 
 | 
 | ||||||
|         res.json(learningObjects); |         res.json(learningObjects); | ||||||
|     } else { // Else he/she wants all learning objects on the path specified by the request parameters.
 |     } else { | ||||||
|  |         // Else he/she wants all learning objects on the path specified by the request parameters.
 | ||||||
|         const learningPathId = getLearningPathIdentifierFromRequest(req); |         const learningPathId = getLearningPathIdentifierFromRequest(req); | ||||||
|         const full = req.query.full; |         const full = req.query.full; | ||||||
| 
 | 
 | ||||||
|  | @ -86,10 +87,9 @@ export async function handlePostLearningObject(req: AuthenticatedRequest, res: R | ||||||
|     if (!req.files || !req.files.learningObject) { |     if (!req.files || !req.files.learningObject) { | ||||||
|         throw new BadRequestException('No file uploaded'); |         throw new BadRequestException('No file uploaded'); | ||||||
|     } |     } | ||||||
|     const learningObject = await learningObjectService.storeLearningObject( |     const learningObject = await learningObjectService.storeLearningObject((req.files.learningObject as UploadedFile).tempFilePath, [ | ||||||
|         (req.files.learningObject as UploadedFile).tempFilePath, |         req.auth!.username, | ||||||
|         [req.auth!.username] |     ]); | ||||||
|     ); |  | ||||||
|     res.json(learningObject); |     res.json(learningObject); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -97,17 +97,17 @@ export async function handleDeleteLearningObject(req: AuthenticatedRequest, res: | ||||||
|     const learningObjectId = getLearningObjectIdentifierFromRequest(req); |     const learningObjectId = getLearningObjectIdentifierFromRequest(req); | ||||||
| 
 | 
 | ||||||
|     if (!learningObjectId.version) { |     if (!learningObjectId.version) { | ||||||
|         throw new BadRequestException("When deleting a learning object, a version must be specified."); |         throw new BadRequestException('When deleting a learning object, a version must be specified.'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const deletedLearningObject = await learningObjectService.deleteLearningObject({ |     const deletedLearningObject = await learningObjectService.deleteLearningObject({ | ||||||
|         hruid: learningObjectId.hruid, |         hruid: learningObjectId.hruid, | ||||||
|         version: learningObjectId.version, |         version: learningObjectId.version, | ||||||
|         language: learningObjectId.language |         language: learningObjectId.language, | ||||||
|     }); |     }); | ||||||
|     if (deletedLearningObject) { |     if (deletedLearningObject) { | ||||||
|         res.json(deletedLearningObject); |         res.json(deletedLearningObject); | ||||||
|     } else { |     } else { | ||||||
|         throw new NotFoundException("Learning object not found"); |         throw new NotFoundException('Learning object not found'); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -60,7 +60,12 @@ export async function getLearningPaths(req: Request, res: Response): Promise<voi | ||||||
|             hruidList = themes.flatMap((theme) => theme.hruids); |             hruidList = themes.flatMap((theme) => theme.hruids); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const learningPaths = await learningPathService.fetchLearningPaths(hruidList, language as Language, `HRUIDs: ${hruidList.join(', ')}`, forGroup); |         const learningPaths = await learningPathService.fetchLearningPaths( | ||||||
|  |             hruidList, | ||||||
|  |             language as Language, | ||||||
|  |             `HRUIDs: ${hruidList.join(', ')}`, | ||||||
|  |             forGroup | ||||||
|  |         ); | ||||||
|         res.json(learningPaths.data); |         res.json(learningPaths.data); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -71,12 +76,12 @@ function postOrPutLearningPath(isPut: boolean): (req: AuthenticatedRequest, res: | ||||||
|         const teacher = await getTeacher(req.auth!.username); |         const teacher = await getTeacher(req.auth!.username); | ||||||
|         if (isPut) { |         if (isPut) { | ||||||
|             if (req.params.hruid !== path.hruid || req.params.language !== path.language) { |             if (req.params.hruid !== path.hruid || req.params.language !== path.language) { | ||||||
|                 throw new BadRequestException("id_not_matching_query_params"); |                 throw new BadRequestException('id_not_matching_query_params'); | ||||||
|             } |             } | ||||||
|             await learningPathService.deleteLearningPath({ hruid: path.hruid, language: path.language as Language }); |             await learningPathService.deleteLearningPath({ hruid: path.hruid, language: path.language as Language }); | ||||||
|         } |         } | ||||||
|         res.json(await learningPathService.createNewLearningPath(path, [teacher])); |         res.json(await learningPathService.createNewLearningPath(path, [teacher])); | ||||||
|     } |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const postLearningPath = postOrPutLearningPath(false); | export const postLearningPath = postOrPutLearningPath(false); | ||||||
|  | @ -85,12 +90,12 @@ export const putLearningPath = postOrPutLearningPath(true); | ||||||
| export async function deleteLearningPath(req: AuthenticatedRequest, res: Response): Promise<void> { | export async function deleteLearningPath(req: AuthenticatedRequest, res: Response): Promise<void> { | ||||||
|     const id: LearningPathIdentifier = { |     const id: LearningPathIdentifier = { | ||||||
|         hruid: req.params.hruid, |         hruid: req.params.hruid, | ||||||
|         language: req.params.language as Language |         language: req.params.language as Language, | ||||||
|     }; |     }; | ||||||
|     const deletedPath = await learningPathService.deleteLearningPath(id); |     const deletedPath = await learningPathService.deleteLearningPath(id); | ||||||
|     if (deletedPath) { |     if (deletedPath) { | ||||||
|         res.json(deletedPath); |         res.json(deletedPath); | ||||||
|     } else { |     } else { | ||||||
|         throw new NotFoundException("The learning path could not be found."); |         throw new NotFoundException('The learning path could not be found.'); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -36,8 +36,8 @@ export class LearningObjectRepository extends DwengoEntityRepository<LearningObj | ||||||
|         return this.find( |         return this.find( | ||||||
|             { |             { | ||||||
|                 admins: { |                 admins: { | ||||||
|                     username: adminUsername |                     username: adminUsername, | ||||||
|                 } |                 }, | ||||||
|             }, |             }, | ||||||
|             { populate: ['admins'] } // Make sure to load admin relations
 |             { populate: ['admins'] } // Make sure to load admin relations
 | ||||||
|         ); |         ); | ||||||
|  | @ -50,5 +50,4 @@ export class LearningObjectRepository extends DwengoEntityRepository<LearningObj | ||||||
|         } |         } | ||||||
|         return learningObject; |         return learningObject; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,10 +7,7 @@ import { LearningPathTransition } from '../../entities/content/learning-path-tra | ||||||
| 
 | 
 | ||||||
| export class LearningPathRepository extends DwengoEntityRepository<LearningPath> { | export class LearningPathRepository extends DwengoEntityRepository<LearningPath> { | ||||||
|     public async findByHruidAndLanguage(hruid: string, language: Language): Promise<LearningPath | null> { |     public async findByHruidAndLanguage(hruid: string, language: Language): Promise<LearningPath | null> { | ||||||
|         return this.findOne( |         return this.findOne({ hruid: hruid, language: language }, { populate: ['nodes', 'nodes.transitions', 'admins'] }); | ||||||
|             { hruid: hruid, language: language }, |  | ||||||
|             { populate: ['nodes', 'nodes.transitions', 'admins'] } |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -37,10 +34,10 @@ export class LearningPathRepository extends DwengoEntityRepository<LearningPath> | ||||||
|         return this.findAll({ |         return this.findAll({ | ||||||
|             where: { |             where: { | ||||||
|                 admins: { |                 admins: { | ||||||
|                     username: adminUsername |                     username: adminUsername, | ||||||
|                 } |  | ||||||
|                 }, |                 }, | ||||||
|             populate: ['nodes', 'nodes.transitions', 'admins'] |             }, | ||||||
|  |             populate: ['nodes', 'nodes.transitions', 'admins'], | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ export class Attachment { | ||||||
|     @ManyToOne({ |     @ManyToOne({ | ||||||
|         entity: () => LearningObject, |         entity: () => LearningObject, | ||||||
|         primary: true, |         primary: true, | ||||||
|         deleteRule: 'cascade' |         deleteRule: 'cascade', | ||||||
|     }) |     }) | ||||||
|     learningObject!: LearningObject; |     learningObject!: LearningObject; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,14 +1,4 @@ | ||||||
| import { | import { ArrayType, Collection, Embedded, Entity, Enum, ManyToMany, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'; | ||||||
|     ArrayType, |  | ||||||
|     Collection, |  | ||||||
|     Embedded, |  | ||||||
|     Entity, |  | ||||||
|     Enum, |  | ||||||
|     ManyToMany, |  | ||||||
|     OneToMany, |  | ||||||
|     PrimaryKey, |  | ||||||
|     Property |  | ||||||
| } from '@mikro-orm/core'; |  | ||||||
| import { Attachment } from './attachment.entity.js'; | import { Attachment } from './attachment.entity.js'; | ||||||
| import { Teacher } from '../users/teacher.entity.js'; | import { Teacher } from '../users/teacher.entity.js'; | ||||||
| import { DwengoContentType } from '../../services/learning-objects/processing/content-type.js'; | import { DwengoContentType } from '../../services/learning-objects/processing/content-type.js'; | ||||||
|  | @ -92,7 +82,7 @@ export class LearningObject { | ||||||
| 
 | 
 | ||||||
|     @OneToMany({ |     @OneToMany({ | ||||||
|         entity: () => Attachment, |         entity: () => Attachment, | ||||||
|         mappedBy: 'learningObject' |         mappedBy: 'learningObject', | ||||||
|     }) |     }) | ||||||
|     attachments: Collection<Attachment> = new Collection<Attachment>(this); |     attachments: Collection<Attachment> = new Collection<Attachment>(this); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -116,14 +116,8 @@ export const authenticateUser = [verifyJwtToken, addAuthenticationInfo]; | ||||||
|  * @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates |  * @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates | ||||||
|  *                        to true. |  *                        to true. | ||||||
|  */ |  */ | ||||||
| export function authorize( | export function authorize(accessCondition: (auth: AuthenticationInfo, req: AuthenticatedRequest) => boolean | Promise<boolean>): RequestHandler { | ||||||
|     accessCondition: (auth: AuthenticationInfo, req: AuthenticatedRequest) => boolean | Promise<boolean> |     return async (req: AuthenticatedRequest, _res: express.Response, next: express.NextFunction): Promise<void> => { | ||||||
| ): RequestHandler { |  | ||||||
|     return async ( |  | ||||||
|         req: AuthenticatedRequest, |  | ||||||
|         _res: express.Response, |  | ||||||
|         next: express.NextFunction |  | ||||||
|     ): Promise<void> => { |  | ||||||
|         if (!req.auth) { |         if (!req.auth) { | ||||||
|             throw new UnauthorizedException(); |             throw new UnauthorizedException(); | ||||||
|         } else if (!(await accessCondition(req.auth, req))) { |         } else if (!(await accessCondition(req.auth, req))) { | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| import { Language } from "@dwengo-1/common/util/language"; | import { Language } from '@dwengo-1/common/util/language'; | ||||||
| import learningObjectService from "../../../services/learning-objects/learning-object-service"; | import learningObjectService from '../../../services/learning-objects/learning-object-service'; | ||||||
| import { authorize } from "../auth"; | import { authorize } from '../auth'; | ||||||
| import { AuthenticatedRequest } from "../authenticated-request"; | import { AuthenticatedRequest } from '../authenticated-request'; | ||||||
| import { AuthenticationInfo } from "../authentication-info"; | import { AuthenticationInfo } from '../authentication-info'; | ||||||
| 
 | 
 | ||||||
| export const onlyAdminsForLearningObject = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | export const onlyAdminsForLearningObject = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | ||||||
|     const { hruid } = req.params; |     const { hruid } = req.params; | ||||||
|  | @ -10,7 +10,7 @@ export const onlyAdminsForLearningObject = authorize(async (auth: Authentication | ||||||
|     const admins = await learningObjectService.getAdmins({ |     const admins = await learningObjectService.getAdmins({ | ||||||
|         hruid, |         hruid, | ||||||
|         language: language as Language, |         language: language as Language, | ||||||
|         version: parseInt(version as string) |         version: parseInt(version as string), | ||||||
|     }); |     }); | ||||||
|     return admins.includes(auth.username); |     return admins.includes(auth.username); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| import { Language } from "@dwengo-1/common/util/language"; | import { Language } from '@dwengo-1/common/util/language'; | ||||||
| import learningPathService from "../../../services/learning-paths/learning-path-service"; | import learningPathService from '../../../services/learning-paths/learning-path-service'; | ||||||
| import { authorize } from "../auth"; | import { authorize } from '../auth'; | ||||||
| import { AuthenticatedRequest } from "../authenticated-request"; | import { AuthenticatedRequest } from '../authenticated-request'; | ||||||
| import { AuthenticationInfo } from "../authentication-info"; | import { AuthenticationInfo } from '../authentication-info'; | ||||||
| 
 | 
 | ||||||
| export const onlyAdminsForLearningPath = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | export const onlyAdminsForLearningPath = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | ||||||
|     const adminsForLearningPath = await learningPathService.getAdmins({ |     const adminsForLearningPath = await learningPathService.getAdmins({ | ||||||
|         hruid: req.params.hruid, |         hruid: req.params.hruid, | ||||||
|         language: req.params.language as Language |         language: req.params.language as Language, | ||||||
|     }); |     }); | ||||||
|     return adminsForLearningPath && adminsForLearningPath.includes(auth.username); |     return adminsForLearningPath && adminsForLearningPath.includes(auth.username); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -5,12 +5,12 @@ import { | ||||||
|     getLearningObject, |     getLearningObject, | ||||||
|     getLearningObjectHTML, |     getLearningObjectHTML, | ||||||
|     handleDeleteLearningObject, |     handleDeleteLearningObject, | ||||||
|     handlePostLearningObject |     handlePostLearningObject, | ||||||
| } from '../controllers/learning-objects.js'; | } from '../controllers/learning-objects.js'; | ||||||
| 
 | 
 | ||||||
| import submissionRoutes from './submissions.js'; | import submissionRoutes from './submissions.js'; | ||||||
| import questionRoutes from './questions.js'; | import questionRoutes from './questions.js'; | ||||||
| import fileUpload from "express-fileupload"; | import fileUpload from 'express-fileupload'; | ||||||
| import { teachersOnly } from '../middleware/auth/auth.js'; | import { teachersOnly } from '../middleware/auth/auth.js'; | ||||||
| import { onlyAdminsForLearningObject } from '../middleware/auth/checks/learning-object-auth-checks.js'; | import { onlyAdminsForLearningObject } from '../middleware/auth/checks/learning-object-auth-checks.js'; | ||||||
| 
 | 
 | ||||||
|  | @ -28,7 +28,7 @@ const router = express.Router(); | ||||||
| // Example 2: http://localhost:3000/learningObject?full=true&hruid=un_artificiele_intelligentie
 | // Example 2: http://localhost:3000/learningObject?full=true&hruid=un_artificiele_intelligentie
 | ||||||
| router.get('/', getAllLearningObjects); | router.get('/', getAllLearningObjects); | ||||||
| 
 | 
 | ||||||
| router.post('/', teachersOnly, fileUpload({useTempFiles: true}), handlePostLearningObject) | router.post('/', teachersOnly, fileUpload({ useTempFiles: true }), handlePostLearningObject); | ||||||
| 
 | 
 | ||||||
| // Parameter: hruid of learning object
 | // Parameter: hruid of learning object
 | ||||||
| // Query: language
 | // Query: language
 | ||||||
|  | @ -40,7 +40,7 @@ router.get('/:hruid', getLearningObject); | ||||||
| // Query: language
 | // Query: language
 | ||||||
| // Route to delete a learning object based on its hruid.
 | // Route to delete a learning object based on its hruid.
 | ||||||
| // Example: http://localhost:3000/learningObject/un_ai7?language=nl&version=1
 | // Example: http://localhost:3000/learningObject/un_ai7?language=nl&version=1
 | ||||||
| router.delete('/:hruid', onlyAdminsForLearningObject, handleDeleteLearningObject) | router.delete('/:hruid', onlyAdminsForLearningObject, handleDeleteLearningObject); | ||||||
| 
 | 
 | ||||||
| router.use('/:hruid/submissions', submissionRoutes); | router.use('/:hruid/submissions', submissionRoutes); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ const router = express.Router(); | ||||||
| // Example: http://localhost:3000/learningPath?theme=kiks
 | // Example: http://localhost:3000/learningPath?theme=kiks
 | ||||||
| 
 | 
 | ||||||
| router.get('/', getLearningPaths); | router.get('/', getLearningPaths); | ||||||
| router.post('/', teachersOnly, postLearningPath) | router.post('/', teachersOnly, postLearningPath); | ||||||
| 
 | 
 | ||||||
| router.put('/:hruid/:language', onlyAdminsForLearningPath, putLearningPath); | router.put('/:hruid/:language', onlyAdminsForLearningPath, putLearningPath); | ||||||
| router.delete('/:hruid/:language', onlyAdminsForLearningPath, deleteLearningPath); | router.delete('/:hruid/:language', onlyAdminsForLearningPath, deleteLearningPath); | ||||||
|  |  | ||||||
|  | @ -116,10 +116,8 @@ const databaseLearningObjectProvider: LearningObjectProvider = { | ||||||
|     async getLearningObjectsAdministratedBy(adminUsername: string): Promise<FilteredLearningObject[]> { |     async getLearningObjectsAdministratedBy(adminUsername: string): Promise<FilteredLearningObject[]> { | ||||||
|         const learningObjectRepo = getLearningObjectRepository(); |         const learningObjectRepo = getLearningObjectRepository(); | ||||||
|         const learningObjects = await learningObjectRepo.findAllByAdmin(adminUsername); |         const learningObjects = await learningObjectRepo.findAllByAdmin(adminUsername); | ||||||
|         return learningObjects |         return learningObjects.map((it) => convertLearningObject(it)).filter((it) => it != null); | ||||||
|                 .map(it => convertLearningObject(it)) |     }, | ||||||
|                 .filter(it => it != null); |  | ||||||
|     } |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default databaseLearningObjectProvider; | export default databaseLearningObjectProvider; | ||||||
|  |  | ||||||
|  | @ -141,7 +141,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { | ||||||
|      */ |      */ | ||||||
|     async getLearningObjectsAdministratedBy(_adminUsername: string): Promise<FilteredLearningObject[]> { |     async getLearningObjectsAdministratedBy(_adminUsername: string): Promise<FilteredLearningObject[]> { | ||||||
|         return []; // The dwengo database does not contain any learning objects administrated by users.
 |         return []; // The dwengo database does not contain any learning objects administrated by users.
 | ||||||
|     } |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default dwengoApiLearningObjectProvider; | export default dwengoApiLearningObjectProvider; | ||||||
|  |  | ||||||
|  | @ -2,14 +2,10 @@ import dwengoApiLearningObjectProvider from './dwengo-api-learning-object-provid | ||||||
| import { LearningObjectProvider } from './learning-object-provider.js'; | import { LearningObjectProvider } from './learning-object-provider.js'; | ||||||
| import { envVars, getEnvVar } from '../../util/envVars.js'; | import { envVars, getEnvVar } from '../../util/envVars.js'; | ||||||
| import databaseLearningObjectProvider from './database-learning-object-provider.js'; | import databaseLearningObjectProvider from './database-learning-object-provider.js'; | ||||||
| import { | import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||||
|     FilteredLearningObject, | import { getLearningObjectRepository, getTeacherRepository } from '../../data/repositories'; | ||||||
|     LearningObjectIdentifierDTO, | import { processLearningObjectZip } from './learning-object-zip-processing-service'; | ||||||
|     LearningPathIdentifier | import { LearningObject } from '../../entities/content/learning-object.entity'; | ||||||
| } from '@dwengo-1/common/interfaces/learning-content'; |  | ||||||
| import {getLearningObjectRepository, getTeacherRepository} from "../../data/repositories"; |  | ||||||
| import {processLearningObjectZip} from "./learning-object-zip-processing-service"; |  | ||||||
| import {LearningObject} from "../../entities/content/learning-object.entity"; |  | ||||||
| import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js'; | import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js'; | ||||||
| import { NotFoundException } from '../../exceptions/not-found-exception.js'; | import { NotFoundException } from '../../exceptions/not-found-exception.js'; | ||||||
| 
 | 
 | ||||||
|  | @ -74,10 +70,8 @@ const learningObjectService = { | ||||||
| 
 | 
 | ||||||
|         // Lookup the admin teachers based on their usernames and add them to the admins of the learning object.
 |         // Lookup the admin teachers based on their usernames and add them to the admins of the learning object.
 | ||||||
|         const teacherRepo = getTeacherRepository(); |         const teacherRepo = getTeacherRepository(); | ||||||
|         const adminTeachers = await Promise.all( |         const adminTeachers = await Promise.all(admins.map(async (it) => teacherRepo.findByUsername(it))); | ||||||
|             admins.map(async it => teacherRepo.findByUsername(it)) |         adminTeachers.forEach((it) => { | ||||||
|         ); |  | ||||||
|         adminTeachers.forEach(it => { |  | ||||||
|             if (it !== null) { |             if (it !== null) { | ||||||
|                 learningObject.admins.add(it); |                 learningObject.admins.add(it); | ||||||
|             } |             } | ||||||
|  | @ -109,10 +103,10 @@ const learningObjectService = { | ||||||
|         const learningObjectRepo = getLearningObjectRepository(); |         const learningObjectRepo = getLearningObjectRepository(); | ||||||
|         const learningObject = await learningObjectRepo.findByIdentifier(id); |         const learningObject = await learningObjectRepo.findByIdentifier(id); | ||||||
|         if (!learningObject) { |         if (!learningObject) { | ||||||
|             throw new NotFoundException("The specified learning object does not exist."); |             throw new NotFoundException('The specified learning object does not exist.'); | ||||||
|         } |  | ||||||
|         return learningObject.admins.map(admin => admin.username); |  | ||||||
|         } |         } | ||||||
|  |         return learningObject.admins.map((admin) => admin.username); | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default learningObjectService; | export default learningObjectService; | ||||||
|  |  | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| import unzipper from 'unzipper'; | import unzipper from 'unzipper'; | ||||||
| import mime from 'mime-types'; | import mime from 'mime-types'; | ||||||
| import {LearningObject} from "../../entities/content/learning-object.entity"; | import { LearningObject } from '../../entities/content/learning-object.entity'; | ||||||
| import {getAttachmentRepository, getLearningObjectRepository} from "../../data/repositories"; | import { getAttachmentRepository, getLearningObjectRepository } from '../../data/repositories'; | ||||||
| import {BadRequestException} from "../../exceptions/bad-request-exception"; | import { BadRequestException } from '../../exceptions/bad-request-exception'; | ||||||
| import {LearningObjectMetadata} from "@dwengo-1/common/interfaces/learning-content"; | import { LearningObjectMetadata } from '@dwengo-1/common/interfaces/learning-content'; | ||||||
| import { DwengoContentType } from './processing/content-type'; | import { DwengoContentType } from './processing/content-type'; | ||||||
| 
 | 
 | ||||||
| const METADATA_PATH_REGEX = /.*[/^]metadata\.json$/; | const METADATA_PATH_REGEX = /.*[/^]metadata\.json$/; | ||||||
|  | @ -18,21 +18,20 @@ export async function processLearningObjectZip(filePath: string): Promise<Learni | ||||||
|     try { |     try { | ||||||
|         zip = await unzipper.Open.file(filePath); |         zip = await unzipper.Open.file(filePath); | ||||||
|     } catch (_: unknown) { |     } catch (_: unknown) { | ||||||
|         throw new BadRequestException("invalidZip"); |         throw new BadRequestException('invalidZip'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     let metadata: LearningObjectMetadata | undefined = undefined; |     let metadata: LearningObjectMetadata | undefined = undefined; | ||||||
|     const attachments: {name: string, content: Buffer}[] = []; |     const attachments: { name: string; content: Buffer }[] = []; | ||||||
|     let content: Buffer | undefined = undefined; |     let content: Buffer | undefined = undefined; | ||||||
| 
 | 
 | ||||||
|     if (zip.files.length === 0) { |     if (zip.files.length === 0) { | ||||||
|         throw new BadRequestException("emptyZip") |         throw new BadRequestException('emptyZip'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     await Promise.all( |     await Promise.all( | ||||||
|         zip.files.map(async file => { |         zip.files.map(async (file) => { | ||||||
|             if (file.type !== "Directory") { |             if (file.type !== 'Directory') { | ||||||
|                 if (METADATA_PATH_REGEX.test(file.path)) { |                 if (METADATA_PATH_REGEX.test(file.path)) { | ||||||
|                     metadata = await processMetadataJson(file); |                     metadata = await processMetadataJson(file); | ||||||
|                 } else if (CONTENT_PATH_REGEX.test(file.path)) { |                 } else if (CONTENT_PATH_REGEX.test(file.path)) { | ||||||
|  | @ -40,7 +39,7 @@ export async function processLearningObjectZip(filePath: string): Promise<Learni | ||||||
|                 } else { |                 } else { | ||||||
|                     attachments.push({ |                     attachments.push({ | ||||||
|                         name: file.path, |                         name: file.path, | ||||||
|                         content: await processFile(file) |                         content: await processFile(file), | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -48,27 +47,24 @@ export async function processLearningObjectZip(filePath: string): Promise<Learni | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     if (!metadata) { |     if (!metadata) { | ||||||
|         throw new BadRequestException("missingMetadata"); |         throw new BadRequestException('missingMetadata'); | ||||||
|     } |     } | ||||||
|     if (!content) { |     if (!content) { | ||||||
|         throw new BadRequestException("missingIndex"); |         throw new BadRequestException('missingIndex'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     const learningObject = createLearningObject(metadata, content, attachments); |     const learningObject = createLearningObject(metadata, content, attachments); | ||||||
| 
 | 
 | ||||||
|     return learningObject; |     return learningObject; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createLearningObject( | function createLearningObject(metadata: LearningObjectMetadata, content: Buffer, attachments: { name: string; content: Buffer }[]): LearningObject { | ||||||
|     metadata: LearningObjectMetadata, content: Buffer, attachments: { name: string; content: Buffer; }[] |  | ||||||
| ): LearningObject { |  | ||||||
|     const learningObjectRepo = getLearningObjectRepository(); |     const learningObjectRepo = getLearningObjectRepository(); | ||||||
|     const attachmentRepo = getAttachmentRepository(); |     const attachmentRepo = getAttachmentRepository(); | ||||||
| 
 | 
 | ||||||
|     const returnValue = { |     const returnValue = { | ||||||
|         callbackUrl: metadata.return_value?.callback_url ?? "", |         callbackUrl: metadata.return_value?.callback_url ?? '', | ||||||
|         callbackSchema: metadata.return_value?.callback_schema ? JSON.stringify(metadata.return_value.callback_schema) : "" |         callbackSchema: metadata.return_value?.callback_schema ? JSON.stringify(metadata.return_value.callback_schema) : '', | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     const learningObject = learningObjectRepo.create({ |     const learningObject = learningObjectRepo.create({ | ||||||
|  | @ -76,26 +72,30 @@ function createLearningObject( | ||||||
|         available: metadata.available ?? true, |         available: metadata.available ?? true, | ||||||
|         content: content, |         content: content, | ||||||
|         contentType: metadata.content_type as DwengoContentType, |         contentType: metadata.content_type as DwengoContentType, | ||||||
|         copyright: metadata.copyright ?? "", |         copyright: metadata.copyright ?? '', | ||||||
|         description: metadata.description ?? "", |         description: metadata.description ?? '', | ||||||
|         educationalGoals: metadata.educational_goals ?? [], |         educationalGoals: metadata.educational_goals ?? [], | ||||||
|         hruid: metadata.hruid, |         hruid: metadata.hruid, | ||||||
|         keywords: metadata.keywords, |         keywords: metadata.keywords, | ||||||
|         language: metadata.language, |         language: metadata.language, | ||||||
|         license: metadata.license ?? "", |         license: metadata.license ?? '', | ||||||
|         returnValue, |         returnValue, | ||||||
|         skosConcepts: metadata.skos_concepts ?? [], |         skosConcepts: metadata.skos_concepts ?? [], | ||||||
|         teacherExclusive: metadata.teacher_exclusive, |         teacherExclusive: metadata.teacher_exclusive, | ||||||
|         title: metadata.title, |         title: metadata.title, | ||||||
|         version: metadata.version |         version: metadata.version, | ||||||
|     }); |     }); | ||||||
|     const attachmentEntities = attachments.map(it => attachmentRepo.create({ |     const attachmentEntities = attachments.map((it) => | ||||||
|  |         attachmentRepo.create({ | ||||||
|             name: it.name, |             name: it.name, | ||||||
|             content: it.content, |             content: it.content, | ||||||
|         mimeType: mime.lookup(it.name) || "text/plain", |             mimeType: mime.lookup(it.name) || 'text/plain', | ||||||
|         learningObject |             learningObject, | ||||||
|     })); |         }) | ||||||
|     attachmentEntities.forEach(it => { learningObject.attachments.add(it); }); |     ); | ||||||
|  |     attachmentEntities.forEach((it) => { | ||||||
|  |         learningObject.attachments.add(it); | ||||||
|  |     }); | ||||||
|     return learningObject; |     return learningObject; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -112,12 +112,13 @@ async function convertNode( | ||||||
|         ) |         ) | ||||||
|         .map((trans, i) => { |         .map((trans, i) => { | ||||||
|             try { |             try { | ||||||
|                 return convertTransition(trans, i, nodesToLearningObjects) |                 return convertTransition(trans, i, nodesToLearningObjects); | ||||||
|             } catch (_: unknown) { |             } catch (_: unknown) { | ||||||
|                 logger.error(`Transition could not be resolved: ${JSON.stringify(trans)}`); |                 logger.error(`Transition could not be resolved: ${JSON.stringify(trans)}`); | ||||||
|                 return undefined; // Do not crash on invalid transitions, just ignore them so the rest of the learning path keeps working.
 |                 return undefined; // Do not crash on invalid transitions, just ignore them so the rest of the learning path keeps working.
 | ||||||
|             } |             } | ||||||
|         }).filter(it => it !== undefined); |         }) | ||||||
|  |         .filter((it) => it !== undefined); | ||||||
|     return { |     return { | ||||||
|         _id: learningObject.uuid, |         _id: learningObject.uuid, | ||||||
|         language: learningObject.language, |         language: learningObject.language, | ||||||
|  |  | ||||||
|  | @ -110,9 +110,7 @@ const learningPathService = { | ||||||
|      * Fetch the learning paths administrated by the teacher with the given username. |      * Fetch the learning paths administrated by the teacher with the given username. | ||||||
|      */ |      */ | ||||||
|     async getLearningPathsAdministratedBy(adminUsername: string): Promise<LearningPath[]> { |     async getLearningPathsAdministratedBy(adminUsername: string): Promise<LearningPath[]> { | ||||||
|         const providerResponses = await Promise.all( |         const providerResponses = await Promise.all(allProviders.map(async (provider) => provider.getLearningPathsAdministratedBy(adminUsername))); | ||||||
|             allProviders.map(async (provider) => provider.getLearningPathsAdministratedBy(adminUsername)) |  | ||||||
|         ); |  | ||||||
|         return providerResponses.flat(); |         return providerResponses.flat(); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|  | @ -157,7 +155,7 @@ const learningPathService = { | ||||||
|         if (deletedPath) { |         if (deletedPath) { | ||||||
|             return deletedPath; |             return deletedPath; | ||||||
|         } |         } | ||||||
|         throw new NotFoundException("No learning path with the given identifier found."); |         throw new NotFoundException('No learning path with the given identifier found.'); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -168,10 +166,10 @@ const learningPathService = { | ||||||
|         const repo = getLearningPathRepository(); |         const repo = getLearningPathRepository(); | ||||||
|         const path = await repo.findByHruidAndLanguage(id.hruid, id.language); |         const path = await repo.findByHruidAndLanguage(id.hruid, id.language); | ||||||
|         if (!path) { |         if (!path) { | ||||||
|             throw new NotFoundException("No learning path with the given identifier found."); |             throw new NotFoundException('No learning path with the given identifier found.'); | ||||||
|         } |  | ||||||
|         return path.admins.map(admin => admin.username); |  | ||||||
|         } |         } | ||||||
|  |         return path.admins.map((admin) => admin.username); | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default learningPathService; | export default learningPathService; | ||||||
|  |  | ||||||
|  | @ -1,17 +1,17 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import { useI18n } from 'vue-i18n'; |     import { useI18n } from "vue-i18n"; | ||||||
| 
 | 
 | ||||||
|     const props = defineProps<{ |     const props = defineProps<{ | ||||||
|         text: string, |         text: string; | ||||||
|         prependIcon?: string, |         prependIcon?: string; | ||||||
|         appendIcon?: string, |         appendIcon?: string; | ||||||
|         confirmQueryText: string, |         confirmQueryText: string; | ||||||
|         variant?: "flat" | "text" | "elevated" | "tonal" | "outlined" | "plain" | undefined, |         variant?: "flat" | "text" | "elevated" | "tonal" | "outlined" | "plain" | undefined; | ||||||
|         color?: string, |         color?: string; | ||||||
|         disabled?: boolean |         disabled?: boolean; | ||||||
|     }>(); |     }>(); | ||||||
| 
 | 
 | ||||||
|     const emit = defineEmits<{ (e: 'confirm'): void }>() |     const emit = defineEmits<{ (e: "confirm"): void }>(); | ||||||
| 
 | 
 | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|  | @ -45,7 +45,10 @@ | ||||||
| 
 | 
 | ||||||
|                     <v-btn |                     <v-btn | ||||||
|                         :text="t('yes')" |                         :text="t('yes')" | ||||||
|           @click="confirm(); isActive.value = false" |                         @click=" | ||||||
|  |                             confirm(); | ||||||
|  |                             isActive.value = false; | ||||||
|  |                         " | ||||||
|                     ></v-btn> |                     ></v-btn> | ||||||
|                     <v-btn |                     <v-btn | ||||||
|                         :text="t('cancel')" |                         :text="t('cancel')" | ||||||
|  | @ -57,5 +60,4 @@ | ||||||
|     </v-dialog> |     </v-dialog> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped></style> | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -46,16 +46,21 @@ export abstract class BaseController { | ||||||
|      * @param queryParams The query parameters. |      * @param queryParams The query parameters. | ||||||
|      * @returns The response the POST request generated. |      * @returns The response the POST request generated. | ||||||
|      */ |      */ | ||||||
|     protected async postFile<T>(path: string, formFieldName: string, file: File, queryParams?: QueryParams): Promise<T> { |     protected async postFile<T>( | ||||||
|  |         path: string, | ||||||
|  |         formFieldName: string, | ||||||
|  |         file: File, | ||||||
|  |         queryParams?: QueryParams, | ||||||
|  |     ): Promise<T> { | ||||||
|         const formData = new FormData(); |         const formData = new FormData(); | ||||||
|         formData.append(formFieldName, file); |         formData.append(formFieldName, file); | ||||||
|         const response = await apiClient.post<T>(this.absolutePathFor(path), formData, { |         const response = await apiClient.post<T>(this.absolutePathFor(path), formData, { | ||||||
|             params: queryParams, |             params: queryParams, | ||||||
|             headers: { |             headers: { | ||||||
|                 'Content-Type': 'multipart/form-data' |                 "Content-Type": "multipart/form-data", | ||||||
|             } |             }, | ||||||
|         }); |         }); | ||||||
|         BaseController.assertSuccessResponse(response) |         BaseController.assertSuccessResponse(response); | ||||||
|         return response.data; |         return response.data; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ export class LearningPathNode { | ||||||
|         this.learningobjectHruid = options.learningobjectHruid; |         this.learningobjectHruid = options.learningobjectHruid; | ||||||
|         this.version = options.version; |         this.version = options.version; | ||||||
|         this.language = options.language; |         this.language = options.language; | ||||||
|         this.transitions = options.transitions.map(it => ({ next: it.next, default: it.default ?? false })); |         this.transitions = options.transitions.map((it) => ({ next: it.next, default: it.default ?? false })); | ||||||
|         this.createdAt = options.createdAt; |         this.createdAt = options.createdAt; | ||||||
|         this.updatedAt = options.updatedAt; |         this.updatedAt = options.updatedAt; | ||||||
|         this.done = options.done || false; |         this.done = options.done || false; | ||||||
|  |  | ||||||
|  | @ -82,7 +82,7 @@ export class LearningPath { | ||||||
|             keywords: dto.keywords.split(" "), |             keywords: dto.keywords.split(" "), | ||||||
|             targetAges: { |             targetAges: { | ||||||
|                 min: dto.min_age ?? NaN, |                 min: dto.min_age ?? NaN, | ||||||
|                 max: dto.max_age ?? NaN |                 max: dto.max_age ?? NaN, | ||||||
|             }, |             }, | ||||||
|             startNode: LearningPathNode.fromDTOAndOtherNodes(LearningPath.getStartNode(dto), dto.nodes), |             startNode: LearningPathNode.fromDTOAndOtherNodes(LearningPath.getStartNode(dto), dto.nodes), | ||||||
|             image: dto.image, |             image: dto.image, | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ import * as directives from "vuetify/directives"; | ||||||
| import i18n from "./i18n/i18n.ts"; | import i18n from "./i18n/i18n.ts"; | ||||||
| 
 | 
 | ||||||
| // JSON-editor
 | // JSON-editor
 | ||||||
| import JsonEditorVue from 'json-editor-vue'; | import JsonEditorVue from "json-editor-vue"; | ||||||
| 
 | 
 | ||||||
| // Components
 | // Components
 | ||||||
| import App from "./App.vue"; | import App from "./App.vue"; | ||||||
|  | @ -20,7 +20,7 @@ import { de, en, fr, nl } from "vuetify/locale"; | ||||||
| const app = createApp(App); | const app = createApp(App); | ||||||
| 
 | 
 | ||||||
| app.use(router); | app.use(router); | ||||||
| app.use(JsonEditorVue, {}) | app.use(JsonEditorVue, {}); | ||||||
| 
 | 
 | ||||||
| const link = document.createElement("link"); | const link = document.createElement("link"); | ||||||
| link.rel = "stylesheet"; | link.rel = "stylesheet"; | ||||||
|  | @ -39,9 +39,9 @@ const vuetify = createVuetify({ | ||||||
|     }, |     }, | ||||||
|     locale: { |     locale: { | ||||||
|         locale: i18n.global.locale, |         locale: i18n.global.locale, | ||||||
|         fallback: 'en', |         fallback: "en", | ||||||
|         messages: { nl, en, de, fr } |         messages: { nl, en, de, fr }, | ||||||
|     } |     }, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const queryClient = new QueryClient({ | const queryClient = new QueryClient({ | ||||||
|  |  | ||||||
|  | @ -1,6 +1,12 @@ | ||||||
| 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 { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; | import { | ||||||
|  |     useMutation, | ||||||
|  |     useQuery, | ||||||
|  |     useQueryClient, | ||||||
|  |     type UseMutationReturnType, | ||||||
|  |     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"; | ||||||
|  | @ -58,7 +64,7 @@ export function useLearningObjectListForPathQuery( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useLearningObjectListForAdminQuery( | export function useLearningObjectListForAdminQuery( | ||||||
|     admin: MaybeRefOrGetter<string | undefined> |     admin: MaybeRefOrGetter<string | undefined>, | ||||||
| ): UseQueryReturnType<LearningObject[], Error> { | ): UseQueryReturnType<LearningObject[], Error> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_OBJECT_KEY, "forAdmin", admin], |         queryKey: [LEARNING_OBJECT_KEY, "forAdmin", admin], | ||||||
|  | @ -66,24 +72,39 @@ export function useLearningObjectListForAdminQuery( | ||||||
|             const adminVal = toValue(admin); |             const adminVal = toValue(admin); | ||||||
|             return await learningObjectController.getAllAdministratedBy(adminVal!); |             return await learningObjectController.getAllAdministratedBy(adminVal!); | ||||||
|         }, |         }, | ||||||
|         enabled: () => toValue(admin) !== undefined |         enabled: () => toValue(admin) !== undefined, | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useUploadLearningObjectMutation(): UseMutationReturnType<LearningObject, AxiosError, {learningObjectZip: File}, unknown> { | export function useUploadLearningObjectMutation(): UseMutationReturnType< | ||||||
|  |     LearningObject, | ||||||
|  |     AxiosError, | ||||||
|  |     { learningObjectZip: File }, | ||||||
|  |     unknown | ||||||
|  | > { | ||||||
|     const queryClient = useQueryClient(); |     const queryClient = useQueryClient(); | ||||||
| 
 | 
 | ||||||
|     return useMutation({ |     return useMutation({ | ||||||
|         mutationFn: async ({ learningObjectZip }) => await learningObjectController.upload(learningObjectZip), |         mutationFn: async ({ learningObjectZip }) => await learningObjectController.upload(learningObjectZip), | ||||||
|         onSuccess: async () => { await queryClient.invalidateQueries({queryKey: [LEARNING_OBJECT_KEY, "forAdmin"]}); } |         onSuccess: async () => { | ||||||
|  |             await queryClient.invalidateQueries({ queryKey: [LEARNING_OBJECT_KEY, "forAdmin"] }); | ||||||
|  |         }, | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useDeleteLearningObjectMutation(): UseMutationReturnType<LearningObject, AxiosError, {hruid: string, language: Language, version: number}, unknown> { | export function useDeleteLearningObjectMutation(): UseMutationReturnType< | ||||||
|  |     LearningObject, | ||||||
|  |     AxiosError, | ||||||
|  |     { hruid: string; language: Language; version: number }, | ||||||
|  |     unknown | ||||||
|  | > { | ||||||
|     const queryClient = useQueryClient(); |     const queryClient = useQueryClient(); | ||||||
| 
 | 
 | ||||||
|     return useMutation({ |     return useMutation({ | ||||||
|         mutationFn: async ({ hruid, language, version }) => await learningObjectController.deleteLearningObject(hruid, language, version), |         mutationFn: async ({ hruid, language, version }) => | ||||||
|         onSuccess: async () => { await queryClient.invalidateQueries({queryKey: [LEARNING_OBJECT_KEY, "forAdmin"]}); } |             await learningObjectController.deleteLearningObject(hruid, language, version), | ||||||
|  |         onSuccess: async () => { | ||||||
|  |             await queryClient.invalidateQueries({ queryKey: [LEARNING_OBJECT_KEY, "forAdmin"] }); | ||||||
|  |         }, | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,12 @@ | ||||||
| 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 { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; | import { | ||||||
|  |     useMutation, | ||||||
|  |     useQuery, | ||||||
|  |     useQueryClient, | ||||||
|  |     type UseMutationReturnType, | ||||||
|  |     type UseQueryReturnType, | ||||||
|  | } from "@tanstack/vue-query"; | ||||||
| import { getLearningPathController } from "@/controllers/controllers"; | import { getLearningPathController } from "@/controllers/controllers"; | ||||||
| import type { AxiosError } from "axios"; | import type { AxiosError } from "axios"; | ||||||
| import type { LearningPath as LearningPathDTO } from "@dwengo-1/common/interfaces/learning-content"; | import type { LearningPath as LearningPathDTO } from "@dwengo-1/common/interfaces/learning-content"; | ||||||
|  | @ -35,42 +41,54 @@ export function useGetAllLearningPathsByThemeQuery( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useGetAllLearningPathsByAdminQuery( | export function useGetAllLearningPathsByAdminQuery( | ||||||
|     admin: MaybeRefOrGetter<string | undefined> |     admin: MaybeRefOrGetter<string | undefined>, | ||||||
| ): UseQueryReturnType<LearningPathDTO[], AxiosError> { | ): UseQueryReturnType<LearningPathDTO[], AxiosError> { | ||||||
|     return useQuery({ |     return useQuery({ | ||||||
|         queryKey: [LEARNING_PATH_KEY, "getAllByAdmin", admin], |         queryKey: [LEARNING_PATH_KEY, "getAllByAdmin", admin], | ||||||
|         queryFn: async () => learningPathController.getAllByAdminRaw(toValue(admin)!), |         queryFn: async () => learningPathController.getAllByAdminRaw(toValue(admin)!), | ||||||
|         enabled: () => Boolean(toValue(admin)) |         enabled: () => Boolean(toValue(admin)), | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function usePostLearningPathMutation(): | export function usePostLearningPathMutation(): UseMutationReturnType< | ||||||
|     UseMutationReturnType<LearningPathDTO, AxiosError, { learningPath: LearningPathDTO }, unknown> { |     LearningPathDTO, | ||||||
|  |     AxiosError, | ||||||
|  |     { learningPath: LearningPathDTO }, | ||||||
|  |     unknown | ||||||
|  | > { | ||||||
|     const queryClient = useQueryClient(); |     const queryClient = useQueryClient(); | ||||||
| 
 | 
 | ||||||
|     return useMutation({ |     return useMutation({ | ||||||
|         mutationFn: async ({ learningPath }) => learningPathController.postLearningPath(learningPath), |         mutationFn: async ({ learningPath }) => learningPathController.postLearningPath(learningPath), | ||||||
|         onSuccess: async () => queryClient.invalidateQueries({ queryKey: [LEARNING_PATH_KEY] }) |         onSuccess: async () => queryClient.invalidateQueries({ queryKey: [LEARNING_PATH_KEY] }), | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function usePutLearningPathMutation(): | export function usePutLearningPathMutation(): UseMutationReturnType< | ||||||
|     UseMutationReturnType<LearningPathDTO, AxiosError, { learningPath: LearningPathDTO }, unknown> { |     LearningPathDTO, | ||||||
|  |     AxiosError, | ||||||
|  |     { learningPath: LearningPathDTO }, | ||||||
|  |     unknown | ||||||
|  | > { | ||||||
|     const queryClient = useQueryClient(); |     const queryClient = useQueryClient(); | ||||||
| 
 | 
 | ||||||
|     return useMutation({ |     return useMutation({ | ||||||
|         mutationFn: async ({ learningPath }) => learningPathController.putLearningPath(learningPath), |         mutationFn: async ({ learningPath }) => learningPathController.putLearningPath(learningPath), | ||||||
|         onSuccess: async () => queryClient.invalidateQueries({ queryKey: [LEARNING_PATH_KEY] }) |         onSuccess: async () => queryClient.invalidateQueries({ queryKey: [LEARNING_PATH_KEY] }), | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function useDeleteLearningPathMutation(): | export function useDeleteLearningPathMutation(): UseMutationReturnType< | ||||||
|     UseMutationReturnType<LearningPathDTO, AxiosError, { hruid: string, language: Language }, unknown> { |     LearningPathDTO, | ||||||
|  |     AxiosError, | ||||||
|  |     { hruid: string; language: Language }, | ||||||
|  |     unknown | ||||||
|  | > { | ||||||
|     const queryClient = useQueryClient(); |     const queryClient = useQueryClient(); | ||||||
| 
 | 
 | ||||||
|     return useMutation({ |     return useMutation({ | ||||||
|         mutationFn: async ({ hruid, language }) => learningPathController.deleteLearningPath(hruid, language), |         mutationFn: async ({ hruid, language }) => learningPathController.deleteLearningPath(hruid, language), | ||||||
|         onSuccess: async () => queryClient.invalidateQueries({ queryKey: [LEARNING_PATH_KEY] }) |         onSuccess: async () => queryClient.invalidateQueries({ queryKey: [LEARNING_PATH_KEY] }), | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -110,7 +110,7 @@ const router = createRouter({ | ||||||
|             path: "/my-content", |             path: "/my-content", | ||||||
|             name: "OwnLearningContentPage", |             name: "OwnLearningContentPage", | ||||||
|             component: OwnLearningContentPage, |             component: OwnLearningContentPage, | ||||||
|             meta: { requiresAuth: true } |             meta: { requiresAuth: true }, | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             path: "/learningPath", |             path: "/learningPath", | ||||||
|  | @ -126,7 +126,7 @@ const router = createRouter({ | ||||||
|                     name: "LearningPath", |                     name: "LearningPath", | ||||||
|                     component: LearningPathPage, |                     component: LearningPathPage, | ||||||
|                     props: true, |                     props: true, | ||||||
|                     meta: { requiresAuth: true } |                     meta: { requiresAuth: true }, | ||||||
|                 }, |                 }, | ||||||
|             ], |             ], | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import { useLearningObjectListForAdminQuery } from "@/queries/learning-objects.ts"; |     import { useLearningObjectListForAdminQuery } from "@/queries/learning-objects.ts"; | ||||||
|     import OwnLearningObjectsView from "@/views/own-learning-content/learning-objects/OwnLearningObjectsView.vue" |     import OwnLearningObjectsView from "@/views/own-learning-content/learning-objects/OwnLearningObjectsView.vue"; | ||||||
|     import OwnLearningPathsView from "@/views/own-learning-content/learning-paths/OwnLearningPathsView.vue" |     import OwnLearningPathsView from "@/views/own-learning-content/learning-paths/OwnLearningPathsView.vue"; | ||||||
|     import authService from "@/services/auth/auth-service.ts"; |     import authService from "@/services/auth/auth-service.ts"; | ||||||
|     import UsingQueryResult from "@/components/UsingQueryResult.vue"; |     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
|     import type { LearningObject } from "@/data-objects/learning-objects/learning-object"; |     import type { LearningObject } from "@/data-objects/learning-objects/learning-object"; | ||||||
|  | @ -12,11 +12,13 @@ | ||||||
| 
 | 
 | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|     const learningObjectsQuery = |     const learningObjectsQuery = useLearningObjectListForAdminQuery( | ||||||
|         useLearningObjectListForAdminQuery(authService.authState.user?.profile.preferred_username); |         authService.authState.user?.profile.preferred_username, | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     const learningPathsQuery = |     const learningPathsQuery = useGetAllLearningPathsByAdminQuery( | ||||||
|         useGetAllLearningPathsByAdminQuery(authService.authState.user?.profile.preferred_username); |         authService.authState.user?.profile.preferred_username, | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     type Tab = "learningObjects" | "learningPaths"; |     type Tab = "learningObjects" | "learningPaths"; | ||||||
|     const tab: Ref<Tab> = ref("learningObjects"); |     const tab: Ref<Tab> = ref("learningObjects"); | ||||||
|  | @ -27,12 +29,18 @@ | ||||||
|         <h1 class="title">{{ t("ownLearningContentTitle") }}</h1> |         <h1 class="title">{{ t("ownLearningContentTitle") }}</h1> | ||||||
| 
 | 
 | ||||||
|         <v-tabs v-model="tab"> |         <v-tabs v-model="tab"> | ||||||
|         <v-tab value="learningObjects">{{ t('learningObjects') }}</v-tab> |             <v-tab value="learningObjects">{{ t("learningObjects") }}</v-tab> | ||||||
|         <v-tab value="learningPaths">{{ t('learningPaths') }}</v-tab> |             <v-tab value="learningPaths">{{ t("learningPaths") }}</v-tab> | ||||||
|         </v-tabs> |         </v-tabs> | ||||||
| 
 | 
 | ||||||
|         <v-tabs-window v-model="tab" class="main-content"> |         <v-tabs-window | ||||||
|             <v-tabs-window-item value="learningObjects" class="main-content"> |             v-model="tab" | ||||||
|  |             class="main-content" | ||||||
|  |         > | ||||||
|  |             <v-tabs-window-item | ||||||
|  |                 value="learningObjects" | ||||||
|  |                 class="main-content" | ||||||
|  |             > | ||||||
|                 <using-query-result |                 <using-query-result | ||||||
|                     :query-result="learningObjectsQuery" |                     :query-result="learningObjectsQuery" | ||||||
|                     v-slot="response: { data: LearningObject[] }" |                     v-slot="response: { data: LearningObject[] }" | ||||||
|  |  | ||||||
|  | @ -1,21 +1,21 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import type { LearningObject } from '@/data-objects/learning-objects/learning-object'; |     import type { LearningObject } from "@/data-objects/learning-objects/learning-object"; | ||||||
|     import UsingQueryResult from '@/components/UsingQueryResult.vue'; |     import UsingQueryResult from "@/components/UsingQueryResult.vue"; | ||||||
|     import LearningObjectContentView from '../../learning-paths/learning-object/content/LearningObjectContentView.vue'; |     import LearningObjectContentView from "../../learning-paths/learning-object/content/LearningObjectContentView.vue"; | ||||||
|     import ButtonWithConfirmation from '@/components/ButtonWithConfirmation.vue'; |     import ButtonWithConfirmation from "@/components/ButtonWithConfirmation.vue"; | ||||||
|     import { useDeleteLearningObjectMutation, useLearningObjectHTMLQuery } from '@/queries/learning-objects'; |     import { useDeleteLearningObjectMutation, useLearningObjectHTMLQuery } from "@/queries/learning-objects"; | ||||||
|     import { useI18n } from 'vue-i18n'; |     import { useI18n } from "vue-i18n"; | ||||||
| 
 | 
 | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|     const props = defineProps<{ |     const props = defineProps<{ | ||||||
|         selectedLearningObject?: LearningObject |         selectedLearningObject?: LearningObject; | ||||||
|     }>(); |     }>(); | ||||||
| 
 | 
 | ||||||
|     const learningObjectQueryResult = useLearningObjectHTMLQuery( |     const learningObjectQueryResult = useLearningObjectHTMLQuery( | ||||||
|         () => props.selectedLearningObject?.key, |         () => props.selectedLearningObject?.key, | ||||||
|         () => props.selectedLearningObject?.language, |         () => props.selectedLearningObject?.language, | ||||||
|         () => props.selectedLearningObject?.version |         () => props.selectedLearningObject?.version, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     const { isPending, mutate } = useDeleteLearningObjectMutation(); |     const { isPending, mutate } = useDeleteLearningObjectMutation(); | ||||||
|  | @ -25,7 +25,7 @@ | ||||||
|             mutate({ |             mutate({ | ||||||
|                 hruid: props.selectedLearningObject.key, |                 hruid: props.selectedLearningObject.key, | ||||||
|                 language: props.selectedLearningObject.language, |                 language: props.selectedLearningObject.language, | ||||||
|                 version: props.selectedLearningObject.version |                 version: props.selectedLearningObject.version, | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -37,7 +37,10 @@ | ||||||
|         :title="t('previewFor') + selectedLearningObject.title" |         :title="t('previewFor') + selectedLearningObject.title" | ||||||
|     > |     > | ||||||
|         <template v-slot:text> |         <template v-slot:text> | ||||||
|             <using-query-result :query-result="learningObjectQueryResult" v-slot="response: { data: Document }"> |             <using-query-result | ||||||
|  |                 :query-result="learningObjectQueryResult" | ||||||
|  |                 v-slot="response: { data: Document }" | ||||||
|  |             > | ||||||
|                 <learning-object-content-view :learning-object-content="response.data"></learning-object-content-view> |                 <learning-object-content-view :learning-object-content="response.data"></learning-object-content-view> | ||||||
|             </using-query-result> |             </using-query-result> | ||||||
|         </template> |         </template> | ||||||
|  | @ -53,5 +56,4 @@ | ||||||
|     </v-card> |     </v-card> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped></style> | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import { useUploadLearningObjectMutation } from '@/queries/learning-objects'; |     import { useUploadLearningObjectMutation } from "@/queries/learning-objects"; | ||||||
|     import { ref, watch, type Ref } from 'vue'; |     import { ref, watch, type Ref } from "vue"; | ||||||
|     import { useI18n } from 'vue-i18n'; |     import { useI18n } from "vue-i18n"; | ||||||
|     import { VFileUpload } from 'vuetify/labs/VFileUpload'; |     import { VFileUpload } from "vuetify/labs/VFileUpload"; | ||||||
| 
 | 
 | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|  | @ -30,14 +30,18 @@ | ||||||
|     } |     } | ||||||
| </script> | </script> | ||||||
| <template> | <template> | ||||||
|     <v-dialog max-width="500" v-model="dialogOpen"> |     <v-dialog | ||||||
|  |         max-width="500" | ||||||
|  |         v-model="dialogOpen" | ||||||
|  |     > | ||||||
|         <template v-slot:activator="{ props: activatorProps }"> |         <template v-slot:activator="{ props: activatorProps }"> | ||||||
|             <v-btn |             <v-btn | ||||||
|                 prepend-icon="mdi mdi-plus" |                 prepend-icon="mdi mdi-plus" | ||||||
|                 :text="t('newLearningObject')" |                 :text="t('newLearningObject')" | ||||||
|                 v-bind="activatorProps" |                 v-bind="activatorProps" | ||||||
|                 color="rgb(14, 105, 66)" |                 color="rgb(14, 105, 66)" | ||||||
|                 size="large"> |                 size="large" | ||||||
|  |             > | ||||||
|             </v-btn> |             </v-btn> | ||||||
|         </template> |         </template> | ||||||
| 
 | 
 | ||||||
|  | @ -75,5 +79,4 @@ | ||||||
|         </template> |         </template> | ||||||
|     </v-dialog> |     </v-dialog> | ||||||
| </template> | </template> | ||||||
| <style scoped> | <style scoped></style> | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -1,30 +1,32 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import type { LearningObject } from '@/data-objects/learning-objects/learning-object'; |     import type { LearningObject } from "@/data-objects/learning-objects/learning-object"; | ||||||
|     import LearningObjectUploadButton from '@/views/own-learning-content/learning-objects/LearningObjectUploadButton.vue' |     import LearningObjectUploadButton from "@/views/own-learning-content/learning-objects/LearningObjectUploadButton.vue"; | ||||||
|     import LearningObjectPreviewCard from './LearningObjectPreviewCard.vue'; |     import LearningObjectPreviewCard from "./LearningObjectPreviewCard.vue"; | ||||||
|     import { computed, ref, watch, type Ref } from 'vue'; |     import { computed, ref, watch, type Ref } from "vue"; | ||||||
|     import { useI18n } from 'vue-i18n'; |     import { useI18n } from "vue-i18n"; | ||||||
| 
 | 
 | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
|     const props = defineProps<{ |     const props = defineProps<{ | ||||||
|         learningObjects: LearningObject[] |         learningObjects: LearningObject[]; | ||||||
|     }>(); |     }>(); | ||||||
| 
 | 
 | ||||||
|     const tableHeaders = [ |     const tableHeaders = [ | ||||||
|         { title: t("hruid"), width: "250px", key: "key" }, |         { title: t("hruid"), width: "250px", key: "key" }, | ||||||
|         { title: t("language"), width: "50px", key: "language" }, |         { title: t("language"), width: "50px", key: "language" }, | ||||||
|         { title: t("version"), width: "50px", key: "version" }, |         { title: t("version"), width: "50px", key: "version" }, | ||||||
|         { title: t("title"), key: "title" } |         { title: t("title"), key: "title" }, | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     const selectedLearningObjects: Ref<LearningObject[]> = ref([]); |     const selectedLearningObjects: Ref<LearningObject[]> = ref([]); | ||||||
| 
 | 
 | ||||||
|     watch(() => props.learningObjects, () => selectedLearningObjects.value = []); |     watch( | ||||||
| 
 |         () => props.learningObjects, | ||||||
|     const selectedLearningObject = computed(() => |         () => (selectedLearningObjects.value = []), | ||||||
|         selectedLearningObjects.value ? selectedLearningObjects.value[0] : undefined |  | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     const selectedLearningObject = computed(() => | ||||||
|  |         selectedLearningObjects.value ? selectedLearningObjects.value[0] : undefined, | ||||||
|  |     ); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -41,8 +43,14 @@ | ||||||
|                 return-object |                 return-object | ||||||
|             /> |             /> | ||||||
|         </div> |         </div> | ||||||
|         <div class="preview-container" v-if="selectedLearningObject"> |         <div | ||||||
|             <learning-object-preview-card class="preview" :selectedLearningObject="selectedLearningObject"/> |             class="preview-container" | ||||||
|  |             v-if="selectedLearningObject" | ||||||
|  |         > | ||||||
|  |             <learning-object-preview-card | ||||||
|  |                 class="preview" | ||||||
|  |                 :selectedLearningObject="selectedLearningObject" | ||||||
|  |             /> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -1,30 +1,34 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import { useI18n } from 'vue-i18n'; |     import { useI18n } from "vue-i18n"; | ||||||
|     import { computed, ref, watch, type Ref } from 'vue'; |     import { computed, ref, watch, type Ref } from "vue"; | ||||||
|     import JsonEditorVue from 'json-editor-vue' |     import JsonEditorVue from "json-editor-vue"; | ||||||
|     import ButtonWithConfirmation from '@/components/ButtonWithConfirmation.vue' |     import ButtonWithConfirmation from "@/components/ButtonWithConfirmation.vue"; | ||||||
|     import { useDeleteLearningPathMutation, usePostLearningPathMutation, usePutLearningPathMutation } from '@/queries/learning-paths'; |     import { | ||||||
|     import { Language } from '@/data-objects/language'; |         useDeleteLearningPathMutation, | ||||||
|     import type { LearningPath } from '@dwengo-1/common/interfaces/learning-content'; |         usePostLearningPathMutation, | ||||||
|     import type { AxiosError } from 'axios'; |         usePutLearningPathMutation, | ||||||
| import { parse } from 'uuid'; |     } from "@/queries/learning-paths"; | ||||||
|  |     import { Language } from "@/data-objects/language"; | ||||||
|  |     import type { LearningPath } from "@dwengo-1/common/interfaces/learning-content"; | ||||||
|  |     import type { AxiosError } from "axios"; | ||||||
|  |     import { parse } from "uuid"; | ||||||
| 
 | 
 | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
| 
 | 
 | ||||||
|     const props = defineProps<{ |     const props = defineProps<{ | ||||||
|         selectedLearningPath?: LearningPath |         selectedLearningPath?: LearningPath; | ||||||
|     }>(); |     }>(); | ||||||
| 
 | 
 | ||||||
|     const { isPending, mutate, error: deleteError, isSuccess: deleteSuccess } = useDeleteLearningPathMutation(); |     const { isPending, mutate, error: deleteError, isSuccess: deleteSuccess } = useDeleteLearningPathMutation(); | ||||||
| 
 | 
 | ||||||
|     const DEFAULT_LEARNING_PATH: LearningPath = { |     const DEFAULT_LEARNING_PATH: LearningPath = { | ||||||
|         language: 'en', |         language: "en", | ||||||
|         hruid: '...', |         hruid: "...", | ||||||
|         title: '...', |         title: "...", | ||||||
|         description: '...', |         description: "...", | ||||||
|         nodes: [ |         nodes: [ | ||||||
|             { |             { | ||||||
|                 learningobject_hruid: '...', |                 learningobject_hruid: "...", | ||||||
|                 language: Language.English, |                 language: Language.English, | ||||||
|                 version: 1, |                 version: 1, | ||||||
|                 start_node: true, |                 start_node: true, | ||||||
|  | @ -33,17 +37,17 @@ import { parse } from 'uuid'; | ||||||
|                         default: true, |                         default: true, | ||||||
|                         condition: "(remove if the transition should be unconditinal)", |                         condition: "(remove if the transition should be unconditinal)", | ||||||
|                         next: { |                         next: { | ||||||
|                             hruid: '...', |                             hruid: "...", | ||||||
|                             version: 1, |                             version: 1, | ||||||
|                             language: '...' |                             language: "...", | ||||||
|                         } |                         }, | ||||||
|                     } |                     }, | ||||||
|                 ] |  | ||||||
|             } |  | ||||||
|                 ], |                 ], | ||||||
|         keywords: 'Keywords separated by spaces', |             }, | ||||||
|         target_ages: [] |         ], | ||||||
|     } |         keywords: "Keywords separated by spaces", | ||||||
|  |         target_ages: [], | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     const { isPending: isPostPending, error: postError, mutate: doPost } = usePostLearningPathMutation(); |     const { isPending: isPostPending, error: postError, mutate: doPost } = usePostLearningPathMutation(); | ||||||
|     const { isPending: isPutPending, error: putError, mutate: doPut } = usePutLearningPathMutation(); |     const { isPending: isPutPending, error: putError, mutate: doPut } = usePutLearningPathMutation(); | ||||||
|  | @ -51,11 +55,13 @@ import { parse } from 'uuid'; | ||||||
|     const learningPath: Ref<LearningPath | string> = ref(DEFAULT_LEARNING_PATH); |     const learningPath: Ref<LearningPath | string> = ref(DEFAULT_LEARNING_PATH); | ||||||
| 
 | 
 | ||||||
|     const parsedLearningPath = computed(() => |     const parsedLearningPath = computed(() => | ||||||
|         typeof learningPath.value === "string" ? JSON.parse(learningPath.value) as LearningPath |         typeof learningPath.value === "string" ? (JSON.parse(learningPath.value) as LearningPath) : learningPath.value, | ||||||
|                                                : learningPath.value |  | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     watch(() => props.selectedLearningPath, () => learningPath.value = props.selectedLearningPath ?? DEFAULT_LEARNING_PATH); |     watch( | ||||||
|  |         () => props.selectedLearningPath, | ||||||
|  |         () => (learningPath.value = props.selectedLearningPath ?? DEFAULT_LEARNING_PATH), | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     function uploadLearningPath(): void { |     function uploadLearningPath(): void { | ||||||
|         if (props.selectedLearningPath) { |         if (props.selectedLearningPath) { | ||||||
|  | @ -69,7 +75,7 @@ import { parse } from 'uuid'; | ||||||
|         if (props.selectedLearningPath) { |         if (props.selectedLearningPath) { | ||||||
|             mutate({ |             mutate({ | ||||||
|                 hruid: props.selectedLearningPath.hruid, |                 hruid: props.selectedLearningPath.hruid, | ||||||
|                 language: props.selectedLearningPath.language as Language |                 language: props.selectedLearningPath.language as Language, | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -78,11 +84,11 @@ import { parse } from 'uuid'; | ||||||
|         return (error.response?.data as { error: string }).error ?? error.message; |         return (error.response?.data as { error: string }).error ?? error.message; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const isIdModified = computed(() => |     const isIdModified = computed( | ||||||
|         props.selectedLearningPath !== undefined && ( |         () => | ||||||
|             props.selectedLearningPath.hruid !== parsedLearningPath.value.hruid |             props.selectedLearningPath !== undefined && | ||||||
|             || props.selectedLearningPath.language !== parsedLearningPath.value.language |             (props.selectedLearningPath.hruid !== parsedLearningPath.value.hruid || | ||||||
|         ) |                 props.selectedLearningPath.language !== parsedLearningPath.value.language), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     function getErrorMessage(): string | null { |     function getErrorMessage(): string | null { | ||||||
|  | @ -93,16 +99,14 @@ import { parse } from 'uuid'; | ||||||
|         } else if (deleteError.value) { |         } else if (deleteError.value) { | ||||||
|             return t(extractErrorMessage(deleteError.value)); |             return t(extractErrorMessage(deleteError.value)); | ||||||
|         } else if (isIdModified.value) { |         } else if (isIdModified.value) { | ||||||
|             return t('learningPathCantModifyId'); |             return t("learningPathCantModifyId"); | ||||||
|         } |         } | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|     <v-card |     <v-card :title="props.selectedLearningPath ? t('editLearningPath') : t('newLearningPath')"> | ||||||
|         :title="props.selectedLearningPath ? t('editLearningPath') : t('newLearningPath')" |  | ||||||
|     > |  | ||||||
|         <template v-slot:text> |         <template v-slot:text> | ||||||
|             <json-editor-vue v-model="learningPath"></json-editor-vue> |             <json-editor-vue v-model="learningPath"></json-editor-vue> | ||||||
|             <v-alert |             <v-alert | ||||||
|  | @ -120,7 +124,7 @@ import { parse } from 'uuid'; | ||||||
|                 :loading="isPostPending || isPutPending" |                 :loading="isPostPending || isPutPending" | ||||||
|                 :disabled="parsedLearningPath.hruid === DEFAULT_LEARNING_PATH.hruid || isIdModified" |                 :disabled="parsedLearningPath.hruid === DEFAULT_LEARNING_PATH.hruid || isIdModified" | ||||||
|             > |             > | ||||||
|                 {{ props.selectedLearningPath ? t('saveChanges') : t('create') }} |                 {{ props.selectedLearningPath ? t("saveChanges") : t("create") }} | ||||||
|             </v-btn> |             </v-btn> | ||||||
|             <button-with-confirmation |             <button-with-confirmation | ||||||
|                 @confirm="deleteLearningPath" |                 @confirm="deleteLearningPath" | ||||||
|  | @ -136,11 +140,10 @@ import { parse } from 'uuid'; | ||||||
|                 prepend-icon="mdi mdi-open-in-new" |                 prepend-icon="mdi mdi-open-in-new" | ||||||
|                 :disabled="!props.selectedLearningPath" |                 :disabled="!props.selectedLearningPath" | ||||||
|             > |             > | ||||||
|                 {{ t('open') }} |                 {{ t("open") }} | ||||||
|             </v-btn> |             </v-btn> | ||||||
|         </template> |         </template> | ||||||
|     </v-card> |     </v-card> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped></style> | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -1,27 +1,30 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import LearningPathPreviewCard from './LearningPathPreviewCard.vue'; |     import LearningPathPreviewCard from "./LearningPathPreviewCard.vue"; | ||||||
|     import { computed, ref, watch, type Ref } from 'vue'; |     import { computed, ref, watch, type Ref } from "vue"; | ||||||
|     import { useI18n } from 'vue-i18n'; |     import { useI18n } from "vue-i18n"; | ||||||
|     import type { LearningPath as LearningPathDTO } from '@dwengo-1/common/interfaces/learning-content'; |     import type { LearningPath as LearningPathDTO } from "@dwengo-1/common/interfaces/learning-content"; | ||||||
| 
 | 
 | ||||||
|     const { t } = useI18n(); |     const { t } = useI18n(); | ||||||
|     const props = defineProps<{ |     const props = defineProps<{ | ||||||
|         learningPaths: LearningPathDTO[] |         learningPaths: LearningPathDTO[]; | ||||||
|     }>(); |     }>(); | ||||||
| 
 | 
 | ||||||
|     const tableHeaders = [ |     const tableHeaders = [ | ||||||
|         { title: t("hruid"), width: "250px", key: "hruid" }, |         { title: t("hruid"), width: "250px", key: "hruid" }, | ||||||
|         { title: t("language"), width: "50px", key: "language" }, |         { title: t("language"), width: "50px", key: "language" }, | ||||||
|         { title: t("title"), key: "title" } |         { title: t("title"), key: "title" }, | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     const selectedLearningPaths: Ref<LearningPathDTO[]> = ref([]); |     const selectedLearningPaths: Ref<LearningPathDTO[]> = ref([]); | ||||||
| 
 | 
 | ||||||
|     const selectedLearningPath = computed(() => |     const selectedLearningPath = computed(() => | ||||||
|         selectedLearningPaths.value ? selectedLearningPaths.value[0] : undefined |         selectedLearningPaths.value ? selectedLearningPaths.value[0] : undefined, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     watch(() => props.learningPaths, () => selectedLearningPaths.value = []); |     watch( | ||||||
|  |         () => props.learningPaths, | ||||||
|  |         () => (selectedLearningPaths.value = []), | ||||||
|  |     ); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
|  | @ -38,7 +41,10 @@ | ||||||
|             /> |             /> | ||||||
|         </div> |         </div> | ||||||
|         <div class="preview-container"> |         <div class="preview-container"> | ||||||
|             <learning-path-preview-card class="preview" :selectedLearningPath="selectedLearningPath"/> |             <learning-path-preview-card | ||||||
|  |                 class="preview" | ||||||
|  |                 :selectedLearningPath="selectedLearningPath" | ||||||
|  |             /> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Lint Action
						Lint Action