This commit implements displaying lesson chapters/timeline as marketing content on the Product Detail page for bootcamp products, helping potential buyers understand the detailed breakdown of what they'll learn. ## Changes ### Product Detail Page (src/pages/ProductDetail.tsx) - Updated Lesson interface to include optional chapters property - Modified fetchCurriculum to fetch chapters along with lessons - Enhanced renderCurriculumPreview to display chapters as nested content under lessons - Chapters shown with timestamps and titles, clickable to navigate to bootcamp access page - Visual hierarchy: Module → Lesson → Chapters with proper indentation and styling ### Review System Fixes - Fixed review prompt re-appearing after submission (before admin approval) - Added hasSubmittedReview check to prevent showing prompt when review exists - Fixed edit review functionality to pre-populate form with existing data - ReviewModal now handles both INSERT (new) and UPDATE (edit) operations - Edit resets is_approved to false requiring re-approval ### Video Player Enhancements - Implemented Adilo/Video.js integration for M3U8/HLS playback - Added video progress tracking with refs pattern for reliability - Implemented chapter navigation for both Adilo and YouTube players - Added keyboard shortcuts (Space, Arrows, F, M, J, L) - Resume prompt for returning users with saved progress ### Database Migrations - Added Adilo video support fields (m3u8_url, mp4_url, video_host) - Created video_progress table for tracking user watch progress - Fixed consulting slots user_id foreign key - Added chapters support to products and bootcamp_lessons tables ### Documentation - Added Adilo implementation plan and quick reference docs - Cleaned up transcript analysis files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
12 KiB
Adilo Custom Video Player with Chapter System - Implementation Plan
Project Overview
Build a React video player component that uses Adilo's M3U8 streaming URL with custom chapter navigation system for LearnHub LMS.
Architecture
Components Structure
VideoLesson/
├── AdiloVideoPlayer.jsx (Main player component)
├── ChapterNavigation.jsx (Chapter sidebar/timeline)
├── VideoControls.jsx (Custom controls - optional)
└── hooks/
├── useAdiloPlayer.js (HLS player logic)
└── useChapterTracking.js (Chapter progress tracking)
Data Flow
Adilo M3U8 URL
↓
HLS.js (streaming)
↓
HTML5 <video> element
↓
currentTime tracking
↓
Chapter UI sync
↓
Supabase (save progress)
Step-by-Step Implementation
PHASE 1: Dependencies Setup
Install required packages:
npm install hls.js
npm install @supabase/supabase-js # For progress tracking
No additional UI library needed - use native HTML5 video + your own CSS for chapters.
PHASE 2: Core Hook - useAdiloPlayer
File: hooks/useAdiloPlayer.js
Purpose: Handle HLS streaming with HLS.js library
Responsibilities:
- Initialize HLS instance
- Load M3U8 URL
- Handle browser compatibility (Safari native HLS vs HLS.js)
- Expose video element ref for external control
- Emit events (play, pause, ended, timeupdate)
Function signature:
const {
videoRef,
isReady,
isPlaying,
currentTime,
duration,
error
} = useAdiloPlayer({
m3u8Url: string,
autoplay: boolean,
onTimeUpdate: (time: number) => void,
onEnded: () => void,
onError: (error) => void
})
Key features:
- Auto-dispose HLS instance on unmount
- Handle loading states
- Error boundary for failed streams
- Track play/pause states
PHASE 3: Core Hook - useChapterTracking
File: hooks/useChapterTracking.js
Purpose: Track which chapter user is currently viewing
Responsibilities:
- Determine active chapter from currentTime
- Calculate chapter progress percentage
- Detect chapter transitions
- Export chapter completion data
Function signature:
const {
activeChapter,
activeChapterId,
chapterProgress, // 0-100%
completedChapters,
chapterTimeline // for progress bar
} = useChapterTracking({
chapters: Chapter[],
currentTime: number,
onChapterChange: (chapter) => void
})
Chapter object structure:
{
id: string,
startTime: number, // in seconds
endTime: number, // in seconds
title: string,
description?: string, // optional
thumbnail?: string // optional
}
PHASE 4: Main Component - AdiloVideoPlayer
File: components/AdiloVideoPlayer.jsx
Purpose: Main video player component that combines HLS streaming + chapter tracking
Props:
{
m3u8Url: string, // From Adilo dashboard
videoId: string, // For database tracking
chapters: Chapter[], // Your chapter data
autoplay: boolean, // Default: false
showChapters: boolean, // Default: true
onVideoComplete: (data) => void, // Callback when video ends
onChapterChange: (chapter) => void,
onProgressUpdate: (progress) => void
}
Component structure:
<div className="adilo-player">
{/* Video container */}
<div className="video-container">
<video
ref={videoRef}
controls
controlsList="nodownload"
/>
{/* Loading indicator */}
{!isReady && <LoadingSpinner />}
</div>
{/* Chapter Navigation */}
{showChapters && (
<ChapterNavigation
chapters={chapters}
activeChapterId={activeChapterId}
currentTime={currentTime}
onChapterClick={jumpToChapter}
completedChapters={completedChapters}
/>
)}
{/* Progress bar (optional) */}
<ProgressBar
chapters={chapters}
currentTime={currentTime}
/>
</div>
Key methods:
jumpToChapter(startTime)- Seek to chapterplay()/pause()- Control playbackgetCurrentProgress()- Get session progress
PHASE 5: Chapter Navigation Component
File: components/ChapterNavigation.jsx
Purpose: Display chapters as sidebar/timeline with click-to-jump
Layout options:
- Sidebar - Vertical list on side (desktop)
- Horizontal - Timeline below video (mobile)
- Collapsible - Toggle on mobile
Features:
- Show current/upcoming chapters
- Highlight active chapter
- Show time remaining for current chapter
- Progress indicators
- Drag-to-seek on timeline (optional)
Chapter item structure:
<div className="chapter-item">
<div className="chapter-time">{formatTime(startTime)}</div>
<div className="chapter-title">{title}</div>
<div className="chapter-progress">{progressBar}</div>
<button onClick={() => jumpToChapter(startTime)}>
Jump to Chapter
</button>
</div>
PHASE 6: Supabase Integration (Optional)
File: services/progressService.js
Purpose: Save video progress to database
Database table structure:
CREATE TABLE video_progress (
id uuid PRIMARY KEY,
user_id uuid NOT NULL,
video_id uuid NOT NULL,
last_position int, -- seconds
completed_chapters text[], -- array of chapter IDs
watched_percentage int, -- 0-100
is_completed boolean,
completed_at timestamp,
created_at timestamp,
updated_at timestamp,
UNIQUE(user_id, video_id)
)
Functions to implement:
// Save current progress
saveProgress(userId, videoId, currentTime, completedChapters)
// Resume from last position
getLastPosition(userId, videoId)
// Mark video as complete
markVideoComplete(userId, videoId)
// Get completion analytics
getVideoAnalytics(userId, videoId)
PHASE 7: Styling
File: styles/AdiloVideoPlayer.module.css or your preferred CSS approach
Key styles needed:
/* Video container */
.video-container {
position: relative;
width: 100%;
aspect-ratio: 16/9;
background: #000;
}
video {
width: 100%;
height: 100%;
}
/* Chapter sidebar */
.chapters-sidebar {
background: #f5f5f5;
padding: 16px;
max-height: 400px;
overflow-y: auto;
}
.chapter-item {
padding: 12px;
border-radius: 8px;
cursor: pointer;
transition: background 0.2s;
}
.chapter-item.active {
background: #e0f2fe;
border-left: 4px solid #0ea5e9;
}
.chapter-time {
font-weight: 600;
color: #333;
}
.chapter-title {
font-size: 14px;
color: #666;
margin-top: 4px;
}
/* Progress bar */
.progress-bar {
display: flex;
gap: 2px;
height: 4px;
background: #e5e5e5;
border-radius: 2px;
}
.progress-segment {
background: #0ea5e9;
flex: 1;
border-radius: 1px;
}
.progress-segment.completed {
background: #10b981;
}
/* Responsive */
@media (max-width: 768px) {
.chapters-sidebar {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
}
Implementation Checklist
Setup Phase
- Install dependencies (hls.js, @supabase/supabase-js)
- Set up folder structure
- Configure Supabase client (if using progress tracking)
Core Development
- Implement
useAdiloPlayerhook - Implement
useChapterTrackinghook - Create
AdiloVideoPlayercomponent - Create
ChapterNavigationcomponent - Add styling/CSS
Integration
- Implement Supabase progress service
- Add error handling & loading states
- Add accessibility features (ARIA labels, keyboard navigation)
- Test HLS streaming on different browsers
Testing
- Test video playback (Chrome, Firefox, Safari, mobile)
- Test chapter navigation
- Test progress saving to Supabase
- Test responsive design
- Test error scenarios (bad M3U8 URL, network issues)
Optional Enhancements
- Add playback speed control
- Add quality selector (if HLS variants available)
- Add full-screen mode
- Add picture-in-picture
- Add watch history
- Add completion badges
Code Example Template
Main Usage in LearnHub
import AdiloVideoPlayer from '@/components/AdiloVideoPlayer';
function LessonPage({ lessonId }) {
const [lesson, setLesson] = useState(null);
const [userProgress, setUserProgress] = useState(null);
const user = useAuth().user;
useEffect(() => {
// Fetch lesson with chapters
fetchLessonData(lessonId).then(setLesson);
// Get user's last progress
getLastPosition(user.id, lessonId).then(setUserProgress);
}, [lessonId]);
const handleChapterChange = (chapter) => {
console.log(`Chapter changed: ${chapter.title}`);
};
const handleVideoComplete = (data) => {
// Mark as complete in Supabase
markVideoComplete(user.id, lessonId);
// Show completion message
toast.success('Lesson completed! 🎉');
};
const handleProgressUpdate = (progress) => {
// Save progress every 10 seconds
if (progress.currentTime % 10 === 0) {
saveProgress(user.id, lessonId, progress.currentTime, progress.completedChapters);
}
};
if (!lesson) return <LoadingPage />;
return (
<div className="lesson-container">
<h1>{lesson.title}</h1>
<AdiloVideoPlayer
m3u8Url={lesson.m3u8Url}
videoId={lesson.id}
chapters={lesson.chapters}
autoplay={false}
showChapters={true}
onChapterChange={handleChapterChange}
onVideoComplete={handleVideoComplete}
onProgressUpdate={handleProgressUpdate}
/>
<div className="lesson-content">
<h2>Lesson Details</h2>
<p>{lesson.description}</p>
</div>
</div>
);
}
export default LessonPage;
Important Notes
Video URL Storage
Store the M3U8 URL in your Supabase videos or lessons table:
CREATE TABLE lessons (
id uuid PRIMARY KEY,
title text,
description text,
m3u8_url text, -- Store the Adilo M3U8 URL here
chapters jsonb, -- Store chapters as JSON array
created_at timestamp
)
Security Considerations
- ✅ Don't expose M3U8 URL in frontend code - fetch from backend
- ✅ Validate M3U8 URLs - only allow Adilo domains
- ✅ Use CORS headers - ensure Adilo allows cross-origin requests
- ✅ Log access - track who watches which videos
Browser Compatibility
- ✅ Chrome/Edge: HLS.js library
- ✅ Firefox: HLS.js library
- ✅ Safari: Native HLS support (no library needed)
- ✅ Mobile browsers: Auto-detects capability
Performance Tips
- 🚀 Lazy load chapter data
- 🚀 Debounce progress updates
- 🚀 Memoize chapter calculations
- 🚀 Use video preload="metadata"
Common Pitfalls to Avoid
-
❌ Don't forget to dispose HLS instance - Memory leak
- ✅ Do: Clean up in useEffect return
-
❌ Don't update state on every timeupdate - Performance issue
- ✅ Do: Debounce or throttle updates
-
❌ Don't hardcode M3U8 URLs in component - Security issue
- ✅ Do: Fetch from backend API
-
❌ Don't assume HLS.js works everywhere - Safari native support exists
- ✅ Do: Check
Hls.isSupported()and fallback
- ✅ Do: Check
-
❌ Don't forget CORS headers - Cross-origin requests fail
- ✅ Do: Verify Adilo allows your domain
Testing Commands
# Install dev dependencies
npm install --save-dev @testing-library/react @testing-library/jest-dom
# Run tests
npm test
# Build for production
npm run build
# Check bundle size
npm run analyze
Next Steps
- Get M3U8 URL from Adilo dashboard ✅ (You found it!)
- Store URL in your Supabase lessons table
- Create the hooks (start with
useAdiloPlayer) - Build the component (AdiloVideoPlayer)
- Integrate chapters (ChapterNavigation)
- Add progress tracking (Supabase integration)
- Style and polish (CSS/responsive design)
- Test thoroughly (all browsers, mobile, edge cases)
Files to Create
src/
├── components/
│ ├── AdiloVideoPlayer.jsx
│ ├── ChapterNavigation.jsx
│ └── ProgressBar.jsx
├── hooks/
│ ├── useAdiloPlayer.js
│ └── useChapterTracking.js
├── services/
│ ├── progressService.js
│ └── adiloService.js
├── styles/
│ └── AdiloVideoPlayer.module.css
└── types/
└── video.types.js
Ready to implement? Start with Phase 1 & 2 (setup + useAdiloPlayer hook). Let me know if you need help with any specific phase!