Yimaru-Admin/src/types/course.types.ts
Yared Yemane b8a73c73db feat(content): admin UX for forms, practices, lessons, and content hub
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>
2026-05-20 08:00:31 -07:00

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
}