diff --git a/src/api/courses.api.ts b/src/api/courses.api.ts index 23f1e7f..5d5d9d6 100644 --- a/src/api/courses.api.ts +++ b/src/api/courses.api.ts @@ -78,8 +78,17 @@ type CourseHierarchyRow = { sub_module_title?: string | null } +async function withSingleRetry(request: () => Promise, retryDelayMs = 400): Promise { + try { + return await request() + } catch { + await new Promise((resolve) => setTimeout(resolve, retryDelayMs)) + return request() + } +} + export const getCourseCategories = () => - http.get("/course-management/hierarchy").then((res) => { + withSingleRetry(() => http.get("/course-management/hierarchy")).then((res) => { const rows: UnifiedHierarchyRow[] = res.data?.data ?? [] const categoriesMap = new Map< number, @@ -160,7 +169,7 @@ export const deleteCourseSubCategory = (subCategoryId: number) => http.delete(`/course-management/sub-categories/${subCategoryId}`) export const getCoursesByCategory = (categoryId: number) => - http.get("/course-management/hierarchy").then((res) => { + withSingleRetry(() => http.get("/course-management/hierarchy")).then((res) => { const rows: UnifiedHierarchyRow[] = res.data?.data ?? [] const requestedCategoryRows = rows.filter((r) => r.category_id === categoryId) @@ -608,7 +617,7 @@ export const getHumanLanguageLessonsByCourse = (courseId: number, cefr_level: st }) export const getHumanLanguageHierarchy = () => - http.get("/course-management/hierarchy").then(async (res) => { + withSingleRetry(() => http.get("/course-management/hierarchy")).then(async (res) => { const payload = res.data?.data as unknown if (payload && typeof payload === "object" && !Array.isArray(payload) && "sub_categories" in payload) { return res diff --git a/src/pages/content-management/AddNewLessonPage.tsx b/src/pages/content-management/AddNewLessonPage.tsx index 1ec97b5..5ab04e6 100644 --- a/src/pages/content-management/AddNewLessonPage.tsx +++ b/src/pages/content-management/AddNewLessonPage.tsx @@ -99,6 +99,13 @@ function isDirectVideoFile(url: string): boolean { return /\.(mp4|webm|ogg|mov|m4v)$/.test(clean) } +function questionTypeLabel(type: QuestionType): string { + if (type === "TRUE_FALSE") return "True/False" + if (type === "SHORT") return "Short Answer" + if (type === "AUDIO") return "Audio" + return "Multiple Choice" +} + export function AddNewLessonPage() { const { categoryId, courseId, subModuleId } = useParams() const navigate = useNavigate() @@ -188,6 +195,11 @@ export function AddNewLessonPage() { return null }, [introVideoUrl]) + const populatedQuestions = useMemo( + () => questions.filter((question) => question.questionText.trim().length > 0), + [questions], + ) + const addQuestion = () => setQuestions((prev) => [...prev, createEmptyQuestion(String(Date.now()))]) const removeQuestion = (id: string) => setQuestions((prev) => (prev.length > 1 ? prev.filter((q) => q.id !== id) : prev)) const updateQuestion = (id: string, updates: Partial) => @@ -485,22 +497,102 @@ export function AddNewLessonPage() {

Step 3: Review & publish

+

Confirm lesson details and questions before saving or publishing.

-
-
-
-

Question set

-

Title: {lessonTitle || "Untitled Lesson"}

-

Description: {lessonDescription || "—"}

-

Status: Draft/Published (selected on save)

+
+
+
+
+

Basic Information

+ +
+
+
+ Title + {lessonTitle || "Untitled Lesson"} +
+
+ Description + + {lessonDescription || "—"} + +
+
+ Intro video URL + {introVideoUrl || "—"} +
+
+ Sub-module + {subModuleId ?? "—"} +
+
+
+ +
+
+

+ Questions + + {populatedQuestions.length} + +

+ +
+
+ {populatedQuestions.length === 0 ? ( +
+ No question content added yet. +
+ ) : ( + populatedQuestions.map((question, idx) => ( +
+
+ + {idx + 1} + + + {questionTypeLabel(question.questionType)} + + + {question.difficultyLevel} + + {question.points} pt +
+

{question.questionText}

+ {question.questionType === "MCQ" ? ( +
+ {question.options.map((option, optionIdx) => ( +
+ {option.text || `Option ${optionIdx + 1}`} +
+ ))} +
+ ) : null} +
+ )) + )} +
+
-
-

Lesson link

-

Sub-module: {subModuleId ?? "—"}

-

Intro video: {introVideoUrl || "—"}

-

Questions: {questions.length}

-
-
+

{resultStatus === "success" ? "Your lesson is now active." : resultMessage}

+
+ {resultStatus === "success" ? ( - - ) : null} + ) : ( + + )}
) : null}