diff --git a/src/components/CourseProgress/ChapterCard.jsx b/src/components/CourseProgress/ChapterCard.jsx
new file mode 100644
index 00000000..9ca581d1
--- /dev/null
+++ b/src/components/CourseProgress/ChapterCard.jsx
@@ -0,0 +1,80 @@
+import React, { useCallback } from 'react';
+import { useLocation } from '@docusaurus/router';
+import styles from './styles.module.css';
+
+const ChapterCard = ({
+ id,
+ title,
+ status,
+ progress,
+ timeEstimate,
+ onStart,
+ path,
+ isCurrent
+}) => {
+ const isCompleted = status === 'completed';
+ const isInProgress = status === 'in-progress';
+ const location = useLocation();
+
+ const handleAction = useCallback((e) => {
+ e.preventDefault();
+ if (!isCompleted && !isInProgress) {
+ onStart?.();
+ }
+ // If the chapter has a path, let the link handle navigation
+ // The progress update will happen via the URL change detection
+ }, [isCompleted, isInProgress, onStart]);
+
+ return (
+
+
+ Chapter {id}
+ {isCompleted ? (
+
+ ✓ Completed
+
+ ) : isInProgress ? (
+
+ In Progress
+
+ ) : null}
+
+
+
{title}
+
+
+
+
+
+ );
+};
+
+export default ChapterCard;
diff --git a/src/components/CourseProgress/ProgressBar.jsx b/src/components/CourseProgress/ProgressBar.jsx
new file mode 100644
index 00000000..156a62c2
--- /dev/null
+++ b/src/components/CourseProgress/ProgressBar.jsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import styles from './styles.module.css';
+
+const ProgressBar = ({ percentage }) => {
+ return (
+
+
+ {percentage}% Complete
+
+
+ );
+};
+
+export default ProgressBar;
diff --git a/src/components/CourseProgress/index.jsx b/src/components/CourseProgress/index.jsx
new file mode 100644
index 00000000..b4feb2cd
--- /dev/null
+++ b/src/components/CourseProgress/index.jsx
@@ -0,0 +1,112 @@
+import React, { useState, useEffect } from 'react';
+import { useLocation } from '@docusaurus/router';
+import styles from './styles.module.css';
+import ProgressBar from './ProgressBar';
+import ChapterCard from './ChapterCard';
+
+// Helper function to get/set progress from localStorage
+const getStoredProgress = (courseId) => {
+ if (typeof window === 'undefined') return {};
+ const stored = localStorage.getItem(`course-progress-${courseId}`);
+ return stored ? JSON.parse(stored) : {};
+};
+
+const saveProgress = (courseId, progress) => {
+ if (typeof window !== 'undefined') {
+ localStorage.setItem(`course-progress-${courseId}`, JSON.stringify(progress));
+ }
+};
+
+const CourseProgress = ({ courseId, chapters: initialChapters }) => {
+ const location = useLocation();
+ const [chapters, setChapters] = useState(initialChapters);
+
+ // Initialize progress from localStorage
+ useEffect(() => {
+ const storedProgress = getStoredProgress(courseId);
+
+ setChapters(prevChapters =>
+ prevChapters.map(chapter => ({
+ ...chapter,
+ status: storedProgress[chapter.id]?.status || 'not-started',
+ progress: storedProgress[chapter.id]?.progress || 0
+ }))
+ );
+ }, [courseId]);
+
+ // Update chapter status and save to localStorage
+ const updateChapterStatus = (chapterId, status, progress) => {
+ setChapters(prevChapters => {
+ const updatedChapters = prevChapters.map(chapter =>
+ chapter.id === chapterId
+ ? { ...chapter, status, progress }
+ : chapter
+ );
+
+ // Save to localStorage
+ const progressToSave = {};
+ updatedChapters.forEach(chapter => {
+ progressToSave[chapter.id] = {
+ status: chapter.status,
+ progress: chapter.progress
+ };
+ });
+
+ saveProgress(courseId, progressToSave);
+
+ return updatedChapters;
+ });
+ };
+
+ // Mark chapter as started/continued
+ const handleChapterStart = (chapterId) => {
+ updateChapterStatus(chapterId, 'in-progress', 10); // Start with 10% progress
+ // You can add navigation logic here
+ };
+
+ // Calculate overall progress
+ const totalChapters = chapters.length;
+ const completedChapters = chapters.filter(chapter => chapter.status === 'completed').length;
+ const inProgressChapters = chapters.filter(chapter => chapter.status === 'in-progress').length;
+ const overallProgress = Math.round((completedChapters / totalChapters) * 100);
+
+ // Auto-detect current chapter based on URL
+ useEffect(() => {
+ if (!chapters.length) return;
+
+ // Get the current path and find the matching chapter
+ const currentPath = location.pathname;
+ const currentChapter = chapters.find(chapter =>
+ chapter.path && currentPath.includes(chapter.path)
+ );
+
+ if (currentChapter && currentChapter.status === 'not-started') {
+ updateChapterStatus(currentChapter.id, 'in-progress', 10);
+ }
+ }, [location.pathname, chapters]);
+
+ return (
+
+
Course Progress
+
+
+
+ {chapters.map((chapter) => (
+ handleChapterStart(chapter.id)}
+ path={chapter.path}
+ isCurrent={location.pathname.includes(chapter.path || '')}
+ />
+ ))}
+
+
+ );
+};
+
+export default CourseProgress;
diff --git a/src/components/CourseProgress/styles.module.css b/src/components/CourseProgress/styles.module.css
new file mode 100644
index 00000000..64c5ec09
--- /dev/null
+++ b/src/components/CourseProgress/styles.module.css
@@ -0,0 +1,186 @@
+.courseProgress {
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 2rem 1rem;
+}
+
+/* Progress Bar Styles */
+.progressBarContainer {
+ width: 100%;
+ height: 24px;
+ background-color: #f0f0f0;
+ border-radius: 12px;
+ margin: 1rem 0 2rem;
+ overflow: hidden;
+}
+
+.progressBar {
+ height: 100%;
+ background-color: #00b4d8;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ padding-right: 10px;
+ color: white;
+ font-size: 0.8rem;
+ font-weight: 500;
+ transition: width 0.3s ease;
+}
+
+.progressText {
+ color: white;
+ font-size: 0.8rem;
+ font-weight: 500;
+}
+
+/* Chapter List Styles */
+.chapterList {
+ display: grid;
+ gap: 1.5rem;
+ margin-top: 2rem;
+}
+
+/* Chapter Card Styles */
+.chapterCard {
+ position: relative;
+ background: white;
+ border-radius: 8px;
+ padding: 1.5rem;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.chapterCard:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.chapterCard.currentChapter {
+ border-left: 4px solid #00b4d8;
+ background-color: #f8fafc;
+}
+
+.chapterCard.currentChapter .chapterTitle {
+ color: #00b4d8;
+}
+
+.chapterHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 0.75rem;
+}
+
+.chapterNumber {
+ font-size: 0.9rem;
+ color: #666;
+ font-weight: 500;
+}
+
+.statusBadge {
+ background-color: #e6f7ee;
+ color: #10b981;
+ padding: 0.25rem 0.75rem;
+ border-radius: 20px;
+ font-size: 0.8rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-weight: 500;
+}
+
+.inProgressBadge {
+ background-color: #e0f2fe;
+ color: #0ea5e9;
+}
+
+.checkmark {
+ font-weight: bold;
+}
+
+.chapterTitle {
+ margin: 0.5rem 0 1.5rem;
+ color: #1a1a1a;
+ font-size: 1.25rem;
+}
+
+.progressContainer {
+ width: 100%;
+ height: 6px;
+ background-color: #f0f0f0;
+ border-radius: 3px;
+ margin: 1rem 0;
+ overflow: hidden;
+}
+
+.chapterProgress {
+ height: 100%;
+ background-color: #00b4d8;
+ transition: width 0.3s ease;
+}
+
+.chapterFooter {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 1rem;
+}
+
+.timeEstimate {
+ font-size: 0.9rem;
+ color: #666;
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+}
+
+.actionButton {
+ background-color: #00b4d8;
+ color: white;
+ border: none;
+ padding: 0.5rem 1.25rem;
+ border-radius: 4px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ text-decoration: none;
+ display: inline-block;
+ text-align: center;
+ font-size: 0.9rem;
+}
+
+.actionButton:disabled {
+ opacity: 0.7;
+ cursor: not-allowed;
+}
+
+.actionButton:not(:disabled):hover {
+ background-color: #0096c7;
+ transform: translateY(-1px);
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.completedButton {
+ background-color: #e6f7ee;
+ color: #10b981;
+}
+
+.completedButton:hover {
+ background-color: #d1fae5;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .chapterCard {
+ padding: 1.25rem;
+ }
+
+ .chapterTitle {
+ font-size: 1.1rem;
+ }
+
+ .actionButton {
+ padding: 0.4rem 1rem;
+ font-size: 0.9rem;
+ }
+}
diff --git a/src/components/index.js b/src/components/index.js
index acc76219..0318bb4c 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -1,3 +1,5 @@
+// Re-export components for easier imports
+export { default as CourseProgress } from './CourseProgress';
import clsx from 'clsx';
import Heading from '@theme/Heading';
import styles from './styles.module.css';
diff --git a/src/utils/progress.js b/src/utils/progress.js
new file mode 100644
index 00000000..6a92c282
--- /dev/null
+++ b/src/utils/progress.js
@@ -0,0 +1,89 @@
+// Helper function to get progress from localStorage
+export const getStoredProgress = (courseId) => {
+ if (typeof window === 'undefined') return {};
+ try {
+ const stored = localStorage.getItem(`course-progress-${courseId}`);
+ return stored ? JSON.parse(stored) : {};
+ } catch (error) {
+ console.error('Error reading progress from localStorage:', error);
+ return {};
+ }
+};
+
+// Helper function to save progress to localStorage
+export const saveProgress = (courseId, progress) => {
+ if (typeof window !== 'undefined') {
+ try {
+ localStorage.setItem(`course-progress-${courseId}`, JSON.stringify(progress));
+ } catch (error) {
+ console.error('Error saving progress to localStorage:', error);
+ }
+ }
+};
+
+// Calculate overall course progress
+export const calculateCourseProgress = (chapters) => {
+ if (!chapters?.length) return 0;
+
+ const totalChapters = chapters.length;
+ const completedChapters = chapters.filter(chapter => chapter.status === 'completed').length;
+
+ return Math.round((completedChapters / totalChapters) * 100);
+};
+
+// Mark a chapter as completed
+export const markChapterComplete = (courseId, chapterId, chapters) => {
+ const updatedChapters = chapters.map(chapter => {
+ if (chapter.id === chapterId) {
+ return {
+ ...chapter,
+ status: 'completed',
+ progress: 100,
+ completedAt: new Date().toISOString()
+ };
+ }
+ return chapter;
+ });
+
+ // Save to localStorage
+ const progressToSave = {};
+ updatedChapters.forEach(chapter => {
+ progressToSave[chapter.id] = {
+ status: chapter.status,
+ progress: chapter.progress,
+ completedAt: chapter.completedAt
+ };
+ });
+
+ saveProgress(courseId, progressToSave);
+ return updatedChapters;
+};
+
+// Get the next chapter to continue
+// Helper function to get the next chapter to continue
+export const getNextChapter = (currentPath, chapters) => {
+ if (!chapters?.length) return null;
+
+ // Find the current chapter index
+ const currentIndex = chapters.findIndex(chapter =>
+ chapter.path && currentPath.includes(chapter.path)
+ );
+
+ // If current chapter not found or it's the last one, return null
+ if (currentIndex === -1 || currentIndex === chapters.length - 1) {
+ return null;
+ }
+
+ // Return the next chapter
+ return chapters[currentIndex + 1];
+};
+
+// Get chapter by ID
+export const getChapterById = (chapters, chapterId) => {
+ return chapters.find(chapter => chapter.id === chapterId);
+};
+
+// Get chapter by path
+export const getChapterByPath = (chapters, path) => {
+ return chapters.find(chapter => chapter.path && path.includes(chapter.path));
+};