> ## Documentation Index
> Fetch the complete documentation index at: https://ihub.learnille.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Course System Codemap

> Migrated from the previous Learnille internal documentation hub

## 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 | OpenSearch 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 OpenSearch Integration

**Files:** `server/src/opensearch/`

| File | Role |
| - | - |
| `course-elastic.service.ts` | Index: upsert, delete, search, update `gigActive` |
| `types/opensearch-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 OpenSearch 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
* `refreshVideoUpload` for expiring signatures
* `getVideoPlayback` returns 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`, `error`
* `updateProgress`, `updateSection`, `updateSubsection`, `updateItem`
* `setCurrentPosition`, `getItemProgress`, `markItemCompleted`, `updateVideoTime`
* `getSectionProgress`, `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`

```typescript theme={null}
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 CRUD
* `instructor/src/services/course-section.service.ts` — Section CRUD
* `instructor/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
  → OpenSearch 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 OpenSearch (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 (OpenSearch-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/opensearch-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`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.