JSPM

@mailmarc/react-course-viewer

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 11
  • Score
    100M100P100Q28102F
  • License MIT

A powerful React component for creating interactive learning courses with markdown support

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@mailmarc/react-course-viewer) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    @mailmarc/react-course-viewer

    Test NPM Version NPM Downloads License TypeScript React

    A powerful React component library for creating interactive learning courses with markdown content. Build engaging educational experiences with section-based navigation, progress tracking, achievements, and celebration effects.

    Features

    Section-based Navigation: Proper course structure with sections and lessons
    Dynamic Content Loading: Load markdown files from docs folders
    Progress Tracking: Persistent progress with localStorage
    Achievement System: Built-in achievements for course milestones
    Keyboard Navigation: Arrow keys, Space, Enter support
    Touch Gestures: Swipe navigation on mobile devices
    Theme Support: Light/dark/system themes
    Responsive Design: Works on desktop and mobile
    Accessibility: WCAG compliant with focus management
    TypeScript: Full type safety and IntelliSense

    Installation

    npm install @mailmarc/react-course-viewer
    # or
    yarn add @mailmarc/react-course-viewer
    # or
    pnpm add @mailmarc/react-course-viewer

    Quick Start

    1. Basic Usage

    import { CourseViewer, CourseLoader } from '@mailmarc/react-course-viewer';
    
    function MyTrainingPage() {
      const [course, setCourse] = useState(null);
    
      useEffect(() => {
        const loadCourse = async () => {
          const loader = new CourseLoader();
          const course = await loader.loadCourse(COURSE_CONFIGS.dmarc);
          setCourse(course);
        };
        loadCourse();
      }, []);
    
      if (!course) return <div>Loading...</div>;
    
      return (
        <CourseViewer
          course={course}
          onComplete={(cert) => handleCourseComplete(cert)}
          theme="light"
          autoAdvance={true}
        />
      );
    }

    2. With Loading States

    import { DMARCCourseExample } from '../components/ui/CourseViewer';
    
    function LearningPage() {
      return <DMARCCourseExample />;
    }

    3. Custom Course

    const customCourse = {
      id: 'security-basics',
      title: 'Security Basics',
      description: 'Learn fundamental security concepts',
      version: '1.0.0',
      sections: [
        {
          id: 'intro',
          title: 'Introduction',
          slug: 'intro',
          order: 1,
          lessons: [
            {
              id: 'what-is-security',
              title: 'What is Security?',
              slug: 'what-is-security',
              content: '# What is Security?\n\nSecurity protects your data...',
              duration: 5,
              order: 1,
            },
          ],
        },
      ],
    };
    
    <CourseViewer course={customCourse} />

    CourseViewer Props

    Prop Type Default Description
    course Course required Course data structure
    onComplete (cert: Certificate) => void - Called when course is completed
    onProgress (progress: CourseProgress) => void - Called when progress updates
    theme 'light' | 'dark' | 'system' 'light' UI theme
    autoAdvance boolean true Auto-advance after lesson completion
    autoAdvanceDelay number 2 Seconds to wait before auto-advance
    enableKeyboardNavigation boolean true Enable keyboard shortcuts
    enableSwipeGestures boolean true Enable touch gestures
    enableSoundEffects boolean false Play sounds for achievements
    className string '' Additional CSS classes

    Course Data Structure

    Course Type

    interface Course {
      id: string;                    // Unique course identifier
      title: string;                 // Course display title
      description: string;           // Course description
      version: string;               // Course version
      sections: Section[];           // Course sections
      metadata?: {
        author: string;
        difficulty: 'beginner' | 'intermediate' | 'advanced';
        estimatedHours: number;
      };
    }

    Section Type

    interface Section {
      id: string;                    // Unique section identifier
      title: string;                 // Section display title
      slug: string;                  // URL-friendly section name
      description?: string;          // Optional section description
      lessons: Lesson[];             // Lessons in this section
      order: number;                 // Display order
    }

    Lesson Type

    interface Lesson {
      id: string;                    // Unique lesson identifier
      title: string;                 // Lesson display title
      slug: string;                  // URL-friendly lesson name
      content: string;               // Markdown content
      duration?: number;             // Estimated reading time (minutes)
      tags?: string[];               // Topic tags
      order: number;                 // Display order within section
      canMarkComplete?: boolean;     // Whether lesson can be marked complete
    }

    CourseLoader

    The CourseLoader class handles loading markdown files from the filesystem and converting them into course data.

    Configuration

    const courseConfig = {
      id: 'my-course',
      title: 'My Course',
      description: 'Learn amazing things',
      version: '1.0.0',
      docsPath: '/docs/learning-centre',  // Path to markdown files
      sectionsConfig: [
        {
          id: '01-introduction',
          title: 'Introduction',
          order: 1,
          lessons: [
            {
              id: '01-intro-basics',
              title: 'Basics',
              fileName: 'basics.md',           // Actual markdown file name
              duration: 5,
              order: 1,
            },
          ],
        },
      ],
    };
    
    const loader = new CourseLoader();
    const course = await loader.loadCourse(courseConfig);

    Built-in Configurations

    import { COURSE_CONFIGS } from '../components/ui/CourseViewer';
    
    // DMARC course with proper docs structure
    const dmarcCourse = await loader.loadCourse(COURSE_CONFIGS.dmarc);

    Progress Tracking

    Progress is automatically saved to localStorage and includes:

    • Current section and lesson position
    • Completed lessons list
    • Time spent per lesson
    • Earned achievements
    • Course start and last access times
    // Access progress data
    const progress = {
      courseId: 'dmarc-expert-course',
      currentSectionIndex: 2,
      currentLessonIndex: 1,
      completedLessons: ['lesson-1', 'lesson-2', 'lesson-3'],
      achievements: [{ id: 'first-step', name: 'First Step!', ... }],
      timeSpent: { 'lesson-1': 5, 'lesson-2': 3 },
      startedAt: new Date(),
      lastAccessed: new Date(),
    };

    Keyboard Shortcuts

    • or Space: Next lesson (if current lesson is complete)
    • : Previous lesson
    • Enter: Mark current lesson as complete
    • Esc: Menu (future feature)

    Touch Gestures

    • Swipe Left: Next lesson (if current lesson is complete)
    • Swipe Right: Previous lesson

    Section-Based Navigation

    Unlike the old flattened approach, the new CourseViewer shows:

    Section 2: SPF (Safe Postman Friends) - Lesson 3 of 4

    Instead of:

    Lesson 7 of 15  ❌ (old flattened approach)

    Achievements

    Built-in achievements are unlocked automatically:

    • 👶 First Step: Complete your first lesson
    • 📬 Quarter Master: 25% course completion
    • 🦸 Halfway Hero: 50% course completion
    • 🚀 Almost There: 75% course completion
    • 🎓 Course Expert: 100% course completion

    Theming

    The component supports three themes:

    <CourseViewer theme="light" />   // Light mode
    <CourseViewer theme="dark" />    // Dark mode  
    <CourseViewer theme="system" />  // Follow system preference

    CSS custom properties can be overridden:

    .course-viewer[data-theme="custom"] {
      --course-primary: #your-color;
      --course-background: #your-bg;
      /* etc. */
    }

    File Structure

    frontend/src/components/ui/CourseViewer/
    ├── index.tsx              # Main CourseViewer component
    ├── types.ts               # TypeScript types
    ├── CourseLoader.ts        # Dynamic content loading
    ├── useProgress.ts         # Progress tracking hook
    ├── CourseViewer.css       # Component styles
    ├── examples.tsx           # Usage examples
    └── README.md              # This documentation

    Markdown Content Setup

    1. Copy docs to public directory:

      cd frontend/public && cp -r ../../docs/learning-centre docs/
    2. Or use the provided script:

      cd frontend/public/docs && ./copy_docs.sh
    3. Docs structure should match config:

      public/docs/learning-centre/
      ├── 01-introduction/
      │   ├── what-is-dmarc.md
      │   ├── why-email-authentication-matters.md
      │   └── dmarc-vs-spf-vs-dkim.md
      ├── 02-spf/
      │   └── spf-basics.md
      └── ...

    Migration from Old CourseViewer

    The new CourseViewer fixes several issues from the old implementation:

    ✅ Fixed Issues

    1. Flattened Lessons: Now shows proper section-based progress
    2. Static Content: Now loads actual markdown files dynamically
    3. Hard-coded Course: Now supports any course configuration
    4. Poor Progress Display: Now shows "Section X - Lesson Y of Z"

    🔄 Migration Steps

    1. Replace import:

      // Old
      import CourseViewer from '../components/CourseViewer';
      
      // New  
      import { CourseViewer } from '../components/ui/CourseViewer';
    2. Update usage:

      // Old (static course)
      <CourseViewer course={LOADED_DMARC_COURSE} />
      
      // New (dynamic loading)
      const [course, setCourse] = useState(null);
      useEffect(() => {
        const loadCourse = async () => {
          const loader = new CourseLoader();
          const course = await loader.loadCourse(COURSE_CONFIGS.dmarc);
          setCourse(course);
        };
        loadCourse();
      }, []);
    3. Add loading states:

      if (!course) return <div>Loading course...</div>;

    Browser Compatibility

    • ✅ Chrome 90+
    • ✅ Firefox 88+
    • ✅ Safari 14+
    • ✅ Edge 90+
    • ✅ Mobile browsers

    Accessibility Features

    • ✅ Keyboard navigation
    • ✅ Focus management
    • ✅ ARIA labels
    • ✅ Screen reader support
    • ✅ High contrast themes
    • ✅ Reduced motion support

    Performance

    • Bundle size: ~15KB gzipped (component + dependencies)
    • Rendering: Optimized with React.memo and useMemo
    • Content loading: Lazy loading of markdown files
    • Progress tracking: Debounced localStorage updates

    Contributing

    To add a new course:

    1. Create markdown files in docs/ folder structure
    2. Add course configuration to COURSE_CONFIGS
    3. Copy docs to public/docs/ directory
    4. Use CourseLoader to load the course

    For custom features, extend the Course, Section, or Lesson interfaces in types.ts.