diff --git a/package-lock.json b/package-lock.json index cca95441..a30151fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "@types/supertest": "^6.0.2", "eslint": "^9.12.0", "eslint-plugin-n": "^17.10.3", + "express-async-handler": "^1.2.0", "find": "^0.3.0", "fs-extra": "^11.2.0", "jasmine": "^5.3.1", @@ -2189,6 +2190,12 @@ "express": "^4.16.2" } }, + "node_modules/express-async-handler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/express-async-handler/-/express-async-handler-1.2.0.tgz", + "integrity": "sha512-rCSVtPXRmQSW8rmik/AIb2P0op6l7r1fMW538yyvTMltCO4xQEWMmobfrIxN2V1/mVrgxB8Az3reYF6yUZw37w==", + "dev": true + }, "node_modules/express/node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -3791,7 +3798,8 @@ "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true }, "node_modules/parseurl": { "version": "1.3.3", diff --git a/package.json b/package.json index ca7b6191..b5aefc33 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "scripts": { "build": "npx tsc ", - "dev": "NODE_ENV=development npx ts-node ./src", + "dev": " npx ts-node ./src", "dev:hot": "nodemon --exec \"npm run dev\" --watch ./src -e ts", "lint": "npx eslint ./src", "lint:tests": "npx eslint ./spec", @@ -55,6 +55,7 @@ "@types/supertest": "^6.0.2", "eslint": "^9.12.0", "eslint-plugin-n": "^17.10.3", + "express-async-handler": "^1.2.0", "find": "^0.3.0", "fs-extra": "^11.2.0", "jasmine": "^5.3.1", diff --git a/src/controller/enrollment.ts b/src/controller/enrollment.ts new file mode 100644 index 00000000..1770372c --- /dev/null +++ b/src/controller/enrollment.ts @@ -0,0 +1,24 @@ +import { Request, Response } from 'express'; +import asyncHandler from 'express-async-handler'; +import { EnrollmentService } from '../service/enrollment'; + +const enrollmentService = new EnrollmentService(); + +export const enrollInCourse = asyncHandler(async (req: Request, res: Response) => { + const { userId, courseId } = req.body; + const enrollment = await enrollmentService.enrollUserInCourse(userId, courseId); + res.status(201).json({ message: 'Enrolled successfully', enrollment }); +}); + +export const getUserEnrollments = asyncHandler(async (req: Request, res: Response) => { + const { userId } = req.params; + const enrollments = await enrollmentService.getUserEnrollments(Number(userId)); + res.status(200).json(enrollments); +}); + +export const updateLessonProgress = asyncHandler(async (req: Request, res: Response) => { + const { enrollmentId, lessonId } = req.params; + const { progress } = req.body; + const enrollmentLesson = await enrollmentService.updateLessonProgress(Number(enrollmentId), Number(lessonId), progress); + res.status(200).json({ message: 'Lesson progress updated', enrollmentLesson }); +}); diff --git a/src/entity/Comment.ts b/src/entity/Comment.ts index a7ad31ad..a0eafb03 100644 --- a/src/entity/Comment.ts +++ b/src/entity/Comment.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - JoinColumn, - CreateDateColumn, - UpdateDateColumn, -} from 'typeorm'; +import { Entity,Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn, } from 'typeorm'; import { Review } from './Review'; @Entity('comments') diff --git a/src/entity/Component.ts b/src/entity/Component.ts index 3025784f..a6c5c137 100644 --- a/src/entity/Component.ts +++ b/src/entity/Component.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - JoinColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { Lesson } from './Lesson'; @Entity('components') diff --git a/src/entity/Course.ts b/src/entity/Course.ts index fd6fa3d0..9eb1d83a 100644 --- a/src/entity/Course.ts +++ b/src/entity/Course.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - JoinColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { User } from './User'; @Entity('courses') diff --git a/src/entity/Enrollment.ts b/src/entity/Enrollment.ts index abbb8be6..504dc6d6 100644 --- a/src/entity/Enrollment.ts +++ b/src/entity/Enrollment.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - JoinColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { User } from './User'; import { Course } from './Course'; diff --git a/src/entity/EnrollmentLesson.ts b/src/entity/EnrollmentLesson.ts index 49485173..f7b3a2f5 100644 --- a/src/entity/EnrollmentLesson.ts +++ b/src/entity/EnrollmentLesson.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - JoinColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { Enrollment } from './Enrollment'; import { Lesson } from './Lesson'; diff --git a/src/entity/Lesson.ts b/src/entity/Lesson.ts index 0791c4ff..a20d9e58 100644 --- a/src/entity/Lesson.ts +++ b/src/entity/Lesson.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - JoinColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { Section } from './Section'; @Entity('lessons') diff --git a/src/entity/Payment.ts b/src/entity/Payment.ts index b632f04c..a139fe97 100644 --- a/src/entity/Payment.ts +++ b/src/entity/Payment.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - JoinColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { User } from './User'; import { Course } from './Course'; diff --git a/src/entity/Review.ts b/src/entity/Review.ts index 2c26bd78..dc6152e6 100644 --- a/src/entity/Review.ts +++ b/src/entity/Review.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - JoinColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { User } from './User'; import { Course } from './Course'; diff --git a/src/entity/Section.ts b/src/entity/Section.ts index e8ae1b7a..247251ae 100644 --- a/src/entity/Section.ts +++ b/src/entity/Section.ts @@ -1,11 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - JoinColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm'; import { Course } from './Course'; @Entity('sections') diff --git a/src/entity/User.ts b/src/entity/User.ts index ed1811c3..1a5c27e9 100644 --- a/src/entity/User.ts +++ b/src/entity/User.ts @@ -1,9 +1,4 @@ -import { Entity, - Column, - PrimaryGeneratedColumn, - CreateDateColumn, - UpdateDateColumn -} from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm'; @Entity('users') export class User { diff --git a/src/route/enrollment.ts b/src/route/enrollment.ts new file mode 100644 index 00000000..16e4b13b --- /dev/null +++ b/src/route/enrollment.ts @@ -0,0 +1,12 @@ +import { Router } from 'express'; +import { enrollInCourse, getUserEnrollments, updateLessonProgress } from '../controller/enrollment'; + +const router = Router(); + +router.post('/', enrollInCourse); + +router.get('/:userId', getUserEnrollments); + +router.put('/:enrollmentId/lessons/:lessonId/progress', updateLessonProgress); + +export default router; diff --git a/src/route/index.ts b/src/route/index.ts new file mode 100644 index 00000000..576c4669 --- /dev/null +++ b/src/route/index.ts @@ -0,0 +1,8 @@ +import { Router } from 'express'; +import enrollmentRoutes from './enrollment'; + +const router = Router(); + +router.use('/enrollments', enrollmentRoutes); + +export default router; diff --git a/src/service/enrollment.ts b/src/service/enrollment.ts new file mode 100644 index 00000000..1d9ead78 --- /dev/null +++ b/src/service/enrollment.ts @@ -0,0 +1,70 @@ +import { AppDataSource } from '../repos/db'; +import { Enrollment } from '../entity/Enrollment'; +import { User } from '../entity/User'; +import { Course } from '../entity/Course'; +import { EnrollmentLesson } from '../entity/EnrollmentLesson'; +import { Lesson } from '../entity/Lesson'; + +export class EnrollmentService { + private enrollmentRepository = AppDataSource.getRepository(Enrollment); + private courseRepository = AppDataSource.getRepository(Course); + private userRepository = AppDataSource.getRepository(User); + private enrollmentLessonRepository = AppDataSource.getRepository(EnrollmentLesson); + private lessonRepository = AppDataSource.getRepository(Lesson); + + async enrollUserInCourse(userId: number, courseId: number): Promise { + const user = await this.userRepository.findOne({ where: { id: userId } }); + const course = await this.courseRepository.findOne({ where: { id: courseId } }); + + if (!user || !course) { + throw new Error('User or Course not found'); + } + + const enrollment = this.enrollmentRepository.create({ + user, + course, + enrollment_date: new Date(), + progress: 0, + }); + + await this.enrollmentRepository.save(enrollment); + + const lessons = await this.lessonRepository.find({ where: { section: { course_id: courseId } } }); + const enrollmentLessons = lessons.map(lesson => this.enrollmentLessonRepository.create({ + enrollment, + lesson, + progress: 0, + })); + + await this.enrollmentLessonRepository.save(enrollmentLessons); + + return enrollment; + } + + async getUserEnrollments(userId: number): Promise { + return this.enrollmentRepository.find({ + where: { user: { id: userId } }, + relations: ['course'], + }); + } + + async updateLessonProgress(enrollmentId: number, lessonId: number, progress: number): Promise { + const enrollmentLesson = await this.enrollmentLessonRepository.findOne({ + where: { enrollment_id: enrollmentId, lesson_id: lessonId }, + }); + + if (!enrollmentLesson) { + throw new Error('EnrollmentLesson not found'); + } + + enrollmentLesson.progress = progress; + + if (progress === 100) { + enrollmentLesson.completion_date = new Date(); + } + + await this.enrollmentLessonRepository.save(enrollmentLesson); + + return enrollmentLesson; + } +}