+ {/* Header */}
+
+
+
+
+
+ Content Management
+
+
+ Manage courses, speaking exercises, practices, and questions
+
+
+
+
-
+ {/* Tab bar */}
+
+
{tabs.map((t) => (
cn(
- "rounded-lg px-4 py-2 text-sm font-semibold text-grayScale-500 transition",
- "hover:text-brand-600",
- isActive && "bg-brand-500 text-white hover:text-white",
+ "relative whitespace-nowrap rounded-xl px-5 py-2 text-sm font-semibold transition-all duration-200 ease-in-out",
+ "text-grayScale-500 hover:bg-white/80 hover:text-brand-600 hover:shadow-sm",
+ isActive &&
+ "bg-brand-500 text-white shadow-md shadow-brand-500/25 hover:bg-brand-600 hover:text-white",
)
}
>
@@ -33,9 +52,8 @@ export function ContentManagementLayout() {
))}
+ {/* Page content */}
)
}
-
-
diff --git a/src/pages/content-management/ContentOverviewPage.tsx b/src/pages/content-management/ContentOverviewPage.tsx
index 18a29ca..d79d2fe 100644
--- a/src/pages/content-management/ContentOverviewPage.tsx
+++ b/src/pages/content-management/ContentOverviewPage.tsx
@@ -1,11 +1,62 @@
import { useEffect, useState } from "react"
import { Link, useParams } from "react-router-dom"
-import { BookOpen, Mic, Briefcase, HelpCircle, ArrowLeft } from "lucide-react"
+import { BookOpen, Mic, Briefcase, HelpCircle, ArrowLeft, ArrowRight, ChevronRight } from "lucide-react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../components/ui/card"
import { Button } from "../../components/ui/button"
import { getCourseCategories } from "../../api/courses.api"
import type { CourseCategory } from "../../types/course.types"
+const contentSections = [
+ {
+ key: "courses",
+ pathFn: (categoryId: string | undefined) => `/content/category/${categoryId}/courses`,
+ icon: BookOpen,
+ title: "Courses",
+ description: "Manage course videos and educational content",
+ action: "Manage Courses",
+ count: 12,
+ countLabel: "courses",
+ gradient: "from-brand-500/10 via-brand-400/5 to-transparent",
+ accentBorder: "group-hover:border-brand-400",
+ },
+ {
+ key: "speaking",
+ pathFn: () => "/content/speaking",
+ icon: Mic,
+ title: "Speaking",
+ description: "Manage speaking practice sessions and exercises",
+ action: "Manage Speaking",
+ count: 8,
+ countLabel: "sessions",
+ gradient: "from-purple-500/10 via-purple-400/5 to-transparent",
+ accentBorder: "group-hover:border-purple-400",
+ },
+ {
+ key: "practices",
+ pathFn: () => "/content/practices",
+ icon: Briefcase,
+ title: "Practice",
+ description: "Manage practice details, members, and leadership",
+ action: "Manage Practice",
+ count: 5,
+ countLabel: "practices",
+ gradient: "from-indigo-500/10 via-indigo-400/5 to-transparent",
+ accentBorder: "group-hover:border-indigo-400",
+ },
+ {
+ key: "questions",
+ pathFn: () => "/content/questions",
+ icon: HelpCircle,
+ title: "Questions",
+ description: "Manage questions, quizzes, and assessments",
+ action: "Manage Questions",
+ count: 34,
+ countLabel: "questions",
+ gradient: "from-rose-500/10 via-rose-400/5 to-transparent",
+ accentBorder: "group-hover:border-rose-400",
+ },
+] as const
+
export function ContentOverviewPage() {
const { categoryId } = useParams<{ categoryId: string }>()
const [category, setCategory] = useState
(null)
@@ -27,81 +78,114 @@ export function ContentOverviewPage() {
}, [categoryId])
return (
-
-
-
-
-
-
+
+ {/* Header & Breadcrumb */}
+
+
+
+
+
+
+
+
+ Content
+
+
+
+ {category?.name ?? "Overview"}
+
+
+
+
+
{category?.name ?? "Content Management"}
-
-
-
-
-
-
- Courses
- Manage course videos and educational content
-
-
-
-
-
-
-
-
-
-
-
-
- Speaking
- Manage speaking practice sessions and exercises
-
-
-
-
-
-
-
+ {/* Gradient Divider */}
+
-
-
-
-
-
- Practice
- Manage practice details, members, and leadership
-
-
-
-
-
-
-
+ {/* Cards Grid */}
+
+ {contentSections.map((section) => {
+ const Icon = section.icon
+ return (
+
+
+ {/* Subtle gradient background on icon area */}
+
-
-
-
-
-
- Questions
- Manage questions, quizzes, and assessments
-
-
-
-
+
+
+ {/* Icon with gradient ring */}
+
+
+
+
+ {/* Decorative dot */}
+
+
+
+ {/* Count Badge */}
+
+ {section.count} {section.countLabel}
+
+
+
+
+ {section.title}
+
+
+ {section.description}
+
+
+
+
+ {/* Thin separator */}
+
+
+
+ {section.action}
+
+
+
+
-
-
+ )
+ })}
)
}
-
-
diff --git a/src/pages/content-management/CourseCategoryPage.tsx b/src/pages/content-management/CourseCategoryPage.tsx
index a2fd87e..fd44f88 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 } from "lucide-react"
+import { FolderOpen, RefreshCw, AlertCircle, BookOpen } from "lucide-react"
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card"
import { getCourseCategories } from "../../api/courses.api"
import type { CourseCategory } from "../../types/course.types"
@@ -10,63 +10,121 @@ export function CourseCategoryPage() {
const [loading, setLoading] = useState(true)
const [error, setError] = useState
(null)
- useEffect(() => {
- const fetchCategories = async () => {
- try {
- const res = await getCourseCategories()
- setCategories(res.data.data.categories)
- } catch (err) {
- console.error("Failed to fetch categories:", err)
- setError("Failed to load categories")
- } finally {
- setLoading(false)
- }
+ const fetchCategories = async () => {
+ setLoading(true)
+ setError(null)
+ try {
+ const res = await getCourseCategories()
+ setCategories(res.data.data.categories)
+ } catch (err) {
+ console.error("Failed to fetch categories:", err)
+ setError("Failed to load categories")
+ } finally {
+ setLoading(false)
}
+ }
+ useEffect(() => {
fetchCategories()
}, [])
if (loading) {
return (
-
-
Loading categories...
+
)
}
if (error) {
return (
-
-
{error}
+
+
+
+
+
{error}
+
+ Please check your connection and try again
+
+
+
+
)
}
return (
-
-
Course Categories
-
- {categories.map((category) => (
-
-
-
-
-
-
- {category.name}
-
-
-
- View Courses →
-
-
-
-
- ))}
+
+ {/* Page header */}
+
+
Course Categories
+
+ Browse and manage your course categories below
+
- {categories.length === 0 && (
-
No categories found
+ {categories.length === 0 ? (
+
+
+
+
No categories yet
+
+ Course categories will appear here once created. Start by adding your first category.
+
+
+
+ ) : (
+
+ {categories.map((category) => (
+
+
+ {/* Decorative gradient strip */}
+
+
+
+
+
+
+
+ {category.name}
+
+
+
+
+
+ View Courses
+
+ →
+
+
+
+
+
+ ))}
+
)}
)
diff --git a/src/pages/content-management/CoursesPage.tsx b/src/pages/content-management/CoursesPage.tsx
index c68073b..ccb9be2 100644
--- a/src/pages/content-management/CoursesPage.tsx
+++ b/src/pages/content-management/CoursesPage.tsx
@@ -1,6 +1,6 @@
- import { useEffect, useState, useRef } from "react"
+import { useEffect, useState, useRef } from "react"
import { Link, useParams, useNavigate } from "react-router-dom"
-import { Plus, ArrowLeft, BookOpen, ToggleLeft, ToggleRight, X, Trash2, MoreVertical, Edit } from "lucide-react"
+import { Plus, ArrowLeft, BookOpen, ToggleLeft, ToggleRight, X, Trash2, MoreVertical, Edit, RefreshCw, AlertCircle } from "lucide-react"
import { Card, CardContent } from "../../components/ui/card"
import { Button } from "../../components/ui/button"
import { Badge } from "../../components/ui/badge"
@@ -237,55 +237,73 @@ export function CoursesPage() {
if (loading) {
return (
-
-
Loading courses...
+
+
+
+
+
Loading courses...
)
}
if (error) {
return (
-
-
{error}
+
)
}
return (
-
-
-
-
-
-
-
- {category?.name} Courses
-
-
{courses.length} courses available
+ {/* Header */}
+
+
+
+
+
+
+
+
+ {category?.name} Courses
+
+
+ {courses.length} courses available
+
+
+
-
+ {/* Course grid or empty state */}
{courses.length === 0 ? (
-
-
-
- No courses found in this category
-