From e5d1ba9b8dd29563996389f22e6e9b47cbea8224 Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Tue, 14 Apr 2026 06:22:36 -0700 Subject: [PATCH] add category delete UI and streamline module creation loading Add deletion controls for course categories and avoid full-page loading overlay during module/sub-module creation so button-level spinners remain the only loading indicator. Made-with: Cursor --- src/api/courses.api.ts | 3 + .../content-management/CourseCategoryPage.tsx | 73 +++++++++++++++++-- .../content-management/HumanLanguagePage.tsx | 10 +-- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/api/courses.api.ts b/src/api/courses.api.ts index 2b03358..98d4752 100644 --- a/src/api/courses.api.ts +++ b/src/api/courses.api.ts @@ -153,6 +153,9 @@ export const createCourseCategory = (data: CreateCourseCategoryRequest) => ? http.post("/course-management/sub-categories", { category_id: data.parent_id, name: data.name }) : http.post("/course-management/categories", { name: data.name }) +export const deleteCourseCategory = (categoryId: number) => + http.delete(`/course-management/categories/${categoryId}`) + export const deleteCourseSubCategory = (subCategoryId: number) => http.delete(`/course-management/sub-categories/${subCategoryId}`) diff --git a/src/pages/content-management/CourseCategoryPage.tsx b/src/pages/content-management/CourseCategoryPage.tsx index 6cbb4f8..08f04b5 100644 --- a/src/pages/content-management/CourseCategoryPage.tsx +++ b/src/pages/content-management/CourseCategoryPage.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react" import { Link } from "react-router-dom" -import { FolderOpen, RefreshCw, BookOpen, Plus } from "lucide-react" +import { FolderOpen, RefreshCw, BookOpen, Plus, Trash2 } from "lucide-react" import spinnerSrc from "../../assets/Circular-indeterminate progress indicator.svg" import alertSrc from "../../assets/Alert.svg" import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card" @@ -11,10 +11,11 @@ import { Dialog, DialogContent, DialogDescription, + DialogFooter, DialogHeader, DialogTitle, } from "../../components/ui/dialog" -import { getCourseCategories, createCourseCategory } from "../../api/courses.api" +import { getCourseCategories, createCourseCategory, deleteCourseCategory } from "../../api/courses.api" import type { CourseCategory } from "../../types/course.types" import { toast } from "sonner" @@ -29,6 +30,8 @@ export function CourseCategoryPage() { const [newSubCategoryName, setNewSubCategoryName] = useState("") const [pendingSubCategories, setPendingSubCategories] = useState([]) const [searchQuery, setSearchQuery] = useState("") + const [deleteTarget, setDeleteTarget] = useState(null) + const [deleting, setDeleting] = useState(false) const fetchCategories = async () => { setLoading(true) @@ -164,12 +167,26 @@ export function CourseCategoryPage() { - - View Sub-categories - - → +
+ + View Sub-categories + + → + - + +
@@ -335,7 +352,7 @@ export function CourseCategoryPage() { if (createdCategoryId && pendingSubCategories.length > 0) { await Promise.all( pendingSubCategories.map((subName) => - createCourseCategory({ name: subName }), + createCourseCategory({ name: subName, parent_id: createdCategoryId }), ), ) } @@ -371,6 +388,46 @@ export function CourseCategoryPage() { + + !open && setDeleteTarget(null)}> + + + Delete category? + + {deleteTarget + ? `This will permanently delete "${deleteTarget.name}" and all linked sub-categories/courses.` + : ""} + + + + + + + + ) } diff --git a/src/pages/content-management/HumanLanguagePage.tsx b/src/pages/content-management/HumanLanguagePage.tsx index d746e9f..64a4087 100644 --- a/src/pages/content-management/HumanLanguagePage.tsx +++ b/src/pages/content-management/HumanLanguagePage.tsx @@ -380,8 +380,8 @@ export function HumanLanguagePage() { label?: string, ) => - const loadHierarchy = async () => { - setLoading(true) + const loadHierarchy = async (showLoading = true) => { + if (showLoading) setLoading(true) try { const res = await getHumanLanguageHierarchy() const data = res.data?.data @@ -404,7 +404,7 @@ export function HumanLanguagePage() { setCollapsedModuleIds(moduleIds) setCollapsedSubModuleIds(subModuleIds) } finally { - setLoading(false) + if (showLoading) setLoading(false) } } @@ -525,7 +525,7 @@ export function HumanLanguagePage() { const next = nextMissingPositive(usedNumbers) const title = `Module-${next}` await createModuleInLevel(levelNode.level_id, title, `${level} ${title}`, next) - await loadHierarchy() + await loadHierarchy(false) } catch (error) { console.error("Failed to create module:", error) toast.error("Failed to create module") @@ -556,7 +556,7 @@ export function HumanLanguagePage() { const next = nextMissingPositive(usedNumbers) const title = `Module-${moduleNo}.${next}` await createSubModuleInModule(moduleId, title, `${level} ${title}`, next) - await loadHierarchy() + await loadHierarchy(false) } catch (error) { console.error("Failed to create sub-module:", error) toast.error("Failed to create sub-module")