Implement timeline chapters for webinar and bootcamp videos with click-to-jump functionality:
**Components:**
- VideoPlayerWithChapters: Plyr.io-based player with chapter support
- TimelineChapters: Clickable chapter markers with active state
- ChaptersEditor: Admin UI for managing video chapters
**Features:**
- YouTube videos: Clickable timestamps that jump to specific time
- Embed videos: Static timeline display (non-clickable)
- Real-time chapter tracking during playback
- Admin-defined accent color for Plyr theme
- Auto-hides timeline when no chapters configured
**Database:**
- Add chapters JSONB column to products table (webinars)
- Add chapters JSONB column to bootcamp_lessons table
- Create indexes for faster queries
**Updated Pages:**
- WebinarRecording: Two-column layout (video + timeline)
- Bootcamp: Per-lesson chapter support
- AdminProducts: Chapter editor for webinars
- CurriculumEditor: Chapter editor for lessons
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement deep linking to individual lessons with URL pattern:
- Route: /bootcamp/{product-slug}/{lessonId}
- lessonId parameter is optional for backward compatibility
- When no lessonId provided, defaults to first lesson
- Clicking lessons updates URL without page reload
- URL parameter drives lesson selection on page load
Changes:
- Update App.tsx route to accept optional :lessonId parameter
- Add lessonId extraction in Bootcamp.tsx useParams
- Implement handleSelectLesson to update URL on lesson click
- Update lesson selection logic to read from URL parameter
- Fallback to first lesson if lessonId not found
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add .trim() checks to all video source conditions
- Prevents rendering empty youtube_url as valid video
- Fixes double embed card display issue
- Update sidebar icon check to use optional chaining with trim
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Phase 1: Rich Text Editor with Code Syntax Highlighting
- Add TipTap CodeBlock extension with lowlight for syntax highlighting
- Support multiple languages (JavaScript, TypeScript, Python, Java, C++, HTML, CSS, JSON)
- Add copy-to-clipboard button on code blocks
- Add line numbers display with CSS
- Replace textarea with RichTextEditor in CurriculumEditor
- Add DOMPurify sanitization in Bootcamp display
- Add dark theme syntax highlighting styles
Phase 2: Admin Curriculum Management Page
- Create dedicated ProductCurriculum page at /admin/products/:id/curriculum
- Three-column layout: Modules (3) | Lessons (5) | Editor (4)
- Full-page UX with drag-and-drop reordering
- Add "Manage Curriculum" button for bootcamp products in AdminProducts
- Breadcrumb navigation back to products
Phase 3: Product-Level Video Source Toggle
- Add youtube_url and embed_code columns to bootcamp_lessons table
- Add video_source and video_source_config columns to products table
- Update ProductCurriculum with separate YouTube URL and Embed Code fields
- Create smart VideoPlayer component in Bootcamp.tsx
- Support YouTube ↔ Embed switching with smart fallback
- Show "Konten tidak tersedia" warning when no video configured
- Maintain backward compatibility with existing video_url field
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Bootcamp page changes:
- Add UserReview interface to store full review data
- Fetch review data with is_approved status
- Add celebratory UI when review is approved:
- Gradient background with brand accent
- "Ulasan Anda Terbit!" heading with "Disetujui" badge
- Display user's review with stars, title, body
- Publication date
- Show pending state with clock icon while waiting approval
- Update onSuccess callback to refresh review data
MemberAccess page changes:
- Change "Lanjutkan Bootcamp" to "Mulai Bootcamp" (clearer)
- Fix webinar action buttons:
- Check if event_start has passed
- Only show "Gabung Webinar" if webinar hasn't ended
- Show "Tonton Rekaman" button if recording_url exists
- Show "Rekaman segera tersedia" badge for passed webinars without recording
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Bootcamp fixes:
- Change "Sudah Selesai" to "Selesai" (shorter, cleaner)
- Replace "Selanjutnya" button with "Beri Ulasan" when all lessons completed
- Makes more sense than "Lanjutkan" when there's nothing to continue
Webinar fixes:
- Check if webinar has ended based on event_start date
- Only show "Gabung Webinar" button if webinar hasn't ended AND has meeting link
- Show "Rekaman segera tersedia" badge for passed webinars without recording
- Only show recording player/video if recording_url exists
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>