Skip to main content

Overview

Effective content management is crucial for maintaining high-quality courses. SkillRise provides intuitive tools to structure, organize, and update your course content.

Content Hierarchy

Understand the three-level structure:
Course (Top level)
├── Chapter 1 (Section/Module)
│   ├── Lecture 1 (Video content)
│   ├── Lecture 2
│   └── Lecture 3
├── Chapter 2
│   ├── Lecture 1
│   └── Lecture 2
└── Chapter 3
    └── Lecture 1

Chapter Management

Creating Chapters

Chapters organize your course into logical sections:
1

Click Add Chapter

Find the “Add Chapter” button at the bottom of your content list
2

Enter Title

Type a descriptive chapter name (e.g., “Introduction to React Hooks”)
3

Confirm

Press Enter or click “Add” to create the chapter
Chapter Structure:
{
  chapterId: "uuid-v4-format",
  chapterTitle: "Chapter 1: Getting Started",
  chapterOrder: 1,
  chapterContent: [], // Array of lectures
  collapsed: false // UI state (not stored in database)
}

Renaming Chapters

Quickly update chapter titles:
Double-click on any chapter title to enter edit mode. Type the new name and press Enter to save.
// Edit mode trigger
<button
  onDoubleClick={() => {
    setEditingChapterId(chapter.chapterId)
    setEditingChapterTitle(chapter.chapterTitle)
  }}
>
  {chapter.chapterTitle}
</button>

Collapsing Chapters

Manage large courses by collapsing chapters:
  • Click the arrow icon next to the chapter number
  • Collapsed chapters hide all lectures (UI only)
  • Useful when working with many chapters
  • State is not persisted (resets on page refresh)

Deleting Chapters

Destructive Action: Deleting a chapter removes all its lectures permanently. This action cannot be undone.
To delete a chapter:
  1. Click the trash icon on the chapter header
  2. Chapter and all its lectures are immediately removed
  3. Remaining chapters maintain their order
const removeChapter = (id) => {
  setChapters((prev) => prev.filter((c) => c.chapterId !== id))
}

Lecture Management

Adding Lectures

Lectures are the core content of your course:
1

Expand Chapter

Ensure the target chapter is expanded (not collapsed)
2

Click Add Lecture

Find the “Add Lecture” button at the bottom of the chapter
3

Fill Modal

Complete all required fields in the lecture modal
4

Save

Click “Add Lecture” to insert the video into the chapter

Lecture Properties

lectureTitle
string
required
Lecture TitleDescriptive name for the video lesson
  • Keep concise but informative
  • Include key topics covered
  • Example: “Introduction to React Hooks”
lectureDuration
number
required
Duration (minutes)Length of the video in minutes
  • Must be a valid number
  • Used for total course duration calculation
  • Displayed to students as “15m”, “2h 30m”, etc.
lectureUrl
url
required
Video URLLink to hosted video content
  • Supports: YouTube, Vimeo, direct video links
  • Must be publicly accessible
  • Validated for proper URL format
  • Example: https://youtube.com/watch?v=abc123
isPreviewFree
boolean
default:"false"
Free PreviewAllow non-enrolled students to watch
  • Toggle on/off in the lecture modal
  • Free lectures show a “FREE” badge
  • Useful for introduction or overview lectures
  • Helps students sample your teaching style

Lecture Data Structure

const lecture = {
  lectureId: "8f4e5d3a-1b2c-4d5e-8f7g-9h0i1j2k3l4m",
  lectureTitle: "Understanding State Management",
  lectureDuration: 22, // minutes
  lectureUrl: "https://youtube.com/watch?v=abc123",
  isPreviewFree: false,
  lectureOrder: 1 // Position within chapter
}

Lecture Display

Lectures are displayed with visual indicators:
🎬 1. Understanding State Management    22m
  • Play icon
  • Lecture number and title
  • Duration badge

Deleting Lectures

Remove individual lectures without affecting others:
  1. Hover over the lecture row
  2. Click the X icon that appears
  3. Lecture is immediately removed from the chapter
const removeLecture = (chapterId, lectureIndex) => {
  setChapters((prev) =>
    prev.map((chapter) => {
      if (chapter.chapterId !== chapterId) return chapter
      
      const content = [...chapter.chapterContent]
      content.splice(lectureIndex, 1) // Remove at index
      
      return { ...chapter, chapterContent: content }
    })
  )
}

Media Upload

Supported Video Platforms

YouTube

Most common choice for educators
  • Free hosting
  • Reliable streaming
  • Built-in player

Vimeo

Professional video hosting
  • Ad-free playback
  • Better privacy controls
  • Higher quality options

Direct Links

Self-hosted or CDN videos
  • Full control
  • Custom players
  • Requires own hosting

Video URL Validation

The system validates URLs before accepting:
try {
  new URL(details.lectureUrl) // Throws if invalid
  // URL is valid, proceed
} catch {
  toast.error('Please enter a valid URL')
  return
}
Video URLs must start with http:// or https:// and follow standard URL format.

Thumbnail Upload

Course thumbnails are uploaded separately: Upload Process:
1

Select File

Drag and drop or click to browse for image file
2

Client Preview

Image is previewed locally using URL.createObjectURL()
3

Server Upload

On course submission, file is sent to backend
4

Cloudinary Processing

Backend uploads to Cloudinary CDN
5

URL Stored

Cloudinary secure URL is saved to database
// Backend thumbnail processing
const imageUpload = await cloudinary.uploader.upload(imageFile.path)
newCourse.courseThumbnail = imageUpload.secure_url
await newCourse.save()
Image Requirements:
  • Formats: PNG, JPG, WEBP
  • Recommended: 1280 × 720 pixels (16:9 ratio)
  • Clear, high-contrast design
  • No blur or pixelation

Content Organization Best Practices

Chapter Structuring

  • Start with “Getting Started” or “Introduction” chapter
  • Include setup/installation in first chapter
  • Progress from basic to advanced concepts
  • End with “Next Steps” or “Conclusion” chapter
  • Brief prerequisites chapter
  • Dive into core topics immediately
  • Group related advanced concepts
  • Include real-world project chapters
  • Planning and setup chapter
  • Feature-by-feature chapters
  • Testing and deployment chapter
  • Improvements and extensions chapter

Lecture Optimization

5-20 Minutes Per Lecture
  • Maintains student attention
  • Easy to fit into busy schedules
  • Natural break points
  • Better completion rates
// Good structure
{
  lectureDuration: 12, // Sweet spot
  lectureTitle: "Focused, single-concept title"
}

Free Preview Strategy

Recommended Free Lectures:
  1. First lecture - Welcome and course overview
  2. Sample lecture - Mid-course example of teaching style
  3. Project demo - Show final result to motivate enrollment
// Strategic free previews
const courseContent = [
  {
    chapterTitle: "Introduction",
    chapterContent: [
      {
        lectureTitle: "Welcome & Course Overview",
        isPreviewFree: true // ✓ First impression
      }
    ]
  },
  {
    chapterTitle: "Core Concepts",
    chapterContent: [
      {
        lectureTitle: "Component Fundamentals",
        isPreviewFree: true // ✓ Show teaching quality
      }
    ]
  },
  {
    chapterTitle: "Final Project",
    chapterContent: [
      {
        lectureTitle: "Project Demo",
        isPreviewFree: true // ✓ Motivate purchase
      }
    ]
  }
]

Auto-Calculated Metrics

The system automatically tracks:

Total Lectures

// Frontend calculation
const totalLectures = chapters.reduce(
  (sum, chapter) => sum + chapter.chapterContent.length,
  0
)

// Backend verification
let totalLectures = 0
courseContent.forEach((chapter) => {
  if (Array.isArray(chapter.chapterContent)) {
    totalLectures += chapter.chapterContent.length
  }
})

Total Duration

// Sum all lecture durations
let totalDurationMinutes = 0

chapters.forEach((chapter) => {
  chapter.chapterContent.forEach((lecture) => {
    totalDurationMinutes += lecture.lectureDuration
  })
})

// Display formatting
const hours = Math.floor(totalDurationMinutes / 60)
const minutes = totalDurationMinutes % 60
const display = hours > 0 
  ? `${hours}h ${minutes}m`
  : `${minutes}m`

Content Completeness

Validation checks ensure quality:
// Section validation
const step3Complete = 
  chapters.length > 0 &&      // At least one chapter
  totalLectures > 0 &&        // At least one lecture
  chapters.every(chapter =>   // All chapters have content
    chapter.chapterContent.length > 0
  )
These metrics update in real-time as you add or remove content.

ID Generation

Unique IDs are generated for chapters and lectures:
const createId = () =>
  typeof crypto !== 'undefined' && crypto.randomUUID
    ? crypto.randomUUID() // Modern browsers
    : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}` // Fallback

// Example IDs:
// "8f4e5d3a-1b2c-4d5e-8f7g-9h0i1j2k3l4m" (UUID v4)
// "l5x8k9m2-a1b2c3d4" (Fallback)
Never modify IDs manually. Always use the system-generated IDs.

Database Schema

Course Model

const courseSchema = new mongoose.Schema({
  // Basic info
  courseTitle: { type: String, required: true },
  courseDescription: { type: String, required: true },
  courseThumbnail: { type: String }, // Cloudinary URL
  coursePrice: { type: Number, required: true },
  discount: { type: Number, required: true, min: 0, max: 100 },
  isPublished: { type: Boolean, default: true },
  
  // Content
  courseContent: [chapterSchema],
  
  // Calculated fields
  totalLectures: { type: Number, default: 0 },
  totalDurationMinutes: { type: Number, default: 0 },
  
  // Relationships
  educatorId: { type: String, ref: 'User', required: true },
  enrolledStudents: [{ type: String, ref: 'User' }],
  
  // Ratings
  courseRatings: [{
    userId: { type: String },
    rating: { type: Number, min: 1, max: 5 }
  }],
  averageRating: { type: Number, default: 0 },
  totalRatings: { type: Number, default: 0 }
}, { timestamps: true })

Chapter Schema

const chapterSchema = new mongoose.Schema({
  chapterId: { type: String, required: true },
  chapterOrder: { type: Number, required: true },
  chapterTitle: { type: String, required: true },
  chapterContent: [lectureSchema]
}, { _id: false })

Lecture Schema

const lectureSchema = new mongoose.Schema({
  lectureId: { type: String, required: true },
  lectureTitle: { type: String, required: true },
  lectureDuration: { type: Number, required: true },
  lectureUrl: { type: String, required: true },
  isPreviewFree: { type: Boolean, required: true },
  lectureOrder: { type: Number, required: true }
}, { _id: false })

Updating Existing Courses

The current implementation creates new courses. To add course editing functionality, you would need to:
  1. Fetch existing course by ID
  2. Pre-populate the form with current data
  3. Use PUT/PATCH endpoint instead of POST
  4. Handle partial updates
  5. Maintain existing enrollment data
Future Edit Implementation:
// Fetch for editing
const { data } = await axios.get(
  `${backendUrl}/api/educator/course/${courseId}`,
  { headers: { Authorization: `Bearer ${token}` } }
)

// Populate form
setCourseTitle(data.course.courseTitle)
setDescription(data.course.courseDescription)
setChapters(data.course.courseContent)
// ... etc

// Update instead of create
const { data } = await axios.put(
  `${backendUrl}/api/educator/course/${courseId}`,
  formData,
  { headers: { Authorization: `Bearer ${token}` } }
)

Troubleshooting

Possible causes:
  • Chapter is collapsed (expand it first)
  • Required fields are empty in modal
  • Video URL is invalid format
  • Duration is not a number
Solution: Ensure all fields are properly filled and validated before clicking “Add Lecture”
Possible causes:
  • Accidentally clicked delete
  • Browser refreshed before saving
  • Network error during submission
Solution: Content is only saved when you click “Publish Course”. Save frequently and verify before refreshing.
Possible causes:
  • Lecture durations entered incorrectly
  • Non-numeric values in duration field
Solution: Verify each lecture’s duration is accurate. The total is calculated automatically.
Possible causes:
  • Video is private/unlisted on hosting platform
  • URL is broken or changed
  • Video was deleted from source
Solution: Test video URLs before adding. Ensure videos are public and accessible.

Next Steps

Monitor Performance

Track course enrollments and revenue

View Students

See who’s enrolled in your courses

Build docs developers (and LLMs) love