Course System Codemap
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ SERVER (NestJS) — single source of truth │
│ Route prefix: /course │
│ 4 controllers → 10 services → 12 entities → 26 DTOs │
├─────────────────────────────────────────────────────────────────┤
│ FRONTENDS — each defines own types, consumes HTTP API │
│ instructor/ editor + dashboard │
│ admin/ management + analytics │
│ student/ player + progress │
│ public/ marketplace + search │
│ landing-page/ marketing │
└─────────────────────────────────────────────────────────────────┘
Cross-package rule: No type imports between frontend packages. Each defines local interfaces.
1. Server: Course Module
Module: server/src/course/course.module.ts
1.1 Hierarchy
Course
├── CourseSection (course_sections)
│ └── CourseSubSection (course_subsections)
│ └── CourseItem (course_items) — VIDEO, FILE, TEXT, etc.
├── Level (levels)
├── CourseManager (course_managers) — junction: instructor → course
├── Requirement (course requirements with icon+text)
├── Inclusion (course inclusions/benefits with icon+text)
└── Audience (target audience with icon+text)
1.2 Controllers
File: server/src/course/course.controller.ts — Route prefix /course
| Method | Endpoint | Service | Description |
|---|---|---|---|
POST | /course | CourseWriteService.create | Create course |
GET | /course | CourseReadService.findAll | Paginated list, view-based serialization |
GET | /course/:id | CourseReadService.findOne | By ID, ?view=admin|dashboard|public |
GET | /course/slug/:slug | CourseReadService.findOne | By slug, public access allowed |
PUT | /course/:id | CourseWriteService.update | Update course |
DELETE | /course/:id | CourseWriteService.remove | Hard delete |
PUT | /course/:id/visibility | CourseModerationService.updateCourseVisibility | Admin only — isFeatured, isHidden |
GET | /course/:id/player | CoursePlayerService.getCourseForPlayer | Student player with section locking |
GET | /course/:id/curriculum | CourseReadService.getNormalizedCurriculum | Sections → subsections → items |
GET | /course/:id/performance | CourseModerationService.getCoursePerformance | Analytics metrics |
GET | /course/summaries/:id | CourseModerationService.getCourseSummary | Instructor summaries |
GET | /course/:id/statistics | CourseModerationService.getInstructorStatistics | Instructor stats |
GET | /course/levels | CourseReadService.getAllLevels | All levels |
POST | /course/levels | CourseReadService.createLevel | Admin only |
Section endpoints (nested under /course):
| Method | Endpoint | Service |
|---|---|---|
GET | /course/section?courseId= | CourseSectionService.findAll |
GET | /course/section/:sectionId | CourseSectionService.findOne |
POST | /course/section | CourseSectionService.create |
PUT | /course/section/:sectionId | CourseSectionService.update |
DELETE | /course/section/:sectionId | CourseSectionService.remove |
Subsection endpoints:
| Method | Endpoint | Service |
|---|---|---|
GET | /course/subsection?sectionId= | CourseSubSectionService.findAll |
GET | /course/subsection/:subsectionId | CourseSubSectionService.findOne |
POST | /course/subsection | CourseSubSectionService.create |
PUT | /course/subsection/:subsectionId | CourseSubSectionService.update |
DELETE | /course/subsection/:subsectionId | CourseSubSectionService.remove |
POST | /course/subsection/batch-update-order | CourseSubSectionService.batchUpdateOrder |
Item endpoints:
| Method | Endpoint | Service |
|---|---|---|
GET | /course/item?subsectionId= | CourseItemService.findAll |
GET | /course/item/:courseItemId | CourseItemService.findOne |
POST | /course/item | CourseItemService.create |
PUT | /course/item/:courseItemId | CourseItemService.update |
DELETE | /course/item/:courseItemId | CourseItemService.remove |
GET | /course/download/:courseItemId | CourseItemService.downloadFile |
POST | /course/item/:courseItemId/video/upload | CourseItemService.initVideoUpload (Bunny TUS) |
POST | /course/item/:courseItemId/video/refresh | CourseItemService.refreshVideoUpload |
GET | /course/item/:courseItemId/video | CourseItemService.getVideoPlayback |
Property endpoints (Requirements, Inclusions, Audiences):
| Method | Endpoint |
|---|---|
GET | /course/:id/:type |
GET | /course/:courseId/:type/:propId |
POST | /course/:courseId/:type |
PUT | /course/:courseId/:type/replace |
PATCH | /course/:courseId/properties/:type/:propId |
DELETE | /course/:courseId/:type/:propId |
Batch controller (batch-update.controller.ts):
| Method | Endpoint |
|---|---|
PATCH | /course/batch/:courseId/sections |
PATCH | /course/batch/:sectionId/subsections |
PATCH | /course/batch/:subsectionId/items |
Course Manager controller (course-manager.controller.ts):
| Method | Endpoint |
|---|---|
POST | /course-managers |
GET | /course-managers/:id |
GET | /course-managers/course/:courseId |
GET | /course-managers/instructor/:instructorId |
PUT | /course-managers/:id |
DELETE | /course-managers/:id |
1.3 Services
| Service | File | Key Methods |
|---|---|---|
CourseReadService | services/course-read.service.ts | findAll (paginated, nestjs-paginate), findOne (by id/slug with view), getAllLevels, createLevel, getNormalizedCurriculum |
CourseWriteService | services/course-write.service.ts | create, update, remove — emits COURSE_UPDATED, COURSE_REMOVED events |
CourseModerationService | services/course-moderation.service.ts | getCourseSummary, getInstructorStatistics, getCoursePerformance, updateCourseVisibility |
CoursePlayerService | services/course-player.service.ts | getCourseForPlayer — assembles player data with section locking, progress, pre-signed video URLs, enrollment validation |
CourseResponseService | services/course-response.service.ts | toResponseDto — maps entities to response DTOs with serialization groups |
CourseSectionService | course-section.service.ts | CRUD for sections, batch order update |
CourseSubSectionService | course-subsection.service.ts | CRUD for subsections, batch order |
CourseItemService | course-item.service.ts | CRUD for items, Bunny Stream video upload init/refresh/playback, file downloads |
CourseManagerService | course-manager.service.ts | CRUD for course-instructor assignments |
ProgressService | progress.service.ts | trackProgress, getProgress, updateVideoProgress, markVideoCompleted, getCourseCompletionStatus |
TimeCalculationService | (in progress module) | Video time calculations |
1.4 Entities
Course (entities/course.entity.ts) — Table: courses
| Field | Type | Notes |
|---|---|---|
id | UUID PK | |
title | string | Unique |
slug | string | Unique, nullable |
subtitle | string | |
category | Relation→Category | |
subcategory | Relation→Category | |
level | Relation→Level | Eager |
language | string | |
subtitleLanguage | string | |
publishStatus | enum | DRAFT / PUBLISHED / ARCHIVED |
description | string | |
thumbnail | Relation→File | OneToOne |
introductoryVideo | Relation→File | OneToOne |
priceInCents | int | |
currency | enum | USD default |
isFree | boolean | |
saleActive | boolean | |
salePercentage | int | |
saleStart / saleEnd | Date | |
approved | boolean | |
isFeatured | boolean | |
isHidden | boolean | |
gigActive | boolean | Elasticsearch indexed flag |
duration | int | |
durationMeasure | enum | MINUTES/HOURS/DAYS/WEEKS/MONTHS/YEARS |
instructor | Relation→Instructor | Eager |
managers | Relation→CourseManager[] | OneToMany |
sections | Relation→CourseSection[] | OneToMany, cascade |
requirements | Relation→Requirement[] | OneToMany, cascade |
inclusions | Relation→Inclusion[] | OneToMany, cascade |
audiences | Relation→Audience[] | OneToMany, cascade |
welcomeMessage | string | |
completionMessage | string | |
shareUrl | string | |
publishedAt | Date | |
approvedAt | Date | |
timing | string | |
lessonNote | string |
CourseSection (entities/sections.entity.ts) — Table: course_sections
| Field | Type |
|---|---|
id | UUID PK |
title | string |
description | string |
priceInCents | int (for à-la-carte) |
saleActive / salePercentage | |
isActive | boolean |
order | int |
course | Relation→Course |
subsections | Relation→CourseSubSection[] |
CourseSubSection (entities/subsections.entity.ts) — Table: course_subsections
| Field | Type |
|---|---|
id | UUID PK |
title | string |
description | string |
isActive | boolean |
order | int |
section | Relation→CourseSection |
items | Relation→CourseItem[] |
CourseItem (entities/course_item.entity.ts) — Table: course_items
| Field | Type |
|---|---|
id | UUID PK |
title | string |
type | enum: VIDEO / FILE / CAPTION / LECTURE_NOTE / TEXT / DESCRIPTION / ASSIGNMENT / QUIZ |
size | int (for files/video) |
duration | int (minutes for video) |
content | text (HTML for text-based types) |
fileUrl | string |
bunnyVideoId | string |
bunnyVideoStatus | string |
isActive | boolean |
order | int |
subSection | Relation→CourseSubSection |
Level (entities/level.entity.ts) — Table: levels — id, text
CourseManager (entities/manager.entity.ts) — Junction: course ↔ instructor
1.5 Enums
| Enum | File | Values |
|---|---|---|
PublishStatus | enums/publish-status.enum.ts | DRAFT, PUBLISHED, ARCHIVED |
CourseItemType | enums/course-item.enum.ts | VIDEO, FILE, CAPTION, LECTURE_NOTE, TEXT, DESCRIPTION, ASSIGNMENT, QUIZ |
ItemCompletionStatus | enums/item-status.enum.ts | NOT_STARTED, IN_PROGRESS, NOT_AVAILABLE, COMPLETED |
DurationMeasure | enums/duration-measure.enum.ts | MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS |
1.6 CASL Permissions
File: course.permissions.ts
| Role | Permission |
|---|---|
everyone | Read published courses |
admin | Manage all courses |
support | Read/update courses |
instructor | Own courses only (by instructor ID) |
Hooks: CourseHook loads course entity for permission checks. CourseSectionHook loads section's parent course.
1.7 WebSocket Gateway
File: course-progress.gateway.ts — Namespace course-progress
Events: heartbeat, time_update, mark_completed, get_last_watched. 5s debounce on writes. Syncs video position to UserVideoProgress.
1.8 Background Jobs
File: jobs/certificate-generation.job.ts — BullMQ job for completion certificates.
2. Server: Enrollment Module
Module: server/src/enrollment/enrollment.module.ts — Route /enrollment
2.1 Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /enrollment/me | Current user's enrollments |
GET | /enrollment/check/:productType/:productId | Check enrollment status |
GET | /enrollment/:id | Single enrollment |
GET | /enrollment/:id/completion | Completion check |
GET | /enrollment | Admin: all enrollments |
Enrollment Entity (entities/enrollment.entity.ts) — Table: enrollments
| Field | Type |
|---|---|
id | UUID PK |
type | ProductItemType (COURSE / CONSULTATION) |
student | Relation→Student |
course | Relation→Course |
status | EnrollmentStatus |
payment | Relation→Payment |
order | Relation→Order |
sectionEnrollments | Relation→SectionEnrollment[] |
enrolledAt | Date |
accessEndDate | Date |
EnrollmentStatus enum: PENDING, APPROVED, COMPLETED, REJECTED, REFUNDED
2.2 Enrollment Flow
Payment success event
→ EnrollmentListener listens for AppEvents.PAYMENT_SUCCESSFUL
→ enrollmentService.createEnrollment()
→ Creates Enrollment record
→ Creates SectionEnrollment records
→ Emits AppEvents.ENROLLMENT_CREATED
→ InstantListener creates chat room participant
→ NotificationListener sends product enrollment notification
3. Server: Cross-Cutting
3.1 Elasticsearch Integration
Files: server/src/elastic/
| File | Role |
|---|---|
course-elastic.service.ts | Index: upsert, delete, search, update gigActive |
types/elastic-course.type.ts | Indexed document shape |
properties/course.property.ts | ES property mappings |
listeners/course.listener.ts | Listens COURSE_UPDATED, COURSE_REMOVED, PRODUCT_ACTION_NEEDED → syncs to ES |
Courses are indexed with gigActive flag. Search queries filter gigActive: true.
3.2 Marketplace (Public Course Search)
Route: /marketplace/course (separate controller)
Used by public/ for course discovery. Wraps Elasticsearch queries.
3.3 Analytics
File: server/src/analytics/entities/course-analytics-daily.entity.ts
Daily aggregations: enrollments, revenue, views, completions per course.
3.4 Video Infrastructure
- Bunny Stream for video hosting
- TUS resumable upload protocol via
initVideoUpload→ returns upload URL + signature refreshVideoUploadfor expiring signaturesgetVideoPlaybackreturns playback URL, thumbnail, available resolutions
4. Student Frontend
4.1 Service Layer
File: student/src/services/course.service.ts
Static HTTP client class. Defines local Course, Section, Subsection, Item, Level, Category, Instructor, etc. interfaces.
| Method | API Call |
|---|---|
getCourseById(id, view) | GET /course/:id?view= |
getCourseBySlug(slug, view) | GET /course/slug/:slug?view= |
getAllSectionsForCourse(courseId, view) | GET /course/section?courseId=&view= |
getSectionById(id) | GET /course/section/:id |
getAllSubsectionsForSection(sectionId) | GET /course/subsection?sectionId= |
getSubsectionById(id) | GET /course/subsection/:id |
getAllItemsForSubsection(subsectionId) | GET /course/item?subsectionId= |
getItemById(id) | GET /course/item/:id |
getItemVideoPlayback(id) | GET /course/item/:id/video |
downloadCourseItemFile(courseItemId) | GET /course/download/:courseItemId |
getAllLevels() | GET /course/levels |
4.2 Player Service
File: student/src/services/course-player.service.ts
| Method | API Call |
|---|---|
getCourseForPlayer(courseId) | GET /course/:id/player |
Returns StudentCoursePlayerDto with section locking, progress, pre-signed video URLs.
4.3 Progress Service
File: student/src/services/course-progress.service.ts
| Method | API Call |
|---|---|
getCourseProgress(courseId) | GET /course/progress/:courseId |
updateCourseProgress(courseId, data) | PATCH /course/progress/:courseId |
getLastWatchedPosition(courseId) | GET /course/progress/:courseId/last-watched |
4.4 React Query Hooks
| Hook | File | Key |
|---|---|---|
useCourse(courseId) | hooks/useCourse.ts | ["course", courseId] |
useCoursePlayer(courseId) | hooks/useCoursePlayer.ts | ["coursePlayer", courseId] |
useCourseProgress(courseId) | hooks/useCourseProgress.ts | ["courseProgress", courseId] — includes optimistic updates + utility methods |
useEnrollments(type?) | hooks/useEnrollments.ts | ["enrollments", type] |
useCourseProgress returns:
progress,isLoading,errorupdateProgress,updateSection,updateSubsection,updateItemsetCurrentPosition,getItemProgress,markItemCompleted,updateVideoTimegetSectionProgress,getSubsectionProgress
Optimistic update pattern: onMutate snapshots + onError rollback + onSettled invalidation.
4.5 Zustand Store
File: student/src/stores/my-course.ts
Persisted store (my-course-store key) for current player position: sectionId, subsectionId, itemId, videoCurrentTime.
4.6 Progress Types
File: student/src/types/course-progress.d.ts
CourseProgress {
currentSectionId, currentSubsectionId, currentItemId,
currentVideoSeekTime, completionPercentage,
sections: [{ id, progressPercentage, completed,
subsections: [{ id, completed,
items: [{ id, completed, seekTime, lastAccessed }]
}]
}]
}
4.7 Pages
| Route | Component | Path |
|---|---|---|
/my-courses | MyCourses.tsx | pages/my-courses/MyCourses.tsx |
/my-courses/ongoing | OnGoingCourse.tsx | pages/my-courses/OnGoingCourse.tsx |
/my-courses/completed | Completed.tsx | pages/my-courses/Completed.tsx |
/my-courses/:id | MyCourseDetails.tsx | pages/my-courses/[id]/MyCourseDetails.tsx |
—/description | Description.tsx | pages/my-courses/[id]/Description.tsx |
—/attached | Attached.tsx | pages/my-courses/[id]/Attached.tsx |
—/lesson-note | LessonNote.tsx | pages/my-courses/[id]/LessonNote.tsx |
—/comments | Comments.tsx | pages/my-courses/[id]/Comments/ |
4.8 Player Components
| Component | Path |
|---|---|
Section | components/Curriculum/Section/index.tsx |
Lecture | components/Curriculum/Section/Lecture.tsx |
SubSection | components/Curriculum/Section/SubSection.tsx |
CourseContent | components/course-slider/CourseContent.jsx |
5. Instructor Frontend
5.1 Service Layer
File: instructor/src/services/course.service.ts
Full CRUD for course management. Defines local CourseResponseDto, CreateCourseDto, UpdateCourseDto, CourseSummaryDto, CourseManagerDto.
| Method | API Call |
|---|---|
createCourse(data) | POST /course |
getAllCourses(query) | GET /course?view=dashboard&... — paginated with filters |
getCourseById(id) | GET /course/:id?view=dashboard |
getCourseBySlug(slug) | GET /course/slug/:slug?view=dashboard |
getNormalizedCurriculum(id) | GET /course/:id/curriculum?view=dashboard — Zod validated |
updateCourse(id, data) | PUT /course/:id |
deleteCourse(id) | DELETE /course/:id |
batchUpdateSections(courseId, data) | PATCH /course/batch/:courseId/sections |
batchUpdateSubsections(sectionId, data) | PATCH /course/batch/:sectionId/subsections |
batchUpdateItems(subsectionId, data) | PATCH /course/batch/:subsectionId/items |
getAllLevels() | GET /course/levels?view=dashboard |
getCourseSummaries(instructorId) | GET /course/summaries/:instructorId?view=dashboard |
| Property CRUD | GET/POST/PUT/DELETE /course/:id/:type |
replaceAllProperties(courseId, type, data) | PUT /course/:courseId/:type/replace |
| Course Manager CRUD | GET/POST/PUT/DELETE /course-managers |
Additional services:
instructor/src/services/course-item.service.ts— Item CRUDinstructor/src/services/course-section.service.ts— Section CRUDinstructor/src/services/course-subsection.service.ts— Subsection CRUD
5.2 Course Editor
Multi-step form with Zustand store slices:
| Slice | File |
|---|---|
| State definition | stores/course-editor/state.ts |
| Types | stores/course-editor/types.ts |
| Curriculum | stores/course-editor/curriculum.slice.ts — sections/subsections/items |
| Form | stores/course-editor/form.slice.ts |
| Step wizard | stores/course-editor/step.slice.ts |
| Navigation | stores/course-editor/navigation.slice.ts |
| Sync | stores/course-editor/sync.slice.ts |
| Step handler registry | stores/course-editor/step-handler-registry.ts |
Editor pages:
| Step | Component | Path |
|---|---|---|
| Basic Info | BasicInformation.tsx | pages/editor/BasicInformation.tsx |
| Advanced Info | AdvanceInformation.tsx | pages/editor/AdvanceInformation.tsx |
| Curriculum | Curriculum.tsx | pages/editor/Curriculum.tsx |
| Pricing | Pricing.tsx | pages/editor/Pricing.tsx |
| Publish | PublishCourse.tsx | pages/editor/PublishCourse.tsx |
Editor components:
| Component | Path | Role |
|---|---|---|
CurriculumSection | components/course/CurriculumSection.tsx | Drag-and-drop section |
CurriculumSubsection | components/course/CurriculumSubsection.tsx | Drag-and-drop subsection |
CourseItem | components/course/CourseItem.tsx | Item type selector/editor |
CourseImage | components/course/CourseImage.tsx | Thumbnail upload |
SubsectionContent | components/course/SubsectionContent.tsx | Content editor |
SubmitButton | components/course/SubmitButton.tsx | Publish/submit |
bunny-video | components/course/bunny-video.ts | Bunny Stream integration |
sortable | components/course/sortable.tsx | DnD wrapper |
5.3 Dashboard Pages
| Page | File |
|---|---|
| Course list | pages/course/CoursesMain.tsx |
| Course detail/edit | pages/course/CourseDetailsPage.tsx |
| Activity dashboard | components/course/CourseActivityDashboard.tsx |
| Activity summary | components/course/CourseActivitySummary.tsx |
5.4 Zod Validation
File: instructor/src/utils/schema/course.schema.ts — normalizedCurriculumSchema
6. Admin Frontend
6.1 Service Layer
File: admin/src/services/course.service.ts
| Method | API Call |
|---|---|
getAllCourses(params) | GET /course — paginated |
getAdminCourses(params) | GET /course with data normalization |
getAdminCourse(id) | GET /course/:id?view=admin |
deleteCourse(id) | DELETE /course/:id |
updateVisibility(id, update) | PUT /course/:id/visibility |
getCourseCurriculum(id) | GET /course/:id/curriculum |
deleteSection(sectionId) | DELETE /course/section/:sectionId |
deleteCourseItem(itemId) | DELETE /course/item/:itemId |
getCourseAnalytics(id) | GET /analytics/course/:id |
getCourseReviews(courseId) | GET /review/all?id=&type=course |
deleteReview(reviewId) | DELETE /review/:reviewId |
6.2 Admin Course Types
File: admin/src/types/course.types.ts
Defines AdminCourse (280+ lines), CourseListParams, CurriculumData, CourseFilters, CourseStatistics, CourseDetailAnalytics, CourseReview, PublishStatus enum, DurationMeasure enum, VisibilityUpdate.
6.3 Zod Schemas
File: admin/src/schemas/course.schema.ts
courseFiltersSchema, visibilityUpdateSchema, courseRejectionSchema, courseApprovalSchema.
6.4 Data Provider Handler
File: admin/src/providers/data-provider/handlers/courses.handler.ts
Integrates with Refine/React Admin for list, show, delete operations.
6.5 Pages & Components
| Page/Component | Path |
|---|---|
| Course list | pages/courses/list.tsx |
| Course detail | pages/courses/show.tsx with tabs |
| CourseCard | pages/courses/CourseCard.tsx |
| FilterBar | pages/courses/components/FilterBar.tsx |
| BulkActionsBar | pages/courses/components/CourseBulkActionsBar.tsx |
| StatsGrid | pages/courses/components/CourseStatsGrid.tsx |
| OverviewTab | pages/courses/components/OverviewTab/ |
| CurriculumTab | pages/courses/components/CurriculumTab/ |
| AnalyticsTab | pages/courses/components/AnalyticsTab/ |
| ReviewsTab | pages/courses/components/ReviewsTab/ |
| AdminTab | pages/courses/components/AdminTab/ — approve/reject |
| CourseHeader | pages/courses/components/CourseHeader/ |
| SharedComponents | pages/courses/components/SharedComponents.tsx |
| DeleteCourseDialog | pages/courses/components/Modals/DeleteCourseDialog.tsx |
7. Public/Marketplace Frontend
7.1 Service Layer
File: public/src/services/course.service.ts
| Method | API Call | View |
|---|---|---|
getCourseById(id) | GET /course/:id | PUBLIC |
getCourseBySlug(slug) | GET /course/slug/:slug | PUBLIC |
getAllSectionsForCourse(courseId) | GET /course/section?courseId= | default |
getSectionById(id) | GET /course/section/:id | |
getAllSubsectionsForSection(sectionId) | GET /course/subsection?sectionId= | |
getSubsectionById(id) | GET /course/subsection/:id | |
getAllItemsForSubsection(subsectionId) | GET /course/item?subsectionId= | |
getItemById(id) | GET /course/item/:id | |
getAllLevels() | GET /course/levels | |
getCoursesByInstructor(instructorId, limit) | GET /course?instructorId= | |
searchMarketplaceCourses(params) | GET /marketplace/course |
7.2 Hooks
| Hook | File |
|---|---|
useCoursePage() | hooks/useCoursePage.ts — slug → course, enrollment check, section selection |
useCoursesPage() | hooks/useCoursesPage.ts — categories, best-selling, top-rated, most-popular, new-arrival |
useCourseSearch() | hooks/useCourseSearch.ts |
useSimilarCourses() | hooks/useSimilarCourses.ts |
useInstructorAvailableCourses() | hooks/useInstructorAvailableCourses.ts |
7.3 Store
File: public/src/stores/course.ts
Zustand store: currentTab, selectedSections for à-la-carte purchasing with section selection/deselection and price calculation.
8. SDK (Auto-generated)
Files: sdk/src/
| File | Content |
|---|---|
axios/course/course.ts | 50+ API client functions (orval-generated) |
axios/course-managers/course-managers.ts | Course manager API client |
hooks/course/course.ts | React Query hooks (useQuery + useMutation) |
hooks/course-managers/course-managers.ts | Course manager query hooks |
9. Data Flow Diagrams
Course Creation (Instructor)
Instructor Editor
→ CourseEditor.tsx (multi-step wizard)
→ instructor/src/services/course.service.ts
→ POST /course (create)
→ POST /course/:id/section (add sections)
→ POST /course/section/:sectionId/subsection (add subsections)
→ POST /course/item (add items)
→ POST /course/item/:id/video/upload (upload video via Bunny TUS)
→ Server: CourseWriteService.create
→ Course entity saved
→ Event emitted: COURSE_CREATED
→ Elasticsearch listener indexes course
Course Player (Student)
Student opens /my-courses/:id
→ MyCourseDetails.tsx
→ useCoursePlayer(courseId)
→ GET /course/:id/player
→ CoursePlayerService.getCourseForPlayer()
→ Validates enrollment
→ Loads course + sections + subsections + items
→ Computes section locking (previous section must be completed)
→ Generates pre-signed video URLs
→ Returns StudentCoursePlayerDto with progress
→ useCourseProgress(courseId)
→ GET /course/progress/:courseId
→ Tracks: currentSection, currentItem, videoTime, completion
→ Optimistic updates via React Query mutation
→ WebSocket: course-progress gateway for real-time heartbeat
→ PATCH /course/progress/:courseId on debounce
Course Approval (Admin)
Admin opens course detail
→ GET /course/:id?view=admin
→ AdminTab: Approve / Reject
→ PUT /course/:id (sets approved=true + publishStatus=PUBLISHED)
→ AppEvents.COURSE_APPROVED emitted
→ NotificationListener → email to instructor
→ InstantListener → creates GROUP chat room
→ Course added to Elasticsearch (gigActive = true)
→ Marketplace now shows course
Enrollment (Student Purchase)
Student purchases course
→ Payment successful → AppEvents.PAYMENT_SUCCESSFUL
→ EnrollmentListener → enrollmentService.createEnrollment()
→ Creates Enrollment + SectionEnrollment records
→ Emits AppEvents.ENROLLMENT_CREATED
→ InstantListener: adds student as chat room participant
→ NotificationListener: sends enrollment confirmation
→ Student can now access /course/:id/player
Course Search (Marketplace)
User searches on landing/public
→ public/src/hooks/useCoursesPage.ts
→ GET /marketplace/course (Elasticsearch-backed)
→ Filters: category, level, price, rating, search term
→ Returns paginated results with totalHits
→ Course card → GET /course/slug/:slug
→ Checks enrollment (GET /enrollment/check/course/:id)
→ Shows enroll/buy CTA
10. File Index by Package
Server: Course Module (~58 files)
| Category | Count | Key Paths |
|---|---|---|
| Module | 1 | course.module.ts |
| Controllers | 4 | course.controller.ts, course-manager.controller.ts, progress.controller.ts, batch-update.controller.ts |
| Services | 10 | services/course-read.service.ts, services/course-write.service.ts, services/course-moderation.service.ts, services/course-player.service.ts, services/course-response.service.ts, course-section.service.ts, course-subsection.service.ts, course-item.service.ts, course-manager.service.ts, progress.service.ts |
| Entities | 12 | entities/course.entity.ts, entities/sections.entity.ts, entities/subsections.entity.ts, entities/course_item.entity.ts, entities/level.entity.ts, entities/manager.entity.ts, entities/progress.entity.ts, entities/user-video-progress.entity.ts, entities/user-video-session.entity.ts, entities/requirement.entity.ts, entities/audience.entity.ts, entities/benefit.entity.ts |
| Enums | 4 | enums/publish-status.enum.ts, enums/course-item.enum.ts, enums/item-status.enum.ts, enums/duration-measure.enum.ts |
| DTOs | 26 | dto/ — create, update, response, player, video, batch, manager, progress, curriculum |
| Hooks/Perms | 4 | course.hook.ts, course-section.hook.ts, course.permissions.ts |
| Gateway | 1 | course-progress.gateway.ts |
| Jobs | 1 | jobs/certificate-generation.job.ts |
| Seeds | 1 | seeds/level.seeder.ts |
Server: Enrollment Module (~16 files)
enrollment.module.ts, enrollment.controller.ts, enrollment.service.ts, entities/enrollment.entity.ts, entities/section-enrollments.entity.ts, enums/enrollment-status.entity.ts, dto/ (7 files), listeners/enrollment.listener.ts, enrollment.permissions.ts, enrollment.hook.ts
Server: Cross-Cutting
elastic/course-elastic.service.ts, elastic/types/elastic-course.type.ts, elastic/properties/course.property.ts, elastic/listeners/course.listener.ts, analytics/entities/course-analytics-daily.entity.ts, faker/flows/course.flow.ts, validation/schemas/course.schema.ts, templates/emails/course.tsx
Student Frontend (~18 files)
| Category | Key Paths |
|---|---|
| Services | services/course.service.ts, services/course-player.service.ts, services/course-progress.service.ts |
| Hooks | hooks/useCourse.ts, hooks/useCoursePlayer.ts, hooks/useCourseProgress.ts, hooks/useEnrollments.ts |
| Store | stores/my-course.ts |
| Types | types/course-progress.d.ts |
| Pages | pages/my-courses/MyCourses.tsx, pages/my-courses/OnGoingCourse.tsx, pages/my-courses/Completed.tsx, pages/my-courses/[id]/MyCourseDetails.tsx, pages/my-courses/[id]/Description.tsx, pages/my-courses/[id]/Attached.tsx, pages/my-courses/[id]/LessonNote.tsx, pages/my-courses/[id]/Comments/ |
| Components | components/Curriculum/Section/, components/course-slider/CourseContent.jsx |
| Layout | layout/CoursePageLayout.jsx |
Instructor Frontend (~30 files)
| Category | Key Paths |
|---|---|
| Services | services/course.service.ts, services/course-item.service.ts, services/course-section.service.ts, services/course-subsection.service.ts |
| Store | stores/course-editor/ (9 slices) |
| Pages | pages/course/CoursesMain.tsx, pages/course/CourseDetailsPage.tsx, pages/editor/ (6 files) |
| Components | components/course/ (10 files) |
Admin Frontend (~18 files)
| Category | Key Paths |
|---|---|
| Service | services/course.service.ts |
| Types | types/course.types.ts |
| Schemas | schemas/course.schema.ts |
| Handler | providers/data-provider/handlers/courses.handler.ts |
| Pages | pages/courses/list.tsx, pages/courses/show.tsx |
| Tab Components | components/ — OverviewTab, CurriculumTab, AnalyticsTab, ReviewsTab, AdminTab |
Public Marketplace (~7 files)
| Category | Key Paths |
|---|---|
| Service | services/course.service.ts |
| Hooks | hooks/useCoursePage.ts, hooks/useCoursesPage.ts, hooks/useCourseSearch.ts, hooks/useSimilarCourses.ts, hooks/useInstructorAvailableCourses.ts |
| Store | stores/course.ts |
SDK (~4 files)
axios/course/course.ts, axios/course-managers/course-managers.ts, hooks/course/course.ts, hooks/course-managers/course-managers.ts
Landing Page (~3 files)
components/cards/courses_card/CoursesCard.jsx, pages/home/courseSearch/CourseSearch.jsx