diff --git a/src/api/courses.api.ts b/src/api/courses.api.ts index 457d3e7..a3c10f9 100644 --- a/src/api/courses.api.ts +++ b/src/api/courses.api.ts @@ -145,8 +145,8 @@ export const updateCourseStatus = (courseId: number, isActive: boolean) => export const updateCourse = (courseId: number, data: UpdateCourseRequest) => http.put(`/course-management/courses/${courseId}`, data) -// SubCourse APIs (New Hierarchy) -export const getSubCoursesByCourse = (courseId: number) => +// Sub-Module APIs (Unified Hierarchy) +export const getSubModulesByCourse = (courseId: number) => http.get(`/course-management/courses/${courseId}/hierarchy`).then((res) => { const rows: CourseHierarchyRow[] = res.data?.data ?? [] const subModuleMap = new Map() @@ -178,7 +178,7 @@ export const getSubCoursesByCourse = (courseId: number) => } as unknown as { data: GetSubCoursesResponse } }) -export const createSubCourse = (data: CreateSubCourseRequest) => +export const createSubModule = (data: CreateSubCourseRequest) => http .post("/course-management/levels", { course_id: data.course_id, @@ -205,23 +205,23 @@ export const createSubCourse = (data: CreateSubCourseRequest) => }), ) -export const updateSubCourseThumbnail = (subCourseId: number, thumbnailUrl: string) => - http.post(`/course-management/sub-courses/${subCourseId}/thumbnail`, { +export const updateSubModuleThumbnail = (subModuleId: number, thumbnailUrl: string) => + http.post(`/course-management/sub-courses/${subModuleId}/thumbnail`, { thumbnail_url: thumbnailUrl, }) -export const updateSubCourse = (subCourseId: number, data: UpdateSubCourseRequest) => - http.put(`/course-management/sub-modules/${subCourseId}`, data) +export const updateSubModule = (subModuleId: number, data: UpdateSubCourseRequest) => + http.put(`/course-management/sub-modules/${subModuleId}`, data) -export const updateSubCourseStatus = (subCourseId: number, data: UpdateSubCourseStatusRequest) => - http.put(`/course-management/sub-modules/${subCourseId}`, data) +export const updateSubModuleStatus = (subModuleId: number, data: UpdateSubCourseStatusRequest) => + http.put(`/course-management/sub-modules/${subModuleId}`, data) -export const deleteSubCourse = (subCourseId: number) => - http.delete(`/course-management/sub-modules/${subCourseId}`) +export const deleteSubModule = (subModuleId: number) => + http.delete(`/course-management/sub-modules/${subModuleId}`) -// SubCourse Video APIs -export const getVideosBySubCourse = (subCourseId: number) => - http.get(`/course-management/sub-modules/${subCourseId}/videos`) +// Sub-Module Video APIs +export const getVideosBySubModule = (subModuleId: number) => + http.get(`/course-management/sub-modules/${subModuleId}/videos`) export const createSubCourseVideo = (data: CreateSubCourseVideoRequest) => http.post("/course-management/sub-module-videos", { @@ -250,11 +250,10 @@ export const updateSubCourseVideo = (videoId: number, data: UpdateSubCourseVideo export const deleteSubCourseVideo = (videoId: number) => http.delete(`/course-management/sub-module-videos/${videoId}`) -// Practice APIs - for SubCourse practices (New Hierarchy) -// Practices are question sets: POST /question-sets with set_type: "PRACTICE", owner_type: "SUB_COURSE". -export const getPracticesBySubCourse = (subCourseId: number) => +// Practice APIs - for Sub-Module practices (Unified Hierarchy) +export const getPracticesBySubModule = (subModuleId: number) => http.get("/question-sets/by-owner", { - params: { owner_type: "SUB_MODULE", owner_id: subCourseId }, + params: { owner_type: "SUB_MODULE", owner_id: subModuleId }, }) export const createPractice = (data: CreatePracticeRequest) => @@ -427,15 +426,15 @@ export const deleteQuestionSet = (questionSetId: number) => export const createVimeoVideo = (data: CreateVimeoVideoRequest) => http.post("/course-management/videos/vimeo", data) -// Sub-course Prerequisite APIs -export const getSubCoursePrerequisites = (subCourseId: number) => - http.get(`/course-management/sub-courses/${subCourseId}/prerequisites`) +// Sub-module Prerequisite APIs +export const getSubModulePrerequisites = (subModuleId: number) => + http.get(`/course-management/sub-courses/${subModuleId}/prerequisites`) -export const addSubCoursePrerequisite = (subCourseId: number, data: AddSubCoursePrerequisiteRequest) => - http.post(`/course-management/sub-courses/${subCourseId}/prerequisites`, data) +export const addSubModulePrerequisite = (subModuleId: number, data: AddSubCoursePrerequisiteRequest) => + http.post(`/course-management/sub-courses/${subModuleId}/prerequisites`, data) -export const removeSubCoursePrerequisite = (subCourseId: number, prerequisiteId: number) => - http.delete(`/course-management/sub-courses/${subCourseId}/prerequisites/${prerequisiteId}`) +export const removeSubModulePrerequisite = (subModuleId: number, prerequisiteId: number) => + http.delete(`/course-management/sub-courses/${subModuleId}/prerequisites/${prerequisiteId}`) // Learning Path APIs export const getLearningPath = (courseId: number) => @@ -476,9 +475,9 @@ export const createHumanLanguageLesson = (data: CreateHumanLanguageLessonRequest }), ) -export const getSubCourseEntryAssessment = (subCourseId: number) => +export const getSubModuleEntryAssessment = (subModuleId: number) => http.get( - `/question-sets/sub-courses/${subCourseId}/entry-assessment`, + `/question-sets/sub-courses/${subModuleId}/entry-assessment`, ) const buildReorderPayload = (items: ReorderItem[]) => { @@ -508,9 +507,24 @@ export const reorderCategories = (items: ReorderItem[]) => export const reorderCourses = (items: ReorderItem[]) => http.put("/course-management/courses/reorder", buildReorderPayload(items)) -export const reorderSubCourses = (items: ReorderItem[]) => +export const reorderSubModules = (items: ReorderItem[]) => http.put("/course-management/sub-courses/reorder", buildReorderPayload(items)) +// Backward-compatible aliases +export const getSubCoursesByCourse = getSubModulesByCourse +export const createSubCourse = createSubModule +export const updateSubCourseThumbnail = updateSubModuleThumbnail +export const updateSubCourse = updateSubModule +export const updateSubCourseStatus = updateSubModuleStatus +export const deleteSubCourse = deleteSubModule +export const getVideosBySubCourse = getVideosBySubModule +export const getPracticesBySubCourse = getPracticesBySubModule +export const getSubCoursePrerequisites = getSubModulePrerequisites +export const addSubCoursePrerequisite = addSubModulePrerequisite +export const removeSubCoursePrerequisite = removeSubModulePrerequisite +export const getSubCourseEntryAssessment = getSubModuleEntryAssessment +export const reorderSubCourses = reorderSubModules + export const reorderVideos = (items: ReorderItem[]) => http.put("/course-management/videos/reorder", buildReorderPayload(items)) diff --git a/src/app/AppRoutes.tsx b/src/app/AppRoutes.tsx index 093dde9..5c75232 100644 --- a/src/app/AppRoutes.tsx +++ b/src/app/AppRoutes.tsx @@ -10,8 +10,8 @@ import { ContentOverviewPage } from "../pages/content-management/ContentOverview import { CoursesPage } from "../pages/content-management/CoursesPage" import { PracticeQuestionsPage } from "../pages/content-management/PracticeQuestionsPage" import { AddNewPracticePage } from "../pages/content-management/AddNewPracticePage" -import { SubCoursesPage } from "../pages/content-management/SubCoursesPage" -import { SubCourseContentPage } from "../pages/content-management/SubCourseContentPage" +import { SubModulesPage } from "../pages/content-management/SubCoursesPage" +import { SubModuleContentPage } from "../pages/content-management/SubCourseContentPage" import { SpeakingPage } from "../pages/content-management/SpeakingPage" import { AddVideoPage } from "../pages/content-management/AddVideoPage" import { AddPracticePage } from "../pages/content-management/AddPracticePage" @@ -80,24 +80,29 @@ export function AppRoutes() { } /> } /> } /> } /> } /> } /> } /> - {/* Course → Sub-course → Video/Practice */} - } /> - } /> - } /> - } /> + {/* Course → Sub-module → Lesson/Practice */} + } /> + } /> + } /> + } /> + {/* Legacy aliases */} + } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx index 33a28a5..cf2394b 100644 --- a/src/pages/DashboardPage.tsx +++ b/src/pages/DashboardPage.tsx @@ -219,7 +219,7 @@ export function DashboardPage() { icon={BookOpen} label="Courses" value={dashboard.courses.total_courses.toLocaleString()} - deltaLabel={`${dashboard.courses.total_sub_courses} sub-courses, ${dashboard.courses.total_videos} videos`} + deltaLabel={`${dashboard.courses.total_sub_courses} sub-modules, ${dashboard.courses.total_videos} videos`} deltaPositive /> { if (location.pathname.includes("/content/human-language/") && location.pathname.includes("/sub-module/")) { - return `/content/human-language/${categoryId}/${courseId}/sub-module/${subCourseId}` + return `/content/human-language/${categoryId}/${courseId}/sub-module/${subModuleId}` } if (source === "human-language") return "/content/human-language" - return `/content/category/${categoryId}/courses/${courseId}/sub-courses/${subCourseId}` - }, [location.pathname, source, categoryId, courseId, subCourseId]) + return `/content/category/${categoryId}/courses/${courseId}/sub-modules/${subModuleId}` + }, [location.pathname, source, categoryId, courseId, subModuleId]) const [currentStep, setCurrentStep] = useState(1) const [saving, setSaving] = useState(false) @@ -312,7 +312,7 @@ export function AddNewPracticePage() { title: practiceTitle || "Untitled Practice", set_type: "PRACTICE", owner_type: "SUB_MODULE", - owner_id: Number(subCourseId), + owner_id: Number(subModuleId), ...(practiceDescription.trim() ? { description: practiceDescription.trim() } : {}), ...(persona?.name ? { persona: persona.name } : {}), shuffle_questions: shuffleQuestions, diff --git a/src/pages/content-management/AllCoursesPage.tsx b/src/pages/content-management/AllCoursesPage.tsx index ac0247d..10c44de 100644 --- a/src/pages/content-management/AllCoursesPage.tsx +++ b/src/pages/content-management/AllCoursesPage.tsx @@ -332,7 +332,7 @@ export function AllCoursesPage() { className="group cursor-pointer" onClick={() => navigate( - `/content/category/${course.category_id}/courses/${course.id}/sub-courses`, + `/content/category/${course.category_id}/courses/${course.id}/sub-modules`, ) } > @@ -393,7 +393,7 @@ export function AllCoursesPage() { onClick={(e) => { e.stopPropagation() navigate( - `/content/category/${course.category_id}/courses/${course.id}/sub-courses`, + `/content/category/${course.category_id}/courses/${course.id}/sub-modules`, ) }} > diff --git a/src/pages/content-management/CourseFlowBuilderPage.tsx b/src/pages/content-management/CourseFlowBuilderPage.tsx index 146306c..f2e3bf4 100644 --- a/src/pages/content-management/CourseFlowBuilderPage.tsx +++ b/src/pages/content-management/CourseFlowBuilderPage.tsx @@ -34,10 +34,10 @@ import { getCoursesByCategory, getLearningPath, getQuestionSetsByOwner, - getSubCourseEntryAssessment, + getSubModuleEntryAssessment, reorderCategories, reorderCourses, - reorderSubCourses, + reorderSubModules, reorderVideos, reorderPractices, } from "../../api/courses.api" @@ -320,7 +320,7 @@ export function CourseFlowBuilderPage() { try { const [setsRes, entryRes] = await Promise.allSettled([ getQuestionSetsByOwner("SUB_COURSE", subCourseId), - getSubCourseEntryAssessment(subCourseId), + getSubModuleEntryAssessment(subCourseId), ]) // No practice sets is a valid empty-state scenario; do not toast for 404/empty. @@ -429,12 +429,12 @@ export function CourseFlowBuilderPage() { })) const previous = items setLearningPath((prev) => (prev ? { ...prev, sub_courses: reordered } : prev)) - setSavingKey("sub-courses") + setSavingKey("sub-modules") try { - await reorderSubCourses(toReorderItems(reordered)) + await reorderSubModules(toReorderItems(reordered)) } catch (err: any) { setLearningPath((prev) => (prev ? { ...prev, sub_courses: previous } : prev)) - toast.error(err?.response?.data?.message || "Failed to reorder sub-courses.") + toast.error(err?.response?.data?.message || "Failed to reorder sub-modules.") } finally { setSavingKey(null) } diff --git a/src/pages/content-management/CoursesPage.tsx b/src/pages/content-management/CoursesPage.tsx index f1a5aca..e081157 100644 --- a/src/pages/content-management/CoursesPage.tsx +++ b/src/pages/content-management/CoursesPage.tsx @@ -245,7 +245,7 @@ export function CoursesPage() { } const handleCourseClick = (courseId: number) => { - navigate(`/content/category/${categoryId}/courses/${courseId}/sub-courses`) + navigate(`/content/category/${categoryId}/courses/${courseId}/sub-modules`) } const handleViewRatings = async (courseId: number) => { diff --git a/src/pages/content-management/HumanLanguagePage.tsx b/src/pages/content-management/HumanLanguagePage.tsx index a976902..d6e2ec9 100644 --- a/src/pages/content-management/HumanLanguagePage.tsx +++ b/src/pages/content-management/HumanLanguagePage.tsx @@ -37,7 +37,7 @@ import { createHumanLanguageLesson, deleteQuestionSet, deleteQuestion, - deleteSubCourse, + deleteSubModule, getHumanLanguageHierarchy, getQuestionById, getPracticeQuestions, @@ -569,7 +569,7 @@ export function HumanLanguagePage() { setDeletingKey(key) try { for (const id of ids) { - await deleteSubCourse(id) + await deleteSubModule(id) } toast.success(successMessage) await loadHierarchy() diff --git a/src/pages/content-management/HumanLanguageSubModulePage.tsx b/src/pages/content-management/HumanLanguageSubModulePage.tsx index 2f2c79f..6408dac 100644 --- a/src/pages/content-management/HumanLanguageSubModulePage.tsx +++ b/src/pages/content-management/HumanLanguageSubModulePage.tsx @@ -8,9 +8,9 @@ import { Badge } from "../../components/ui/badge" import { Button } from "../../components/ui/button" import { Input } from "../../components/ui/input" import { - getSubCoursesByCourse, + getSubModulesByCourse, getQuestionSetsByOwner, - getVideosBySubCourse, + getVideosBySubModule, updatePractice, deleteQuestionSet, createCourseVideo, @@ -34,10 +34,10 @@ type StatusFilter = "all" | "published" | "draft" | "archived" /** Human Language–only sub-module editor: lesson (videos) + practice tabs; not used by general course flows. */ export function HumanLanguageSubModulePage() { - const { categoryId, courseId, subCourseId } = useParams<{ + const { categoryId, courseId, subModuleId } = useParams<{ categoryId: string courseId: string - subCourseId: string + subModuleId: string }>() const navigate = useNavigate() @@ -92,12 +92,12 @@ export function HumanLanguageSubModulePage() { useEffect(() => { const fetchData = async () => { - if (!subCourseId || !courseId) return + if (!subModuleId || !courseId) return try { - const subCoursesRes = await getSubCoursesByCourse(Number(courseId)) + const subCoursesRes = await getSubModulesByCourse(Number(courseId)) const foundSubCourse = subCoursesRes.data.data.sub_courses?.find( - (sc) => sc.id === Number(subCourseId) + (sc) => sc.id === Number(subModuleId) ) setSubCourse(foundSubCourse ?? null) } catch (err) { @@ -109,13 +109,13 @@ export function HumanLanguageSubModulePage() { } fetchData() - }, [subCourseId, courseId]) + }, [subModuleId, courseId]) const fetchPractices = async () => { - if (!subCourseId) return + if (!subModuleId) return setPracticesLoading(true) try { - const res = await getQuestionSetsByOwner("SUB_MODULE", Number(subCourseId)) + const res = await getQuestionSetsByOwner("SUB_MODULE", Number(subModuleId)) const raw = res.data.data const list = Array.isArray(raw) ? raw : raw?.question_sets ?? [] setPractices(list) @@ -127,10 +127,10 @@ export function HumanLanguageSubModulePage() { } const fetchVideos = async () => { - if (!subCourseId) return + if (!subModuleId) return setVideosLoading(true) try { - const res = await getVideosBySubCourse(Number(subCourseId)) + const res = await getVideosBySubModule(Number(subModuleId)) setVideos(res.data.data.videos ?? []) } catch (err) { console.error("Failed to fetch videos:", err) @@ -145,10 +145,10 @@ export function HumanLanguageSubModulePage() { } else if (activeTab === "lesson") { fetchVideos() } - }, [activeTab, subCourseId]) + }, [activeTab, subModuleId]) const handleAddPractice = () => { - navigate(`/content/human-language/${categoryId}/${courseId}/sub-module/${subCourseId}/add-practice`) + navigate(`/content/human-language/${categoryId}/${courseId}/sub-module/${subModuleId}/add-practice`) } @@ -207,7 +207,7 @@ export function HumanLanguageSubModulePage() { } const handlePracticeClick = (practiceId: number) => { - navigate(`/content/human-language/${categoryId}/${courseId}/sub-module/${subCourseId}/practices/${practiceId}/questions`) + navigate(`/content/human-language/${categoryId}/${courseId}/sub-module/${subModuleId}/practices/${practiceId}/questions`) } const handleAddVideo = () => { @@ -248,7 +248,7 @@ export function HumanLanguageSubModulePage() { } const handleSaveNewVideo = async () => { - if (!subCourseId || !videoFile) return + if (!subModuleId || !videoFile) return setSaving(true) setSaveError(null) try { @@ -270,7 +270,7 @@ export function HumanLanguageSubModulePage() { const finalTitle = videoTitle.trim() || videoFile.name await createCourseVideo({ - sub_course_id: Number(subCourseId), + sub_module_id: Number(subModuleId), title: finalTitle, description: videoDescription.trim(), video_url: finalVideoUrl, diff --git a/src/pages/content-management/PracticeQuestionsPage.tsx b/src/pages/content-management/PracticeQuestionsPage.tsx index 55549e2..5de5680 100644 --- a/src/pages/content-management/PracticeQuestionsPage.tsx +++ b/src/pages/content-management/PracticeQuestionsPage.tsx @@ -58,7 +58,7 @@ const typeColors: Record = { } export function PracticeQuestionsPage() { - const { categoryId, courseId, subCourseId, practiceId } = useParams() + const { categoryId, courseId, subModuleId, practiceId } = useParams() const location = useLocation() const [questions, setQuestions] = useState([]) @@ -103,10 +103,10 @@ export function PracticeQuestionsPage() { const backLink = useMemo(() => { if (location.pathname.includes("/content/human-language/") && location.pathname.includes("/sub-module/")) { - return `/content/human-language/${categoryId}/${courseId}/sub-module/${subCourseId}` + return `/content/human-language/${categoryId}/${courseId}/sub-module/${subModuleId}` } - return `/content/category/${categoryId}/courses/${courseId}/sub-courses/${subCourseId}` - }, [location.pathname, categoryId, courseId, subCourseId]) + return `/content/category/${categoryId}/courses/${courseId}/sub-modules/${subModuleId}` + }, [location.pathname, categoryId, courseId, subModuleId]) const buildDefaultOptions = (type: QuestionType, sampleAnswerText?: string): DraftOption[] => { if (type === "TRUE_FALSE") { diff --git a/src/pages/content-management/SpeakingPage.tsx b/src/pages/content-management/SpeakingPage.tsx index ee37608..3bc4d87 100644 --- a/src/pages/content-management/SpeakingPage.tsx +++ b/src/pages/content-management/SpeakingPage.tsx @@ -11,7 +11,7 @@ import { getCourseCategories, getCoursesByCategory, getQuestionById, - getSubCoursesByCourse, + getSubModulesByCourse, createQuestion, createQuestionSet, // getQuestions, @@ -441,7 +441,7 @@ export function SpeakingPage() { const subCourseResponses = await Promise.all( courseRecords.map(async ({ category, course }) => { - const res = await getSubCoursesByCourse(course.id) + const res = await getSubModulesByCourse(course.id) const subCourses = res.data?.data?.sub_courses ?? [] return subCourses.map((subCourse: SubCourse) => ({ id: subCourse.id, diff --git a/src/pages/content-management/SubCourseContentPage.tsx b/src/pages/content-management/SubCourseContentPage.tsx index 9f7d899..fd3024c 100644 --- a/src/pages/content-management/SubCourseContentPage.tsx +++ b/src/pages/content-management/SubCourseContentPage.tsx @@ -8,9 +8,9 @@ import { Badge } from "../../components/ui/badge" import { Button } from "../../components/ui/button" import { Input } from "../../components/ui/input" import { - getSubCoursesByCourse, + getSubModulesByCourse, getQuestionSetsByOwner, - getVideosBySubCourse, + getVideosBySubModule, updatePractice, deleteQuestionSet, createCourseVideo, @@ -34,11 +34,11 @@ import { SpinnerIcon } from "../../components/ui/spinner-icon" type TabType = "video" | "practice" | "ratings" type StatusFilter = "all" | "published" | "draft" | "archived" -export function SubCourseContentPage() { - const { categoryId, courseId, subCourseId } = useParams<{ +export function SubModuleContentPage() { + const { categoryId, courseId, subModuleId } = useParams<{ categoryId: string courseId: string - subCourseId: string + subModuleId: string }>() const navigate = useNavigate() @@ -99,12 +99,12 @@ export function SubCourseContentPage() { useEffect(() => { const fetchData = async () => { - if (!subCourseId || !courseId) return + if (!subModuleId || !courseId) return try { - const subCoursesRes = await getSubCoursesByCourse(Number(courseId)) + const subCoursesRes = await getSubModulesByCourse(Number(courseId)) const foundSubCourse = subCoursesRes.data.data.sub_courses?.find( - (sc) => sc.id === Number(subCourseId) + (sc) => sc.id === Number(subModuleId) ) setSubCourse(foundSubCourse ?? null) } catch (err) { @@ -116,13 +116,13 @@ export function SubCourseContentPage() { } fetchData() - }, [subCourseId, courseId]) + }, [subModuleId, courseId]) const fetchPractices = async () => { - if (!subCourseId) return + if (!subModuleId) return setPracticesLoading(true) try { - const res = await getQuestionSetsByOwner("SUB_MODULE", Number(subCourseId)) + const res = await getQuestionSetsByOwner("SUB_MODULE", Number(subModuleId)) setPractices(res.data.data ?? []) } catch (err) { console.error("Failed to fetch practices:", err) @@ -132,10 +132,10 @@ export function SubCourseContentPage() { } const fetchVideos = async () => { - if (!subCourseId) return + if (!subModuleId) return setVideosLoading(true) try { - const res = await getVideosBySubCourse(Number(subCourseId)) + const res = await getVideosBySubModule(Number(subModuleId)) setVideos(res.data.data.videos ?? []) } catch (err) { console.error("Failed to fetch videos:", err) @@ -145,12 +145,12 @@ export function SubCourseContentPage() { } const fetchRatings = async (offset = 0) => { - if (!subCourseId) return + if (!subModuleId) return setRatingsLoading(true) try { const res = await getRatings({ target_type: "sub_course", - target_id: Number(subCourseId), + target_id: Number(subModuleId), limit: ratingsPageSize, offset, }) @@ -170,7 +170,7 @@ export function SubCourseContentPage() { } else if (activeTab === "ratings") { fetchRatings(ratingsPage * ratingsPageSize) } - }, [activeTab, subCourseId]) + }, [activeTab, subModuleId]) useEffect(() => { if (activeTab === "ratings") { @@ -179,7 +179,7 @@ export function SubCourseContentPage() { }, [ratingsPage]) const handleAddPractice = () => { - navigate(`/content/category/${categoryId}/courses/${courseId}/sub-courses/${subCourseId}/add-practice`) + navigate(`/content/category/${categoryId}/courses/${courseId}/sub-modules/${subModuleId}/add-practice`) } @@ -238,7 +238,7 @@ export function SubCourseContentPage() { } const handlePracticeClick = (practiceId: number) => { - navigate(`/content/category/${categoryId}/courses/${courseId}/sub-courses/${subCourseId}/practices/${practiceId}/questions`) + navigate(`/content/category/${categoryId}/courses/${courseId}/sub-modules/${subModuleId}/practices/${practiceId}/questions`) } const handleAddVideo = () => { @@ -279,7 +279,7 @@ export function SubCourseContentPage() { } const handleSaveNewVideo = async () => { - if (!subCourseId || !videoFile) return + if (!subModuleId || !videoFile) return setSaving(true) setSaveError(null) try { @@ -301,7 +301,7 @@ export function SubCourseContentPage() { const finalTitle = videoTitle.trim() || videoFile.name await createCourseVideo({ - sub_course_id: Number(subCourseId), + sub_module_id: Number(subModuleId), title: finalTitle, description: videoDescription.trim(), video_url: finalVideoUrl, @@ -444,7 +444,7 @@ export function SubCourseContentPage() {
{/* Back Button */} diff --git a/src/pages/content-management/SubCoursesPage.tsx b/src/pages/content-management/SubCoursesPage.tsx index 4f67b72..c2f708f 100644 --- a/src/pages/content-management/SubCoursesPage.tsx +++ b/src/pages/content-management/SubCoursesPage.tsx @@ -24,16 +24,16 @@ import alertSrc from "../../assets/Alert.svg"; import { Badge } from "../../components/ui/badge"; import { Button } from "../../components/ui/button"; import { - getSubCoursesByCourse, + getSubModulesByCourse, getCoursesByCategory, getCourseCategories, - createSubCourse, - updateSubCourse, - updateSubCourseStatus, - deleteSubCourse, - getSubCoursePrerequisites, - addSubCoursePrerequisite, - removeSubCoursePrerequisite, + createSubModule, + updateSubModule, + updateSubModuleStatus, + deleteSubModule, + getSubModulePrerequisites, + addSubModulePrerequisite, + removeSubModulePrerequisite, } from "../../api/courses.api"; import { uploadImageFile } from "../../api/files.api"; import { Input } from "../../components/ui/input"; @@ -47,7 +47,7 @@ import type { import { SpinnerIcon } from "../../components/ui/spinner-icon"; import { toast } from "sonner"; -export function SubCoursesPage() { +export function SubModulesPage() { const { categoryId, courseId } = useParams<{ categoryId: string; courseId: string; @@ -122,10 +122,10 @@ export function SubCoursesPage() { if (!courseId) return; try { - const subCoursesRes = await getSubCoursesByCourse(Number(courseId)); + const subCoursesRes = await getSubModulesByCourse(Number(courseId)); setSubCourses(subCoursesRes.data.data.sub_courses ?? []); } catch (err) { - console.error("Failed to fetch sub-courses:", err); + console.error("Failed to fetch sub-modules:", err); } }; @@ -135,7 +135,7 @@ export function SubCoursesPage() { try { const results = await Promise.all( scs.map((sc) => - getSubCoursePrerequisites(sc.id).then((res) => ({ + getSubModulePrerequisites(sc.id).then((res) => ({ id: sc.id, data: res.data.data ?? [], })), @@ -159,7 +159,7 @@ export function SubCoursesPage() { try { const [subCoursesRes, coursesRes, categoriesRes] = await Promise.all([ - getSubCoursesByCourse(Number(courseId)), + getSubModulesByCourse(Number(courseId)), getCoursesByCategory(Number(categoryId)), getCourseCategories(), ]); @@ -176,7 +176,7 @@ export function SubCoursesPage() { ); setCategory(foundCategory ?? null); } catch (err) { - console.error("Failed to fetch sub-courses:", err); + console.error("Failed to fetch sub-modules:", err); setError("Failed to load courses"); } finally { setLoading(false); @@ -195,7 +195,7 @@ export function SubCoursesPage() { const handleToggleStatus = async (subCourse: SubCourse) => { setTogglingId(subCourse.id); try { - await updateSubCourseStatus(subCourse.id, { + await updateSubModuleStatus(subCourse.id, { is_active: !subCourse.is_active, level: subCourse.level, title: subCourse.title, @@ -218,7 +218,7 @@ export function SubCoursesPage() { setDeleting(true); try { - await deleteSubCourse(subCourseToDelete.id); + await deleteSubModule(subCourseToDelete.id); setShowDeleteModal(false); setSubCourseToDelete(null); await fetchSubCourses(); @@ -264,7 +264,7 @@ export function SubCoursesPage() { ? parsedOrder : nextSubCourseDisplayOrder(); - await createSubCourse({ + await createSubModule({ course_id: Number(courseId), title: title.trim(), description: description.trim(), @@ -309,7 +309,7 @@ export function SubCoursesPage() { setSaving(true); setSaveError(null); try { - await updateSubCourse(subCourseToEdit.id, { + await updateSubModule(subCourseToEdit.id, { title, description, level, @@ -328,9 +328,9 @@ export function SubCoursesPage() { } }; - const handleSubCourseClick = (subCourseId: number) => { + const handleSubModuleClick = (subModuleId: number) => { navigate( - `/content/category/${categoryId}/courses/${courseId}/sub-courses/${subCourseId}`, + `/content/category/${categoryId}/courses/${courseId}/sub-modules/${subModuleId}`, ); }; @@ -340,7 +340,7 @@ export function SubCoursesPage() { setPrereqLoading(true); setSelectedPrereqId(0); try { - const res = await getSubCoursePrerequisites(subCourse.id); + const res = await getSubModulePrerequisites(subCourse.id); setPrerequisites(res.data.data ?? []); } catch (err) { console.error("Failed to fetch prerequisites:", err); @@ -354,10 +354,10 @@ export function SubCoursesPage() { if (!prereqSubCourse || !selectedPrereqId) return; setPrereqAdding(true); try { - await addSubCoursePrerequisite(prereqSubCourse.id, { + await addSubModulePrerequisite(prereqSubCourse.id, { prerequisite_sub_course_id: selectedPrereqId, }); - const res = await getSubCoursePrerequisites(prereqSubCourse.id); + const res = await getSubModulePrerequisites(prereqSubCourse.id); setPrerequisites(res.data.data ?? []); setSelectedPrereqId(0); } catch (err) { @@ -371,8 +371,8 @@ export function SubCoursesPage() { if (!prereqSubCourse) return; setPrereqRemoving(prereqId); try { - await removeSubCoursePrerequisite(prereqSubCourse.id, prereqId); - const res = await getSubCoursePrerequisites(prereqSubCourse.id); + await removeSubModulePrerequisite(prereqSubCourse.id, prereqId); + const res = await getSubModulePrerequisites(prereqSubCourse.id); setPrerequisites(res.data.data ?? []); } catch (err) { console.error("Failed to remove prerequisite:", err); @@ -385,7 +385,7 @@ export function SubCoursesPage() { const flowLayers = (() => { if (subCourses.length === 0) return []; - // Find sub-courses with no prerequisites (roots) + // Find sub-modules with no prerequisites (roots) const hasPrereqs = new Set(); const isPrereqOf = new Map(); // prereqId -> [subCourseIds that depend on it] @@ -557,7 +557,7 @@ export function SubCoursesPage() { handleSubCourseClick(subCourse.id)} + onClick={() => handleSubModuleClick(subCourse.id)} > {/* Thumbnail with level badge */}
@@ -738,7 +738,7 @@ export function SubCoursesPage() {
)} - {/* Add Sub-course Modal — POST /course-management/sub-courses */} + {/* Add Sub-module Modal */} {showAddModal && (