- {tabs.map(({ id, label, icon: Icon }) => (
-
- ))}
+
+ {/* Content Area */}
+
+ {activeTab === "subscription" && }
+
-
- {/* Tab content */}
- {activeTab === "profile" &&
}
- {activeTab === "security" &&
}
- {activeTab === "notifications" &&
}
- {activeTab === "appearance" &&
}
);
}
diff --git a/src/pages/content-management/ContentManagementLayout.tsx b/src/pages/content-management/ContentManagementLayout.tsx
index 014ad8a..29419a6 100644
--- a/src/pages/content-management/ContentManagementLayout.tsx
+++ b/src/pages/content-management/ContentManagementLayout.tsx
@@ -1,10 +1,11 @@
-import { Outlet } from "react-router-dom"
+import { Outlet } from "react-router-dom";
+import { ContentHierarchyList } from "./components/ContentHierarchyList";
export function ContentManagementLayout() {
return (
-
+
@@ -15,9 +16,11 @@ export function ContentManagementLayout() {
+
+
- )
+ );
}
diff --git a/src/pages/content-management/components/ContentHierarchyList.tsx b/src/pages/content-management/components/ContentHierarchyList.tsx
new file mode 100644
index 0000000..93c98af
--- /dev/null
+++ b/src/pages/content-management/components/ContentHierarchyList.tsx
@@ -0,0 +1,539 @@
+import React, { useState } from "react";
+import {
+ DndContext,
+ closestCenter,
+ KeyboardSensor,
+ PointerSensor,
+ useSensor,
+ useSensors,
+ DragOverlay,
+} from "@dnd-kit/core";
+import type {
+ DragEndEvent,
+ DragStartEvent,
+ UniqueIdentifier,
+} from "@dnd-kit/core";
+import {
+ arrayMove,
+ SortableContext,
+ sortableKeyboardCoordinates,
+ verticalListSortingStrategy,
+ useSortable,
+} from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+import {
+ GripVertical,
+ ChevronDown,
+ ChevronRight,
+ LayoutGrid,
+ BookOpen,
+ Layers,
+ PlayCircle,
+ RotateCcw,
+ Edit2,
+ Trash2,
+ Image as ImageIcon,
+} from "lucide-react";
+import { cn } from "../../../lib/utils";
+
+// --- Types ---
+export type ItemType = "program" | "course" | "module" | "lesson";
+
+export interface BaseItem {
+ id: string;
+ name: string;
+ thumbnail?: string;
+}
+
+export interface Program extends BaseItem {}
+export interface Course extends BaseItem {
+ programId: string;
+}
+export interface Module extends BaseItem {
+ courseId: string;
+}
+export interface Lesson extends BaseItem {
+ moduleId: string;
+}
+
+// --- Mock Data ---
+const initialPrograms: Program[] = [
+ {
+ id: "p1",
+ name: "Web Development Masterclass",
+ thumbnail:
+ "https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=100&h=100&fit=crop",
+ },
+ {
+ id: "p2",
+ name: "Mobile App Development",
+ thumbnail:
+ "https://images.unsplash.com/photo-1512941937669-90a1b58e7e9c?w=100&h=100&fit=crop",
+ },
+ {
+ id: "p3",
+ name: "UI/UX Design Fundamentals",
+ thumbnail:
+ "https://images.unsplash.com/photo-1586717791821-3f44a563eb4c?w=100&h=100&fit=crop",
+ },
+];
+
+const initialCourses: Course[] = [
+ { id: "c1", name: "React for Beginners", programId: "p1" },
+ { id: "c2", name: "Advanced Node.js", programId: "p1" },
+ { id: "c3", name: "Swift UI Intro", programId: "p2" },
+];
+
+const initialModules: Module[] = [
+ { id: "m1", name: "Introduction to Hooks", courseId: "c1" },
+ { id: "m2", name: "State Management", courseId: "c1" },
+ { id: "m3", name: "Backend Architecture", courseId: "c2" },
+];
+
+const initialLessons: Lesson[] = [
+ { id: "l1", name: "What is useState?", moduleId: "m1" },
+ { id: "l2", name: "useEffect deep dive", moduleId: "m1" },
+ { id: "l3", name: "Redux Setup", moduleId: "m2" },
+];
+
+// --- Components ---
+
+interface SortableItemProps {
+ id: string;
+ name: string;
+ icon: React.ReactNode;
+ thumbnail?: string;
+ onEdit?: (id: string) => void;
+ onDelete?: (id: string) => void;
+}
+
+function SortableItem({
+ id,
+ name,
+ icon,
+ thumbnail,
+ onEdit,
+ onDelete,
+}: SortableItemProps) {
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ transform,
+ transition,
+ isDragging,
+ } = useSortable({ id });
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ };
+
+ return (
+
+
+
+
+
+ {/* Thumbnail/Icon Container */}
+
+ {thumbnail ? (
+

+ ) : (
+
+ {icon}
+
+ )}
+
+
+
+
+ {name}
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+interface DraggableListProps {
+ items: BaseItem[];
+ onReorder: (activeId: string, overId: string) => void;
+ icon: React.ReactNode;
+ onEdit?: (id: string) => void;
+ onDelete?: (id: string) => void;
+}
+
+function DraggableList({
+ items,
+ onReorder,
+ icon,
+ onEdit,
+ onDelete,
+}: DraggableListProps) {
+ const [activeId, setActiveId] = useState
(null);
+
+ const sensors = useSensors(
+ useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
+ useSensor(KeyboardSensor, {
+ coordinateGetter: sortableKeyboardCoordinates,
+ }),
+ );
+
+ const handleDragStart = (event: DragStartEvent) =>
+ setActiveId(event.active.id);
+
+ const handleDragEnd = (event: DragEndEvent) => {
+ const { active, over } = event;
+ if (over && active.id !== over.id) {
+ onReorder(active.id as string, over.id as string);
+ }
+ setActiveId(null);
+ };
+
+ const activeItem = items.find((i) => i.id === activeId);
+
+ return (
+
+
+
+ {items.map((item) => (
+
+ ))}
+
+
+
+ {activeItem ? (
+
+
+
+
+
+
+
+ {activeItem.thumbnail ? (
+

+ ) : (
+
{icon}
+ )}
+
+
+ {activeItem.name}
+
+
+
+
+ ) : null}
+
+
+ );
+}
+
+interface SectionProps {
+ title: string;
+ icon: React.ReactNode;
+ isOpen: boolean;
+ onToggle: () => void;
+ children: React.ReactNode;
+}
+
+function HierarchySection({
+ title,
+ icon,
+ isOpen,
+ onToggle,
+ children,
+}: SectionProps) {
+ return (
+
+
+
+
+ );
+}
+
+export function ContentHierarchyList() {
+ const [programs, setPrograms] = useState(initialPrograms);
+ const [courses, setCourses] = useState(initialCourses);
+ const [modules, setModules] = useState(initialModules);
+ const [lessons, setLessons] = useState(initialLessons);
+ const [openSections, setOpenSections] = useState>({
+ program: true,
+ });
+
+ const toggleSection = (id: string) => {
+ setOpenSections((prev) => ({ ...prev, [id]: !prev[id] }));
+ };
+
+ const reorder = (
+ list: T[],
+ setList: React.Dispatch>,
+ activeId: string,
+ overId: string,
+ ) => {
+ const oldIndex = list.findIndex((i) => i.id === activeId);
+ const newIndex = list.findIndex((i) => i.id === overId);
+ if (oldIndex !== -1 && newIndex !== -1) {
+ setList(arrayMove(list, oldIndex, newIndex));
+ }
+ };
+
+ const handleEdit = (type: ItemType, id: string) => {
+ console.log(`Edit ${type}: ${id}`);
+ // Logic for opening edit modal would go here
+ };
+
+ const handleDelete = (type: ItemType, id: string) => {
+ if (!window.confirm(`Are you sure you want to delete this ${type}?`))
+ return;
+
+ switch (type) {
+ case "program":
+ setPrograms((prev) => prev.filter((p) => p.id !== id));
+ break;
+ case "course":
+ setCourses((prev) => prev.filter((c) => c.id !== id));
+ break;
+ case "module":
+ setModules((prev) => prev.filter((m) => m.id !== id));
+ break;
+ case "lesson":
+ setLessons((prev) => prev.filter((l) => l.id !== id));
+ break;
+ }
+ };
+
+ const handleReset = () => {
+ setPrograms(initialPrograms);
+ setCourses(initialCourses);
+ setModules(initialModules);
+ setLessons(initialLessons);
+ };
+
+ return (
+
+
+
+
+ Content Hierarchy
+
+
+ Manage the ordering and structure of your educational content
+
+
+
+
+
+
+ {/* Program Section */}
+
}
+ isOpen={openSections.program}
+ onToggle={() => toggleSection("program")}
+ >
+
+ reorder(programs, setPrograms, active, over)
+ }
+ icon={}
+ onEdit={(id) => handleEdit("program", id)}
+ onDelete={(id) => handleDelete("program", id)}
+ />
+
+
+ {/* Course Section */}
+ }
+ isOpen={openSections.course}
+ onToggle={() => toggleSection("course")}
+ >
+ {programs.map((program) => {
+ const programCourses = courses.filter(
+ (c) => c.programId === program.id,
+ );
+ if (programCourses.length === 0) return null;
+ return (
+
+
+ {program.name}
+
+
+ reorder(courses, setCourses, active, over)
+ }
+ icon={}
+ onEdit={(id) => handleEdit("course", id)}
+ onDelete={(id) => handleDelete("course", id)}
+ />
+
+ );
+ })}
+
+
+ {/* Module Section */}
+ }
+ isOpen={openSections.module}
+ onToggle={() => toggleSection("module")}
+ >
+ {courses.map((course) => {
+ const courseModules = modules.filter(
+ (m) => m.courseId === course.id,
+ );
+ if (courseModules.length === 0) return null;
+ return (
+
+
+ {course.name}
+
+
+ reorder(modules, setModules, active, over)
+ }
+ icon={}
+ onEdit={(id) => handleEdit("module", id)}
+ onDelete={(id) => handleDelete("module", id)}
+ />
+
+ );
+ })}
+
+
+ {/* Lesson Section */}
+ }
+ isOpen={openSections.lesson}
+ onToggle={() => toggleSection("lesson")}
+ >
+ {modules.map((module) => {
+ const moduleLessons = lessons.filter(
+ (l) => l.moduleId === module.id,
+ );
+ if (moduleLessons.length === 0) return null;
+ return (
+
+
+ {module.name}
+
+
+ reorder(lessons, setLessons, active, over)
+ }
+ icon={}
+ onEdit={(id) => handleEdit("lesson", id)}
+ onDelete={(id) => handleDelete("lesson", id)}
+ />
+
+ );
+ })}
+
+
+
+ );
+}