Files
meet-hub/adilo-ai-agent-quick-ref.md
dwindown 60baf32f73 Display bootcamp lesson chapters on Product Detail page as marketing content
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>
2026-01-01 23:54:32 +07:00

373 lines
9.1 KiB
Markdown

# Adilo Video Player - Quick AI Agent Reference
## For Your Windsurf/IDE AI Agent
Copy this into your `.codebase` instructions or share with AI agent:
---
## Project: LearnHub - Adilo M3U8 Video Player with Custom Chapters
### Problem Statement
Build a React video player that:
- Streams video from Adilo using M3U8 (HLS) direct URL
- Displays custom chapter navigation
- Allows click-to-jump to chapters
- Tracks user progress
- Saves completion data to Supabase
### Tech Stack
- **React 18+** (Hooks, Context)
- **HLS.js** - for M3U8 streaming
- **Supabase** - for progress tracking
- **HTML5 Video API** - native controls
- **CSS Modules** - styling
---
## Quick Command Reference
### Install Dependencies
```bash
npm install hls.js @supabase/supabase-js
```
### Project Structure to Create
```
src/
├── components/
│ ├── AdiloVideoPlayer.jsx # Main component
│ ├── ChapterNavigation.jsx # Chapter sidebar
│ └── ProgressBar.jsx # Progress indicator
├── hooks/
│ ├── useAdiloPlayer.js # HLS streaming logic
│ └── useChapterTracking.js # Chapter tracking
├── services/
│ ├── adiloService.js # Adilo API calls
│ └── progressService.js # Supabase progress
├── styles/
│ └── AdiloVideoPlayer.module.css
└── types/
└── video.types.js
```
---
## Implementation Phases (In Order)
### ⭐ PHASE 1: useAdiloPlayer Hook
**Goal**: Get HLS.js working with M3U8 URL
**What to build:**
- React hook that initializes HLS.js instance
- Return: videoRef, isReady, isPlaying, currentTime, duration
- Handle browser compatibility (Safari vs HLS.js)
- Clean up HLS instance on unmount
- Emit callbacks: onTimeUpdate, onEnded, onError
**Test with:**
```javascript
const { videoRef, currentTime, isReady } = useAdiloPlayer({
m3u8Url: "https://adilo.bigcommand.com/m3u8/...",
autoplay: false,
onTimeUpdate: (time) => console.log(time)
});
```
---
### ⭐ PHASE 2: useChapterTracking Hook
**Goal**: Determine which chapter is currently active
**What to build:**
- React hook that tracks active chapter
- Input: chapters array, currentTime
- Return: activeChapter, activeChapterId, chapterProgress
- Detect chapter transitions
- Calculate progress percentage
**Chapter data structure:**
```javascript
{
id: "ch1",
startTime: 0,
endTime: 120,
title: "Introduction",
description: "Welcome to the course"
}
```
**Test with:**
```javascript
const { activeChapter, chapterProgress } = useChapterTracking({
chapters: [...],
currentTime: 45
});
// activeChapter should be chapter with startTime ≤ 45 < endTime
```
---
### ⭐ PHASE 3: AdiloVideoPlayer Component
**Goal**: Main player combining both hooks
**What to build:**
- Component that uses both hooks
- Renders: <video> element + video controls
- Props: m3u8Url, videoId, chapters, autoplay, showChapters
- Methods: jumpToChapter(), play(), pause()
- Callbacks: onChapterChange, onVideoComplete, onProgressUpdate
**Usage example:**
```jsx
<AdiloVideoPlayer
m3u8Url="https://adilo.bigcommand.com/m3u8/..."
chapters={[{id: "1", startTime: 0, endTime: 120, title: "Intro"}]}
onVideoComplete={() => markComplete()}
onChapterChange={(ch) => console.log(ch.title)}
/>
```
---
### ⭐ PHASE 4: ChapterNavigation Component
**Goal**: Display chapters user can click to jump
**What to build:**
- Sidebar/timeline showing all chapters
- Highlight current active chapter
- Show time for each chapter
- Click handler to jump to chapter
- Progress bar for each chapter
**Props:**
- chapters: Chapter[]
- activeChapterId: string
- currentTime: number
- onChapterClick: (startTime: number) => void
- completedChapters: string[]
---
### ⭐ PHASE 5: Supabase Integration
**Goal**: Save video progress to database
**Database schema needed:**
```sql
CREATE TABLE video_progress (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL,
video_id uuid NOT NULL,
last_position int,
completed_chapters text[],
watched_percentage int,
is_completed boolean DEFAULT false,
completed_at timestamp,
created_at timestamp DEFAULT now(),
updated_at timestamp DEFAULT now(),
UNIQUE(user_id, video_id)
);
```
**Functions to implement:**
- `saveProgress(userId, videoId, currentTime, completedChapters)`
- `getLastPosition(userId, videoId)` - resume from last position
- `markVideoComplete(userId, videoId)`
- `getVideoAnalytics(userId, videoId)`
---
### ⭐ PHASE 6: Styling
**Goal**: Make it look good and responsive
**Key CSS classes needed:**
- `.adilo-player` - main container
- `.video-container` - video wrapper
- `.chapters-sidebar` - chapter list
- `.chapter-item` - individual chapter
- `.chapter-item.active` - highlight active
- `.progress-bar` - progress visualization
**Responsive breakpoints:**
- Desktop: sidebar on right
- Tablet: sidebar below video
- Mobile: horizontal timeline under video
---
## Key Implementation Details
### HLS.js Initialization Pattern
```javascript
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(m3u8Url);
hls.attachMedia(videoElement);
hls.on(Hls.Events.MANIFEST_PARSED, () => {
videoElement.play();
});
} else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
// Safari native HLS support
videoElement.src = m3u8Url;
}
```
### Chapter Jump Implementation
```javascript
const jumpToChapter = (startTime) => {
if (videoRef.current) {
videoRef.current.currentTime = startTime;
videoRef.current.play();
}
};
```
### Track Current Chapter Pattern
```javascript
const current = chapters.find(
ch => currentTime >= ch.startTime && currentTime < ch.endTime
);
setActiveChapter(current?.id);
```
### Debounce Progress Saves
```javascript
// Save progress every 5 seconds, not on every timeupdate
const saveProgressDebounced = debounce(
(userId, videoId, time) => saveProgress(userId, videoId, time),
5000
);
```
---
## Common Tasks for AI Agent
When asking your AI agent to implement:
### Task: "Create useAdiloPlayer hook"
**Should generate:**
- Import HLS from 'hls.js'
- useRef for video element
- useEffect to initialize HLS
- useCallback for event handlers
- Clean up logic in return
### Task: "Add chapter jump functionality"
**Should implement:**
- Button click handler
- Call jumpToChapter(startTime)
- Update videoRef.current.currentTime
- Play the video
### Task: "Save progress to Supabase"
**Should implement:**
- Create/update row in video_progress table
- Include: user_id, video_id, last_position, completed_chapters
- Handle conflicts (UPSERT)
- Error handling
### Task: "Make chapters responsive"
**Should implement:**
- CSS Grid for desktop (sidebar)
- Flex column for mobile
- Media query at 768px breakpoint
- Adjust spacing and font sizes
---
## Testing Checklist
### Unit Tests
- [ ] useAdiloPlayer hook returns correct refs/values
- [ ] useChapterTracking calculates active chapter correctly
- [ ] jumpToChapter updates video.currentTime
- [ ] Progress saves to Supabase
### Integration Tests
- [ ] Video plays when component mounts
- [ ] Chapter changes highlight in UI
- [ ] Clicking chapter jumps player
- [ ] Progress saves on interval
- [ ] Completion triggers callback
### Browser Tests
- [ ] Works on Chrome/Edge (HLS.js)
- [ ] Works on Firefox (HLS.js)
- [ ] Works on Safari (native HLS)
- [ ] Works on mobile browsers
### Edge Cases
- [ ] Bad M3U8 URL shows error
- [ ] Network interruption handled
- [ ] Video paused mid-chapter
- [ ] Page refresh preserves position
---
## Environment Variables Needed
```
VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_key
```
---
## Debugging Tips
### HLS.js not loading?
- Check M3U8 URL is correct from Adilo
- Verify CORS headers from Adilo
- Check browser console for HLS.js errors
- Try `hls.on(Hls.Events.ERROR, console.error)`
### Chapter not highlighting?
- Add console.log(currentTime, chapters) to track values
- Verify chapter startTime/endTime are correct
- Check activeChapter state is updating
### Progress not saving?
- Verify Supabase connection works
- Check user_id and video_id are defined
- Add error logs to saveProgress function
- Check database table schema matches
---
## Performance Optimization Tips
1. **Memoize chapters list** to prevent re-renders
```javascript
const chapters = useMemo(() => chaptersData, [chaptersData]);
```
2. **Debounce timeupdate events** (fires 60x per second!)
```javascript
const updateChapter = debounce(() => {...}, 100);
video.addEventListener('timeupdate', updateChapter);
```
3. **Lazy load chapter images/thumbnails**
```javascript
<img loading="lazy" src={chapter.thumbnail} />
```
4. **Use React.memo for ChapterNavigation**
```javascript
export default React.memo(ChapterNavigation);
```
---
## Resources
- **HLS.js Docs**: https://github.com/video-dev/hls.js/wiki
- **HTML5 Video API**: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video
- **Supabase JS Client**: https://supabase.com/docs/reference/javascript/introduction
---
**Status**: Ready to implement! 🚀
Start with PHASE 1 (useAdiloPlayer hook), then PHASE 2-6 in order.