Remove description fields from course, unit, and module create/edit dialogs. Add unit sort order on create, lesson publish status and sort order, video duration on lesson cards, and personas API integration for Learn English practice flows. Move Manage Question Types to the new content hub, add Reorder Content page with hierarchy drag-and-drop, shared practice review UI, module practice cards, and publish-practice controls on course listings. Co-authored-by: Cursor <cursoragent@cursor.com>
1642 lines
36 KiB
TypeScript
1642 lines
36 KiB
TypeScript
import type { DynamicQuestionPayload } from "./questionTypeDefinition.types"
|
|
|
|
export interface CourseCategory {
|
|
id: number
|
|
name: string
|
|
is_active: boolean
|
|
parent_id?: number | null
|
|
created_at: string
|
|
}
|
|
|
|
export interface CreateCourseCategoryRequest {
|
|
name: string
|
|
parent_id?: number | null
|
|
}
|
|
|
|
export interface GetCourseCategoriesResponse {
|
|
message: string
|
|
data: {
|
|
categories: CourseCategory[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface Course {
|
|
id: number
|
|
category_id: number
|
|
sub_category_id?: number | null
|
|
title: string
|
|
description: string
|
|
thumbnail: string
|
|
is_active: boolean
|
|
}
|
|
|
|
export interface GetCoursesResponse {
|
|
message: string
|
|
data: {
|
|
courses: Course[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface CreateCourseRequest {
|
|
category_id: number
|
|
sub_category_id?: number | null
|
|
title: string
|
|
description: string
|
|
}
|
|
|
|
export interface UpdateCourseRequest {
|
|
title?: string
|
|
description?: string
|
|
thumbnail?: string
|
|
is_active?: boolean
|
|
}
|
|
|
|
/** Row from GET /programs (e.g. Beginner / Intermediate program buckets) */
|
|
export interface LearningProgramListItem {
|
|
id: number
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
sort_order: number
|
|
created_at: string
|
|
}
|
|
|
|
export interface UpdateLearningProgramRequest {
|
|
name: string
|
|
description: string
|
|
thumbnail: string
|
|
sort_order: number
|
|
}
|
|
|
|
export interface CreateLearningProgramRequest {
|
|
name: string
|
|
description: string
|
|
thumbnail: string
|
|
sort_order: number
|
|
}
|
|
|
|
export interface CreateLearningProgramResponse {
|
|
message: string
|
|
data: LearningProgramListItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface GetLearningProgramsResponse {
|
|
message: string
|
|
data: {
|
|
programs: LearningProgramListItem[]
|
|
total_count: number
|
|
limit?: number
|
|
offset?: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Row from GET /programs/:program_id/courses */
|
|
export interface ProgramCourseListItem {
|
|
id: number
|
|
program_id: number
|
|
name: string
|
|
description: string
|
|
sort_order: number
|
|
created_at: string
|
|
thumbnail?: string | null
|
|
/** Some list endpoints may expose the image as `thumbnail_url` instead. */
|
|
thumbnail_url?: string | null
|
|
/** GET /programs/:id/courses aggregates. */
|
|
module_count?: number
|
|
lesson_count?: number
|
|
practice_count?: number
|
|
/** Legacy aggregate field names; prefer module_count, lesson_count, practice_count. */
|
|
modules_count?: number
|
|
videos_count?: number
|
|
practices_count?: number
|
|
}
|
|
|
|
/** Body for PUT /courses/:id (program-linked Learn English courses). */
|
|
export interface UpdateTopLevelCourseRequest {
|
|
name: string
|
|
description: string
|
|
thumbnail: string
|
|
sort_order: number
|
|
}
|
|
|
|
/** Body for POST /programs/:program_id/courses */
|
|
export interface CreateProgramCourseRequest {
|
|
name: string
|
|
description: string
|
|
thumbnail: string
|
|
sort_order: number
|
|
}
|
|
|
|
export interface CreateProgramCourseResponse {
|
|
message: string
|
|
data: ProgramCourseListItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
/** Exam prep catalog course row (e.g. IELTS / DET cards) */
|
|
export interface ExamPrepCatalogCourseItem {
|
|
id: number
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
sort_order?: number
|
|
units_count?: number
|
|
modules_count?: number
|
|
lessons_count?: number
|
|
created_at?: string
|
|
updated_at?: string
|
|
}
|
|
|
|
export interface CreateExamPrepCatalogCourseRequest {
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
}
|
|
|
|
export interface CreateExamPrepCatalogCourseResponse {
|
|
message: string
|
|
data: ExamPrepCatalogCourseItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface GetExamPrepCatalogCoursesResponse {
|
|
message: string
|
|
data: {
|
|
offset: number
|
|
limit: number
|
|
total_count: number
|
|
catalog_courses: ExamPrepCatalogCourseItem[]
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface UpdateExamPrepCatalogCourseRequest {
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
sort_order: number
|
|
}
|
|
|
|
export interface UpdateExamPrepCatalogCourseResponse {
|
|
message: string
|
|
data: ExamPrepCatalogCourseItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface ExamPrepCatalogUnitItem {
|
|
id: number
|
|
catalog_course_id: number
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
sort_order?: number
|
|
modules_count?: number
|
|
lessons_count?: number
|
|
videos_count?: number
|
|
practices_count?: number
|
|
created_at?: string
|
|
updated_at?: string
|
|
}
|
|
|
|
export interface CreateExamPrepCatalogUnitRequest {
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
sort_order: number
|
|
}
|
|
|
|
export interface CreateExamPrepCatalogUnitResponse {
|
|
message: string
|
|
data: ExamPrepCatalogUnitItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface UpdateExamPrepCatalogUnitRequest {
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
sort_order: number
|
|
}
|
|
|
|
export interface UpdateExamPrepCatalogUnitResponse {
|
|
message: string
|
|
data: ExamPrepCatalogUnitItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface GetExamPrepCatalogUnitsResponse {
|
|
message: string
|
|
data: {
|
|
offset: number
|
|
limit: number
|
|
total_count: number
|
|
units: ExamPrepCatalogUnitItem[]
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface ExamPrepUnitModuleItem {
|
|
id: number
|
|
unit_id: number
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
icon?: string | null
|
|
sort_order?: number
|
|
lessons_count?: number
|
|
videos_count?: number
|
|
practices_count?: number
|
|
created_at?: string
|
|
updated_at?: string
|
|
}
|
|
|
|
export interface CreateExamPrepUnitModuleRequest {
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
icon?: string | null
|
|
}
|
|
|
|
export interface CreateExamPrepUnitModuleResponse {
|
|
message: string
|
|
data: ExamPrepUnitModuleItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface UpdateExamPrepUnitModuleRequest {
|
|
name: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
icon?: string | null
|
|
sort_order: number
|
|
}
|
|
|
|
export interface UpdateExamPrepUnitModuleResponse {
|
|
message: string
|
|
data: ExamPrepUnitModuleItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface GetExamPrepUnitModulesResponse {
|
|
message: string
|
|
data: {
|
|
offset: number
|
|
limit: number
|
|
total_count: number
|
|
modules: ExamPrepUnitModuleItem[]
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface ExamPrepModuleLessonItem {
|
|
id: number
|
|
unit_module_id: number
|
|
title: string
|
|
video_url: string
|
|
thumbnail?: string | null
|
|
description?: string | null
|
|
sort_order?: number
|
|
/** Total length in seconds when the API provides it. */
|
|
duration?: number | null
|
|
duration_seconds?: number | null
|
|
created_at?: string
|
|
updated_at?: string
|
|
}
|
|
|
|
export interface CreateExamPrepModuleLessonRequest {
|
|
title: string
|
|
video_url: string
|
|
thumbnail?: string | null
|
|
description?: string | null
|
|
publish_status: PracticePublishStatus
|
|
}
|
|
|
|
export interface CreateExamPrepModuleLessonResponse {
|
|
message: string
|
|
data: ExamPrepModuleLessonItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface UpdateExamPrepModuleLessonRequest {
|
|
title: string
|
|
video_url?: string | null
|
|
thumbnail?: string | null
|
|
description?: string | null
|
|
sort_order: number
|
|
}
|
|
|
|
export interface UpdateExamPrepModuleLessonResponse {
|
|
message: string
|
|
data: ExamPrepModuleLessonItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface GetExamPrepModuleLessonsResponse {
|
|
message: string
|
|
data: {
|
|
lessons: ExamPrepModuleLessonItem[]
|
|
total_count: number
|
|
limit: number
|
|
offset: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export interface GetProgramCoursesResponse {
|
|
message: string
|
|
data: {
|
|
total_count: number
|
|
limit: number
|
|
offset: number
|
|
courses: ProgramCourseListItem[]
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
/** Row from GET /courses/:courseId/modules (Learn English track). */
|
|
export interface TopLevelCourseModuleItem {
|
|
id: number
|
|
program_id: number
|
|
course_id: number
|
|
name: string
|
|
description: string
|
|
icon?: string | null
|
|
sort_order: number
|
|
created_at: string
|
|
}
|
|
|
|
export interface GetTopLevelCourseModulesResponse {
|
|
message: string
|
|
data: {
|
|
limit: number
|
|
offset: number
|
|
modules: TopLevelCourseModuleItem[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
/** Body for PUT /modules/:id (Learn English top-level modules). */
|
|
export interface UpdateTopLevelCourseModuleRequest {
|
|
name: string
|
|
description: string
|
|
icon: string
|
|
sort_order: number
|
|
}
|
|
|
|
/** Body for POST /courses/:courseId/modules */
|
|
export interface CreateTopLevelCourseModuleRequest {
|
|
name: string
|
|
description: string
|
|
icon: string
|
|
sort_order: number
|
|
}
|
|
|
|
export interface CreateTopLevelCourseModuleResponse {
|
|
message: string
|
|
data: TopLevelCourseModuleItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
/** Row from GET /modules/:moduleId/lessons (Learn English top-level module lessons). */
|
|
export interface TopLevelModuleLessonItem {
|
|
id: number
|
|
module_id: number
|
|
title: string
|
|
video_url: string
|
|
thumbnail: string
|
|
description: string
|
|
sort_order: number
|
|
publish_status?: PracticePublishStatus | string | null
|
|
has_practice?: boolean
|
|
/** Total length in seconds when the API provides it. */
|
|
duration?: number | null
|
|
duration_seconds?: number | null
|
|
created_at: string
|
|
}
|
|
|
|
export interface GetTopLevelModuleLessonsResponse {
|
|
message: string
|
|
data: {
|
|
total_count: number
|
|
limit: number
|
|
offset: number
|
|
lessons: TopLevelModuleLessonItem[]
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
/** Practice returned by GET /courses|modules|lessons/.../practices (Learn English parent-linked practice). */
|
|
export interface ParentContextPractice {
|
|
id: number
|
|
parent_kind: string
|
|
parent_id: number
|
|
title: string
|
|
story_description: string
|
|
story_image: string
|
|
question_set_id: number
|
|
quick_tips: string
|
|
publish_status?: PracticePublishStatus | string | null
|
|
persona_id?: number | null
|
|
created_at: string
|
|
}
|
|
|
|
export interface GetPracticesByParentContextResponse {
|
|
message: string
|
|
data: {
|
|
offset: number
|
|
limit: number
|
|
practices: ParentContextPractice[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
export type PracticeParentKind = "COURSE" | "MODULE" | "LESSON"
|
|
|
|
export type PracticePublishStatus = "DRAFT" | "PUBLISHED"
|
|
|
|
/** POST /practices — create practice linked to a course, module, or lesson (Learn English). */
|
|
export interface CreateParentLinkedPracticeRequest {
|
|
parent_kind: PracticeParentKind
|
|
parent_id: number
|
|
title: string
|
|
story_description: string
|
|
story_image: string
|
|
question_set_id: number
|
|
quick_tips: string
|
|
publish_status: PracticePublishStatus
|
|
persona_id?: number
|
|
}
|
|
|
|
export interface CreateParentLinkedPracticeResponse {
|
|
message: string
|
|
data: ParentContextPractice
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
/** Body for PUT /practices/:id (Learn English parent-linked practice). */
|
|
export interface UpdateParentLinkedPracticeRequest {
|
|
title?: string
|
|
story_description?: string
|
|
story_image?: string
|
|
question_set_id?: number
|
|
quick_tips?: string
|
|
publish_status?: PracticePublishStatus
|
|
persona_id?: number | null
|
|
}
|
|
|
|
/** Publish-only patch: PUT /practices/:id with { publish_status: "PUBLISHED" }. */
|
|
export interface PublishParentLinkedPracticeRequest {
|
|
publish_status: PracticePublishStatus
|
|
}
|
|
|
|
export interface UpdateParentLinkedPracticeResponse {
|
|
message: string
|
|
data: ParentContextPractice
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
/** Body for PUT /lessons/:id (Learn English top-level module lessons). */
|
|
export interface UpdateTopLevelModuleLessonRequest {
|
|
title: string
|
|
video_url: string
|
|
thumbnail: string
|
|
description: string
|
|
sort_order: number
|
|
}
|
|
|
|
/** Publish-only patch: PUT /lessons/:id with { publish_status }. */
|
|
export interface PublishTopLevelModuleLessonRequest {
|
|
publish_status: PracticePublishStatus
|
|
}
|
|
|
|
/** Body for POST /modules/:moduleId/lessons. */
|
|
export interface CreateTopLevelModuleLessonRequest {
|
|
title: string
|
|
video_url: string
|
|
thumbnail: string
|
|
description: string
|
|
sort_order: number
|
|
publish_status: PracticePublishStatus
|
|
}
|
|
|
|
export interface CreateTopLevelModuleLessonResponse {
|
|
message: string
|
|
data: TopLevelModuleLessonItem
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown | null
|
|
}
|
|
|
|
// ============================================
|
|
// Legacy Types (deprecated - using SubCourse hierarchy now)
|
|
// Keeping for backward compatibility with existing API endpoints
|
|
// ============================================
|
|
|
|
/** @deprecated Use SubCourse instead */
|
|
export interface Program {
|
|
id: number
|
|
course_id: number
|
|
title: string
|
|
description: string
|
|
thumbnail: string
|
|
display_order: number
|
|
is_active: boolean
|
|
}
|
|
|
|
/** @deprecated Use GetSubCoursesResponse instead */
|
|
export interface GetProgramsResponse {
|
|
message: string
|
|
data: {
|
|
programs: Program[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** @deprecated Use UpdateSubCourseStatusRequest instead */
|
|
export interface UpdateProgramStatusRequest {
|
|
is_active: boolean
|
|
}
|
|
|
|
/** @deprecated Use CreateSubCourseRequest instead */
|
|
export interface CreateProgramRequest {
|
|
course_id: number
|
|
title: string
|
|
description: string
|
|
}
|
|
|
|
/** @deprecated Use UpdateSubCourseRequest instead */
|
|
export interface UpdateProgramRequest {
|
|
title: string
|
|
description: string
|
|
is_active: boolean
|
|
}
|
|
|
|
/** @deprecated Use SubCourse instead */
|
|
export interface Level {
|
|
id: number
|
|
program_id: number
|
|
title: string
|
|
description: string
|
|
level_index: number
|
|
number_of_modules: number
|
|
number_of_practices: number
|
|
number_of_videos: number
|
|
is_active: boolean
|
|
}
|
|
|
|
/** @deprecated Use GetSubCoursesResponse instead */
|
|
export interface GetLevelsResponse {
|
|
message: string
|
|
data: {
|
|
levels: Level[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** @deprecated Use CreateSubCourseRequest instead */
|
|
export interface CreateLevelRequest {
|
|
program_id: number
|
|
title: string
|
|
description: string
|
|
}
|
|
|
|
/** @deprecated Use UpdateSubCourseRequest instead */
|
|
export interface UpdateLevelRequest {
|
|
title: string
|
|
description: string
|
|
}
|
|
|
|
/** @deprecated Use UpdateSubCourseStatusRequest instead */
|
|
export interface UpdateLevelStatusRequest {
|
|
is_active: boolean
|
|
}
|
|
|
|
/** @deprecated Use SubCourse hierarchy instead */
|
|
export interface Module {
|
|
id: number
|
|
level_id: number
|
|
title: string
|
|
content: string
|
|
display_order: number
|
|
is_active: boolean
|
|
}
|
|
|
|
/** @deprecated Use GetSubCoursesResponse instead */
|
|
export interface GetModulesResponse {
|
|
message: string
|
|
data: {
|
|
modules: Module[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** @deprecated Use CreateSubCourseRequest instead */
|
|
export interface CreateModuleRequest {
|
|
level_id: number
|
|
title: string
|
|
/** Legacy field kept for backward compatibility. */
|
|
content?: string
|
|
/** Preferred field for module detail text. */
|
|
description?: string
|
|
icon_url?: string
|
|
display_order?: number
|
|
is_active?: boolean
|
|
}
|
|
|
|
/** @deprecated Use UpdateSubCourseRequest instead */
|
|
export interface UpdateModuleRequest {
|
|
title: string
|
|
content: string
|
|
}
|
|
|
|
/** @deprecated Use UpdateSubCourseStatusRequest instead */
|
|
export interface UpdateModuleStatusRequest {
|
|
is_active: boolean
|
|
}
|
|
|
|
// ============================================
|
|
// New Hierarchy: SubCourse (replaces Program/Level/Module)
|
|
// ============================================
|
|
export interface SubCourse {
|
|
id: number
|
|
course_id: number
|
|
/** Present when derived from course hierarchy rows (levels → modules → sub-modules). */
|
|
level_id?: number
|
|
module_id?: number
|
|
title: string
|
|
description: string
|
|
level: string
|
|
cefr_level?: string
|
|
thumbnail: string
|
|
display_order: number
|
|
sub_level?: string
|
|
is_active: boolean
|
|
}
|
|
|
|
export interface GetSubCoursesResponse {
|
|
message: string
|
|
data: {
|
|
sub_courses: SubCourse[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Compatibility request used to create sub-modules */
|
|
export interface CreateSubCourseRequest {
|
|
course_id: number
|
|
title: string
|
|
description: string
|
|
thumbnail: string
|
|
display_order: number
|
|
level: string
|
|
sub_level: string
|
|
}
|
|
|
|
export interface UpdateSubCourseRequest {
|
|
title: string
|
|
description: string
|
|
level: string
|
|
}
|
|
|
|
export interface UpdateSubCourseStatusRequest {
|
|
is_active: boolean
|
|
level: string
|
|
title: string
|
|
}
|
|
|
|
// SubCourse Video
|
|
export interface SubCourseVideo {
|
|
id: number
|
|
sub_course_id?: number
|
|
sub_module_id?: number
|
|
title: string
|
|
description: string
|
|
video_url: string
|
|
duration: number
|
|
thumbnail: string
|
|
is_published: boolean
|
|
is_active: boolean
|
|
}
|
|
|
|
export interface GetSubCourseVideosResponse {
|
|
message: string
|
|
data: {
|
|
videos: SubCourseVideo[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface CreateSubCourseVideoRequest {
|
|
sub_course_id?: number
|
|
sub_module_id?: number
|
|
title: string
|
|
description: string
|
|
video_url: string
|
|
}
|
|
|
|
export type VideoVisibility = "PUBLISHED" | "DRAFT" | "PRIVATE" | "UNLISTED" | string
|
|
export type VideoStatus = "PUBLISHED" | "DRAFT" | "ARCHIVED" | string
|
|
|
|
export interface CreateCourseVideoRequest {
|
|
sub_course_id?: number
|
|
sub_module_id?: number
|
|
title: string
|
|
description: string
|
|
video_url: string
|
|
duration: number
|
|
resolution?: string
|
|
visibility?: VideoVisibility
|
|
display_order?: number
|
|
status?: VideoStatus
|
|
}
|
|
|
|
export interface CreateVimeoVideoRequest {
|
|
sub_course_id?: number
|
|
sub_module_id?: number
|
|
title: string
|
|
description: string
|
|
source_url: string
|
|
file_size: number
|
|
duration: number
|
|
}
|
|
|
|
export interface UpdateSubCourseVideoRequest {
|
|
title: string
|
|
description: string
|
|
video_url: string
|
|
}
|
|
|
|
// Practice now belongs to SubCourse
|
|
export interface Practice {
|
|
id: number
|
|
sub_course_id?: number
|
|
sub_module_id?: number
|
|
title: string
|
|
description: string
|
|
thumbnail?: string
|
|
intro_video_url?: string
|
|
question_set_id?: number
|
|
banner_image: string
|
|
persona: string
|
|
is_active: boolean
|
|
}
|
|
|
|
export interface GetPracticesResponse {
|
|
message: string
|
|
data: {
|
|
practices: Practice[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface CreatePracticeRequest {
|
|
sub_course_id?: number
|
|
sub_module_id?: number
|
|
title: string
|
|
description: string
|
|
thumbnail?: string
|
|
intro_video_url?: string
|
|
persona?: string
|
|
}
|
|
|
|
export interface UpdatePracticeRequest {
|
|
title: string
|
|
description: string
|
|
persona?: string
|
|
}
|
|
|
|
export interface UpdatePracticeStatusRequest {
|
|
is_active: boolean
|
|
}
|
|
|
|
export interface PracticeQuestion {
|
|
id: number
|
|
practice_id: number
|
|
question: string
|
|
points?: number
|
|
difficulty_level?: string
|
|
question_voice_prompt: string
|
|
sample_answer_voice_prompt: string
|
|
sample_answer: string
|
|
tips: string
|
|
type: "MCQ" | "TRUE_FALSE" | "SHORT" | "AUDIO"
|
|
}
|
|
|
|
export interface GetPracticeQuestionsResponse {
|
|
message: string
|
|
data: {
|
|
questions: PracticeQuestion[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface CreatePracticeQuestionRequest {
|
|
practice_id: number
|
|
question: string
|
|
question_voice_prompt?: string
|
|
sample_answer_voice_prompt?: string
|
|
sample_answer: string
|
|
tips?: string
|
|
explanation?: string
|
|
difficulty_level?: string
|
|
points?: number
|
|
options?: QuestionOption[]
|
|
short_answers?: string[]
|
|
type: "MCQ" | "TRUE_FALSE" | "SHORT"
|
|
}
|
|
|
|
export interface UpdatePracticeQuestionRequest {
|
|
question: string
|
|
question_voice_prompt?: string
|
|
sample_answer_voice_prompt?: string
|
|
sample_answer: string
|
|
tips?: string
|
|
explanation?: string
|
|
difficulty_level?: string
|
|
points?: number
|
|
options?: QuestionOption[]
|
|
short_answers?: string[]
|
|
type: "MCQ" | "TRUE_FALSE" | "SHORT"
|
|
}
|
|
|
|
// Question Sets (Practice sets fetched via /question-sets)
|
|
export type QuestionSetType = "PRACTICE" | "EXAM"
|
|
export type QuestionSetStatus = "PUBLISHED" | "DRAFT" | "ARCHIVED"
|
|
export type QuestionSetOwnerType = "SUB_COURSE" | "SUB_MODULE" | "COURSE"
|
|
|
|
export interface QuestionSet {
|
|
id: number
|
|
title: string
|
|
description: string
|
|
set_type: QuestionSetType
|
|
owner_type: QuestionSetOwnerType
|
|
owner_id: number
|
|
persona: string
|
|
shuffle_questions: boolean
|
|
status: QuestionSetStatus
|
|
created_at: string
|
|
}
|
|
|
|
export interface GetQuestionSetsResponse {
|
|
message: string
|
|
data: QuestionSet[] | { question_sets: QuestionSet[]; total_count?: number }
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface GetQuestionSetsParams {
|
|
set_type?: "PRACTICE" | "INITIAL_ASSESSMENT" | "EXAM" | string
|
|
owner_type?: "SUB_COURSE" | "SUB_MODULE" | "COURSE" | string
|
|
owner_id?: number
|
|
status?: "DRAFT" | "PUBLISHED" | "ARCHIVED" | string
|
|
limit?: number
|
|
offset?: number
|
|
}
|
|
|
|
export interface QuestionSetDetail {
|
|
id: number
|
|
title: string
|
|
description: string
|
|
set_type: string
|
|
owner_type: string
|
|
owner_id: number
|
|
banner_image?: string | null
|
|
persona?: string | null
|
|
time_limit_minutes?: number | null
|
|
passing_score?: number | null
|
|
shuffle_questions?: boolean
|
|
status: string
|
|
sub_course_video_id?: number | null
|
|
intro_video_url?: string | null
|
|
created_at: string
|
|
question_count: number
|
|
}
|
|
|
|
export interface GetQuestionSetDetailResponse {
|
|
message: string
|
|
data: QuestionSetDetail
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface QuestionSetQuestion {
|
|
id: number
|
|
set_id: number
|
|
question_id: number
|
|
display_order: number
|
|
question_text: string
|
|
question_type: "MCQ" | "TRUE_FALSE" | "SHORT" | "SHORT_ANSWER" | "AUDIO" | string
|
|
difficulty_level?: string | null
|
|
points?: number
|
|
explanation?: string | null
|
|
tips?: string | null
|
|
voice_prompt?: string | null
|
|
sample_answer_voice_prompt?: string | null
|
|
image_url?: string | null
|
|
audio_correct_answer_text?: string | null
|
|
question_status?: string
|
|
}
|
|
|
|
export interface GetQuestionSetQuestionsResponse {
|
|
message: string
|
|
data: QuestionSetQuestion[]
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface GetPracticeQuestionsByPracticeResponse {
|
|
message: string
|
|
data: {
|
|
questions: QuestionSetQuestion[]
|
|
total_count: number
|
|
limit: number
|
|
offset: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** POST /question-sets — practices use set_type: "PRACTICE", owner_type: "SUB_COURSE", owner_id: sub-course id */
|
|
export interface CreateQuestionSetRequest {
|
|
title: string
|
|
set_type: string
|
|
description?: string | null
|
|
owner_type?: string | null
|
|
owner_id?: number | null
|
|
banner_image?: string | null
|
|
persona?: string | null
|
|
time_limit_minutes?: number | null
|
|
passing_score?: number | null
|
|
shuffle_questions?: boolean | null
|
|
status?: string | null
|
|
sub_course_video_id?: number | null
|
|
intro_video_url?: string | null
|
|
}
|
|
|
|
export interface AddQuestionToSetRequest {
|
|
display_order?: number
|
|
question_id: number
|
|
}
|
|
|
|
export interface QuestionOption {
|
|
option_order: number
|
|
option_text: string
|
|
is_correct: boolean
|
|
}
|
|
|
|
export interface CreateQuestionRequest {
|
|
question_text: string
|
|
question_type: string
|
|
difficulty_level?: string
|
|
points?: number
|
|
tips?: string
|
|
explanation?: string
|
|
status?: string
|
|
image_url?: string
|
|
options?: QuestionOption[]
|
|
voice_prompt?: string
|
|
sample_answer_voice_prompt?: string
|
|
audio_correct_answer_text?: string
|
|
short_answers?: string[] | { acceptable_answer: string; match_type: "EXACT" | "CASE_INSENSITIVE" }[]
|
|
/** Required for `DYNAMIC` questions — links to `/questions/type-definitions/:id` */
|
|
question_type_definition_id?: number
|
|
/** Required for `DYNAMIC` — stimulus/response element instances per definition schema */
|
|
dynamic_payload?: DynamicQuestionPayload
|
|
}
|
|
|
|
export interface CreateQuestionResponse {
|
|
message: string
|
|
data: {
|
|
id: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface QuestionShortAnswer {
|
|
acceptable_answer: string
|
|
match_type?: "EXACT" | "CASE_INSENSITIVE" | string
|
|
}
|
|
|
|
export interface QuestionDetail {
|
|
id: number
|
|
question_text: string
|
|
question_type: "MCQ" | "TRUE_FALSE" | "SHORT_ANSWER" | "SHORT" | string
|
|
difficulty_level?: string | null
|
|
points?: number | null
|
|
status?: string
|
|
created_at?: string
|
|
options?: ({ id?: number } & QuestionOption)[]
|
|
short_answers?: string[] | QuestionShortAnswer[]
|
|
tips?: string | null
|
|
explanation?: string | null
|
|
image_url?: string | null
|
|
voice_prompt?: string | null
|
|
sample_answer_voice_prompt?: string | null
|
|
audio_correct_answer_text?: string | null
|
|
question_type_definition_id?: number | null
|
|
dynamic_payload?: DynamicQuestionPayload | null
|
|
}
|
|
|
|
export interface GetQuestionDetailResponse {
|
|
message: string
|
|
data: QuestionDetail
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface GetQuestionsParams {
|
|
question_type?: "MCQ" | "TRUE_FALSE" | "SHORT_ANSWER" | "AUDIO" | string
|
|
difficulty?: "EASY" | "MEDIUM" | "HARD" | string
|
|
status?: "DRAFT" | "PUBLISHED" | "INACTIVE" | string
|
|
limit?: number
|
|
offset?: number
|
|
}
|
|
|
|
export interface GetQuestionsResponse {
|
|
message: string
|
|
data: QuestionDetail[] | { questions: QuestionDetail[]; total_count?: number }
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface CreateQuestionSetResponse {
|
|
message: string
|
|
data: {
|
|
id: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
// Sub-course Prerequisites
|
|
export interface SubCoursePrerequisite {
|
|
id: number
|
|
sub_course_id: number
|
|
prerequisite_sub_course_id: number
|
|
prerequisite_title: string
|
|
prerequisite_level: string
|
|
prerequisite_display_order: number
|
|
}
|
|
|
|
export interface GetSubCoursePrerequisitesResponse {
|
|
message: string
|
|
data: SubCoursePrerequisite[]
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface AddSubCoursePrerequisiteRequest {
|
|
prerequisite_sub_course_id: number
|
|
}
|
|
|
|
// Learning Path (full tree from GET /courses/:courseId/learning-path)
|
|
export interface LearningPathSubCourse {
|
|
id: number
|
|
title: string
|
|
description: string
|
|
thumbnail: string
|
|
display_order: number
|
|
level: string
|
|
sub_level?: string
|
|
prerequisite_count: number
|
|
video_count: number
|
|
practice_count: number
|
|
prerequisites: { sub_course_id: number; title: string; level: string }[]
|
|
videos: LearningPathVideo[]
|
|
practices: LearningPathPractice[]
|
|
}
|
|
|
|
export interface LearningPathVideo {
|
|
id: number
|
|
title: string
|
|
display_order: number
|
|
duration: number
|
|
video_url: string
|
|
}
|
|
|
|
export interface LearningPathPractice {
|
|
id: number
|
|
title: string
|
|
status: string
|
|
question_count: number
|
|
display_order?: number
|
|
}
|
|
|
|
export interface LearningPath {
|
|
course_id: number
|
|
course_title: string
|
|
description: string
|
|
thumbnail: string
|
|
intro_video_url: string
|
|
category_id: number
|
|
category_name: string
|
|
sub_courses: LearningPathSubCourse[]
|
|
}
|
|
|
|
export interface GetLearningPathResponse {
|
|
message: string
|
|
data: LearningPath
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface HumanLanguageLesson {
|
|
id: number
|
|
course_id: number
|
|
title: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
display_order: number
|
|
level: string
|
|
video_count: number
|
|
practice_count: number
|
|
videos: LearningPathVideo[]
|
|
practices: LearningPathPractice[]
|
|
}
|
|
|
|
export interface SubModuleLessonDetail {
|
|
id: number
|
|
sub_module_id: number
|
|
display_order: number
|
|
is_active: boolean
|
|
created_at: string
|
|
title: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
teaching_text?: string | null
|
|
teaching_image_url?: string | null
|
|
teaching_audio_url?: string | null
|
|
teaching_video_url?: string | null
|
|
}
|
|
|
|
export interface SubModuleLesson {
|
|
id: number
|
|
sub_module_id: number
|
|
display_order: number
|
|
is_active: boolean
|
|
created_at: string
|
|
title: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
teaching_text?: string | null
|
|
teaching_image_url?: string | null
|
|
teaching_audio_url?: string | null
|
|
teaching_video_url?: string | null
|
|
}
|
|
|
|
export interface GetSubModuleLessonDetailResponse {
|
|
message: string
|
|
data: SubModuleLessonDetail
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface UpdateSubModuleLessonRequest {
|
|
title: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
teaching_text?: string | null
|
|
teaching_image_url?: string | null
|
|
teaching_audio_url?: string | null
|
|
teaching_video_url?: string | null
|
|
display_order: number
|
|
is_active: boolean
|
|
}
|
|
|
|
export interface UpdateSubModuleLessonResponse {
|
|
message: string
|
|
data: SubModuleLessonDetail
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface GetSubModuleLessonsResponse {
|
|
message: string
|
|
data: SubModuleLesson[]
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface GetHumanLanguageLessonsResponse {
|
|
message: string
|
|
data: {
|
|
course_id: number
|
|
course_title: string
|
|
cefr_level: string
|
|
lessons: HumanLanguageLesson[]
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Row from GET /course-management/human-language/sub-categories */
|
|
export interface HumanLanguageSubCategoryListItem {
|
|
id: number
|
|
category_id: number
|
|
category_name: string
|
|
name: string
|
|
description?: string | null
|
|
display_order: number
|
|
is_active: boolean
|
|
created_at: string
|
|
/** Present on some payloads; ignore if unused. */
|
|
total_count?: number
|
|
}
|
|
|
|
export interface GetHumanLanguageSubCategoriesResponse {
|
|
message: string
|
|
data: {
|
|
sub_categories: HumanLanguageSubCategoryListItem[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Row from GET /course-management/categories/:categoryId/sub-categories */
|
|
export interface CategorySubCategoryListItem {
|
|
id: number
|
|
category_id: number
|
|
category_name: string
|
|
name: string
|
|
description?: string | null
|
|
display_order: number
|
|
is_active: boolean
|
|
created_at: string
|
|
/** Sometimes echoed per row by the API; safe to ignore. */
|
|
total_count?: number
|
|
}
|
|
|
|
export interface GetCategorySubCategoriesResponse {
|
|
message: string
|
|
data: {
|
|
sub_categories: CategorySubCategoryListItem[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Row from GET /course-management/sub-categories/:subCategoryId/courses */
|
|
export interface SubCategoryCourseListItem {
|
|
id: number
|
|
category_id: number
|
|
sub_category_id: number
|
|
title: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
intro_video_url?: string | null
|
|
is_active: boolean
|
|
total_count?: number
|
|
}
|
|
|
|
export interface GetSubCategoryCoursesResponse {
|
|
message: string
|
|
data: {
|
|
courses: SubCategoryCourseListItem[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Row from GET /course-management/courses/:courseId/levels or GET /course-management/levels */
|
|
export interface CourseLevelRow {
|
|
id: number
|
|
course_id: number
|
|
cefr_level: string
|
|
display_order: number
|
|
is_active: boolean
|
|
created_at: string
|
|
title: string
|
|
description?: string | null
|
|
thumbnail?: string | null
|
|
total_count?: number
|
|
}
|
|
|
|
export interface GetCourseLevelsForCourseResponse {
|
|
message: string
|
|
data: {
|
|
levels: CourseLevelRow[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface GetCourseLevelsAllResponse {
|
|
message: string
|
|
data: {
|
|
levels: CourseLevelRow[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface GetCourseLevelByIdResponse {
|
|
message: string
|
|
data: CourseLevelRow
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Row from GET /course-management/modules/:moduleId/sub-modules */
|
|
export interface CourseSubModuleListItem {
|
|
id: number
|
|
module_id: number
|
|
title: string
|
|
description?: string | null
|
|
display_order: number
|
|
is_active: boolean
|
|
created_at: string
|
|
legacy_sub_course_id?: number | null
|
|
thumbnail?: string | null
|
|
tips?: string | null
|
|
total_count?: number
|
|
}
|
|
|
|
export interface GetSubModulesByModuleResponse {
|
|
message: string
|
|
data: {
|
|
sub_modules: CourseSubModuleListItem[]
|
|
total_count: number
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Row from GET /course-management/human-language/hierarchy */
|
|
export interface HumanLanguageHierarchyFlatRow {
|
|
category_id: number
|
|
category_name: string
|
|
sub_category_id?: number | null
|
|
sub_category_name?: string | null
|
|
course_id?: number | null
|
|
course_title?: string | null
|
|
}
|
|
|
|
export interface GetHumanLanguageHierarchyFlatResponse {
|
|
message: string
|
|
data: HumanLanguageHierarchyFlatRow[]
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
/** Row from GET /course-management/courses/:courseId/hierarchy */
|
|
export interface CourseHierarchyRow {
|
|
course_id: number
|
|
course_title: string
|
|
level_id?: number | null
|
|
cefr_level?: string | null
|
|
level_title?: string | null
|
|
level_description?: string | null
|
|
level_thumbnail?: string | null
|
|
module_id?: number | null
|
|
module_title?: string | null
|
|
module_icon_url?: string | null
|
|
sub_module_id?: number | null
|
|
sub_module_title?: string | null
|
|
sub_module_description?: string | null
|
|
sub_module_thumbnail?: string | null
|
|
sub_module_tips?: string | null
|
|
sub_module_display_order?: number | null
|
|
}
|
|
|
|
export interface GetCourseHierarchyResponse {
|
|
message: string
|
|
data: CourseHierarchyRow[]
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface HumanLanguageSubModule {
|
|
id: number
|
|
title: string
|
|
videos: LearningPathVideo[]
|
|
lessons?: {
|
|
id: number
|
|
question_set_id: number
|
|
title: string
|
|
status: string
|
|
question_count: number
|
|
display_order: number
|
|
intro_video_url?: string | null
|
|
}[]
|
|
practices: LearningPathPractice[]
|
|
}
|
|
|
|
export interface HumanLanguageModule {
|
|
id: number
|
|
title: string
|
|
sub_modules: HumanLanguageSubModule[]
|
|
}
|
|
|
|
export interface HumanLanguageLevelTree {
|
|
level_id?: number
|
|
level: string
|
|
modules: HumanLanguageModule[]
|
|
}
|
|
|
|
export interface HumanLanguageCourseTree {
|
|
course_id: number
|
|
course_name: string
|
|
levels: HumanLanguageLevelTree[]
|
|
}
|
|
|
|
export interface HumanLanguageSubCategoryTree {
|
|
sub_category_id: number
|
|
sub_category_name: string
|
|
courses: HumanLanguageCourseTree[]
|
|
}
|
|
|
|
export interface GetHumanLanguageHierarchyResponse {
|
|
message: string
|
|
data: {
|
|
category_id: number
|
|
category_name: string
|
|
sub_categories: HumanLanguageSubCategoryTree[]
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface CreateHumanLanguageLessonRequest {
|
|
course_id: number
|
|
title: string
|
|
description?: string
|
|
thumbnail?: string
|
|
display_order?: number
|
|
cefr_level: string
|
|
}
|
|
|
|
export interface GetSubCourseEntryAssessmentResponse {
|
|
message: string
|
|
data: QuestionSet | null
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
export interface ReorderItem {
|
|
id: number
|
|
position: number
|
|
}
|
|
|
|
// Ratings
|
|
export interface Rating {
|
|
id: number
|
|
user_id: number
|
|
target_type: string
|
|
target_id: number
|
|
stars: number
|
|
review: string
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface GetRatingsParams {
|
|
target_type: string
|
|
target_id: number
|
|
limit?: number
|
|
offset?: number
|
|
}
|
|
|
|
export interface GetRatingsResponse {
|
|
message: string
|
|
data: Rating[]
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|
|
|
|
// Vimeo Sample Video
|
|
export interface VimeoSampleVideo {
|
|
vimeo_id: string
|
|
uri: string
|
|
name: string
|
|
description: string
|
|
duration: number
|
|
width: number
|
|
height: number
|
|
link: string
|
|
embed_url: string
|
|
embed_html: string
|
|
thumbnail_url: string
|
|
status: string
|
|
transcode_status: string
|
|
}
|
|
|
|
export interface GetVimeoSampleResponse {
|
|
message: string
|
|
data: {
|
|
video: VimeoSampleVideo
|
|
iframe: string
|
|
}
|
|
success: boolean
|
|
status_code: number
|
|
metadata: unknown
|
|
}
|