diff --git a/src/pages/content-management/HumanLanguagePage.tsx b/src/pages/content-management/HumanLanguagePage.tsx index 9026667..b03f000 100644 --- a/src/pages/content-management/HumanLanguagePage.tsx +++ b/src/pages/content-management/HumanLanguagePage.tsx @@ -1,10 +1,10 @@ import { useEffect, useMemo, useState } from "react" import { Link } from "react-router-dom" -import { BookOpen, ChevronDown, ChevronRight, Languages, Loader2, Plus } from "lucide-react" +import { BookOpen, ChevronDown, ChevronRight, Languages, Loader2, Plus, Search } from "lucide-react" import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card" import { Button } from "../../components/ui/button" import { SpinnerIcon } from "../../components/ui/spinner-icon" -import { createCourse, createHumanLanguageLesson, getHumanLanguageHierarchy } from "../../api/courses.api" +import { createCourse, createCourseCategory, createHumanLanguageLesson, getHumanLanguageHierarchy } from "../../api/courses.api" import type { HumanLanguageCourseTree, HumanLanguageSubCategoryTree } from "../../types/course.types" import { toast } from "sonner" @@ -22,6 +22,7 @@ export function HumanLanguagePage() { const [creatingKey, setCreatingKey] = useState(null) const [quickSubCategoryName, setQuickSubCategoryName] = useState("") const [quickCourseName, setQuickCourseName] = useState("") + const [quickSearch, setQuickSearch] = useState("") const [quickCreating, setQuickCreating] = useState(false) const loadHierarchy = async () => { @@ -151,19 +152,24 @@ export function HumanLanguagePage() { } const handleQuickCreatePath = async () => { - if (!categoryId) { - toast.error("Human Language category is not available") - return - } if (!quickSubCategoryName.trim() || !quickCourseName.trim()) { toast.error("Subcategory and course names are required") return } setQuickCreating(true) try { + let effectiveCategoryId = categoryId + if (!effectiveCategoryId) { + const createdCategory = await createCourseCategory({ name: "Human Language" }) + effectiveCategoryId = createdCategory.data?.data?.id ?? null + setCategoryId(effectiveCategoryId) + } + if (!effectiveCategoryId) { + throw new Error("Missing human language category id") + } const title = `${quickSubCategoryName.trim()} - ${quickCourseName.trim()}` await createCourse({ - category_id: categoryId, + category_id: effectiveCategoryId, title, description: `${quickSubCategoryName.trim()} / ${quickCourseName.trim()}`, }) @@ -268,154 +274,166 @@ export function HumanLanguagePage() { ) : (
{availableCourses.length === 0 ? ( - - -

- No Human Language subcategory/course is available yet. Create the language course path first, then you can add incremental modules and sub-modules per level. -

-
+ +
+

Sub-category Management

+
+ setQuickSubCategoryName(e.target.value)} + className="h-11 w-full rounded-xl border border-grayScale-200 bg-white pl-9 pr-3 text-sm" + placeholder="Search sub-categories..." + value={quickSearch} + onChange={(e) => setQuickSearch(e.target.value)} /> - setQuickCourseName(e.target.value)} - /> -
-
- - - - {categoryId ? ( - - - - ) : null} +
+ +
+
+ +
+

No sub-categories yet

+

+ Create your first human-language path. Level listing will appear automatically after creation. +

+
+ setQuickSubCategoryName(e.target.value)} + /> + setQuickCourseName(e.target.value)} + /> + +
) : null} - {CEFR_LEVELS.filter((l) => selectedLevel === "ALL" || l === selectedLevel).map((level) => { - const modulesByCourse = selectedCourses - .map((course: HumanLanguageCourseTree) => { - const levelNode = course.levels.find((item) => item.level.toUpperCase() === level) - return { - course, - modules: levelNode?.modules ?? [], - } - }) - return ( - - - {!collapsedLevels.includes(level) ? ( - - {modulesByCourse.length === 0 ? ( -

No lessons found for this level.

- ) : ( - modulesByCourse.map((entry) => ( -
-
-

{entry.course.course_name}

- -
-
- {entry.modules.length === 0 ? ( -

No modules yet. Use “Add Module” to start.

- ) : ( - entry.modules.map((module) => ( -
-
-

Module: {module.title}

- -
- {module.sub_modules.map((subModule) => ( -
-
-

Sub-module: {subModule.title}

- {categoryId ? ( -
- - - - - - -
- ) : null} -
-
- {subModule.videos.map((video) => ( -
- - {video.title} -
- ))} - {subModule.practices.map((practice) => ( -
- Practice: {practice.title} ({practice.question_count} audio question(s)) -
- ))} -
-
- ))} -
- )) - )} -
+ + {availableCourses.length > 0 + ? CEFR_LEVELS.filter((l) => selectedLevel === "ALL" || l === selectedLevel).map((level) => { + const modulesByCourse = selectedCourses + .map((course: HumanLanguageCourseTree) => { + const levelNode = course.levels.find((item) => item.level.toUpperCase() === level) + return { + course, + modules: levelNode?.modules ?? [], + } + }) + .filter((entry) => entry.modules.length > 0 || selectedCourses.length > 0) + return ( + + + {!collapsedLevels.includes(level) ? ( + + {modulesByCourse.length === 0 ? ( +

No lessons found for this level.

+ ) : ( + modulesByCourse.map((entry) => ( +
+
+

{entry.course.course_name}

+ +
+
+ {entry.modules.length === 0 ? ( +

No modules yet. Use “Add Module” to start.

+ ) : ( + entry.modules.map((module) => ( +
+
+

Module: {module.title}

+ +
+ {module.sub_modules.map((subModule) => ( +
+
+

Sub-module: {subModule.title}

+ {categoryId ? ( +
+ + + + + + +
+ ) : null} +
+
+ {subModule.videos.map((video) => ( +
+ + {video.title} +
+ ))} + {subModule.practices.map((practice) => ( +
+ Practice: {practice.title} ({practice.question_count} audio question(s)) +
+ ))} +
+
+ ))} +
+ )) + )} +
+
+ )) + )} +
+ ) : null} +
+ ) + }) + : null}
)}