Skip to main content

Overview

The Analytics Dashboard (/analytics) provides comprehensive insights into your learning journey. Track course progress, monitor time spent across different pages, view quiz results, and access completion certificates—all in one centralized location.

Dashboard Overview

The dashboard features a tabbed interface with three main sections: Analytics dashboard showing activity, courses, and quizzes tabs

Activity Tab

Time tracking and usage analytics

My Courses Tab

Enrolled courses with progress bars

Quizzes Tab

Quiz history and performance

Activity Tab

The Activity tab displays detailed time-tracking analytics:

Summary Cards

Three key metrics displayed prominently:
1

Total Time

Aggregate time across all pages
formatDuration(totalDuration)
// Example: "2h 34m"
Shows lifetime platform engagement
2

Active Days

Number of days with activity in last 7 days
const activeDays = dailyStats.filter(d => d.duration > 0).length
// Range: 0-7
Measures learning consistency
3

Most Time Spent

Page with highest total duration
const topPage = pageStats[0]?.page || '—'
const topDuration = pageStats[0]?.totalDuration
Identifies primary learning focus
Three summary cards showing total time, active days, and most visited page

Time by Topic

Horizontal bar chart showing duration per page/topic:
{pageStats.map(({ page, totalDuration, visits }) => {
  const percentage = (totalDuration / maxDuration) * 100
  return (
    <div>
      <div className="flex justify-between">
        <span>{getIcon(page)} {page}</span>
        <span>{formatDuration(totalDuration)}</span>
      </div>
      <div className="progress-bar">
        <div style={{ width: `${percentage}%` }} className={getColor(page)} />
      </div>
      <p className="text-xs">{visits} sessions</p>
    </div>
  )
})}
Page Icons & Colors:
Icon: ▶️Color: bg-teal-500Tracks video watching time

Last 7 Days Chart

Vertical bar chart showing daily activity: Bar chart showing time spent each day over the last 7 days Chart Features:
  • 7 bars - One per day (Mon-Sun)
  • Height - Proportional to time spent
  • Color - Today highlighted in teal, past days lighter
  • Labels - Day abbreviation below each bar
  • Tooltips - Hover shows exact time
  • Weekly total - Sum displayed below chart
Implementation:
const maxDailyDuration = Math.max(...dailyStats.map(d => d.duration), 1)

dailyStats.map(({ date, duration }) => {
  const heightPct = Math.max(
    duration > 0 ? 6 : 0, // Minimum visible height
    (duration / maxDailyDuration) * 100
  )
  const isToday = date === new Date().toISOString().split('T')[0]
  
  return (
    <div className="bar" style={{ height: `${heightPct}%` }}>
      <span className="duration">{formatDuration(duration)}</span>
      <span className={isToday ? 'text-teal-600' : 'text-gray-400'}>
        {new Date(date).toLocaleDateString('en-US', { weekday: 'short' })}
      </span>
    </div>
  )
})

Course Breakdown

Detailed per-course and per-chapter analytics: Expandable course breakdown showing video watch time and quiz performance by chapter Course-level Metrics:
  • Course thumbnail (clickable → player)
  • Course title
  • Total watch time
  • Number of learning sessions
  • Total quiz time across all chapters
Chapter-level Metrics:
  • Chapter title
  • Quiz duration for that chapter
  • Number of quiz attempts
  • Horizontal progress bar
Structure:
{courseBreakdown.map(course => (
  <div>
    <div className="course-header" onClick={() => navigate('/player/' + course.id)}>
      <img src={course.thumbnail} />
      <div>
        <p className="font-semibold">{course.title}</p>
        <div className="text-xs text-gray-500">
          {course.learningDuration > 0 && (
            <span>{formatDuration(course.learningDuration)} watched · 
                  {course.learningSessions} sessions</span>
          )}
          {course.totalQuizDuration > 0 && (
            <span>🧠 {formatDuration(course.totalQuizDuration)} in quizzes</span>
          )}
        </div>
      </div>
    </div>
    
    {course.chapters.map(chapter => (
      <div className="chapter-item">
        <div className="flex justify-between">
          <span className="text-xs">{chapter.title}</span>
          <span className="text-xs font-medium">{formatDuration(chapter.quizDuration)}</span>
        </div>
        <div className="progress-bar h-1.5 bg-gray-100">
          <div className="h-full bg-indigo-400" style={{ width: `${chapter.percentage}%` }} />
        </div>
        <p className="text-xs text-gray-400">
          Quiz · {chapter.quizSessions} {chapter.quizSessions === 1 ? 'attempt' : 'attempts'}
        </p>
      </div>
    ))}
  </div>
))}

Empty State

When no activity exists:
<div className="bg-teal-50 border border-teal-100 rounded-2xl p-8 text-center">
  <p className="text-teal-700 font-medium text-lg">Your analytics will appear here</p>
  <p className="text-teal-600 text-sm">
    Browse courses, watch lectures, and use SkillRise AI — your activity is tracked automatically.
  </p>
</div>

My Courses Tab

Displays all enrolled courses with detailed progress tracking:

Summary Cards

Enrolled

Total number of enrolled courses
enrolledCourses.length

In Progress

Courses with partial completion
progressArray.filter(p => 
  p.lectureCompleted > 0 && 
  p.lectureCompleted < p.totalLectures
).length

Completed

Courses with 100% completion
progressArray.filter(p => 
  p.totalLectures > 0 && 
  p.lectureCompleted === p.totalLectures
).length

Course Cards

Each enrolled course displayed as a card: Course card showing thumbnail, title, progress bar, and continue button Card Contents:
  1. Thumbnail - 16:9 aspect ratio course image
  2. Course Title - Truncated to 2 lines
  3. Progress Information:
    • “X / Y lectures” label
    • Percentage (e.g., “67%”)
    • Animated progress bar
  4. Course Duration - Total length formatted
  5. Action Buttons:
    • “Done” badge if 100% complete
    • “Continue” or “Review” button
Progress calculation:
const total = course.totalLectures || 0
const completed = progressData?.lectureCompleted || 0
const percentage = total > 0 ? Math.round((completed / total) * 100) : 0
const isDone = percentage === 100
Progress bar styling:
<div className="h-1.5 bg-gray-100 rounded-full overflow-hidden">
  <div 
    className={`h-full rounded-full transition-all duration-700 ${
      isDone ? 'bg-teal-500' : 'bg-teal-400'
    }`}
    style={{ width: `${percentage}%` }}
  />
</div>

Loading State

While fetching progress data:
{coursesLoading ? (
  <div className="h-1.5 bg-gray-100 rounded-full animate-pulse" />
) : (
  <ProgressBar value={percentage} />
)}

Empty State

When no courses enrolled:
<div className="bg-white rounded-2xl border p-16 text-center">
  <div className="text-5xl mb-4">🎓</div>
  <h3 className="text-lg font-semibold mb-2">No courses yet</h3>
  <p className="text-sm text-gray-500 mb-6">
    Enroll in a course to start tracking your progress here.
  </p>
  <button onClick={() => navigate('/course-list')}>
    Browse courses
  </button>
</div>

Quizzes Tab

Comprehensive quiz history and performance tracking:

Quiz Result Cards

Each quiz attempt displayed chronologically: Quiz result cards showing scores, performance levels, and chapters Card Structure:
<div className="quiz-result-card">
  <div className="flex items-center justify-between">
    <div>
      <p className="font-semibold">{quiz.courseTitle}</p>
      <p className="text-sm text-gray-500">{quiz.chapterTitle}</p>
    </div>
    <div className="text-right">
      <p className="text-2xl font-bold">{quiz.percentage}%</p>
      <span className={`badge ${getBadgeColor(quiz.group)}`}>
        {quiz.group === 'mastered' && '🏆 Mastered'}
        {quiz.group === 'on_track' && '🎯 On Track'}
        {quiz.group === 'needs_review' && '📖 Needs Review'}
      </span>
    </div>
  </div>
  
  <div className="flex justify-between text-sm text-gray-400 mt-3">
    <span>{quiz.score} / {quiz.total} correct</span>
    <span>{formatDate(quiz.completedAt)}</span>
  </div>
  
  <button onClick={() => navigate(`/player/${quiz.courseId}`)}>
    Review Course →
  </button>
</div>

Performance Groups

Quizzes categorized by performance level:
76-100% correctBadge: Green with trophy icon 🏆Indicates strong understanding

Quiz Statistics

Aggregate metrics at top of tab:
<div className="grid grid-cols-3 gap-4 mb-6">
  <div className="stat-card">
    <p className="text-xs text-gray-500">Total Quizzes</p>
    <p className="text-2xl font-bold">{quizHistory.length}</p>
  </div>
  <div className="stat-card">
    <p className="text-xs text-gray-500">Average Score</p>
    <p className="text-2xl font-bold">
      {Math.round(
        quizHistory.reduce((sum, q) => sum + q.percentage, 0) / quizHistory.length
      )}%
    </p>
  </div>
  <div className="stat-card">
    <p className="text-xs text-gray-500">Mastered</p>
    <p className="text-2xl font-bold">
      {quizHistory.filter(q => q.group === 'mastered').length}
    </p>
  </div>
</div>

Empty State

When no quizzes taken:
<div className="text-center py-16">
  <div className="text-5xl mb-4">📝</div>
  <h3 className="text-lg font-semibold mb-2">No quizzes yet</h3>
  <p className="text-sm text-gray-500">
    Complete course chapters to unlock quizzes and test your knowledge.
  </p>
</div>

Certificate Access

Students can download completion certificates from multiple locations:

Certificate in Player

When course reaches 100%, certificate button appears in player sidebar:
{progressPct === 100 && (
  <div className="certificate-card">
    <p className="font-semibold">Certificate</p>
    <p className="text-xs">View your course completion certificate.</p>
    <button onClick={viewCertificate} disabled={certificateLoading}>
      {certificateLoading ? 'Opening...' : 'View Certificate'}
    </button>
  </div>
)}

Certificate API

const viewCertificate = async () => {
  setCertificateLoading(true)
  try {
    GET /api/user/certificate/{courseId}
    
    Response:
    {
      success: true,
      message: "Certificate generated",
      pdfUrl: "https://cdn.example.com/certificates/abc123.pdf"
    }
    
    // Open PDF in new tab
    if (data.pdfUrl) {
      window.open(data.pdfUrl, '_blank', 'noopener,noreferrer')
    }
  } catch (error) {
    if (error.status === 404) {
      toast.info('Certificate not available yet')
    } else {
      toast.error(error.message)
    }
  } finally {
    setCertificateLoading(false)
  }
}

Responsive Design

Mobile Optimizations

Stack vertically on mobile:
className="grid grid-cols-1 sm:grid-cols-3 gap-4"

Performance Considerations

Each tab fetches its data only when activated:
useEffect(() => {
  if (activeTab === 'quizzes' && !quizFetched) {
    fetchQuizHistory()
    setQuizFetched(true)
  }
}, [activeTab])
Course progress data fetched in parallel:
const progressArray = await Promise.all(
  enrolledCourses.map(course => 
    fetchProgress(course._id)
  )
)
Expensive calculations cached:
const activeDays = useMemo(() => 
  dailyStats.filter(d => d.duration > 0).length,
  [dailyStats]
)

Authentication Guard

Dashboard requires authentication:
if (!user) {
  return (
    <div className="min-h-screen flex flex-col items-center justify-center">
      <div className="text-6xl mb-4">📊</div>
      <h2 className="text-2xl font-semibold mb-2">
        Sign in to view your dashboard
      </h2>
      <p className="text-gray-500 mb-4">
        Track time spent, monitor course progress, and see your learning stats.
      </p>
      <button onClick={() => openSignIn()}>
        Sign in
      </button>
    </div>
  )
}