diff --git a/apps/api/src/chapter/adminChapter.service.ts b/apps/api/src/chapter/adminChapter.service.ts index a965531d..6e457ca3 100644 --- a/apps/api/src/chapter/adminChapter.service.ts +++ b/apps/api/src/chapter/adminChapter.service.ts @@ -63,7 +63,7 @@ export class AdminChapterService { // }, // }; // } - // async processLessonItems(lessonItemsList: LessonItemWithContentSchema[]) { + // async processLessonItems(lessonItemsList: AdminLessonWithContentSchema[]) { // const getFileUrl = async (url: string) => { // if (!url || url.startsWith("https://")) return url; // return await this.s3Service.getSignedUrl(url); @@ -123,7 +123,7 @@ export class AdminChapterService { // const lessonItemsList = await this.adminChapterRepository.getLessonItems(id); - // const items = await this.processLessonItems(lessonItemsList as LessonItemWithContentSchema[]); + // const items = await this.processLessonItems(lessonItemsList as AdminLessonWithContentSchema[]); // return { // ...lesson, diff --git a/apps/api/src/chapter/repositories/adminChapter.repository.ts b/apps/api/src/chapter/repositories/adminChapter.repository.ts index c4c34660..2e44d7d6 100644 --- a/apps/api/src/chapter/repositories/adminChapter.repository.ts +++ b/apps/api/src/chapter/repositories/adminChapter.repository.ts @@ -6,7 +6,7 @@ import { chapters, lessons, questionAnswerOptions, questions } from "src/storage import type { UpdateChapterBody } from "../schemas/chapter.schema"; import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"; -import type { LessonItemWithContentSchema, QuestionSchema } from "src/lesson/lesson.schema"; +import type { AdminLessonWithContentSchema, QuestionSchema } from "src/lesson/lesson.schema"; import type * as schema from "src/storage/schema"; @Injectable() @@ -209,7 +209,7 @@ export class AdminChapterRepository { // // : await this.fileService.getFileUrl(lesson.imageUrl), // // }; - async getBetaChapterLessons(chapterId: UUIDType): Promise { + async getBetaChapterLessons(chapterId: UUIDType): Promise { return await this.db .select({ updatedAt: sql`${lessons.updatedAt}`, @@ -229,6 +229,7 @@ export class AdminChapterRepository { 'type', ${questions.type}, 'description', ${questions.description}, 'photoS3Key', ${questions.photoS3Key}, + 'displayOrder', ${questions.displayOrder}, 'photoQuestionType', ${questions.photoQuestionType}, 'options', ( SELECT ARRAY( @@ -236,7 +237,7 @@ export class AdminChapterRepository { 'id', ${questionAnswerOptions.id}, 'optionText', ${questionAnswerOptions.optionText}, 'isCorrect', ${questionAnswerOptions.isCorrect}, - 'position', ${questionAnswerOptions.position} + 'displayOrder', ${questionAnswerOptions.displayOrder} ) FROM ${questionAnswerOptions} questionAnswerOptions WHERE questionAnswerOptions.question_id = questions.id diff --git a/apps/api/src/courses/course.service.ts b/apps/api/src/courses/course.service.ts index c28064a3..81c9916d 100644 --- a/apps/api/src/courses/course.service.ts +++ b/apps/api/src/courses/course.service.ts @@ -57,7 +57,10 @@ import type { CommonShowCourse } from "./schemas/showCourseCommon.schema"; import type { UpdateCourseBody } from "./schemas/updateCourse.schema"; import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"; import type { Pagination, UUIDType } from "src/common"; -import type { LessonForChapterSchema, LessonItemWithContentSchema } from "src/lesson/lesson.schema"; +import type { + AdminLessonWithContentSchema, + LessonForChapterSchema, +} from "src/lesson/lesson.schema"; import type * as schema from "src/storage/schema"; import type { ProgressStatus } from "src/utils/types/progress.type"; @@ -378,7 +381,8 @@ export class CourseService { `, isFreemium: chapters.isFreemium, displayOrder: sql`${chapters.displayOrder}`, - lessons: sql`COALESCE( + lessons: sql` + COALESCE( ( SELECT json_agg(lesson_data) FROM ( @@ -443,7 +447,7 @@ export class CourseService { .select({ id: courses.id, title: courses.title, - thumbnailUrl: sql`COALESCE(${courses.thumbnailS3Key}, '')`, + thumbnailS3Key: sql`COALESCE(${courses.thumbnailS3Key}, '')`, category: categories.title, categoryId: categories.id, description: sql`${courses.description}`, @@ -485,11 +489,11 @@ export class CourseService { return await this.fileService.getFileUrl(url); }; - const thumbnailUrl = await getImageUrl(course.thumbnailUrl); + const thumbnailS3SingedUrl = await getImageUrl(course.thumbnailS3Key); const updatedCourseLessonList = await Promise.all( courseChapterList?.map(async (chapter) => { - const lessons: LessonItemWithContentSchema[] = + const lessons: AdminLessonWithContentSchema[] = await this.adminChapterRepository.getBetaChapterLessons(chapter.id); const lessonsWithSignedUrls = await this.addS3SignedUrlsToLessonsAndQuestions(lessons); @@ -503,7 +507,7 @@ export class CourseService { return { ...course, - thumbnailUrl, + thumbnailS3SingedUrl, chapters: updatedCourseLessonList ?? [], }; } @@ -513,7 +517,7 @@ export class CourseService { .select({ id: courses.id, title: courses.title, - thumbnailUrl: sql`${courses.thumbnailS3Key}`, + thumbnailS3Key: sql`${courses.thumbnailS3Key}`, category: categories.title, categoryId: categories.id, description: sql`${courses.description}`, @@ -552,11 +556,11 @@ export class CourseService { return await this.fileService.getFileUrl(url); }; - const thumbnailUrl = await getImageUrl(course.thumbnailUrl); + const thumbnailS3SingedUrl = await getImageUrl(course.thumbnailS3Key); return { ...course, - thumbnailUrl, + thumbnailS3SingedUrl, chapters: courseChapterList, }; } @@ -927,7 +931,7 @@ export class CourseService { ); } - private async addS3SignedUrlsToLessonsAndQuestions(lessons: LessonItemWithContentSchema[]) { + private async addS3SignedUrlsToLessonsAndQuestions(lessons: AdminLessonWithContentSchema[]) { return await Promise.all( lessons.map(async (lesson) => { const updatedLesson = { ...lesson }; diff --git a/apps/api/src/courses/schemas/showCourseCommon.schema.ts b/apps/api/src/courses/schemas/showCourseCommon.schema.ts index e28bc43e..87fc9af7 100644 --- a/apps/api/src/courses/schemas/showCourseCommon.schema.ts +++ b/apps/api/src/courses/schemas/showCourseCommon.schema.ts @@ -40,6 +40,8 @@ export const commonShowBetaCourseSchema = Type.Object({ isScorm: Type.Optional(Type.Boolean()), priceInCents: Type.Number(), thumbnailUrl: Type.Optional(Type.String()), + thumbnailS3Key: Type.Optional(Type.String()), + thumbnailS3SingedUrl: Type.Optional(Type.String()), title: Type.String(), }); diff --git a/apps/api/src/lesson/lesson.controller.ts b/apps/api/src/lesson/lesson.controller.ts index 8a1986d6..046a44e2 100644 --- a/apps/api/src/lesson/lesson.controller.ts +++ b/apps/api/src/lesson/lesson.controller.ts @@ -18,21 +18,22 @@ import { CurrentUser } from "src/common/decorators/user.decorator"; import { RolesGuard } from "src/common/guards/roles.guard"; import { USER_ROLES } from "src/user/schemas/userRoles"; -import { AdminLessonService } from "./services/adminLesson.service"; import { CreateLessonBody, createLessonSchema, CreateQuizLessonBody, createQuizLessonSchema, - LessonShow, lessonShowSchema, UpdateLessonBody, updateLessonSchema, UpdateQuizLessonBody, updateQuizLessonSchema, } from "./lesson.schema"; +import { AdminLessonService } from "./services/adminLesson.service"; import { LessonService } from "./services/lesson.service"; +import type { LessonShow } from "./lesson.schema"; + @Controller("lesson") @UseGuards(RolesGuard) export class LessonController { @@ -157,56 +158,21 @@ export class LessonController { }); } - // @Patch("course-lesson") - // @Roles(USER_ROLES.TEACHER, USER_ROLES.ADMIN) - // @Validate({ - // request: [ - // { - // type: "body", - // schema: Type.Object({ - // courseId: UUIDSchema, - // lessonId: UUIDSchema, - // isFree: Type.Boolean(), - // }), - // }, - // ], - // response: baseResponse(Type.Object({ isFree: Type.Boolean(), message: Type.String() })), - // }) - // async toggleLessonAsFree( - // @Body() body: { courseId: string; lessonId: string; isFree: boolean }, - // ): Promise> { - // const [toggledLesson] = await this.adminLessonsService.toggleLessonAsFree( - // body.courseId, - // body.lessonId, - // body.isFree, - // ); - // return new BaseResponse({ - // isFree: toggledLesson.isFree, - // message: body.isFree - // ? "Lesson toggled as free successfully" - // : "Lesson toggled as not free successfully", - // }); - // } - - // @Post("evaluation-quiz") - // @Roles(USER_ROLES.STUDENT) - // @Validate({ - // request: [ - // { type: "query", name: "courseId", schema: UUIDSchema }, - // { type: "query", name: "lessonId", schema: UUIDSchema }, - // ], - // response: baseResponse(Type.Object({ message: Type.String() })), - // }) - // async evaluationQuiz( - // @Query("courseId") courseId: string, - // @Query("lessonId") lessonId: string, - // @CurrentUser("userId") currentUserId: UUIDType, - // ): Promise> { - // await this.lessonsService.evaluationQuiz(courseId, lessonId, currentUserId); - // return new BaseResponse({ - // message: "Evaluation quiz successfully", - // }); - // } + // @Post("evaluation-quiz") + // @Roles(USER_ROLES.STUDENT) + // @Validate({ + // request: [{ type: "query", name: "lessonId", schema: UUIDSchema, required: true }], + // response: baseResponse(Type.Object({ message: Type.String() })), + // }) + // async evaluationQuiz( + // @Query("lessonId") lessonId: string, + // @CurrentUser("userId") currentUserId: UUIDType, + // ): Promise> { + // await this.lessonService.evaluationQuiz(lessonId, currentUserId); + // return new BaseResponse({ + // message: "Evaluation quiz successfully", + // }); + // } // @Delete("clear-quiz-progress") // @Roles(USER_ROLES.STUDENT) diff --git a/apps/api/src/lesson/lesson.module.ts b/apps/api/src/lesson/lesson.module.ts index faf189ee..126b294b 100644 --- a/apps/api/src/lesson/lesson.module.ts +++ b/apps/api/src/lesson/lesson.module.ts @@ -2,10 +2,10 @@ import { Module } from "@nestjs/common"; import { FileModule } from "src/file/files.module"; -import { AdminLessonRepository } from "./repositories/adminLesson.repository"; -import { AdminLessonService } from "./services/adminLesson.service"; import { LessonController } from "./lesson.controller"; +import { AdminLessonRepository } from "./repositories/adminLesson.repository"; import { LessonRepository } from "./repositories/lesson.repository"; +import { AdminLessonService } from "./services/adminLesson.service"; import { LessonService } from "./services/lesson.service"; @Module({ diff --git a/apps/api/src/lesson/lesson.schema.ts b/apps/api/src/lesson/lesson.schema.ts index c0ecc4b9..b19f474d 100644 --- a/apps/api/src/lesson/lesson.schema.ts +++ b/apps/api/src/lesson/lesson.schema.ts @@ -1,16 +1,19 @@ import { Type } from "@sinclair/typebox"; import { UUIDSchema } from "src/common"; +import { PROGRESS_STATUSES } from "src/utils/types/progress.type"; -import { PhotoQuestionType, QuestionType } from "./lesson.type"; +import { LESSON_TYPES, PhotoQuestionType, QuestionType } from "./lesson.type"; import type { Static } from "@sinclair/typebox"; export const optionSchema = Type.Object({ id: Type.Optional(UUIDSchema), optionText: Type.String(), + displayOrder: Type.Union([Type.Number(), Type.Null()]), + isStudentAnswer: Type.Optional(Type.Boolean()), isCorrect: Type.Boolean(), - position: Type.Number(), + questionId: Type.Optional(UUIDSchema), }); export const questionSchema = Type.Object({ @@ -18,13 +21,13 @@ export const questionSchema = Type.Object({ type: Type.Enum(QuestionType), description: Type.Optional(Type.Union([Type.String(), Type.Null()])), title: Type.String(), + displayOrder: Type.Optional(Type.Number()), photoQuestionType: Type.Optional(Type.Union([Type.Enum(PhotoQuestionType), Type.Null()])), photoS3Key: Type.Optional(Type.Union([Type.String(), Type.Null()])), options: Type.Optional(Type.Array(optionSchema)), }); export const lessonSchema = Type.Object({ - updatedAt: Type.Optional(Type.String()), id: UUIDSchema, title: Type.String(), type: Type.String(), @@ -33,6 +36,7 @@ export const lessonSchema = Type.Object({ fileS3Key: Type.Optional(Type.Union([Type.String(), Type.Null()])), fileType: Type.Optional(Type.Union([Type.String(), Type.Null()])), questions: Type.Optional(Type.Array(questionSchema)), + updatedAt: Type.Optional(Type.String()), }); const lessonQuizSchema = Type.Object({ @@ -46,7 +50,7 @@ const lessonQuizSchema = Type.Object({ questions: Type.Optional(Type.Array(questionSchema)), }); -export const lessonItemSchema = Type.Object({ +export const adminLessonSchema = Type.Object({ id: UUIDSchema, type: Type.String(), displayOrder: Type.Number(), @@ -73,14 +77,23 @@ export const createQuizLessonSchema = Type.Intersect([ }), ]); +export const questionDetails = Type.Object({ + questions: Type.Array(Type.Any()), + questionCount: Type.Number(), + correctAnswerCount: Type.Union([Type.Number(), Type.Null()]), + wrongAnswerCount: Type.Union([Type.Number(), Type.Null()]), + score: Type.Union([Type.Number(), Type.Null()]), +}); + export const lessonShowSchema = Type.Object({ id: UUIDSchema, title: Type.String(), - type: Type.String(), + type: Type.Enum(LESSON_TYPES), description: Type.String(), fileType: Type.Union([Type.String(), Type.Null()]), fileUrl: Type.Union([Type.String(), Type.Null()]), - questions: Type.Optional(Type.Array(Type.Any())), + quizDetails: Type.Optional(questionDetails), + displayOrder: Type.Number(), }); export const updateLessonSchema = Type.Partial(createLessonSchema); @@ -89,27 +102,30 @@ export const lessonForChapterSchema = Type.Array( Type.Object({ id: UUIDSchema, title: Type.String(), - type: Type.String(), + type: Type.Union([ + Type.Literal(LESSON_TYPES.QUIZ), + Type.Literal(LESSON_TYPES.PRESENTATION), + Type.Literal(LESSON_TYPES.VIDEO), + Type.Literal(LESSON_TYPES.TEXT), + ]), displayOrder: Type.Number(), - status: Type.String(), + status: Type.Union([ + Type.Literal(PROGRESS_STATUSES.COMPLETED), + Type.Literal(PROGRESS_STATUSES.IN_PROGRESS), + Type.Literal(PROGRESS_STATUSES.NOT_STARTED), + ]), quizQuestionCount: Type.Union([Type.Number(), Type.Null()]), }), - // Type.Intersect([ - // Type.Omit(lessonSchema, ["updatedAt", "description", "fileS3Key", "fileType", "questions"]), - // Type.Object({ - // status: Type.String(), - // quizQuestionCount: Type.Union([Type.Number(), Type.Null()]), - // }), - // ]), ); +export type AdminLessonWithContentSchema = Static; export type LessonForChapterSchema = Static; -export type LessonItemWithContentSchema = Static; export type CreateLessonBody = Static; export type UpdateLessonBody = Static; export type UpdateQuizLessonBody = Static; export type CreateQuizLessonBody = Static; // TODO: duplicate +export type OptionBody = Static; export type QuestionBody = Static; export type QuestionSchema = Static; export type LessonShow = Static; diff --git a/apps/api/src/lesson/lesson.type.ts b/apps/api/src/lesson/lesson.type.ts index 5f2e7cc2..52269fb6 100644 --- a/apps/api/src/lesson/lesson.type.ts +++ b/apps/api/src/lesson/lesson.type.ts @@ -1,5 +1,5 @@ export const LESSON_TYPES = { - TEXT_BLOCK: "text_block", + TEXT: "text", FILE: "file", PRESENTATION: "presentation", VIDEO: "video", @@ -11,7 +11,8 @@ export enum QuestionType { MultipleChoice = "multiple_choice", TrueOrFalse = "true_or_false", PhotoQuestion = "photo_question", - FillInTheBlanks = "fill_in_the_blanks", + FillInTheBlanksText = "fill_in_the_blanks_text", + FillInTheBlanksDnd = "fill_in_the_blanks_dnd", BriefResponse = "brief_response", DetailedResponse = "detailed_response", } diff --git a/apps/api/src/lesson/repositories/adminLesson.repository.ts b/apps/api/src/lesson/repositories/adminLesson.repository.ts index 92641b1a..ee8a3298 100644 --- a/apps/api/src/lesson/repositories/adminLesson.repository.ts +++ b/apps/api/src/lesson/repositories/adminLesson.repository.ts @@ -82,7 +82,8 @@ export class AdminLessonRepository { id: questionAnswerOptions.id, optionText: questionAnswerOptions.optionText, isCorrect: questionAnswerOptions.isCorrect, - position: questionAnswerOptions.position, + displayOrder: questionAnswerOptions.displayOrder, + questionId: questionAnswerOptions.questionId, }) .from(questionAnswerOptions) .where(eq(questionAnswerOptions.questionId, questionId)); @@ -390,7 +391,7 @@ export class AdminLessonRepository { id?: string; optionText: string; isCorrect: boolean; - position: number; + displayOrder: number; }, trx?: PostgresJsDatabase, ) { @@ -402,14 +403,14 @@ export class AdminLessonRepository { questionId, optionText: option.optionText, isCorrect: option.isCorrect, - position: option.position, + displayOrder: option.displayOrder, }) .onConflictDoUpdate({ target: questionAnswerOptions.id, set: { optionText: option.optionText, isCorrect: option.isCorrect, - position: option.position, + displayOrder: option.displayOrder, }, }); } diff --git a/apps/api/src/lesson/services/adminLesson.service.ts b/apps/api/src/lesson/services/adminLesson.service.ts index 4e4f8e51..b4efbb0b 100644 --- a/apps/api/src/lesson/services/adminLesson.service.ts +++ b/apps/api/src/lesson/services/adminLesson.service.ts @@ -4,9 +4,9 @@ import { eq, gte, inArray, lte, sql } from "drizzle-orm"; import { DatabasePg } from "src/common"; import { lessons, questionAnswerOptions, questions } from "src/storage/schema"; +import { LESSON_TYPES } from "../lesson.type"; import { AdminLessonRepository } from "../repositories/adminLesson.repository"; import { LessonRepository } from "../repositories/lesson.repository"; -import { LESSON_TYPES } from "../lesson.type"; import type { CreateLessonBody, @@ -150,6 +150,7 @@ export class AdminLessonService { type: question.type, description: question.description || null, title: question.title, + displayOrder: question.displayOrder, photoS3Key: question.photoS3Key, photoQuestionType: question.photoQuestionType || null, })); @@ -162,7 +163,7 @@ export class AdminLessonService { questionId: question.id, optionText: option.optionText, isCorrect: option.isCorrect, - position: option.position, + displayOrder: option.displayOrder, })) || [], ); @@ -212,6 +213,7 @@ export class AdminLessonService { type: question.type, description: question.description || null, title: question.title, + displayOrder: question.displayOrder, photoS3Key: question.photoS3Key, photoQuestionType: question.photoQuestionType || null, }; @@ -258,7 +260,7 @@ export class AdminLessonService { const optionData = { optionText: option.optionText, isCorrect: option.isCorrect, - position: option.position, + displayOrder: option.displayOrder, }; // TODO: extract to repository diff --git a/apps/api/src/lesson/services/lesson.service.ts b/apps/api/src/lesson/services/lesson.service.ts index 5b446514..2656fdf3 100644 --- a/apps/api/src/lesson/services/lesson.service.ts +++ b/apps/api/src/lesson/services/lesson.service.ts @@ -1,39 +1,42 @@ import { Inject, Injectable, NotFoundException } from "@nestjs/common"; -import { EventBus } from "@nestjs/cqrs"; import { eq, sql } from "drizzle-orm"; import { DatabasePg } from "src/common"; +import { FileService } from "src/file/file.service"; +import { lessons, questionAnswerOptions, questions } from "src/storage/schema"; +import { LESSON_TYPES } from "../lesson.type"; + +import type { LessonShow, OptionBody, QuestionBody } from "../lesson.schema"; +import type { LessonTypes, PhotoQuestionType, QuestionType } from "../lesson.type"; import type { UUIDType } from "src/common"; -import { lessons, questionAnswerOptions, questions } from "src/storage/schema"; -import { LESSON_TYPES, PhotoQuestionType } from "../lesson.type"; -import { FileService } from "src/file/file.service"; -import { LessonShow, QuestionBody } from "../lesson.schema"; @Injectable() export class LessonService { constructor( @Inject("DB") private readonly db: DatabasePg, private readonly fileService: FileService, // TODO: add event bus + ) { // private readonly eventBus: EventBus, - ) {} + } async getLessonById(id: UUIDType): Promise { const [lesson] = await this.db .select({ id: lessons.id, - type: lessons.type, + type: sql`${lessons.type}`, title: lessons.title, description: sql`${lessons.description}`, fileUrl: lessons.fileS3Key, fileType: lessons.fileType, + displayOrder: sql`${lessons.displayOrder}`, }) .from(lessons) .where(eq(lessons.id, id)); if (!lesson) throw new NotFoundException("Lesson not found"); - if (lesson.type === LESSON_TYPES.TEXT_BLOCK && !lesson.fileUrl) return lesson; + if (lesson.type === LESSON_TYPES.TEXT && !lesson.fileUrl) return lesson; if (lesson.type !== LESSON_TYPES.QUIZ) { if (!lesson.fileUrl) throw new NotFoundException("Lesson file not found"); @@ -49,15 +52,17 @@ export class LessonService { } } - const questionList = await this.db + const questionList: QuestionBody[] = await this.db .select({ id: questions.id, - type: questions.type, + type: sql`${questions.type}`, title: questions.title, - description: questions.description, - photoS3Key: questions.photoS3Key, - photoQuestionType: questions.photoQuestionType, - options: sql` + description: sql`${questions.description}`, + photoS3Key: sql`COALESCE(${questions.photoS3Key}, undefined)`, + photoQuestionType: sql`COALESCE( + ${questions.photoQuestionType}, undefined + )`, + options: sql` COALESCE( ( SELECT json_agg(question_options) @@ -66,15 +71,15 @@ export class LessonService { ${questionAnswerOptions.id} AS id, ${questionAnswerOptions.optionText} AS optionText, ${questionAnswerOptions.isCorrect} AS "isCorrect", - ${questionAnswerOptions.position} AS "position", + ${questionAnswerOptions.displayOrder} AS "displayOrder", FROM ${questionAnswerOptions} WHERE ${questionAnswerOptions.questionId} = ${questions.id} GROUP BY ${questionAnswerOptions.id}, ${questionAnswerOptions.optionText}, ${questionAnswerOptions.isCorrect}, - ${questionAnswerOptions.position}, - ORDER BY ${questionAnswerOptions.position} + ${questionAnswerOptions.displayOrder}, + ORDER BY ${questionAnswerOptions.displayOrder} ) AS question_options ), undefined @@ -83,15 +88,19 @@ export class LessonService { }) .from(questions) .where(eq(questions.lessonId, id)); - return { ...lesson, questions: questionList }; + + const quizDetails = { + questions: questionList, + questionCount: questionList.length, + score: 1, + correctAnswerCount: 1, + wrongAnswerCount: 1, + }; + return { ...lesson, quizDetails }; } - // async evaluationQuiz(courseId: UUIDType, lessonId: UUIDType, userId: UUIDType) { - // const [accessCourseLessons] = await this.chapterRepository.checkLessonAssignment( - // courseId, - // lessonId, - // userId, - // ); + // async evaluationQuiz(lessonId: UUIDType, userId: UUIDType) { + // const [accessCourseLessons] = await this.checkLessonAssignment(lessonId, userId); // if (!accessCourseLessons.isAssigned && !accessCourseLessons.isFree) // throw new UnauthorizedException("You don't have assignment to this lesson"); @@ -129,6 +138,20 @@ export class LessonService { // return true; // } + // async checkLessonAssignment(id: UUIDType, userId: UUIDType) { + // return this.db + // .select({ + // isAssigned: sql`CASE WHEN ${studentCourses.id} IS NOT NULL THEN TRUE ELSE FALSE END`, + // }) + // .from(lessons) + // .leftJoin(chapters, eq(lessons.chapterId, chapters.id)) + // .leftJoin( + // studentCourses, + // and(eq(studentCourses.courseId, chapters.courseId), eq(studentCourses.studentId, userId)), + // ) + // .where(and(eq(chapters.isPublished, true), eq(lessons.id, id))); + // } + // private async evaluationsQuestions(courseId: UUIDType, lessonId: UUIDType, userId: UUIDType) { // const lesson = await this.chapterRepository.getLessonForUser(courseId, lessonId, userId); // const questionLessonItems = await this.getLessonQuestionsToEvaluation( diff --git a/apps/api/src/scorm/services/scorm.service.ts b/apps/api/src/scorm/services/scorm.service.ts index 65d57db3..7315a61e 100644 --- a/apps/api/src/scorm/services/scorm.service.ts +++ b/apps/api/src/scorm/services/scorm.service.ts @@ -480,7 +480,7 @@ export class ScormService { return match(extension) .with(".mp4", ".webm", () => LESSON_TYPES.VIDEO) .with(".pptx", ".ppt", () => LESSON_TYPES.PRESENTATION) - .with(".html", () => LESSON_TYPES.TEXT_BLOCK) + .with(".html", () => LESSON_TYPES.TEXT) .otherwise(() => LESSON_TYPES.FILE); } diff --git a/apps/api/src/seed/e2e-data-seeds.ts b/apps/api/src/seed/e2e-data-seeds.ts index 88a21241..8398a90a 100644 --- a/apps/api/src/seed/e2e-data-seeds.ts +++ b/apps/api/src/seed/e2e-data-seeds.ts @@ -19,7 +19,7 @@ export const e2eCourses: NiceCourseData[] = [ lessons: [ { id: crypto.randomUUID(), - type: LESSON_TYPES.TEXT_BLOCK, + type: LESSON_TYPES.TEXT, title: "E2E Testing Text Block", description: "E2E Testing Text Block Body", displayOrder: 1, @@ -48,7 +48,7 @@ export const e2eCourses: NiceCourseData[] = [ { optionText: "E2E Testing Answer", isCorrect: true, - position: 0, + displayOrder: 0, }, ], }, @@ -61,12 +61,12 @@ export const e2eCourses: NiceCourseData[] = [ { optionText: "single true", isCorrect: true, - position: 0, + displayOrder: 0, }, { optionText: "single false", isCorrect: false, - position: 1, + displayOrder: 1, }, ], }, @@ -79,17 +79,17 @@ export const e2eCourses: NiceCourseData[] = [ { optionText: "multiple true a", isCorrect: true, - position: 0, + displayOrder: 0, }, { optionText: "multiple true b", isCorrect: false, - position: 1, + displayOrder: 1, }, { optionText: "multiple false c", isCorrect: false, - position: 2, + displayOrder: 2, }, ], }, diff --git a/apps/api/src/seed/nice-data-seeds.ts b/apps/api/src/seed/nice-data-seeds.ts index d17fdd8d..61832851 100644 --- a/apps/api/src/seed/nice-data-seeds.ts +++ b/apps/api/src/seed/nice-data-seeds.ts @@ -22,7 +22,7 @@ export const niceCourses: NiceCourseData[] = [ lessons: [ { id: crypto.randomUUID(), - type: LESSON_TYPES.TEXT_BLOCK, + type: LESSON_TYPES.TEXT, title: "Introduction to HTML", description: "HTML (HyperText Markup Language) is the standard language used to create the structure of web pages. In this lesson, you'll explore basic HTML elements and how they are used to build the framework of a website.", @@ -36,6 +36,7 @@ export const niceCourses: NiceCourseData[] = [ displayOrder: 2, questions: [ { + id: crypto.randomUUID(), type: QuestionType.BriefResponse, title: "Why is HTML considered the backbone of any website?", description: "Explain its role in web development.", @@ -59,7 +60,8 @@ export const niceCourses: NiceCourseData[] = [ displayOrder: 4, questions: [ { - type: QuestionType.FillInTheBlanks, + id: crypto.randomUUID(), + type: QuestionType.FillInTheBlanksText, title: "In CSS, [word] is used to style the layout, while [word] is used to change colors.", description: @@ -68,22 +70,22 @@ export const niceCourses: NiceCourseData[] = [ { optionText: "flexbox", isCorrect: true, - position: 0, + displayOrder: 0, }, { optionText: "color properties", isCorrect: true, - position: 1, + displayOrder: 1, }, { optionText: "grid", isCorrect: false, - position: 2, + displayOrder: 2, }, { optionText: "flex", isCorrect: false, - position: 3, + displayOrder: 3, }, ], }, @@ -105,28 +107,29 @@ export const niceCourses: NiceCourseData[] = [ displayOrder: 6, questions: [ { + id: crypto.randomUUID(), type: QuestionType.SingleChoice, title: "Which HTML tag is used to create a hyperlink?", options: [ { optionText: "", isCorrect: true, - position: 0, + displayOrder: 0, }, { optionText: "", isCorrect: false, - position: 1, + displayOrder: 1, }, { optionText: "